diff --git a/server/index.ts b/server/index.ts new file mode 100644 index 0000000..d56efe1 --- /dev/null +++ b/server/index.ts @@ -0,0 +1,41 @@ +// 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.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();