metis-desk/apps/server/src/app.ts

64 lines
1.9 KiB
TypeScript
Raw Normal View History

import { randomUUID } from "node:crypto";
import type { HttpErrorResponseDTO } from "@metis/core/common";
import { registerCoreRoutes } from "@metis/core/module";
import type { Application, ErrorRequestHandler, RequestHandler } from "express";
import express from "express";
import helmet from "helmet";
import type { HttpLogger } from "./infrastructure/http/http-logger";
import { logHttpRequest } from "./infrastructure/http/http-logger";
export function createApp(
logger: HttpLogger = logHttpRequest,
identity?: { middleware: RequestHandler; register: (app: Application) => void }
) {
const app = express();
app.disable("x-powered-by");
app.use(helmet());
app.use((request, response, next) => {
const requestId = randomUUID();
const startedAt = performance.now();
response.setHeader("x-request-id", requestId);
response.setHeader("cache-control", "no-store");
response.once("finish", () => {
logger({
event: "http_request",
requestId,
method: request.method,
statusCode: response.statusCode,
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
});
});
next();
});
app.use(express.json({ limit: "64kb" }));
if (identity) {
app.use(identity.middleware);
identity.register(app);
}
registerCoreRoutes(app);
app.use((_request, response) => {
const error: HttpErrorResponseDTO = {
code: "NOT_FOUND",
requestId: response.getHeader("x-request-id")?.toString() ?? "",
};
response.status(404).json(error);
});
const errorHandler: ErrorRequestHandler = (_error: unknown, _request, response, next) => {
if (response.headersSent) {
next(_error);
return;
}
const error: HttpErrorResponseDTO = {
code: "INTERNAL_ERROR",
requestId: response.getHeader("x-request-id")?.toString() ?? "",
};
response.status(500).json(error);
};
app.use(errorHandler);
return app;
}