Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
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 })
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user