import express from "express"; import swaggerUi from "swagger-ui-express"; import { existsSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; import { createLogger } from "./infrastructure/logging/logger.js"; import { bootstrap, config } from "./composition/bootstrap.js"; import { runtime } from "./infrastructure/config/runtime.js"; import { SseHub } from "./interfaces/sse/sse.js"; import { createRouter } from "./interfaces/http/routes.js"; import { openapiSpec } from "./interfaces/http/openapi.js"; const log = createLogger("server"); const __dirname = dirname(fileURLToPath(import.meta.url)); const webDist = join(__dirname, "..", "web", "dist"); function main(): void { const ctx = bootstrap(); ctx.start(); const sse = new SseHub(ctx.application); sse.start(); const server = express(); server.use(express.json()); server.use("/api", createRouter(ctx.application, sse)); server.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openapiSpec, { customSiteTitle: "Arb Pulse — API Docs", swaggerOptions: { defaultModelsExpandDepth: 2, defaultModelExpandDepth: 2 }, })); if (existsSync(webDist)) { server.use(express.static(webDist)); server.get("*", (_req, res) => { res.sendFile(join(webDist, "index.html")); }); } else { server.get("/", (_req, res) => { res .status(200) .send("Backend running. Frontend not built yet — run `npm run build` (or `npm run dev:web`)."); }); } const httpServer = server.listen(config.port, () => { log.info(`listening on :${config.port} (demo=${runtime.demoMode})`); }); const shutdown = (): void => { log.info("shutting down"); sse.stop(); ctx.stop(); httpServer.close(() => process.exit(0)); setTimeout(() => process.exit(0), 2000); }; process.on("SIGTERM", shutdown); process.on("SIGINT", shutdown); } main();