summaryrefslogtreecommitdiff
path: root/admin-panel/src/server/ws.ts
diff options
context:
space:
mode:
Diffstat (limited to 'admin-panel/src/server/ws.ts')
-rw-r--r--admin-panel/src/server/ws.ts56
1 files changed, 56 insertions, 0 deletions
diff --git a/admin-panel/src/server/ws.ts b/admin-panel/src/server/ws.ts
new file mode 100644
index 0000000..c0ffd8e
--- /dev/null
+++ b/admin-panel/src/server/ws.ts
@@ -0,0 +1,56 @@
+import { WebSocketServer, WebSocket } from 'ws';
+import type { Server } from 'http';
+
+let wss: WebSocketServer;
+
+interface Subscription {
+ ws: WebSocket;
+ projects: Set<string>;
+}
+
+const subscriptions: Subscription[] = [];
+
+export function setupWebSocket(server: Server): void {
+ wss = new WebSocketServer({ server, path: '/ws' });
+
+ wss.on('connection', (ws) => {
+ const sub: Subscription = { ws, projects: new Set() };
+ subscriptions.push(sub);
+
+ ws.on('message', (data) => {
+ try {
+ const msg = JSON.parse(data.toString());
+ if (msg.type === 'subscribe' && msg.project) {
+ sub.projects.add(msg.project);
+ } else if (msg.type === 'unsubscribe' && msg.project) {
+ sub.projects.delete(msg.project);
+ }
+ } catch {
+ // ignore malformed messages
+ }
+ });
+
+ ws.on('close', () => {
+ const idx = subscriptions.indexOf(sub);
+ if (idx !== -1) subscriptions.splice(idx, 1);
+ });
+ });
+}
+
+export function broadcast(project: string, event: Record<string, unknown>): void {
+ const payload = JSON.stringify(event);
+ for (const sub of subscriptions) {
+ if (sub.projects.has(project) && sub.ws.readyState === WebSocket.OPEN) {
+ sub.ws.send(payload);
+ }
+ }
+}
+
+export function broadcastAll(event: Record<string, unknown>): void {
+ const payload = JSON.stringify(event);
+ for (const sub of subscriptions) {
+ if (sub.ws.readyState === WebSocket.OPEN) {
+ sub.ws.send(payload);
+ }
+ }
+}