import * as plugins from '../plugins.js'; import type { IRemoteIngressStatus } from '../../ts_interfaces/data/remoteingress.js'; import type { RemoteIngressManager } from './classes.remoteingress-manager.js'; export interface ITunnelManagerConfig { tunnelPort?: number; targetHost?: string; } /** * Manages the RemoteIngressHub instance and tracks connected edge statuses. */ export class TunnelManager { private hub: InstanceType; private manager: RemoteIngressManager; private config: ITunnelManagerConfig; private edgeStatuses: Map = new Map(); constructor(manager: RemoteIngressManager, config: ITunnelManagerConfig = {}) { this.manager = manager; this.config = config; this.hub = new plugins.remoteingress.RemoteIngressHub(); // Listen for edge connect/disconnect events this.hub.on('edgeConnected', (data: { edgeId: string }) => { const existing = this.edgeStatuses.get(data.edgeId); this.edgeStatuses.set(data.edgeId, { edgeId: data.edgeId, connected: true, publicIp: existing?.publicIp ?? null, activeTunnels: 0, lastHeartbeat: Date.now(), connectedAt: Date.now(), }); }); this.hub.on('edgeDisconnected', (data: { edgeId: string }) => { const existing = this.edgeStatuses.get(data.edgeId); if (existing) { existing.connected = false; existing.activeTunnels = 0; } }); this.hub.on('streamOpened', (data: { edgeId: string; streamId: number }) => { const existing = this.edgeStatuses.get(data.edgeId); if (existing) { existing.activeTunnels++; existing.lastHeartbeat = Date.now(); } }); this.hub.on('streamClosed', (data: { edgeId: string; streamId: number }) => { const existing = this.edgeStatuses.get(data.edgeId); if (existing && existing.activeTunnels > 0) { existing.activeTunnels--; } }); } /** * Start the tunnel hub and load allowed edges. */ public async start(): Promise { await this.hub.start({ tunnelPort: this.config.tunnelPort ?? 8443, targetHost: this.config.targetHost ?? '127.0.0.1', }); // Send allowed edges to the hub await this.syncAllowedEdges(); } /** * Stop the tunnel hub. */ public async stop(): Promise { await this.hub.stop(); this.edgeStatuses.clear(); } /** * Sync allowed edges from the manager to the hub. * Call this after creating/deleting/updating edges. */ public async syncAllowedEdges(): Promise { const edges = this.manager.getAllowedEdges(); await this.hub.updateAllowedEdges(edges); } /** * Get runtime statuses for all known edges. */ public getEdgeStatuses(): IRemoteIngressStatus[] { return Array.from(this.edgeStatuses.values()); } /** * Get status for a specific edge. */ public getEdgeStatus(edgeId: string): IRemoteIngressStatus | undefined { return this.edgeStatuses.get(edgeId); } /** * Get the count of connected edges. */ public getConnectedCount(): number { let count = 0; for (const status of this.edgeStatuses.values()) { if (status.connected) count++; } return count; } /** * Get the total number of active tunnels across all edges. */ public getTotalActiveTunnels(): number { let total = 0; for (const status of this.edgeStatuses.values()) { total += status.activeTunnels; } return total; } }