initial ws setup
This commit is contained in:
1
package-lock.json
generated
1
package-lock.json
generated
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
36
src/index.js
36
src/index.js
@@ -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`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -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
37
src/ws/server.js
Normal 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 };
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user