2026-04-29 12:58:06 +00:00
|
|
|
import { RemoteIngressEdge } from './classes.remoteingressedge.js';
|
|
|
|
|
import { RemoteIngressHub, type IHubConfig, type TAllowedEdge } from './classes.remoteingresshub.js';
|
|
|
|
|
|
2026-02-16 11:22:23 +00:00
|
|
|
export * from './classes.remoteingresshub.js';
|
|
|
|
|
export * from './classes.remoteingressedge.js';
|
2026-02-17 19:36:40 +00:00
|
|
|
export * from './classes.token.js';
|
2026-04-29 12:58:06 +00:00
|
|
|
|
|
|
|
|
const usage = `remoteingress
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
remoteingress hub [--tunnel-port 8443] [--target-host 127.0.0.1]
|
|
|
|
|
remoteingress edge --token <connection-token>
|
|
|
|
|
remoteingress edge --hub-host <host> --edge-id <id> --secret <secret> [--hub-port 8443]
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
|
REMOTEINGRESS_MODE=hub|edge
|
|
|
|
|
REMOTEINGRESS_TOKEN=<connection-token>
|
|
|
|
|
REMOTEINGRESS_HUB_HOST=<host>
|
|
|
|
|
REMOTEINGRESS_HUB_PORT=8443
|
|
|
|
|
REMOTEINGRESS_EDGE_ID=<id>
|
|
|
|
|
REMOTEINGRESS_SECRET=<secret>
|
|
|
|
|
REMOTEINGRESS_TARGET_HOST=127.0.0.1
|
|
|
|
|
REMOTEINGRESS_ALLOWED_EDGES_JSON='[{"id":"edge-1","secret":"secret","listenPorts":[80,443]}]'
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const readArg = (args: string[], name: string): string | undefined => {
|
|
|
|
|
const prefix = `--${name}=`;
|
|
|
|
|
const inlineValue = args.find((arg) => arg.startsWith(prefix));
|
|
|
|
|
if (inlineValue) {
|
|
|
|
|
return inlineValue.slice(prefix.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const index = args.indexOf(`--${name}`);
|
|
|
|
|
if (index >= 0) {
|
|
|
|
|
return args[index + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const readNumber = (value: string | undefined, fallback: number): number => {
|
|
|
|
|
if (!value) {
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parsed = Number(value);
|
|
|
|
|
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
|
|
|
throw new Error(`Invalid port: ${value}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parsed;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const readJson = <T>(value: string | undefined, fallback: T): T => {
|
|
|
|
|
if (!value) {
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return JSON.parse(value) as T;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const waitForever = async (stop: () => Promise<void>) => {
|
|
|
|
|
let stopping = false;
|
|
|
|
|
const handleStop = async () => {
|
|
|
|
|
if (stopping) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
stopping = true;
|
|
|
|
|
await stop();
|
|
|
|
|
process.exit(0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
process.once('SIGINT', () => void handleStop());
|
|
|
|
|
process.once('SIGTERM', () => void handleStop());
|
|
|
|
|
|
|
|
|
|
await new Promise(() => {});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const runCli = async () => {
|
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
|
if (args.includes('--help') || args.includes('-h')) {
|
|
|
|
|
console.log(usage);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const positionalMode = args[0]?.startsWith('--') ? undefined : args[0];
|
|
|
|
|
const mode = readArg(args, 'mode') ?? positionalMode ?? process.env.REMOTEINGRESS_MODE;
|
|
|
|
|
|
|
|
|
|
if (mode === 'hub') {
|
|
|
|
|
const hub = new RemoteIngressHub();
|
|
|
|
|
const config: IHubConfig = {
|
|
|
|
|
tunnelPort: readNumber(readArg(args, 'tunnel-port') ?? process.env.REMOTEINGRESS_TUNNEL_PORT, 8443),
|
|
|
|
|
targetHost: readArg(args, 'target-host') ?? process.env.REMOTEINGRESS_TARGET_HOST ?? '127.0.0.1',
|
|
|
|
|
tls: {
|
|
|
|
|
certPem: readArg(args, 'tls-cert-pem') ?? process.env.REMOTEINGRESS_TLS_CERT_PEM,
|
|
|
|
|
keyPem: readArg(args, 'tls-key-pem') ?? process.env.REMOTEINGRESS_TLS_KEY_PEM,
|
|
|
|
|
},
|
|
|
|
|
performance: readJson(readArg(args, 'performance-json') ?? process.env.REMOTEINGRESS_PERFORMANCE_JSON, undefined),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await hub.start(config);
|
|
|
|
|
|
|
|
|
|
const allowedEdges = readJson<TAllowedEdge[]>(
|
|
|
|
|
readArg(args, 'allowed-edges-json') ?? process.env.REMOTEINGRESS_ALLOWED_EDGES_JSON,
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
if (allowedEdges.length > 0) {
|
|
|
|
|
await hub.updateAllowedEdges(allowedEdges);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`RemoteIngress hub listening on ${config.tunnelPort}`);
|
|
|
|
|
await waitForever(() => hub.stop());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode === 'edge') {
|
|
|
|
|
const edge = new RemoteIngressEdge();
|
|
|
|
|
const token = readArg(args, 'token') ?? process.env.REMOTEINGRESS_TOKEN;
|
|
|
|
|
|
|
|
|
|
if (token) {
|
|
|
|
|
await edge.start({ token });
|
|
|
|
|
} else {
|
|
|
|
|
const hubHost = readArg(args, 'hub-host') ?? process.env.REMOTEINGRESS_HUB_HOST;
|
|
|
|
|
const edgeId = readArg(args, 'edge-id') ?? process.env.REMOTEINGRESS_EDGE_ID;
|
|
|
|
|
const secret = readArg(args, 'secret') ?? process.env.REMOTEINGRESS_SECRET;
|
|
|
|
|
|
|
|
|
|
if (!hubHost || !edgeId || !secret) {
|
|
|
|
|
throw new Error('Edge mode requires --token or --hub-host, --edge-id, and --secret');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await edge.start({
|
|
|
|
|
hubHost,
|
|
|
|
|
hubPort: readNumber(readArg(args, 'hub-port') ?? process.env.REMOTEINGRESS_HUB_PORT, 8443),
|
|
|
|
|
edgeId,
|
|
|
|
|
secret,
|
|
|
|
|
bindAddress: readArg(args, 'bind-address') ?? process.env.REMOTEINGRESS_BIND_ADDRESS,
|
|
|
|
|
transportMode: readArg(args, 'transport-mode') as any,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('RemoteIngress edge started');
|
|
|
|
|
await waitForever(() => edge.stop());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(usage);
|
|
|
|
|
};
|