import { prisma } from "@/lib/prisma"; /** * Wrapper untuk cron route handler — otomatis log start/finish/error ke * `CronRun`. Idempotent terhadap kegagalan: kalau row gagal dibuat (DB down), * fn tetap jalan dan kegagalannya hanya hilang log. * * Pemakaian: * ```ts * return runCron("auto-complete-trips", async () => { * const result = await tripService.autoCompletePastTrips(); * return { completed: result.count, ids: result.ids }; * }); * ``` * * Caller bertanggung jawab untuk mengembalikan NextResponse — `runCron` * cuma menjalankan fn dan log; return value fn dipassthrough sebagai `payload`. */ export async function runCron( jobName: string, fn: () => Promise ): Promise<{ ok: true; payload: T } | { ok: false; error: string }> { let runId: string | null = null; try { const row = await prisma.cronRun.create({ data: { jobName, status: "RUNNING" }, select: { id: true }, }); runId = row.id; } catch (err) { console.error(`[cron-runner] gagal create row ${jobName}`, err); // Lanjut tanpa log — jangan blok cron karena DB log gagal. } try { const payload = await fn(); if (runId) { await prisma.cronRun .update({ where: { id: runId }, data: { status: "SUCCESS", finishedAt: new Date(), payload: payload as unknown as object, }, }) .catch((e) => console.error(`[cron-runner] gagal update SUCCESS`, e)); } return { ok: true, payload }; } catch (err) { const message = err instanceof Error ? err.message : "Unknown cron failure"; if (runId) { await prisma.cronRun .update({ where: { id: runId }, data: { status: "FAILED", finishedAt: new Date(), errorMessage: message, }, }) .catch((e) => console.error(`[cron-runner] gagal update FAILED`, e)); } return { ok: false, error: message }; } }