// server/index.ts import { serve } from "@hono/node-server"; import { Hono } from "hono"; import { WebSocketServer } from "ws"; import type { IncomingMessage } from "node:http"; import type { Duplex } from "node:stream"; import { env } from "./env.js"; import { runMigrations } from "./db/migrate.js"; import { api } from "./routes/index.js"; import { outputHub } from "./ws/outputHub.js"; import { startWorker } from "./jobs/worker.js"; env.requireMasterKey(); runMigrations(); const app = new Hono(); app.onError((err, c) => { console.error("[api]", err.message); return c.json({ error: err.message || "Erreur serveur" }, 500); }); app.route("/api", api); app.get("/health", (c) => c.json({ ok: true })); const server = serve({ fetch: app.fetch, port: env.port }, (info) => console.log(`[server] http://localhost:${info.port}`), ); // WebSocket: /api/ws/machines/:id/output const wss = new WebSocketServer({ noServer: true }); server.on("upgrade", (req: IncomingMessage, socket: Duplex, head: Buffer) => { const match = /^\/api\/ws\/machines\/([^/]+)\/output$/.exec(req.url ?? ""); if (!match) { socket.destroy(); return; } const machineId = match[1]!; wss.handleUpgrade(req, socket, head, (ws) => { const unsub = outputHub.subscribe(machineId, (chunk) => { if (ws.readyState === ws.OPEN) ws.send(chunk); }); ws.on("close", unsub); }); }); startWorker();