Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-23 21:32:21 +00:00
co-authored by renee-png
parent add1b5837f
commit a0ac547c5d
@@ -0,0 +1,105 @@
import { createClient } from '@supabase/supabase-js'
import { createFileRoute } from '@tanstack/react-router'
// Cron-callable endpoint that finds due task reminders and triggers the
// transactional email send route for each. Authenticated by service-role key
// passed as Bearer token (configured in pg_cron job).
export const Route = createFileRoute('/api/public/process-task-reminders')({
server: {
handlers: {
POST: async ({ request }) => {
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
if (!supabaseUrl || !serviceKey) {
return Response.json({ error: 'config' }, { status: 500 })
}
const auth = request.headers.get('Authorization') ?? ''
const token = auth.startsWith('Bearer ') ? auth.slice(7).trim() : ''
if (token !== serviceKey) {
return Response.json({ error: 'unauthorized' }, { status: 401 })
}
const supabase = createClient<any, any, any>(supabaseUrl, serviceKey)
// Fetch due reminders (limit batch to avoid timeout).
const { data: reminders, error } = await supabase
.from('task_reminders')
.select(
'id, task_id, recipient_email, remind_at, note, ' +
'task:tasks(id, title, due_date, case_id, case:cases(case_number, title))'
)
.is('sent_at', null)
.lte('remind_at', new Date().toISOString())
.order('remind_at', { ascending: true })
.limit(50)
if (error) {
console.error('Failed to load task reminders', error)
return Response.json({ error: 'load_failed' }, { status: 500 })
}
if (!reminders?.length) {
return Response.json({ processed: 0 })
}
const url = new URL(request.url)
const sendUrl = `${url.origin}/lovable/email/transactional/send`
let processed = 0
for (const r of reminders as any[]) {
const task = r.task
const caseInfo = task?.case
const caseTitle = caseInfo
? `${caseInfo.case_number} — ${caseInfo.title}`
: null
const dueDate = task?.due_date
? new Date(task.due_date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
: null
try {
const res = await fetch(sendUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${serviceKey}`,
},
body: JSON.stringify({
templateName: 'task-reminder',
recipientEmail: r.recipient_email,
idempotencyKey: `task-reminder-${r.id}`,
templateData: {
taskTitle: task?.title ?? 'Task',
caseTitle,
dueDate,
note: r.note,
},
}),
})
if (!res.ok) {
const body = await res.text()
console.error('Reminder send failed', { id: r.id, status: res.status, body })
continue
}
await supabase
.from('task_reminders')
.update({ sent_at: new Date().toISOString() })
.eq('id', r.id)
processed++
} catch (e) {
console.error('Reminder dispatch error', { id: r.id, error: e })
}
}
return Response.json({ processed })
},
},
},
})