import { mkdirSync } from "node:fs" import { resolve } from "node:path" import Fastify from "fastify" import cors from "@fastify/cors" import multipart from "@fastify/multipart" import fastifyStatic from "@fastify/static" import { env, isDev } from "./env" import { createSocketServer } from "./socket" import { adminRoutes } from "./admin/routes" import { listCategories } from "./db/quiz-repo" import { listGameHistory } from "./db/history-repo" import "./game/modes" // enregistre les épreuves (registerRound) const app = Fastify({ logger: isDev ? { transport: { target: "pino-pretty" } } : true, }) await app.register(cors, { origin: env.corsOrigins }) // Images (image_reveal) : dossier servi en statique sur /uploads. const uploadsRoot = resolve(env.uploadsDir) mkdirSync(uploadsRoot, { recursive: true }) await app.register(multipart, { limits: { fileSize: 8 * 1024 * 1024 } }) await app.register(fastifyStatic, { root: uploadsRoot, prefix: "/uploads/" }) app.get("/health", async () => ({ status: "ok", uptime: process.uptime() })) // Catégories de quiz disponibles (public, pour le filtre du lobby). app.get("/api/categories", async () => listCategories()) // Historique des parties d'une room (public). app.get<{ Params: { code: string } }>("/api/history/:code", async (req) => listGameHistory(req.params.code.toUpperCase()) ) await app.register(adminRoutes, { prefix: "/api/admin" }) // Socket.IO se greffe sur le serveur HTTP sous-jacent de Fastify. const io = createSocketServer(app.server) try { await app.listen({ host: env.host, port: env.port }) app.log.info(`Socket.IO ready (${io.engine.clientsCount} clients)`) } catch (err) { app.log.error(err) process.exit(1) } // Arrêt propre : on ferme Socket.IO puis Fastify. for (const signal of ["SIGINT", "SIGTERM"] as const) { process.on(signal, async () => { app.log.info(`${signal} received, shutting down`) await io.close() await app.close() process.exit(0) }) }