const adminConfig = require('./adminConfig'); const requestLogStore = require('./requestLogStore'); const SIGNIFICANT_STATUS_CODES = new Set([408, 425, 429, 500, 502, 503, 504]); function createMaintenanceError(message) { const error = new Error(message || 'Wartungsmodus aktiv'); error.code = 'MAINTENANCE_MODE_ACTIVE'; error.statusCode = 503; return error; } function getSnapshot() { const settings = adminConfig.readSettings(); return { active: !!settings.maintenanceModeActive, autoEnabled: !!settings.maintenanceModeAutoEnabled, activatedAt: settings.maintenanceModeActivatedAt || null, reason: settings.maintenanceModeReason || '', errorThreshold: Number(settings.maintenanceModeErrorThreshold) || 8, errorWindowMinutes: Number(settings.maintenanceModeErrorWindowMinutes) || 10 }; } function isActive() { return getSnapshot().active; } function isSignificantOutboundApiError(entry) { if (entry?.direction !== 'outgoing' || entry?.target !== 'foodsharing.de') { return false; } const status = Number(entry?.status); if (Number.isFinite(status)) { return SIGNIFICANT_STATUS_CODES.has(status); } return Boolean(entry?.error); } function activate({ reason } = {}) { const snapshot = getSnapshot(); if (snapshot.active) { return snapshot; } const next = adminConfig.writeSettings({ maintenanceModeActive: true, maintenanceModeActivatedAt: new Date().toISOString(), maintenanceModeReason: reason || 'Automatisch aktiviert' }); console.warn(`[MAINTENANCE] Wartungsmodus aktiviert: ${next.maintenanceModeReason || 'ohne Grundangabe'}`); return { active: !!next.maintenanceModeActive, autoEnabled: !!next.maintenanceModeAutoEnabled, activatedAt: next.maintenanceModeActivatedAt || null, reason: next.maintenanceModeReason || '', errorThreshold: Number(next.maintenanceModeErrorThreshold) || 8, errorWindowMinutes: Number(next.maintenanceModeErrorWindowMinutes) || 10 }; } function activateAutomatically({ reason } = {}) { const snapshot = activate({ reason }); if (!snapshot.active) { return snapshot; } try { const notificationService = require('./notificationService'); Promise.resolve( notificationService.sendAdminMaintenanceModeNotification({ reason: snapshot.reason, activatedAt: snapshot.activatedAt, errorThreshold: snapshot.errorThreshold, errorWindowMinutes: snapshot.errorWindowMinutes }) ).catch((error) => { console.error('[MAINTENANCE] Admin-Benachrichtigung konnte nicht versendet werden:', error.message); }); } catch (error) { console.error('[MAINTENANCE] Notification-Service konnte nicht geladen werden:', error.message); } return snapshot; } function deactivate() { const next = adminConfig.writeSettings({ maintenanceModeActive: false, maintenanceModeActivatedAt: null, maintenanceModeReason: '' }); console.warn('[MAINTENANCE] Wartungsmodus deaktiviert.'); return next; } function ensureInactive(message) { if (!isActive()) { return; } throw createMaintenanceError(message); } function recordOutboundFailure(entry) { if (!isSignificantOutboundApiError(entry)) { return getSnapshot(); } const snapshot = getSnapshot(); if (!snapshot.autoEnabled || snapshot.active) { return snapshot; } const cutoff = Date.now() - snapshot.errorWindowMinutes * 60 * 1000; const logs = requestLogStore.list(); const recentFailures = logs.filter( (item) => Number(item?.timestamp) >= cutoff && isSignificantOutboundApiError(item) ); if (recentFailures.length < snapshot.errorThreshold) { return snapshot; } const statusSummary = recentFailures .slice(0, 5) .map((item) => item.status || item.error || 'unbekannt') .join(', '); return activateAutomatically({ reason: `${recentFailures.length} relevante Outbound-API-Fehler in ${snapshot.errorWindowMinutes} Minuten (z. B. ${statusSummary})` }); } module.exports = { activate, activateAutomatically, createMaintenanceError, deactivate, ensureInactive, getSnapshot, isActive, recordOutboundFailure };