import * as plugins from '../plugins.js'; import type { StorageManager } from '../storage/classes.storagemanager.js'; import type { IRemoteIngress } from '../../ts_interfaces/data/remoteingress.js'; const STORAGE_PREFIX = '/remote-ingress/'; /** * Manages CRUD for remote ingress edge registrations. * Persists edge configs via StorageManager and provides * the allowed edges list for the Rust hub. */ export class RemoteIngressManager { private storageManager: StorageManager; private edges: Map = new Map(); constructor(storageManager: StorageManager) { this.storageManager = storageManager; } /** * Load all edge registrations from storage into memory. */ public async initialize(): Promise { const keys = await this.storageManager.list(STORAGE_PREFIX); for (const key of keys) { const edge = await this.storageManager.getJSON(key); if (edge) { this.edges.set(edge.id, edge); } } } /** * Create a new edge registration. */ public async createEdge( name: string, listenPorts: number[], tags?: string[], ): Promise { const id = plugins.uuid.v4(); const secret = plugins.crypto.randomBytes(32).toString('hex'); const now = Date.now(); const edge: IRemoteIngress = { id, name, secret, listenPorts, enabled: true, tags: tags || [], createdAt: now, updatedAt: now, }; await this.storageManager.setJSON(`${STORAGE_PREFIX}${id}`, edge); this.edges.set(id, edge); return edge; } /** * Get an edge by ID. */ public getEdge(id: string): IRemoteIngress | undefined { return this.edges.get(id); } /** * Get all edge registrations. */ public getAllEdges(): IRemoteIngress[] { return Array.from(this.edges.values()); } /** * Update an edge registration. */ public async updateEdge( id: string, updates: { name?: string; listenPorts?: number[]; enabled?: boolean; tags?: string[]; }, ): Promise { const edge = this.edges.get(id); if (!edge) { return null; } if (updates.name !== undefined) edge.name = updates.name; if (updates.listenPorts !== undefined) edge.listenPorts = updates.listenPorts; if (updates.enabled !== undefined) edge.enabled = updates.enabled; if (updates.tags !== undefined) edge.tags = updates.tags; edge.updatedAt = Date.now(); await this.storageManager.setJSON(`${STORAGE_PREFIX}${id}`, edge); this.edges.set(id, edge); return edge; } /** * Delete an edge registration. */ public async deleteEdge(id: string): Promise { if (!this.edges.has(id)) { return false; } await this.storageManager.delete(`${STORAGE_PREFIX}${id}`); this.edges.delete(id); return true; } /** * Regenerate the secret for an edge. */ public async regenerateSecret(id: string): Promise { const edge = this.edges.get(id); if (!edge) { return null; } edge.secret = plugins.crypto.randomBytes(32).toString('hex'); edge.updatedAt = Date.now(); await this.storageManager.setJSON(`${STORAGE_PREFIX}${id}`, edge); this.edges.set(id, edge); return edge.secret; } /** * Verify an edge's secret using constant-time comparison. */ public verifySecret(id: string, secret: string): boolean { const edge = this.edges.get(id); if (!edge) { return false; } const expected = Buffer.from(edge.secret); const provided = Buffer.from(secret); if (expected.length !== provided.length) { return false; } return plugins.crypto.timingSafeEqual(expected, provided); } /** * Get the list of allowed edges (enabled only) for the Rust hub. */ public getAllowedEdges(): Array<{ id: string; secret: string }> { const result: Array<{ id: string; secret: string }> = []; for (const edge of this.edges.values()) { if (edge.enabled) { result.push({ id: edge.id, secret: edge.secret }); } } return result; } }