feat(remoteingress): derive effective remote ingress listen ports from route configs and expose them via ops API

This commit is contained in:
2026-02-17 10:55:31 +00:00
parent 018efa32f6
commit 69be2295f1
10 changed files with 128 additions and 15 deletions

View File

@@ -1,9 +1,30 @@
import * as plugins from '../plugins.js';
import type { StorageManager } from '../storage/classes.storagemanager.js';
import type { IRemoteIngress } from '../../ts_interfaces/data/remoteingress.js';
import type { IRemoteIngress, IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
const STORAGE_PREFIX = '/remote-ingress/';
/**
* Flatten a port range (number | number[] | Array<{from, to}>) to a sorted unique number array.
*/
function extractPorts(portRange: number | number[] | Array<{ from: number; to: number }>): number[] {
const ports = new Set<number>();
if (typeof portRange === 'number') {
ports.add(portRange);
} else if (Array.isArray(portRange)) {
for (const entry of portRange) {
if (typeof entry === 'number') {
ports.add(entry);
} else if (typeof entry === 'object' && 'from' in entry && 'to' in entry) {
for (let p = entry.from; p <= entry.to; p++) {
ports.add(p);
}
}
}
}
return [...ports].sort((a, b) => a - b);
}
/**
* Manages CRUD for remote ingress edge registrations.
* Persists edge configs via StorageManager and provides
@@ -12,6 +33,7 @@ const STORAGE_PREFIX = '/remote-ingress/';
export class RemoteIngressManager {
private storageManager: StorageManager;
private edges: Map<string, IRemoteIngress> = new Map();
private routes: IDcRouterRouteConfig[] = [];
constructor(storageManager: StorageManager) {
this.storageManager = storageManager;
@@ -30,12 +52,60 @@ export class RemoteIngressManager {
}
}
/**
* Store the current route configs for port derivation.
*/
public setRoutes(routes: IDcRouterRouteConfig[]): void {
this.routes = routes;
}
/**
* Derive listen ports for an edge from routes tagged with remoteIngress.enabled.
* When a route specifies edgeFilter, only edges whose id or tags match get that route's ports.
* When edgeFilter is absent, the route applies to all edges.
*/
public derivePortsForEdge(edgeId: string, edgeTags?: string[]): number[] {
const ports = new Set<number>();
for (const route of this.routes) {
if (!route.remoteIngress?.enabled) continue;
// Apply edge filter if present
const filter = route.remoteIngress.edgeFilter;
if (filter && filter.length > 0) {
const idMatch = filter.includes(edgeId);
const tagMatch = edgeTags?.some((tag) => filter.includes(tag)) ?? false;
if (!idMatch && !tagMatch) continue;
}
// Extract ports from the route match
if (route.match?.ports) {
for (const p of extractPorts(route.match.ports)) {
ports.add(p);
}
}
}
return [...ports].sort((a, b) => a - b);
}
/**
* Get the effective listen ports for an edge.
* Returns manual listenPorts if non-empty, otherwise derives ports from tagged routes.
*/
public getEffectiveListenPorts(edge: IRemoteIngress): number[] {
if (edge.listenPorts && edge.listenPorts.length > 0) {
return edge.listenPorts;
}
return this.derivePortsForEdge(edge.id, edge.tags);
}
/**
* Create a new edge registration.
*/
public async createEdge(
name: string,
listenPorts: number[],
listenPorts: number[] = [],
tags?: string[],
): Promise<IRemoteIngress> {
const id = plugins.uuid.v4();