'use strict'; // import required so Node type-strips this .cts (it has no other import). import type { Db } from '../types/db'; const MAX_RECENT_ERRORS = 10; // Keys written to the settings table for cross-restart persistence const DB_KEY = { workerLastRun: '_worker_last_run_at', workerNextRun: '_worker_next_run_at', workerError: '_worker_last_error', workerStarted: '_worker_started_at', }; interface WorkerState { enabled: boolean; running: boolean; started_at: string | null; last_run_at: string | null; next_run_at: string | null; last_error: string | null; } interface NotificationsState { last_test_at: string | null; last_error: string | null; } interface RuntimeError { timestamp: string; source: string; message: string; } const state: { worker: WorkerState; notifications: NotificationsState; recentErrors: RuntimeError[]; } = { worker: { enabled: false, running: false, started_at: null, last_run_at: null, next_run_at: null, last_error: null, }, notifications: { last_test_at: null, last_error: null, }, recentErrors: [], }; // Seed from DB on first load so status survives container restarts. // Wrapped in try/catch — DB may not be ready at require-time in tests. function seedFromDb(): void { try { const { getSetting } = require('../db/database.cts'); state.worker.last_run_at = getSetting(DB_KEY.workerLastRun) || null; state.worker.next_run_at = getSetting(DB_KEY.workerNextRun) || null; state.worker.last_error = getSetting(DB_KEY.workerError) || null; state.worker.started_at = getSetting(DB_KEY.workerStarted) || null; if (state.worker.last_run_at) state.worker.enabled = true; } catch { /* DB not ready — will be populated when workers run */ } } seedFromDb(); function dbSet(key: string, value: unknown): void { try { const { setSetting } = require('../db/database.cts'); setSetting(key, value == null ? '' : String(value)); } catch { /* non-fatal */ } } function toMessage(error: unknown): string | null { if (!error) return null; if (typeof error === 'string') return error; return (error as any).message || String(error); } function recordError(source: string, error: unknown): void { const message = toMessage(error); if (!message) return; state.recentErrors.unshift({ timestamp: new Date().toISOString(), source, message }); state.recentErrors = state.recentErrors.slice(0, MAX_RECENT_ERRORS); } function markWorkerStarted(nextRunAt: string | null = null): void { const now = new Date().toISOString(); state.worker.enabled = true; state.worker.running = true; state.worker.started_at = state.worker.started_at || now; state.worker.next_run_at = nextRunAt; dbSet(DB_KEY.workerStarted, state.worker.started_at); if (nextRunAt) dbSet(DB_KEY.workerNextRun, nextRunAt); } function markWorkerSuccess(nextRunAt: string | null = null): void { const now = new Date().toISOString(); state.worker.running = false; state.worker.last_run_at = now; state.worker.last_error = null; if (nextRunAt !== undefined) state.worker.next_run_at = nextRunAt; dbSet(DB_KEY.workerLastRun, now); dbSet(DB_KEY.workerError, ''); if (nextRunAt) dbSet(DB_KEY.workerNextRun, nextRunAt); } function markWorkerError(error: unknown, nextRunAt: string | null = null): void { const now = new Date().toISOString(); state.worker.running = false; state.worker.last_run_at = now; state.worker.last_error = toMessage(error); if (nextRunAt !== undefined) state.worker.next_run_at = nextRunAt; dbSet(DB_KEY.workerLastRun, now); dbSet(DB_KEY.workerError, state.worker.last_error || ''); if (nextRunAt) dbSet(DB_KEY.workerNextRun, nextRunAt); recordError('Daily Worker', error); } function markNotificationTestSuccess(): void { state.notifications.last_test_at = new Date().toISOString(); state.notifications.last_error = null; } function markNotificationError(error: unknown): void { state.notifications.last_error = toMessage(error); recordError('Notifications', error); } function markNotificationSuccess(): void { state.notifications.last_error = null; } function getStatusRuntime() { return { worker: { ...state.worker }, notifications: { ...state.notifications }, recentErrors: [...state.recentErrors], }; } module.exports = { getStatusRuntime, markNotificationError, markNotificationSuccess, markNotificationTestSuccess, markWorkerError, markWorkerStarted, markWorkerSuccess, recordError, };