typescript server.ts

This commit is contained in:
2026-02-09 00:37:40 +01:00
parent eae062a23a
commit 999980093a
4 changed files with 110 additions and 7 deletions
+4 -3
View File
@@ -3,9 +3,10 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "node server.mjs",
"build": "next build",
"start": "NODE_ENV=production node server.mjs"
"dev": "npm run buildserver && node ./.next/server.js",
"build": "npm run buildserver && next build",
"start": "npm run buildserver && NODE_ENV=production node ./.next/server.js",
"buildserver": "tsc -p server.tsconfig.json"
},
"dependencies": {
"@codemirror/lang-go": "^6.0.1",
+4 -4
View File
@@ -11,7 +11,7 @@ app.prepare().then(() => {
handle(req, res);
});
const clients = {};
const runs = {};
const wss = new WebSocketServer({ noServer: true });
@@ -19,8 +19,8 @@ app.prepare().then(() => {
wss.on('connection', (ws, req) => {
ws.on('message', (msg) => {
const runId = msg.toString();
if (!clients[runId]) clients[runId] = []
clients[runId].push(ws);
if (!runs[runId]) runs[runId] = { clients: [] }
runs[runId].clients.push(ws);
});
});
@@ -33,7 +33,7 @@ app.prepare().then(() => {
req.on('end', () => {
const move = JSON.parse(body);
clients[move.runId]?.forEach(client => {
runs[move.runId]?.clients.forEach(client => {
if (client.readyState === client.OPEN) {
client.send(JSON.stringify(move.moves));
}
+86
View File
@@ -0,0 +1,86 @@
import { createServer, IncomingMessage } from "http";
import next from "next";
import { WebSocketServer, WebSocket } from "ws";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
/**
* One run = many websocket clients
*/
type LiveRun = {
clients: Set<WebSocket>;
};
const runs: Record<string, LiveRun> = {};
app.prepare().then(() => {
const server = createServer((req, res) => {
handle(req, res);
});
const wss = new WebSocketServer({ noServer: true });
// WebSocket connections
wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
ws.on("message", (msg: Buffer) => {
const runId = msg.toString();
if (!runs[runId]) {
runs[runId] = { clients: new Set() };
}
runs[runId].clients.add(ws);
ws.once("close", () => {
runs[runId]?.clients.delete(ws);
});
});
});
// Handle POST requests
server.on("request", (req, res) => {
if (req.method === "POST" && req.url === "/api/run/move") {
let body = "";
req.on("data", (chunk: Buffer) => {
body += chunk.toString();
});
req.on("end", () => {
try {
const move: {
runId: string;
moves: unknown;
} = JSON.parse(body);
runs[move.runId]?.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(move.moves));
}
});
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok" }));
} catch (err) {
res.writeHead(400);
res.end("Invalid JSON");
}
});
}
});
// Upgrade HTTP → WebSocket
server.on("upgrade", (req, socket, head) => {
if (req.url === "/api/view-run") {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit("connection", ws, req);
});
}
});
server.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
});
+16
View File
@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"outDir": "./.next",
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2020"],
"types": ["node"],
"skipLibCheck": true,
"incremental": false,
"composite": false
},
"include": ["server.ts"]
}