Add gateway auto toggle for client mode
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 13s
Build and Deploy Gateway / deploy (push) Successful in 1s

This commit is contained in:
2026-07-11 15:17:07 +03:00
parent 51312d51cd
commit 0ab912c64c
7 changed files with 197 additions and 14 deletions

View File

@@ -174,6 +174,13 @@ export function createGatewayAutoState() {
};
}
export function applyGatewayPreference(state, enabled) {
return {
...state,
mode: enabled && state.gatewayId ? 'gateway-direct' : 'local-vpn',
};
}
export function nextGatewayAutoState(current, {
network,
verifiedGateway = null,

View File

@@ -5,6 +5,7 @@ import { spawn, spawnSync } from 'node:child_process';
import { isDeepStrictEqual } from 'node:util';
import { settings } from './config.js';
import {
applyGatewayPreference,
buildGatewayPresence,
createGatewayAutoState,
nextGatewayAutoState,
@@ -183,6 +184,7 @@ async function startSingbox() {
function publicState() {
const state = readJson(settings.statePath, {});
const gatewayAutoEnabled = state.gatewayAutoEnabled !== false;
return {
mode: settings.appMode,
port: settings.port,
@@ -197,6 +199,8 @@ function publicState() {
fetchedAt: state.fetchedAt || null,
gatewayAuto: settings.appMode === 'client' ? {
mode: gatewayAutoState.mode,
enabled: gatewayAutoEnabled,
available: Boolean(gatewayAutoState.gatewayId),
address: gatewayAutoState.gateway?.gateway || '',
interface: gatewayAutoState.gateway?.interface || '',
failures: gatewayAutoState.failures,
@@ -291,7 +295,10 @@ function refreshGatewayAutoMode({ reconfigure = true } = {}) {
return gatewayAutoState;
}
await applyGatewayAutoState(
nextGatewayAutoState(gatewayAutoState, { network: latestNetwork, verifiedGateway }),
applyGatewayPreference(
nextGatewayAutoState(gatewayAutoState, { network: latestNetwork, verifiedGateway }),
latestState.gatewayAutoEnabled !== false,
),
{ reconfigure },
);
} catch (error) {
@@ -311,7 +318,10 @@ function refreshGatewayAutoMode({ reconfigure = true } = {}) {
console.warn(`[control] Gateway не используется: ${reason}`);
}
await applyGatewayAutoState(
nextGatewayAutoState(gatewayAutoState, { network: latestNetwork, error: reason }),
applyGatewayPreference(
nextGatewayAutoState(gatewayAutoState, { network: latestNetwork, error: reason }),
latestState.gatewayAutoEnabled !== false,
),
{ reconfigure },
);
}
@@ -458,8 +468,10 @@ async function handleApi(req, res) {
const parsed = await fetchSubscription(normalizedUrl);
await serializeControl(async () => {
writeJson(settings.subscriptionCachePath, { url: normalizedUrl, ...parsed });
const previousState = readJson(settings.statePath, {});
writeJson(settings.statePath, {
subscriptionUrl: normalizedUrl,
gatewayAutoEnabled: previousState.gatewayAutoEnabled !== false,
servers: parsed.servers,
userInfo: parsed.userInfo,
fetchedAt: parsed.fetchedAt,
@@ -481,6 +493,24 @@ async function handleApi(req, res) {
return sendJson(res, 200, await refreshSavedSubscription());
}
if (req.method === 'POST' && req.url === '/api/gateway-auto') {
if (settings.appMode !== 'client') {
return sendJson(res, 400, { success: false, error: 'Режим доступен только в Harbor Connect' });
}
const { enabled } = await readBody(req);
if (typeof enabled !== 'boolean') {
return sendJson(res, 400, { success: false, error: 'Укажите enabled: true или false' });
}
await serializeControl(async () => {
writeJson(settings.statePath, {
...readJson(settings.statePath, {}),
gatewayAutoEnabled: enabled,
});
await applyGatewayAutoState(applyGatewayPreference(gatewayAutoState, enabled));
});
return sendJson(res, 200, { success: true, gatewayAuto: publicState().gatewayAuto });
}
if (req.method === 'DELETE' && req.url === '/api/subscription') {
await serializeControl(async () => {
await stopSingbox();