From 0e207dc5d21a2d8a65e29dac2c1f1ddd5016a575 Mon Sep 17 00:00:00 2001 From: duongvdo Date: Sun, 1 Mar 2026 00:48:27 +0700 Subject: [PATCH] fix: proxy logic bugs and Docker build failure - URL-encode proxy credentials to handle special characters in passwords - Decode URL-encoded credentials during legacy proxy migration - Fix HTTPS proxy default port (443 instead of 8080) in frontend and migration - Add dispatcher cache invalidation when proxy config changes - Cast proxy port to number for SQLite INTEGER column in proxy logger - Fix redundant .replace("//", "") in migration protocol parsing - Copy postinstall script in Dockerfile before npm install Co-Authored-By: Claude Opus 4.6 --- Dockerfile | 1 + open-sse/utils/proxyDispatcher.ts | 15 ++++++++++++--- src/app/api/settings/proxy/route.ts | 3 +++ src/lib/db/settings.ts | 8 ++++---- src/lib/proxyLogger.ts | 2 +- src/shared/components/ProxyConfigModal.tsx | 6 +++++- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71b8c640..e5c61740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM node:22-bookworm-slim AS builder WORKDIR /app COPY package*.json ./ +COPY scripts/postinstall.mjs ./scripts/postinstall.mjs RUN if [ -f package-lock.json ]; then npm ci --no-audit --no-fund; else npm install --no-audit --no-fund; fi COPY . ./ diff --git a/open-sse/utils/proxyDispatcher.ts b/open-sse/utils/proxyDispatcher.ts index e82b279e..6166df96 100644 --- a/open-sse/utils/proxyDispatcher.ts +++ b/open-sse/utils/proxyDispatcher.ts @@ -4,13 +4,22 @@ import { socksDispatcher } from "fetch-socks"; const DISPATCHER_CACHE_KEY = Symbol.for("omniroute.proxyDispatcher.cache"); const SUPPORTED_PROTOCOLS = new Set(["http:", "https:", "socks5:"]); -function getDispatcherCache() { +function getDispatcherCache(): Map { if (!globalThis[DISPATCHER_CACHE_KEY]) { globalThis[DISPATCHER_CACHE_KEY] = new Map(); } return globalThis[DISPATCHER_CACHE_KEY]; } +/** + * Clear all cached proxy dispatchers. + * Call this when proxy configuration changes to avoid stale connections. + */ +export function clearDispatcherCache() { + const cache = getDispatcherCache(); + cache.clear(); +} + function defaultPortForProtocol(protocol) { if (protocol === "https:" || protocol === "wss:") return "443"; if (protocol === "socks5:") return "1080"; @@ -96,8 +105,8 @@ export function proxyConfigToUrl(proxyConfig, { allowSocks5 = isSocks5ProxyEnabl const proxyUrl = new URL(`${type}://${proxyConfig.host}:${port}`); if (proxyConfig.username) { - proxyUrl.username = proxyConfig.username; - proxyUrl.password = proxyConfig.password || ""; + proxyUrl.username = encodeURIComponent(proxyConfig.username); + proxyUrl.password = proxyConfig.password ? encodeURIComponent(proxyConfig.password) : ""; } return normalizeProxyUrl(proxyUrl.toString(), "context proxy", { allowSocks5 }); diff --git a/src/app/api/settings/proxy/route.ts b/src/app/api/settings/proxy/route.ts index f8943763..6ec8ea84 100644 --- a/src/app/api/settings/proxy/route.ts +++ b/src/app/api/settings/proxy/route.ts @@ -5,6 +5,7 @@ import { deleteProxyForLevel, resolveProxyForConnection, } from "../../../../lib/localDb"; +import { clearDispatcherCache } from "@omniroute/open-sse/utils/proxyDispatcher"; const BASE_SUPPORTED_PROXY_TYPES = new Set(["http", "https"]); @@ -129,6 +130,7 @@ export async function PUT(request) { const body = await request.json(); const normalizedBody = normalizeProxyPayload(body); const updated = await setProxyConfig(normalizedBody); + clearDispatcherCache(); return Response.json(updated); } catch (error) { const status = Number(error?.status) || 500; @@ -155,6 +157,7 @@ export async function DELETE(request) { } const updated = await deleteProxyForLevel(level, id); + clearDispatcherCache(); return Response.json(updated); } catch (error) { return Response.json( diff --git a/src/lib/db/settings.ts b/src/lib/db/settings.ts index cdd8344b..67e08767 100644 --- a/src/lib/db/settings.ts +++ b/src/lib/db/settings.ts @@ -211,11 +211,11 @@ function migrateProxyEntry(value: any) { try { const url = new URL(value); return { - type: url.protocol.replace(":", "").replace("//", "") || "http", + type: url.protocol.replace(":", "") || "http", host: url.hostname, - port: url.port || (url.protocol === "socks5:" ? "1080" : "8080"), - username: url.username || "", - password: url.password || "", + port: url.port || (url.protocol === "socks5:" ? "1080" : url.protocol === "https:" ? "443" : "8080"), + username: url.username ? decodeURIComponent(url.username) : "", + password: url.password ? decodeURIComponent(url.password) : "", }; } catch { const parts = value.split(":"); diff --git a/src/lib/proxyLogger.ts b/src/lib/proxyLogger.ts index 1ed5ed98..ff8f8caf 100644 --- a/src/lib/proxyLogger.ts +++ b/src/lib/proxyLogger.ts @@ -134,7 +134,7 @@ export function logProxyEvent(entry: Partial) { status: log.status, proxyType: log.proxy?.type || null, proxyHost: log.proxy?.host || null, - proxyPort: log.proxy?.port || null, + proxyPort: log.proxy?.port ? Number(log.proxy.port) : null, level: log.level, levelId: log.levelId, provider: log.provider, diff --git a/src/shared/components/ProxyConfigModal.tsx b/src/shared/components/ProxyConfigModal.tsx index ba2ddd0e..ee2b6d26 100644 --- a/src/shared/components/ProxyConfigModal.tsx +++ b/src/shared/components/ProxyConfigModal.tsx @@ -48,7 +48,11 @@ export default function ProxyConfigModal({ isOpen, onClose, level, levelId, leve const [hasOwnProxy, setHasOwnProxy] = useState(false); const [formError, setFormError] = useState(null); - const getDefaultPort = (type) => (type === "socks5" ? "1080" : "8080"); + const getDefaultPort = (type) => { + if (type === "socks5") return "1080"; + if (type === "https") return "443"; + return "8080"; + }; // Load existing proxy config when modal opens useEffect(() => {