#!/usr/bin/env node
import { Pool } from 'pg';
const connectionString = process.env.SUPABASE_DB_URL || process.env.DATABASE_URL;
const resendApiKey = process.env.RESEND_API_KEY;
const fromEmail = process.env.REMINDER_FROM_EMAIL;
const appUrl = process.env.REMINDER_APP_URL || 'http://localhost:5173';
const dryRun = process.env.REMINDER_DRY_RUN === 'true';
if (!connectionString) {
console.error('Missing SUPABASE_DB_URL (or DATABASE_URL).');
process.exit(1);
}
if (!dryRun && (!resendApiKey || !fromEmail)) {
console.error('Missing RESEND_API_KEY or REMINDER_FROM_EMAIL.');
process.exit(1);
}
const pool = new Pool({ connectionString });
const findPendingReminders = async (client) => {
const { rows } = await client.query(`
select
a.id,
a.name,
a.pat_type,
a.datum,
a.created_at,
u.email
from public.assessments a
join auth.users u on u.id = a.user_id
where a.is_finalized = false
and a.created_at <= now() - interval '3 days'
and a.finalization_reminder_sent_at is null
and coalesce(u.email, '') <> ''
order by a.created_at asc
`);
return rows;
};
const buildEmailPayload = (row) => {
const subject = `PAT Test noch abschließen: ${row.name || row.pat_type}`;
const html = `
PAT Test noch abschließen
Dein offener Test wurde seit mehr als 3 Tagen nicht final abgeschlossen.
- Name: ${row.name || '—'}
- PAT Typ: ${row.pat_type || '—'}
- Datum: ${row.datum || '—'}
Bitte öffne PAT Stats und schließe den Test final ab.
PAT Stats öffnen
`;
return {
from: fromEmail,
to: [row.email],
subject,
html
};
};
const sendEmail = async (payload) => {
const response = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${resendApiKey}`
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const message = await response.text();
throw new Error(`Resend request failed: ${response.status} ${message}`);
}
};
const markReminderSent = async (client, assessmentId) => {
await client.query(
`
update public.assessments
set finalization_reminder_sent_at = now()
where id = $1
and is_finalized = false
and finalization_reminder_sent_at is null
`,
[assessmentId]
);
};
const run = async () => {
const client = await pool.connect();
try {
const rows = await findPendingReminders(client);
if (!rows.length) {
console.log('No pending finalization reminders.');
return;
}
for (const row of rows) {
if (dryRun) {
console.log(`[dry-run] Would remind ${row.email} for assessment ${row.id}`);
continue;
}
await sendEmail(buildEmailPayload(row));
await markReminderSent(client, row.id);
console.log(`Reminder sent for assessment ${row.id} to ${row.email}`);
}
} finally {
client.release();
await pool.end();
}
};
run().catch((error) => {
console.error('Failed to send finalization reminders:', error.message);
process.exit(1);
});