'use strict'; 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', }; const state = { 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() { try { const { getSetting } = require('../db/database'); 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, value) { try { const { setSetting } = require('../db/database'); setSetting(key, value == null ? '' : String(value)); } catch { /* non-fatal */ } } function toMessage(error) { if (!error) return null; if (typeof error === 'string') return error; return error.message || String(error); } function recordError(source, error) { 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 = null) { 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 = null) { 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, nextRunAt = null) { 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() { state.notifications.last_test_at = new Date().toISOString(); state.notifications.last_error = null; } function markNotificationError(error) { state.notifications.last_error = toMessage(error); recordError('Notifications', error); } function markNotificationSuccess() { 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, };