Add native traffic inspection to Harbor Connect and Gateway

This commit is contained in:
2026-08-31 05:19:15 +03:00
parent 116686a138
commit 4d066cb879
62 changed files with 10975 additions and 220 deletions
+33
View File
@@ -84,6 +84,7 @@ import { createConnectivityDiagnosticsRoute } from './http/routes/connectivityDi
import { createGatewayPresenceRoute } from './http/routes/gatewayPresenceRoute.js';
import { createSharedProxyRoute } from './http/routes/sharedProxyRoute.js';
import { createVersionRoute } from './http/routes/versionRoute.js';
import { createLiveTrafficRoute } from './http/routes/liveTrafficRoute.js';
import { createSingboxSelectorService } from './services/singboxSelectorService.js';
import { createFailoverService } from './features/failover/failoverService.js';
import { createFailoverRoute } from './http/routes/failoverRoute.js';
@@ -260,6 +261,16 @@ function selectRuntime() {
throw new Error('Harbor runtime is not configured');
}
const singboxRuntime = selectRuntime();
const clientLiveTraffic = settings.appMode === 'client'
? (await import('./services/liveTrafficService.js')).createLiveTrafficService({
port: settings.singboxNativeApiPort,
enabled: settings.singboxTrafficSource === 'native',
isRuntimeRunning: () => Boolean(localRuntime?.running),
})
: null;
const liveTraffic = clientLiveTraffic || (remoteDataplane ? {
snapshot: () => requireRemoteRuntime().observeLiveTraffic(),
} : null);
function requireRemoteRuntime() {
if (!remoteRuntime) throw new Error('Harbor dataplane runtime is not configured');
@@ -515,6 +526,10 @@ const versionRoute = createVersionRoute({
? () => requireRemoteRuntime().refresh()
: null,
});
const liveTrafficRoute = createLiveTrafficRoute({
traffic: liveTraffic,
deviceInventory: remoteDataplane ? deviceInventory : null,
});
const subscriptionValidationRoute = createSubscriptionValidationRoute({
validateSubscription: createValidateSubscription(fetchSubscription),
readBody,
@@ -887,6 +902,19 @@ function currentConfigMatchesAppliedTarget(state: StoredState) {
} catch {
return false;
}
if (settings.appMode === 'client' || settings.appMode === 'gateway') {
const apiServices = (Array.isArray(config.services) ? config.services : [])
.map(record)
.filter(({ type }) => type === 'api');
const nativeApiMatches = apiServices.length === 1
&& apiServices[0].listen === '127.0.0.1'
&& apiServices[0].listen_port === settings.singboxNativeApiPort
&& apiServices[0].dashboard === false
&& !Object.hasOwn(apiServices[0], 'secret');
const nativeApiExpected = settings.singboxTrafficSource === 'native'
|| settings.singboxTrafficSource === 'shadow';
if (nativeApiExpected ? !nativeApiMatches : apiServices.length > 0) return false;
}
if (state.appliedFailoverPolicy) {
const expectedRole = state.appliedProfileId === state.appliedFailoverPolicy.reserve.profileId
&& state.appliedServerId === state.appliedFailoverPolicy.reserve.serverId
@@ -955,6 +983,8 @@ async function handleApi(req: IncomingMessage, res: ServerResponse) {
if (await versionRoute.handle(req, res)) return;
if (await liveTrafficRoute.handle(req, res)) return;
if (await sharedProxyRoute.handle(req, res)) return;
if (await deviceInventoryRoute.handle(req, res)) return;
@@ -1004,6 +1034,7 @@ async function shutdown() {
gatewayAutoService.stopDiscovery();
if (deviceDiscoveryTimer) clearInterval(deviceDiscoveryTimer);
await gatewayFailover?.shutdown().catch((error) => console.warn(`[control] failover shutdown: ${errorMessage(error)}`));
await clientLiveTraffic?.stop().catch((error) => console.warn(`[control] traffic shutdown: ${errorMessage(error)}`));
await serializeControl(() => singboxRuntime.shutdown());
process.exit(0);
}
@@ -1073,6 +1104,8 @@ if (deviceInventory) {
.catch((error: unknown) => console.warn(`[control] device policy не применена: ${errorMessage(error)}`));
}
clientLiveTraffic?.start();
server.listen(settings.port, '0.0.0.0', () => {
console.log(`[control] ${settings.appMode} UI слушает :${settings.port}`);
});