feat(runtime): refactor runtime state and proxy event handling for typed WebRTC linking and shared status models
This commit is contained in:
@@ -14,12 +14,36 @@ import { WebSocketServer, WebSocket } from 'ws';
|
||||
import { handleWebRtcSignaling } from './webrtcbridge.ts';
|
||||
import type { VoiceboxManager } from './voicebox.ts';
|
||||
|
||||
// CallManager was previously used for WebRTC call handling. Now replaced by Rust proxy-engine.
|
||||
// Kept as `any` type for backward compat with the function signature until full WebRTC port.
|
||||
type CallManager = any;
|
||||
|
||||
const CONFIG_PATH = path.join(process.cwd(), '.nogit', 'config.json');
|
||||
|
||||
interface IHandleRequestContext {
|
||||
getStatus: () => unknown;
|
||||
log: (msg: string) => void;
|
||||
onStartCall: (number: string, deviceId?: string, providerId?: string) => { id: string } | null;
|
||||
onHangupCall: (callId: string) => boolean;
|
||||
onConfigSaved?: () => void | Promise<void>;
|
||||
voiceboxManager?: VoiceboxManager;
|
||||
}
|
||||
|
||||
interface IWebUiOptions extends IHandleRequestContext {
|
||||
port: number;
|
||||
onWebRtcOffer?: (sessionId: string, sdp: string, ws: WebSocket) => Promise<void>;
|
||||
onWebRtcIce?: (sessionId: string, candidate: unknown) => Promise<void>;
|
||||
onWebRtcClose?: (sessionId: string) => Promise<void>;
|
||||
onWebRtcAccept?: (callId: string, sessionId: string) => void;
|
||||
}
|
||||
|
||||
interface IWebRtcSocketMessage {
|
||||
type?: string;
|
||||
sessionId?: string;
|
||||
callId?: string;
|
||||
sdp?: string;
|
||||
candidate?: unknown;
|
||||
userAgent?: string;
|
||||
_remoteIp?: string | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// WebSocket broadcast
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -82,14 +106,9 @@ function loadStaticFiles(): void {
|
||||
async function handleRequest(
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
getStatus: () => unknown,
|
||||
log: (msg: string) => void,
|
||||
onStartCall: (number: string, deviceId?: string, providerId?: string) => { id: string } | null,
|
||||
onHangupCall: (callId: string) => boolean,
|
||||
onConfigSaved?: () => void,
|
||||
callManager?: CallManager,
|
||||
voiceboxManager?: VoiceboxManager,
|
||||
context: IHandleRequestContext,
|
||||
): Promise<void> {
|
||||
const { getStatus, log, onStartCall, onHangupCall, onConfigSaved, voiceboxManager } = context;
|
||||
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
||||
const method = req.method || 'GET';
|
||||
|
||||
@@ -258,7 +277,7 @@ async function handleRequest(
|
||||
|
||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + '\n');
|
||||
log('[config] updated config.json');
|
||||
onConfigSaved?.();
|
||||
await onConfigSaved?.();
|
||||
return sendJson(res, { ok: true });
|
||||
} catch (e: any) {
|
||||
return sendJson(res, { ok: false, error: e.message }, 400);
|
||||
@@ -339,21 +358,21 @@ async function handleRequest(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function initWebUi(
|
||||
getStatus: () => unknown,
|
||||
log: (msg: string) => void,
|
||||
onStartCall: (number: string, deviceId?: string, providerId?: string) => { id: string } | null,
|
||||
onHangupCall: (callId: string) => boolean,
|
||||
onConfigSaved?: () => void,
|
||||
callManager?: CallManager,
|
||||
voiceboxManager?: VoiceboxManager,
|
||||
/** WebRTC signaling handlers — forwarded to Rust proxy-engine. */
|
||||
onWebRtcOffer?: (sessionId: string, sdp: string, ws: WebSocket) => Promise<void>,
|
||||
onWebRtcIce?: (sessionId: string, candidate: any) => Promise<void>,
|
||||
onWebRtcClose?: (sessionId: string) => Promise<void>,
|
||||
/** Called when browser sends webrtc-accept (callId + sessionId linking). */
|
||||
onWebRtcAccept?: (callId: string, sessionId: string) => void,
|
||||
options: IWebUiOptions,
|
||||
): void {
|
||||
const WEB_PORT = 3060;
|
||||
const {
|
||||
port,
|
||||
getStatus,
|
||||
log,
|
||||
onStartCall,
|
||||
onHangupCall,
|
||||
onConfigSaved,
|
||||
voiceboxManager,
|
||||
onWebRtcOffer,
|
||||
onWebRtcIce,
|
||||
onWebRtcClose,
|
||||
onWebRtcAccept,
|
||||
} = options;
|
||||
|
||||
loadStaticFiles();
|
||||
|
||||
@@ -367,12 +386,12 @@ export function initWebUi(
|
||||
const cert = fs.readFileSync(certPath, 'utf8');
|
||||
const key = fs.readFileSync(keyPath, 'utf8');
|
||||
server = https.createServer({ cert, key }, (req, res) =>
|
||||
handleRequest(req, res, getStatus, log, onStartCall, onHangupCall, onConfigSaved, callManager, voiceboxManager).catch(() => { res.writeHead(500); res.end(); }),
|
||||
handleRequest(req, res, { getStatus, log, onStartCall, onHangupCall, onConfigSaved, voiceboxManager }).catch(() => { res.writeHead(500); res.end(); }),
|
||||
);
|
||||
useTls = true;
|
||||
} catch {
|
||||
server = http.createServer((req, res) =>
|
||||
handleRequest(req, res, getStatus, log, onStartCall, onHangupCall, onConfigSaved, callManager, voiceboxManager).catch(() => { res.writeHead(500); res.end(); }),
|
||||
handleRequest(req, res, { getStatus, log, onStartCall, onHangupCall, onConfigSaved, voiceboxManager }).catch(() => { res.writeHead(500); res.end(); }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -386,12 +405,12 @@ export function initWebUi(
|
||||
|
||||
socket.on('message', (raw) => {
|
||||
try {
|
||||
const msg = JSON.parse(raw.toString());
|
||||
const msg = JSON.parse(raw.toString()) as IWebRtcSocketMessage;
|
||||
if (msg.type === 'webrtc-offer' && msg.sessionId) {
|
||||
// Forward to Rust proxy-engine for WebRTC handling.
|
||||
if (onWebRtcOffer) {
|
||||
if (onWebRtcOffer && typeof msg.sdp === 'string') {
|
||||
log(`[webrtc-ws] offer msg keys: ${Object.keys(msg).join(',')}, sdp type: ${typeof msg.sdp}, sdp len: ${msg.sdp?.length || 0}`);
|
||||
onWebRtcOffer(msg.sessionId, msg.sdp, socket as any).catch((e: any) =>
|
||||
onWebRtcOffer(msg.sessionId, msg.sdp, socket).catch((e: any) =>
|
||||
log(`[webrtc] offer error: ${e.message}`));
|
||||
}
|
||||
} else if (msg.type === 'webrtc-ice' && msg.sessionId) {
|
||||
@@ -409,7 +428,7 @@ export function initWebUi(
|
||||
}
|
||||
} else if (msg.type?.startsWith('webrtc-')) {
|
||||
msg._remoteIp = remoteIp;
|
||||
handleWebRtcSignaling(socket as any, msg);
|
||||
handleWebRtcSignaling(socket, msg);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
});
|
||||
@@ -418,8 +437,8 @@ export function initWebUi(
|
||||
socket.on('error', () => wsClients.delete(socket));
|
||||
});
|
||||
|
||||
server.listen(WEB_PORT, '0.0.0.0', () => {
|
||||
log(`web ui listening on ${useTls ? 'https' : 'http'}://0.0.0.0:${WEB_PORT}`);
|
||||
server.listen(port, '0.0.0.0', () => {
|
||||
log(`web ui listening on ${useTls ? 'https' : 'http'}://0.0.0.0:${port}`);
|
||||
});
|
||||
|
||||
setInterval(() => broadcastWs('status', getStatus()), 1000);
|
||||
|
||||
Reference in New Issue
Block a user