mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-19 21:52:21 +03:00
feat(proxy): SOCKS5 enabled by default (opt-out, not opt-in)
SOCKS5 support gated on ENABLE_SOCKS5_PROXY === "true" (default OFF), so a fresh deploy with no .env (Docker/npm/Electron) silently REJECTED SOCKS5 proxies and the connections fell back to the host IP — a real cause of the codex shared-IP anomaly when operators shipped SOCKS5 proxies but never set the flag. Flip to opt-out: ON by default, disabled only by an explicit falsey value (false/0/no/off). Centralized semantics applied to all gates: isSocks5ProxyEnabled() (dispatcher), isSocks5Enabled() (proxy route), socks5Enabled (proxies route), BUILD_TIME_SOCKS5 (modal), plus updated disabled-state messages and the docs tab. TDD: 4 tests (unset/empty/true-ish ON, falsey OFF). 18/18 proxy tests green, typecheck:core=0.
This commit is contained in:
@@ -138,8 +138,15 @@ function buildProxyUrlString(parsed: URL, port: string): string {
|
||||
return `${parsed.protocol}//${auth}${parsed.hostname}:${port}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* SOCKS5 proxy support defaults ON (opt-OUT). A fresh deploy with no env set
|
||||
* should honour SOCKS5 proxies out of the box — they were silently rejected
|
||||
* before (default OFF), making accounts fall back to the host IP. Only an
|
||||
* explicit falsey value (false/0/no/off) disables it.
|
||||
*/
|
||||
export function isSocks5ProxyEnabled(): boolean {
|
||||
return process.env.ENABLE_SOCKS5_PROXY === "true";
|
||||
const raw = (process.env.ENABLE_SOCKS5_PROXY ?? "").trim().toLowerCase();
|
||||
return !["false", "0", "no", "off"].includes(raw);
|
||||
}
|
||||
|
||||
export function proxyUrlForLogs(proxyUrl: string): string {
|
||||
@@ -173,7 +180,7 @@ export function normalizeProxyUrl(
|
||||
}
|
||||
if (parsed.protocol === "socks5:" && !allowSocks5) {
|
||||
throw new Error(
|
||||
"[ProxyDispatcher] SOCKS5 proxy is disabled (set ENABLE_SOCKS5_PROXY=true to enable)"
|
||||
"[ProxyDispatcher] SOCKS5 proxy is disabled (remove ENABLE_SOCKS5_PROXY=false to enable — it is ON by default)"
|
||||
);
|
||||
}
|
||||
if (!parsed.hostname) {
|
||||
@@ -233,7 +240,7 @@ export function proxyConfigToUrl(
|
||||
}
|
||||
if (protocol === "socks5:" && !allowSocks5) {
|
||||
throw new Error(
|
||||
"[ProxyDispatcher] SOCKS5 proxy is disabled (set ENABLE_SOCKS5_PROXY=true to enable)"
|
||||
"[ProxyDispatcher] SOCKS5 proxy is disabled (remove ENABLE_SOCKS5_PROXY=false to enable — it is ON by default)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ export default function DocumentationTab() {
|
||||
<h3 className="font-semibold mb-2">SOCKS5</h3>
|
||||
<p className="text-sm text-text-muted">
|
||||
{t("proxyDocumentationSocks5DescBefore")}{" "}
|
||||
<code className="bg-surface-alt px-1 rounded">ENABLE_SOCKS5_PROXY=true</code> to enable.
|
||||
<code className="bg-surface-alt px-1 rounded">ENABLE_SOCKS5_PROXY=false</code> to disable
|
||||
(ON by default).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -42,7 +42,10 @@ export async function GET(request: Request) {
|
||||
return Response.json({
|
||||
items: proxies,
|
||||
total: proxies.length,
|
||||
socks5Enabled: process.env.ENABLE_SOCKS5_PROXY === "true",
|
||||
// Default ON (opt-out): only an explicit falsey value disables SOCKS5.
|
||||
socks5Enabled: !["false", "0", "no", "off"].includes(
|
||||
(process.env.ENABLE_SOCKS5_PROXY ?? "").trim().toLowerCase()
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
return createErrorResponseFromUnknown(error, "Failed to load proxies");
|
||||
|
||||
@@ -31,7 +31,9 @@ const PROXY_LEVEL_TO_REGISTRY_SCOPE = {
|
||||
} as const;
|
||||
|
||||
function isSocks5Enabled() {
|
||||
return process.env.ENABLE_SOCKS5_PROXY === "true";
|
||||
// Default ON (opt-out): only an explicit falsey value disables SOCKS5.
|
||||
const raw = (process.env.ENABLE_SOCKS5_PROXY ?? "").trim().toLowerCase();
|
||||
return !["false", "0", "no", "off"].includes(raw);
|
||||
}
|
||||
|
||||
function getSupportedProxyTypes() {
|
||||
@@ -106,7 +108,7 @@ function normalizeAndValidateProxy(
|
||||
const type = String(proxy.type || "http").toLowerCase() as NonNullable<ProxyConfigInput["type"]>;
|
||||
if (type === "socks5" && !isSocks5Enabled()) {
|
||||
throw createInvalidProxyError(
|
||||
"SOCKS5 proxy is disabled (set ENABLE_SOCKS5_PROXY=true to enable)"
|
||||
"SOCKS5 proxy is disabled (remove ENABLE_SOCKS5_PROXY=false to enable — it is ON by default)"
|
||||
);
|
||||
}
|
||||
if (type.startsWith("socks") && type !== "socks5") {
|
||||
|
||||
@@ -12,7 +12,10 @@ const ALL_PROXY_TYPES = [
|
||||
];
|
||||
// Build-time fallback (static deploys). The live value comes from GET /api/settings/proxies
|
||||
// (server ENABLE_SOCKS5_PROXY) so a runtime Docker env is honoured — #3508.
|
||||
const BUILD_TIME_SOCKS5 = process.env.NEXT_PUBLIC_ENABLE_SOCKS5_PROXY === "true";
|
||||
// Default ON (opt-out) to match the server: only an explicit falsey value hides SOCKS5.
|
||||
const BUILD_TIME_SOCKS5 = !["false", "0", "no", "off"].includes(
|
||||
(process.env.NEXT_PUBLIC_ENABLE_SOCKS5_PROXY ?? "").trim().toLowerCase()
|
||||
);
|
||||
export function buildProxyTypes(socks5Enabled: boolean) {
|
||||
return socks5Enabled ? ALL_PROXY_TYPES : ALL_PROXY_TYPES.filter((type) => type.value !== "socks5");
|
||||
}
|
||||
|
||||
43
tests/unit/socks5-default-on.test.ts
Normal file
43
tests/unit/socks5-default-on.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* TDD — SOCKS5 proxy support must default ON (opt-OUT), so a fresh deploy
|
||||
* (Docker/npm/Electron with no .env) honours SOCKS5 proxies out of the box.
|
||||
* Previously the code defaulted OFF (`=== "true"`), so SOCKS5 proxies were
|
||||
* silently rejected unless the operator set the env explicitly — accounts then
|
||||
* fell back to the host IP. Only an explicit falsey value disables it now.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { isSocks5ProxyEnabled } = await import("../../open-sse/utils/proxyDispatcher.ts");
|
||||
|
||||
function withEnv(value: string | undefined, fn: () => void) {
|
||||
const prev = process.env.ENABLE_SOCKS5_PROXY;
|
||||
if (value === undefined) delete process.env.ENABLE_SOCKS5_PROXY;
|
||||
else process.env.ENABLE_SOCKS5_PROXY = value;
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
if (prev === undefined) delete process.env.ENABLE_SOCKS5_PROXY;
|
||||
else process.env.ENABLE_SOCKS5_PROXY = prev;
|
||||
}
|
||||
}
|
||||
|
||||
test("SOCKS5 is ON by default when the env is unset", () => {
|
||||
withEnv(undefined, () => assert.equal(isSocks5ProxyEnabled(), true));
|
||||
});
|
||||
|
||||
test("SOCKS5 is ON for empty string (treated as unset)", () => {
|
||||
withEnv("", () => assert.equal(isSocks5ProxyEnabled(), true));
|
||||
});
|
||||
|
||||
test("SOCKS5 stays ON for explicit true-ish values", () => {
|
||||
for (const v of ["true", "TRUE", "1", "yes", "on"]) {
|
||||
withEnv(v, () => assert.equal(isSocks5ProxyEnabled(), true, `value ${v} must enable`));
|
||||
}
|
||||
});
|
||||
|
||||
test("SOCKS5 is OFF only for explicit falsey values (opt-out)", () => {
|
||||
for (const v of ["false", "FALSE", "0", "no", "off"]) {
|
||||
withEnv(v, () => assert.equal(isSocks5ProxyEnabled(), false, `value ${v} must disable`));
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user