mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-18 14:33:19 +00:00
refactor(backend): extract remote proxy, WebSocket upgrade handler, and server factory (phase 3) (#733)
Phase 3 of the index.ts refactor. Pulls the remote HTTP/WS proxy plumbing,
the WebSocket upgrade dispatcher, and the http/WSS construction out of the
monolith. index.ts drops roughly 620 lines.
New modules:
- proxy/websocketProxy.ts: shared httpProxy.createProxyServer singleton
(used by both the HTTP proxy middleware and the remote WS forwarder)
- proxy/remoteNodeProxy.ts: createRemoteProxyMiddleware() factory; consumes
the isProxyExemptPath helper instead of open-coding the prefix list
- server.ts: createServer(app) returns { server, wss, pilotTunnelWss }
- services/FleetUpdateTrackerService.ts: singleton wrapping the in-flight
fleet update tracker Map with create()/resolve() helpers
- helpers/consoleSession.ts: mintConsoleSession(), isConsoleSessionScope()
- websocket/upgradeHandler.ts: attachUpgrade(server, deps) dispatcher that
runs the manual cookie/JWT verify and delegates to sub-handlers
- websocket/pilotTunnel.ts: handlePilotTunnel (pilot_enroll consumption and
pilot_tunnel registration)
- websocket/notifications.ts: /ws/notifications local subscriber
- websocket/remoteForwarder.ts: remote-node WS proxy with console_session
token exchange for interactive paths
- websocket/logs.ts: /api/stacks/:name/logs supervisor stream
- websocket/hostConsole.ts: /api/system/host-console PTY, Admiral-gated
- websocket/generic.ts: /ws exec + streamStats action dispatch, owns the
terminalWs single-instance reference
- websocket/reject.ts: shared rejectUpgrade helper (replaces five copies)
Service extension:
- NotificationService: setBroadcaster(fn) replaced by subscribe(ws) that
returns an unsubscriber; broadcastToSubscribers is now internal. Subscriber
set lives on the service rather than in index.ts.
Wiring in index.ts:
- const app = createApp() already in place from Phase 2
- const { server, wss, pilotTunnelWss } = createServer(app)
- attachUpgrade(server, { wss, pilotTunnelWss })
- app.use('/api/', createRemoteProxyMiddleware())
- /api/system/console-token route now uses mintConsoleSession()
- deploy/down/update routes read the streaming target via getTerminalWs()
(return type is WebSocket | undefined so the || undefined fallback is gone)
Code review fixes: five duplicated reject helpers collapsed into
websocket/reject.ts; dropped the createTracker/resolveTracker bind
aliases in index.ts so call sites go through the service directly;
removed em dashes; replaced req.url! with req.url || '/'.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import WebSocket from 'ws';
|
||||
import { DatabaseService, NotificationHistory } from './DatabaseService';
|
||||
import { NodeRegistry } from './NodeRegistry';
|
||||
import { isDebugEnabled } from '../utils/debug';
|
||||
@@ -12,7 +13,7 @@ const ALLOWED_CHANNEL_TYPES = new Set(['discord', 'slack', 'webhook']);
|
||||
export class NotificationService {
|
||||
private static instance: NotificationService;
|
||||
private dbService: DatabaseService;
|
||||
private broadcaster: ((notification: NotificationHistory) => void) | null = null;
|
||||
private readonly subscribers = new Set<WebSocket>();
|
||||
|
||||
private constructor() {
|
||||
this.dbService = DatabaseService.getInstance();
|
||||
@@ -25,9 +26,30 @@ export class NotificationService {
|
||||
return NotificationService.instance;
|
||||
}
|
||||
|
||||
/** Wire up the WebSocket push function after the WS server is initialised. */
|
||||
public setBroadcaster(fn: (notification: NotificationHistory) => void): void {
|
||||
this.broadcaster = fn;
|
||||
/**
|
||||
* Register a WebSocket as a live-notification subscriber. Returns an
|
||||
* unsubscribe function the caller should invoke on `'close'` / `'error'`
|
||||
* (callers may guard against double-unsubscribe themselves; the Set
|
||||
* handles repeated deletes safely either way).
|
||||
*/
|
||||
public subscribe(ws: WebSocket): () => void {
|
||||
this.subscribers.add(ws);
|
||||
return () => this.subscribers.delete(ws);
|
||||
}
|
||||
|
||||
public getSubscriberCount(): number {
|
||||
return this.subscribers.size;
|
||||
}
|
||||
|
||||
/** Push a `{type,payload}` envelope to every currently-open subscriber. */
|
||||
private broadcastToSubscribers(notification: NotificationHistory): void {
|
||||
if (this.subscribers.size === 0) return;
|
||||
const msg = JSON.stringify({ type: 'notification', payload: notification });
|
||||
for (const ws of this.subscribers) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,9 +81,7 @@ export class NotificationService {
|
||||
});
|
||||
|
||||
// 2. Push to connected browser clients via WebSocket
|
||||
if (this.broadcaster) {
|
||||
this.broadcaster(notification);
|
||||
}
|
||||
this.broadcastToSubscribers(notification);
|
||||
|
||||
// 3. Check notification routing rules if a stack context is available
|
||||
const errors: string[] = [];
|
||||
|
||||
Reference in New Issue
Block a user