import { createApp } from "./app.js"; import { userService } from "./composition/auth.js"; import { createLogger, toErrorMeta } from "../shared/logging/logger.js"; import { closeDefaultDatabase } from "../db/client.js"; const logger = createLogger("api"); const app = createApp(); const port = Number(process.env.PORT || 3000); const heartbeatIntervalMs = 5 * 60 * 1000; const shutdownTimeoutMs = 15 * 1000; process.on("uncaughtException", (error) => { logger.error("uncaught exception, exiting", toErrorMeta(error)); process.exit(1); }); process.on("unhandledRejection", (reason) => { logger.error("unhandled rejection, exiting", toErrorMeta(reason)); process.exit(1); }); setInterval(() => { const memory = process.memoryUsage(); logger.verbose("heartbeat", { uptimeSeconds: Math.round(process.uptime()), rssMb: Math.round(memory.rss / 1024 / 1024), heapUsedMb: Math.round(memory.heapUsed / 1024 / 1024), }); }, heartbeatIntervalMs).unref(); userService .ensureBootstrapAdmin() .catch((error) => { logger.error("bootstrap admin setup failed", toErrorMeta(error)); process.exit(1); }) .then(() => { const server = app.listen(port, () => { logger.info("server started", { port }); }); let shuttingDown = false; function shutdown(signal: NodeJS.Signals): void { if (shuttingDown) return; shuttingDown = true; logger.info("shutting down", { signal }); // Falls ein Request haengt oder ein Socket nicht freigegeben wird, soll der // Prozess trotzdem enden, statt auf das SIGKILL des Containers zu warten. const forceExit = setTimeout(() => { logger.error("shutdown timed out, forcing exit"); process.exit(1); }, shutdownTimeoutMs); forceExit.unref(); server.close((error) => { if (error) logger.error("http server close failed", toErrorMeta(error)); try { closeDefaultDatabase(); } catch (closeError) { logger.error("database close failed", toErrorMeta(closeError)); } logger.info("shutdown complete"); process.exit(error ? 1 : 0); }); // Ohne das warten offene Keep-Alive-Verbindungen (z. B. die des // Next.js-Servers) den Timeout oben aus; laufende Requests laufen weiter. server.closeIdleConnections(); } process.on("SIGTERM", shutdown); process.on("SIGINT", shutdown); });