feat(proxy): add outbound HTTP proxy support for OAuth + provider requests

- Patch Node fetch via undici ProxyAgent when HTTP_PROXY/HTTPS_PROXY/ALL_PROXY is set
- Ensure proxy patch is loaded for both chat pipeline and OAuth token exchange
- Add Dashboard Settings → Network to edit outbound proxy and apply immediately
- Persist outbound proxy settings in local db and initialize on server startup
- Move proxy helpers to src/lib/network/ for better structure
- Rename src/proxy.js → src/dashboardGuard.js to avoid naming confusion
- Re-apply proxy env after DB import
- Fix: close old dispatcher on proxy URL change to prevent connection pool leak
- Fix: idempotency guard to avoid patching globalThis.fetch multiple times

Made-with: Cursor
This commit is contained in:
gen
2026-02-28 10:11:53 +07:00
parent 833069caac
commit 5a015e5b4d
14 changed files with 450 additions and 29 deletions

View File

@@ -57,7 +57,10 @@ const defaultData = {
observabilityMaxRecords: 1000,
observabilityBatchSize: 20,
observabilityFlushIntervalMs: 5000,
observabilityMaxJsonSize: 1024
observabilityMaxJsonSize: 1024,
outboundProxyEnabled: false,
outboundProxyUrl: "",
outboundNoProxy: ""
},
pricing: {} // NEW: pricing configuration
};
@@ -72,15 +75,18 @@ function cloneDefaultData() {
apiKeys: [],
settings: {
cloudEnabled: false,
tunnelEnabled: false,
tunnelUrl: "",
tunnelEnabled: false,
tunnelUrl: "",
stickyRoundRobinLimit: 3,
requireLogin: true,
observabilityEnabled: true,
observabilityMaxRecords: 1000,
observabilityBatchSize: 20,
observabilityFlushIntervalMs: 5000,
observabilityMaxJsonSize: 1024
observabilityMaxJsonSize: 1024,
outboundProxyEnabled: false,
outboundProxyUrl: "",
outboundNoProxy: "",
},
pricing: {},
};
@@ -114,7 +120,17 @@ function ensureDbShape(data) {
) {
for (const [settingKey, settingDefault] of Object.entries(defaultValue)) {
if (next.settings[settingKey] === undefined) {
next.settings[settingKey] = settingDefault;
// Backward-compat: if users previously saved a proxy URL,
// default to enabled so behavior doesn't silently change.
if (
settingKey === "outboundProxyEnabled" &&
typeof next.settings.outboundProxyUrl === "string" &&
next.settings.outboundProxyUrl.trim()
) {
next.settings.outboundProxyEnabled = true;
} else {
next.settings[settingKey] = settingDefault;
}
changed = true;
}
}