mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-07 09:24:09 +00:00
5cf4323511
Every mutating /api/* request runs an individual INSERT into audit_log which serializes against other writers under burst load (SQLite's single-writer model). Buffer the writes in DatabaseService and flush them in a single transaction either every second or once the buffer reaches 100 entries, whichever comes first. Read paths (getAuditLogs, getAuditLogsInRange, cleanupOldAuditLogs) drain the buffer first so callers always see a consistent view, which keeps the existing test pattern of insert-then-read working. Graceful shutdown flushes before db.close() so no entries are lost on clean exit. The 1s flush timer is unref'd so the buffer cannot keep the process alive on its own. The CLI resetMfa script flushes explicitly before returning since it exits before the timer fires.
61 lines
2.9 KiB
TypeScript
61 lines
2.9 KiB
TypeScript
import type { Server } from 'http';
|
|
import { DatabaseService } from '../services/DatabaseService';
|
|
import { LicenseService } from '../services/LicenseService';
|
|
import { MonitorService } from '../services/MonitorService';
|
|
import { AutoHealService } from '../services/AutoHealService';
|
|
import { DockerEventManager } from '../services/DockerEventManager';
|
|
import { ImageUpdateService } from '../services/ImageUpdateService';
|
|
import { SchedulerService } from '../services/SchedulerService';
|
|
import { MfaService } from '../services/MfaService';
|
|
|
|
/**
|
|
* Wire graceful shutdown handlers. Docker sends SIGTERM when the container
|
|
* stops; Ctrl-C sends SIGINT in dev. We allow in-flight requests to finish,
|
|
* then cleanly stop background services and close the SQLite connection
|
|
* before exiting. A 10 s force-exit timer guards against hung connections.
|
|
*/
|
|
export function installShutdownHandlers(server: Server): void {
|
|
const gracefulShutdown = (signal: string): void => {
|
|
console.log(`[Shutdown] ${signal} received - shutting down gracefully…`);
|
|
|
|
server.close(() => {
|
|
console.log('[Shutdown] HTTP server closed');
|
|
try { LicenseService.getInstance().destroy(); } catch (e) {
|
|
console.warn('[Shutdown] LicenseService cleanup failed:', (e as Error).message);
|
|
}
|
|
try { MonitorService.getInstance().stop(); } catch (e) {
|
|
console.warn('[Shutdown] MonitorService cleanup failed:', (e as Error).message);
|
|
}
|
|
try { AutoHealService.getInstance().stop(); } catch (e) { console.warn('[Shutdown] AutoHealService cleanup failed:', (e as Error).message); }
|
|
try { DockerEventManager.getInstance().stop(); } catch (e) {
|
|
console.warn('[Shutdown] DockerEventManager cleanup failed:', (e as Error).message);
|
|
}
|
|
try { ImageUpdateService.getInstance().stop(); } catch (e) {
|
|
console.warn('[Shutdown] ImageUpdateService cleanup failed:', (e as Error).message);
|
|
}
|
|
try { SchedulerService.getInstance().stop(); } catch (e) {
|
|
console.warn('[Shutdown] SchedulerService cleanup failed:', (e as Error).message);
|
|
}
|
|
try { MfaService.getInstance().stop(); } catch (e) {
|
|
console.warn('[Shutdown] MfaService cleanup failed:', (e as Error).message);
|
|
}
|
|
try { DatabaseService.getInstance().flushAuditLogBuffer(); } catch (e) {
|
|
console.warn('[Shutdown] Audit log flush failed:', (e as Error).message);
|
|
}
|
|
try { DatabaseService.getInstance().getDb().close(); } catch (e) {
|
|
console.warn('[Shutdown] Database close failed:', (e as Error).message);
|
|
}
|
|
console.log('[Shutdown] Done - exiting');
|
|
process.exit(0);
|
|
});
|
|
|
|
setTimeout(() => {
|
|
console.error('[Shutdown] Timed out waiting for connections - forcing exit');
|
|
process.exit(1);
|
|
}, 10_000).unref();
|
|
};
|
|
|
|
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
}
|