Refactor VPN proxy components and update related behavior
Build and Deploy Gateway / build-and-push (push) Successful in 20s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-11 01:27:46 +03:00
parent c89e56942a
commit aa9c959368
58 changed files with 4234 additions and 2755 deletions
@@ -1,6 +1,12 @@
import { isDeepStrictEqual } from 'node:util';
import type { GatewayAutoState, StoredState } from '../../../shared/contracts/state.js';
import {
appliedProfile,
desiredProfile,
type GatewayAutoState,
type StoredState,
} from '../../../shared/contracts/state.js';
import { HarborError } from '../../../shared/errors.js';
import type { RuntimeCommandResult } from '../connection/index.js';
import { finishRollback } from '../../services/rollback.js';
@@ -25,7 +31,7 @@ interface GatewayAutoServiceDependencies {
read(): StoredState;
update(mutator: (state: StoredState) => Record<string, unknown>): StoredState;
};
subscription: { readConfig(): unknown | null };
subscription: { readConfig(profileId: string): unknown | null };
config: {
build(
subscriptionConfig: unknown,
@@ -42,6 +48,7 @@ interface GatewayAutoServiceDependencies {
isRunning(): boolean;
applyCommand(): Promise<RuntimeCommandResult>;
restoreRunning(): Promise<unknown>;
stopCommand(): Promise<RuntimeCommandResult>;
};
discovery: {
readHostNetwork(): HostNetworkState | null;
@@ -108,22 +115,76 @@ export function createGatewayAutoService(dependencies: GatewayAutoServiceDepende
const previousGatewayAuto = current;
const stateChanged = !isDeepStrictEqual(previousGatewayAuto, candidate);
const modeChanged = previousGatewayAuto.mode !== candidate.mode;
const leavesGatewayDirect = previousGatewayAuto.mode === 'gateway-direct'
&& candidate.mode !== 'gateway-direct';
if (!stateChanged && persistEnabled === undefined) return current;
const previousState = dependencies.state.read();
const subscriptionConfig = modeChanged
? dependencies.subscription.readConfig()
const wasRunning = modeChanged ? dependencies.runtime.isRunning() : false;
const targetProfile = wasRunning
? appliedProfile(previousState)
: desiredProfile(previousState);
const targetServerId = wasRunning
? previousState.appliedServerId
: targetProfile?.desiredServerId || '';
const subscriptionConfig = modeChanged && targetProfile
? dependencies.subscription.readConfig(targetProfile.id)
: null;
const candidateConfig = modeChanged && previousState.selectedServerId && subscriptionConfig
? dependencies.config.build(
const stopUnavailableTarget = async (cause: unknown) => {
let runtimeMutationStarted = false;
let gatewayAutoPublished = false;
let stateCommitStarted = false;
try {
const command = await dependencies.runtime.stopCommand();
runtimeMutationStarted = command.mutationStarted;
if (!command.ok) throw command.error;
current = candidate;
gatewayAutoPublished = true;
stateCommitStarted = true;
dependencies.state.update((state) => ({
...state,
connectionDesired: 'stopped',
appliedProfileId: '',
appliedServerId: '',
appliedServerSnapshot: null,
...(persistEnabled === undefined ? {} : { gatewayAutoEnabled: persistEnabled }),
}));
} catch (error) {
await finishRollback(error, [
...(gatewayAutoPublished ? [{ run: () => { current = previousGatewayAuto; } }] : []),
...(runtimeMutationStarted ? [{
run: () => dependencies.runtime.restoreRunning(),
runtime: true,
}] : []),
...(stateCommitStarted ? [{ run: () => dependencies.state.update(() => previousState) }] : []),
], 'Gateway auto safe-stop rollback failed');
}
dependencies.onDiscoveryWarning(errorMessage(cause));
if (modeChanged) dependencies.onRouteChange(candidate);
throw cause;
};
if (modeChanged && wasRunning && (!targetProfile || !targetServerId || !subscriptionConfig)) {
if (leavesGatewayDirect) return stopUnavailableTarget(new HarborError('CONFIG_INVALID'));
throw new HarborError('CONFIG_INVALID');
}
let candidateConfig: unknown | null = null;
if (modeChanged && targetServerId && subscriptionConfig) {
try {
candidateConfig = dependencies.config.build(
subscriptionConfig,
previousState.selectedServerId,
targetServerId,
previousState.routeRules,
candidate,
)
: null;
);
} catch (error) {
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
if (wasRunning && leavesGatewayDirect && ['CONFIG_INVALID', 'SERVER_NOT_FOUND'].includes(code)) {
return stopUnavailableTarget(error);
}
throw error;
}
}
const previousConfig = candidateConfig === null ? null : dependencies.config.read();
const wasRunning = candidateConfig === null ? false : dependencies.runtime.isRunning();
let configMutationStarted = false;
let runtimeMutationStarted = false;
let gatewayAutoPublished = false;
@@ -171,7 +232,9 @@ export function createGatewayAutoService(dependencies: GatewayAutoServiceDepende
const runRefresh = async ({ reconfigure = true }: RefreshOptions) => {
const state = dependencies.state.read();
const network = state.subscriptionUrl
const profile = desiredProfile(state);
const subscriptionUrl = profile?.subscriptionUrl || '';
const network = subscriptionUrl
? dependencies.discovery.readHostNetwork()
: null;
@@ -182,7 +245,7 @@ export function createGatewayAutoService(dependencies: GatewayAutoServiceDepende
error: discoveryError,
});
const candidate = dependencies.transition.applyPreference(
state.subscriptionUrl
subscriptionUrl
? { ...discoveredState, lastError: discoveryError }
: discoveredState,
state.gatewayAutoEnabled !== false,
@@ -204,16 +267,17 @@ export function createGatewayAutoService(dependencies: GatewayAutoServiceDepende
try {
verifiedGateway = await dependencies.discovery.probeGateway({
gateway: network.gateway,
subscriptionUrl: String(state.subscriptionUrl),
subscriptionUrl,
});
} catch (error) {
const reason = errorMessage(error);
const latestState = dependencies.state.read();
const latestNetwork = latestState.subscriptionUrl
const latestSubscriptionUrl = desiredProfile(latestState)?.subscriptionUrl || '';
const latestNetwork = latestSubscriptionUrl
? dependencies.discovery.readHostNetwork()
: null;
if (
latestState.subscriptionUrl !== state.subscriptionUrl ||
latestSubscriptionUrl !== subscriptionUrl ||
!dependencies.transition.sameRoute(network, latestNetwork)
) {
return commitCandidate(dependencies.transition.createInitial(), { reconfigure });
@@ -232,11 +296,12 @@ export function createGatewayAutoService(dependencies: GatewayAutoServiceDepende
}
const latestState = dependencies.state.read();
const latestNetwork = latestState.subscriptionUrl
const latestSubscriptionUrl = desiredProfile(latestState)?.subscriptionUrl || '';
const latestNetwork = latestSubscriptionUrl
? dependencies.discovery.readHostNetwork()
: null;
if (
latestState.subscriptionUrl !== state.subscriptionUrl ||
latestSubscriptionUrl !== subscriptionUrl ||
!dependencies.transition.sameRoute(network, latestNetwork)
) {
return commitCandidate(dependencies.transition.createInitial(), { reconfigure });