/** * Background jobs. * * node-cron inside the application process, rather than an external cron * hitting an HTTP endpoint. The choice is deliberate and worth recording: this * runs as a single container for a single shop, so there is no second instance * to coordinate with, and an endpoint that triggers work is an endpoint that * has to be protected, rate-limited and documented. The trade-off is that the * jobs die with the process — acceptable for work that is idempotent and runs * nightly, and the UI shows the last successful sync either way. */ export async function register(): Promise { // Runs in the edge runtime and during the build too; neither wants a cron. if (process.env.NEXT_RUNTIME !== 'nodejs') { return; } const [{ default: cron }, { syncHolidays }, { prisma }, { loadSettings }] = await Promise.all([ import('node-cron'), import('@/lib/holidays/sync'), import('@/lib/db'), import('@/lib/schedule/context'), ]); const settings = await loadSettings().catch(() => null); const timezone = settings?.timezone ?? 'Europe/Zurich'; // 03:00 local: the rolling twelve-month holiday window. cron.schedule( '0 3 * * *', () => { void syncHolidays() .then((result) => { if (result.ok) { console.info( `[cron] fériés synchronisés : ${result.created} ajoutés, ${result.updated} corrigés, ${result.removed} retirés`, ); } else { // Not fatal: the local cache stands and the UI shows how stale it is. console.warn(`[cron] synchronisation des fériés en échec : ${result.error}`); } }) .catch((error: unknown) => { console.error('[cron] synchronisation des fériés interrompue', error); }); }, { timezone }, ); // 03:15 local: firmware logs are diagnostics, not data. cron.schedule( '15 3 * * *', () => { const cutoff = new Date(Date.now() - 30 * 86_400_000); void prisma.deviceLog .deleteMany({ where: { createdAt: { lt: cutoff } } }) .then(({ count }) => { if (count > 0) { console.info(`[cron] ${count} entrées de journal appareil purgées`); } }) .catch((error: unknown) => { console.error('[cron] purge des journaux interrompue', error); }); }, { timezone }, ); console.info(`[cron] tâches planifiées (fuseau ${timezone})`); }