95 lines
2.5 KiB
JavaScript
95 lines
2.5 KiB
JavaScript
function normalizeControlUrl(value) {
|
|
const raw = String(value || "").trim();
|
|
if (!raw) return "";
|
|
const withProtocol = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
|
|
const url = new URL(withProtocol);
|
|
if (!["http:", "https:"].includes(url.protocol)) {
|
|
throw new Error("Gateway URL must use http or https");
|
|
}
|
|
url.hash = "";
|
|
url.search = "";
|
|
url.pathname = url.pathname.replace(/\/api\/shared-proxy\/?$/, "") || "/";
|
|
return url.toString().replace(/\/$/, "");
|
|
}
|
|
|
|
function proxyHostFromHeader(hostHeader) {
|
|
const raw = String(hostHeader || "").trim();
|
|
if (!raw) return "";
|
|
if (raw.startsWith("[")) {
|
|
const end = raw.indexOf("]");
|
|
return end > 0 ? raw.slice(1, end) : "";
|
|
}
|
|
return raw.split(":")[0];
|
|
}
|
|
|
|
function normalizeProxyInfo(proxy) {
|
|
if (!proxy || typeof proxy !== "object") return null;
|
|
const host = String(proxy.host || "").trim();
|
|
const port = Number.parseInt(proxy.port, 10);
|
|
const protocol = proxy.protocol === "http" ? "http" : "socks5";
|
|
if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
return null;
|
|
}
|
|
return { host, port, protocol };
|
|
}
|
|
|
|
export function buildSharedProxyInfo({
|
|
appMode,
|
|
proxyPort,
|
|
running,
|
|
hostHeader,
|
|
sharedProxyHost,
|
|
}) {
|
|
const host = String(sharedProxyHost || "").trim() || proxyHostFromHeader(hostHeader);
|
|
const port = Number.parseInt(proxyPort, 10);
|
|
const available =
|
|
appMode === "gateway" &&
|
|
Boolean(running) &&
|
|
host &&
|
|
Number.isInteger(port) &&
|
|
port > 0 &&
|
|
port <= 65535;
|
|
|
|
const proxy = available
|
|
? {
|
|
host,
|
|
port,
|
|
protocol: "socks5",
|
|
httpUrl: `http://${host}:${port}`,
|
|
socksUrl: `socks5://${host}:${port}`,
|
|
}
|
|
: null;
|
|
|
|
return {
|
|
success: true,
|
|
available,
|
|
mode: appMode,
|
|
proxy,
|
|
};
|
|
}
|
|
|
|
export async function checkSharedProxyGateway(controlUrl, fetchImpl = fetch) {
|
|
const baseUrl = normalizeControlUrl(controlUrl);
|
|
const response = await fetchImpl(`${baseUrl}/api/shared-proxy`, {
|
|
headers: { accept: "application/json" },
|
|
});
|
|
const data = await response.json().catch(() => ({}));
|
|
if (!response.ok || data.success === false) {
|
|
throw new Error(data.error || `Gateway returned ${response.status}`);
|
|
}
|
|
if (!data.available) {
|
|
throw new Error("Gateway shared proxy is not available");
|
|
}
|
|
|
|
const sharedProxy = normalizeProxyInfo(data.proxy);
|
|
if (!sharedProxy) {
|
|
throw new Error("Gateway returned invalid shared proxy settings");
|
|
}
|
|
|
|
return {
|
|
sharedProxyEnabled: true,
|
|
sharedProxyControlUrl: baseUrl,
|
|
sharedProxy,
|
|
};
|
|
}
|