Introduce stable server IDs for subscription state
Build and Deploy Gateway / build-and-push (push) Successful in 14s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-07-12 11:59:22 +03:00
parent 005c7a101b
commit 267afc5c7e
16 changed files with 323 additions and 145 deletions
+17 -23
View File
@@ -1,4 +1,5 @@
import { normalizeRouteRules } from '../routingRules.js';
import { normalizeServers, resolveServerId } from '../serverIdentity.js';
const MODES = new Set(['client', 'gateway']);
const CONNECTION_STATES = new Set(['running', 'stopped']);
@@ -12,13 +13,21 @@ const dateOrNull = (value) => (
export function normalizeStoredState(value) {
const state = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
const selectedTag = text(state.selectedTag);
const servers = normalizeServers(state.servers);
const selectedServerId = resolveServerId(servers, state.selectedServerId, state.selectedTag);
const appliedServerId = Object.hasOwn(state, 'appliedServerId')
? resolveServerId(servers, state.appliedServerId)
: resolveServerId(servers, '', state.appliedTag || state.selectedTag);
const selectedServer = servers.find((server) => server.id === selectedServerId);
const appliedServer = servers.find((server) => server.id === appliedServerId);
return {
...state,
revision: Number.isSafeInteger(state.revision) && state.revision >= 0 ? state.revision : 0,
selectedTag,
appliedTag: Object.hasOwn(state, 'appliedTag') ? text(state.appliedTag) : selectedTag,
servers: Array.isArray(state.servers) ? state.servers : [],
selectedServerId,
appliedServerId,
selectedTag: selectedServer?.label || '',
appliedTag: appliedServer?.label || '',
servers,
routeRules: normalizeRouteRules(state.routeRules),
appliedRouteRules: normalizeRouteRules(state.appliedRouteRules),
routeRulesRevision: Number.isSafeInteger(state.routeRulesRevision) && state.routeRulesRevision >= 0
@@ -43,22 +52,7 @@ export function createStateSnapshot({
const desired = CONNECTION_STATES.has(stored.connectionDesired)
? stored.connectionDesired
: configExists ? 'running' : 'stopped';
const servers = stored.servers.map((server) => {
const tag = text(server.tag);
return {
...server,
id: tag,
label: tag,
host: text(server.server),
port: Number(server.server_port) || 0,
protocol: text(server.type),
// v0 compatibility: the current UI and integrations still read these aliases.
tag,
server: text(server.server),
server_port: Number(server.server_port) || 0,
type: text(server.type),
};
});
const servers = stored.servers;
const routeMode = mode === 'client' ? gatewayAuto?.mode || 'local-vpn' : 'gateway-transparent';
const activeLocalRules = runtime?.running ? stored.appliedRouteRules : [];
@@ -74,8 +68,8 @@ export function createStateSnapshot({
userInfo: stored.userInfo && typeof stored.userInfo === 'object' ? stored.userInfo : {},
},
selection: {
desiredServerId: stored.selectedTag,
appliedServerId: stored.appliedTag,
desiredServerId: stored.selectedServerId,
appliedServerId: stored.appliedServerId,
},
connection: {
desired,
@@ -120,7 +114,7 @@ export function withStateV0Compatibility(snapshot, {
singboxStartedAt: snapshot.connection.startedAt,
subscriptionHost: snapshot.subscription.host,
hasSubscription: snapshot.subscription.status === 'ready',
selectedTag: snapshot.selection.desiredServerId,
selectedTag: stored.selectedTag,
userInfo: snapshot.subscription.userInfo,
fetchedAt: snapshot.subscription.fetchedAt,
gatewayAuto: snapshot.mode === 'client' ? {