summaryrefslogtreecommitdiff
path: root/admin-panel/src/client/lib/ws.ts
diff options
context:
space:
mode:
authorArseney300 <Arseney300@gmail.com>2026-03-05 09:47:09 +0700
committerArseney300 <Arseney300@gmail.com>2026-03-05 09:47:09 +0700
commit0885d20ac18c252f6735cd288448d5d93e95b80f (patch)
treeedfb6814e97d4000605d9f6a918c30c6aca9e10c /admin-panel/src/client/lib/ws.ts
parent22749b796d7a5e4efa9494156a3fdcfe39028da7 (diff)
WIP: admin_panel: add featurefeature/admin_panel
Diffstat (limited to 'admin-panel/src/client/lib/ws.ts')
-rw-r--r--admin-panel/src/client/lib/ws.ts79
1 files changed, 79 insertions, 0 deletions
diff --git a/admin-panel/src/client/lib/ws.ts b/admin-panel/src/client/lib/ws.ts
new file mode 100644
index 0000000..14e0cec
--- /dev/null
+++ b/admin-panel/src/client/lib/ws.ts
@@ -0,0 +1,79 @@
+type EventHandler = (data: unknown) => void;
+
+class WsClient {
+ private ws: WebSocket | null = null;
+ private handlers = new Map<string, Set<EventHandler>>();
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
+ private subscribedProjects = new Set<string>();
+
+ connect(): void {
+ if (this.ws?.readyState === WebSocket.OPEN) return;
+
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
+ this.ws = new WebSocket(`${protocol}//${location.host}/ws`);
+
+ this.ws.onopen = () => {
+ // Re-subscribe to projects
+ for (const project of this.subscribedProjects) {
+ this.send({ type: 'subscribe', project });
+ }
+ };
+
+ this.ws.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data);
+ const handlers = this.handlers.get(data.type);
+ if (handlers) {
+ for (const handler of handlers) {
+ handler(data);
+ }
+ }
+ // Also fire wildcard handlers
+ const wildcardHandlers = this.handlers.get('*');
+ if (wildcardHandlers) {
+ for (const handler of wildcardHandlers) {
+ handler(data);
+ }
+ }
+ } catch {
+ // ignore
+ }
+ };
+
+ this.ws.onclose = () => {
+ this.reconnectTimer = setTimeout(() => this.connect(), 3000);
+ };
+ }
+
+ disconnect(): void {
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
+ this.ws?.close();
+ this.ws = null;
+ }
+
+ subscribe(project: string): void {
+ this.subscribedProjects.add(project);
+ this.send({ type: 'subscribe', project });
+ }
+
+ unsubscribe(project: string): void {
+ this.subscribedProjects.delete(project);
+ this.send({ type: 'unsubscribe', project });
+ }
+
+ on(event: string, handler: EventHandler): () => void {
+ if (!this.handlers.has(event)) {
+ this.handlers.set(event, new Set());
+ }
+ this.handlers.get(event)!.add(handler);
+ return () => this.handlers.get(event)?.delete(handler);
+ }
+
+ private send(data: unknown): void {
+ if (this.ws?.readyState === WebSocket.OPEN) {
+ this.ws.send(JSON.stringify(data));
+ }
+ }
+}
+
+export const wsClient = new WsClient();