initial ws setup

This commit is contained in:
2026-02-05 20:45:38 -05:00
parent 9eb19b0b9a
commit 9f5ec4fff2
5 changed files with 75 additions and 20 deletions

1
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"express": "^5.2.1", "express": "^5.2.1",
"pg": "^8.18.0", "pg": "^8.18.0",
"postgres": "^3.4.8", "postgres": "^3.4.8",
"ws": "^8.19.0",
"wscat": "^6.1.0", "wscat": "^6.1.0",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },

View File

@@ -24,6 +24,7 @@
"express": "^5.2.1", "express": "^5.2.1",
"pg": "^8.18.0", "pg": "^8.18.0",
"postgres": "^3.4.8", "postgres": "^3.4.8",
"ws": "^8.19.0",
"wscat": "^6.1.0", "wscat": "^6.1.0",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },

View File

@@ -1,22 +1,34 @@
import express from 'express' import express from "express";
import http from "http";
// routes // routes
import { matchRouter } from './routes/matches.route.js' import { matchRouter } from "./routes/matches.route.js";
import { attachWebsocketServer } from "./ws/server.js";
const app = express() const PORT = process.env.PORT || 8081;
const HOST = process.env.HOST || "0.0.0.0";
const port = process.env.PORT || 8081 const app = express();
app.use(express.json()) const server = http.createServer(app);
app.get('/',(_,res)=>{ app.use(express.json());
res.send('Hello from express server!')
})
app.use('/matches', matchRouter) app.get("/", (_, res) => {
res.send("Hello from express server!");
});
app.listen(port, ()=>{ app.use("/matches", matchRouter);
console.info(`Listening on port ${port}`)
})
const { broadcastMatchCreated } = attachWebsocketServer(server);
app.locals.broadcastMatchCreated = broadcastMatchCreated;
server.listen(PORT, HOST, () => {
const baseURL =
HOST === "0.0.0.0" ? `http://localhost:${PORT}` : `http://${HOST}:${PORT}`;
console.info(`Server running on ${baseURL}`);
console.info(
`Websocket server running on ${baseURL.replace("http", "ws")}/ws`,
);
});

View File

@@ -17,7 +17,7 @@ matchRouter.get("/", async (req, res) => {
if (!parsed.success) { if (!parsed.success) {
return res.status(400).json({ return res.status(400).json({
error: "Invalid query", error: "Invalid query",
details: JSON.stringify(parsed.error), details: parsed.error.issues,
}); });
} }
@@ -34,24 +34,24 @@ matchRouter.get("/", async (req, res) => {
} catch (e) { } catch (e) {
return res return res
.status(500) .status(500)
.json({ error: "Failed to list matchs.", details: JSON.stringify(e) }); .json({ error: "Failed to list matchs.", details: e.issues });
} }
}); });
matchRouter.post("/", async (req, res) => { matchRouter.post("/", async (req, res) => {
const parsed = createMatchSchema.safeParse(req.body); const parsed = createMatchSchema.safeParse(req.body);
const {
data: { startTime, endTime, homeScore, awayScore },
} = parsed;
if (!parsed.success) { if (!parsed.success) {
return res.status(400).json({ return res.status(400).json({
error: "Invalid payload", error: "Invalid payload",
details: JSON.stringify(parsed.error), details: parsed.error.issues,
}); });
} }
const {
data: { startTime, endTime, homeScore, awayScore },
} = parsed;
try { try {
const [event] = await db const [event] = await db
.insert(matches) .insert(matches)
@@ -65,10 +65,14 @@ matchRouter.post("/", async (req, res) => {
}) })
.returning(); .returning();
// broadcast this to all clients
if (res.app.locals.broadcastMatchCreated) {
res.app.locals.broadcastMatchCreated(event);
}
res.status(201).json({ data: event }); res.status(201).json({ data: event });
} catch (e) { } catch (e) {
return res return res
.status(500) .status(500)
.json({ error: "Failed to create match.", details: JSON.stringify(e) }); .json({ error: "Failed to create match.", details: e.issues });
} }
}); });

37
src/ws/server.js Normal file
View File

@@ -0,0 +1,37 @@
import { WebSocket, WebSocketServer } from "ws";
const sendJson = (socket, payload) => {
if (socket.readyState !== WebSocket.OPEN) {
return;
}
socket.send(JSON.stringify(payload));
};
const broadcast = (wss, payload) => {
for (const client of wss.clients) {
if (client.readyState !== WebSocket.OPEN) return;
client.send(JSON.stringify(payload));
}
};
export const attachWebsocketServer = (server) => {
const wss = new WebSocketServer({
server,
path: "/ws",
maxPayload: 1024 * 1024, // 1mb
});
wss.on("connection", (socket) => {
sendJson(socket, { type: "welcome" });
socket.on("error", console.error);
});
function broadcastMatchCreated(match) {
broadcast(wss, { type: "match_created", data: match });
}
return { broadcastMatchCreated };
};