feat(F-054): completed feature

This commit is contained in:
chattie
2026-08-19 13:15:30 +02:00
parent 6a74f5ede6
commit de41a42d88
17 changed files with 446 additions and 100 deletions

View File

@@ -4,6 +4,18 @@ import { createPool } from '../db/pool.js';
import { createFlagStore } from '../../modules/flags/index.js';
import { createLogger } from '../logging/logger.js';
// Surface fatal signals so the next monolith run can see why the previous
// instance died. Without these, SIGTERM/SIGKILL from a harness or the OS
// leaves no trace in the runtime log.
process.on('uncaughtException', (err) => {
// eslint-disable-next-line no-console
console.error('uncaughtException', err);
});
process.on('unhandledRejection', (reason) => {
// eslint-disable-next-line no-console
console.error('unhandledRejection', reason);
});
let config;
try {
config = loadConfig(process.env);
@@ -29,6 +41,13 @@ try {
});
await app.listen({ port: config.port, host: config.host });
logger.info({ port: config.port, host: config.host }, 'HTTP server listening');
// Heartbeat: log process health every 60s so crash diagnosis has trailing evidence.
const startTime = Date.now();
setInterval(() => {
const uptimeSec = Math.floor((Date.now() - startTime) / 1000);
logger.info({ uptimeSec, memMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024) }, 'heartbeat');
}, 60_000).unref();
} catch (error) {
logger.error({ err: error }, 'Failed to start HTTP server');
await pool.end();

View File

@@ -107,7 +107,20 @@ export async function registerBackofficeRoutes(
};
app.post('/backoffice/auth/login', { schema: loginSchema }, async (request, reply) => {
const input = credentialsSchema.parse(request.body);
const parseResult = credentialsSchema.safeParse(request.body);
if (!parseResult.success) {
throw new AppError(
400,
'VALIDATION_ERROR',
'Invalid request payload',
parseResult.error.issues.map((issue) => ({
path: issue.path.join('.'),
message: issue.message,
code: issue.code,
})),
);
}
const input = parseResult.data;
try {
const result = await login.execute(input);
setCookie(reply, result.token, true);