feat: link mac client to shared gateway proxy
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 11s
Build and Deploy Gateway / deploy (push) Successful in 0s

This commit is contained in:
2026-05-19 22:47:05 +03:00
parent f914c28bc5
commit 95edefa84f
11 changed files with 501 additions and 29 deletions

View File

@@ -21,6 +21,10 @@ import {
readClientSettings,
writeClientSettings,
} from "./clientSettings.js";
import {
buildSharedProxyInfo,
checkSharedProxyGateway,
} from "./sharedProxy.js";
import { matchRoute, detectRuleConflicts } from "./routeMatcher.js";
import { tcpPing, resolveHost } from "./ping.js";
@@ -722,6 +726,36 @@ async function applySelectedServer(selectedTag) {
});
}
async function applyClientSharedProxy() {
const clientSettings = readClientSettings();
if (!clientSettings.sharedProxyEnabled || !clientSettings.sharedProxy) {
return false;
}
const generated = buildGatewayConfig(
{ outbounds: [], customRules: [] },
"",
);
writeSingboxConfig(generated);
await startSingbox();
pushLog(
"info",
`Mac client uses shared gateway proxy ${clientSettings.sharedProxy.host}:${clientSettings.sharedProxy.port}`,
);
return true;
}
async function applyClientDirectProxy() {
const generated = buildGatewayConfig(
{ outbounds: [], customRules: [] },
"",
);
writeSingboxConfig(generated);
await startSingbox();
pushLog("info", "Mac client routes local proxy directly");
return true;
}
function handleLogsStream(req, res) {
res.writeHead(200, {
"content-type": "text/event-stream; charset=utf-8",
@@ -756,6 +790,20 @@ async function handleApi(req, res) {
return sendJson(res, 200, publicState());
}
if (req.method === "GET" && req.url === "/api/shared-proxy") {
return sendJson(
res,
200,
buildSharedProxyInfo({
appMode: settings.appMode,
proxyPort: settings.proxyPort,
running: Boolean(singboxProcess),
hostHeader: req.headers.host,
sharedProxyHost: settings.sharedProxyHost,
}),
);
}
if (req.method === "GET" && req.url === "/api/config") {
const config = readSingboxConfig();
return sendJson(res, 200, { success: true, config });
@@ -947,12 +995,48 @@ async function handleApi(req, res) {
const clientSettings = writeClientSettings(body.clientSettings || body);
const prevState = readJson(settings.statePath, {});
if (
settings.appMode === "client" &&
prevState.selectedTag &&
readJson(settings.subscriptionCachePath, null)?.config
) {
await applySelectedServer(prevState.selectedTag);
if (settings.appMode === "client") {
if (clientSettings.sharedProxyEnabled) {
await applyClientSharedProxy();
} else if (clientSettings.homeBypassEnabled) {
await applyClientDirectProxy();
} else if (
prevState.selectedTag &&
readJson(settings.subscriptionCachePath, null)?.config
) {
await applySelectedServer(prevState.selectedTag);
} else {
await stopSingbox();
removeSingboxConfig();
}
}
return sendJson(res, 200, {
success: true,
clientSettings,
singboxRunning: Boolean(singboxProcess),
});
}
if (req.method === "POST" && req.url === "/api/client-settings/shared-proxy/check") {
const body = await readBody(req);
const url = String(body.url || "").trim();
if (!url) {
return sendJson(res, 400, {
success: false,
error: "Укажите адрес gateway",
});
}
const patch = await checkSharedProxyGateway(url);
const clientSettings = writeClientSettings({
...readClientSettings(),
...patch,
homeBypassEnabled: false,
});
if (settings.appMode === "client") {
await applyClientSharedProxy();
}
return sendJson(res, 200, {
@@ -1348,6 +1432,14 @@ async function handleApi(req, res) {
error: "selectedTag обязателен",
});
if (settings.appMode === "client") {
writeClientSettings({
...readClientSettings(),
homeBypassEnabled: false,
sharedProxyEnabled: false,
});
}
await applySelectedServer(selectedTag);
return sendJson(res, 200, {
success: true,