Merge branch 'main' of https://github.com/justlovemaki/AIClient-2-API
This commit is contained in:
commit
0f335db090
16 changed files with 397 additions and 12 deletions
67
package-lock.json
generated
67
package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "AIClient2API",
|
||||
"name": "AIClient-2-API",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
@ -11,9 +11,12 @@
|
|||
"deepmerge": "^4.3.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"google-auth-library": "^10.1.0",
|
||||
"http-proxy-agent": "^7.0.2",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^2.0.2",
|
||||
"open": "^10.2.0",
|
||||
"socks-proxy-agent": "^8.0.5",
|
||||
"undici": "^7.12.0",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
|
|
@ -97,6 +100,7 @@
|
|||
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
|
|
@ -2955,6 +2959,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
|
|
@ -4060,6 +4065,19 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
|
|
@ -4131,6 +4149,15 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
|
|
@ -6028,6 +6055,44 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.8.7",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
||||
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ip-address": "^10.0.1",
|
||||
"smart-buffer": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socks-proxy-agent": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
||||
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"socks": "^2.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
|
|
|
|||
|
|
@ -7,9 +7,12 @@
|
|||
"deepmerge": "^4.3.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"google-auth-library": "^10.1.0",
|
||||
"http-proxy-agent": "^7.0.2",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^2.0.2",
|
||||
"open": "^10.2.0",
|
||||
"socks-proxy-agent": "^8.0.5",
|
||||
"undici": "^7.12.0",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import axios from 'axios';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { configureAxiosProxy } from '../proxy-utils.js';
|
||||
|
||||
/**
|
||||
* Claude API Core Service Class.
|
||||
|
|
@ -59,6 +60,9 @@ export class ClaudeApiService {
|
|||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理
|
||||
configureAxiosProxy(axiosConfig, this.config, 'claude-custom');
|
||||
|
||||
return axios.create(axiosConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import * as http from 'http';
|
|||
import * as https from 'https';
|
||||
import { getProviderModels } from '../provider-models.js';
|
||||
import { countTokens } from '@anthropic-ai/tokenizer';
|
||||
import { json } from 'stream/consumers';
|
||||
import { configureAxiosProxy } from '../proxy-utils.js';
|
||||
|
||||
const KIRO_CONSTANTS = {
|
||||
REFRESH_URL: 'https://prod.{{region}}.auth.desktop.kiro.dev/refreshToken',
|
||||
|
|
@ -357,6 +357,9 @@ export class KiroApiService {
|
|||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理
|
||||
configureAxiosProxy(axiosConfig, this.config, 'claude-kiro-oauth');
|
||||
|
||||
this.axiosInstance = axios.create(axiosConfig);
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
|
@ -528,7 +531,7 @@ async initializeAuth(forceRefresh = false) {
|
|||
if (!this.accessToken) {
|
||||
throw new Error('No access token available after initialization and refresh attempts.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text content from OpenAI message format
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ export async function initializeConfig(args = process.argv.slice(2), configFileP
|
|||
MODEL_PROVIDER: MODEL_PROVIDER.GEMINI_CLI,
|
||||
SYSTEM_PROMPT_FILE_PATH: INPUT_SYSTEM_PROMPT_FILE, // Default value
|
||||
SYSTEM_PROMPT_MODE: 'append',
|
||||
PROXY_URL: null, // HTTP/HTTPS/SOCKS5 代理地址,如 http://127.0.0.1:7890 或 socks5://127.0.0.1:1080
|
||||
PROXY_ENABLED_PROVIDERS: [], // 启用代理的提供商列表,如 ['gemini-cli-oauth', 'claude-kiro-oauth']
|
||||
PROMPT_LOG_BASE_NAME: "prompt_log",
|
||||
PROMPT_LOG_MODE: "none",
|
||||
REQUEST_MAX_RETRIES: 3,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import open from 'open';
|
|||
import { formatExpiryTime } from '../common.js';
|
||||
import { getProviderModels } from '../provider-models.js';
|
||||
import { handleGeminiAntigravityOAuth } from '../oauth-handlers.js';
|
||||
import { getProxyConfigForProvider, getGoogleAuthProxyConfig } from '../proxy-utils.js';
|
||||
|
||||
// 配置 HTTP/HTTPS agent 限制连接池大小,避免资源泄漏
|
||||
const httpAgent = new http.Agent({
|
||||
|
|
@ -225,14 +226,25 @@ function ensureRolesInContents(requestBody) {
|
|||
|
||||
export class AntigravityApiService {
|
||||
constructor(config) {
|
||||
// 检查是否需要使用代理
|
||||
const proxyConfig = getGoogleAuthProxyConfig(config, 'gemini-antigravity');
|
||||
|
||||
// 配置 OAuth2Client 使用自定义的 HTTP agent
|
||||
this.authClient = new OAuth2Client({
|
||||
const oauth2Options = {
|
||||
clientId: OAUTH_CLIENT_ID,
|
||||
clientSecret: OAUTH_CLIENT_SECRET,
|
||||
transporterOptions: {
|
||||
};
|
||||
|
||||
if (proxyConfig) {
|
||||
oauth2Options.transporterOptions = proxyConfig;
|
||||
console.log('[Antigravity] Using proxy for OAuth2Client');
|
||||
} else {
|
||||
oauth2Options.transporterOptions = {
|
||||
agent: httpsAgent,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this.authClient = new OAuth2Client(oauth2Options);
|
||||
this.availableModels = [];
|
||||
this.isInitialized = false;
|
||||
|
||||
|
|
@ -252,6 +264,9 @@ export class AntigravityApiService {
|
|||
this.baseUrlAutopush
|
||||
// ANTIGRAVITY_BASE_URL_PROD // 生产环境已注释
|
||||
];
|
||||
|
||||
// 保存代理配置供后续使用
|
||||
this.proxyConfig = getProxyConfigForProvider(config, 'gemini-antigravity');
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import open from 'open';
|
|||
import { API_ACTIONS, formatExpiryTime } from '../common.js';
|
||||
import { getProviderModels } from '../provider-models.js';
|
||||
import { handleGeminiCliOAuth } from '../oauth-handlers.js';
|
||||
import { getProxyConfigForProvider, getGoogleAuthProxyConfig } from '../proxy-utils.js';
|
||||
|
||||
// 配置 HTTP/HTTPS agent 限制连接池大小,避免资源泄漏
|
||||
const httpAgent = new http.Agent({
|
||||
|
|
@ -193,14 +194,25 @@ async function* apply_anti_truncation_to_stream(service, model, requestBody) {
|
|||
|
||||
export class GeminiApiService {
|
||||
constructor(config) {
|
||||
// 检查是否需要使用代理
|
||||
const proxyConfig = getGoogleAuthProxyConfig(config, 'gemini-cli-oauth');
|
||||
|
||||
// 配置 OAuth2Client 使用自定义的 HTTP agent
|
||||
this.authClient = new OAuth2Client({
|
||||
const oauth2Options = {
|
||||
clientId: OAUTH_CLIENT_ID,
|
||||
clientSecret: OAUTH_CLIENT_SECRET,
|
||||
transporterOptions: {
|
||||
};
|
||||
|
||||
if (proxyConfig) {
|
||||
oauth2Options.transporterOptions = proxyConfig;
|
||||
console.log('[Gemini] Using proxy for OAuth2Client');
|
||||
} else {
|
||||
oauth2Options.transporterOptions = {
|
||||
agent: httpsAgent,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this.authClient = new OAuth2Client(oauth2Options);
|
||||
this.availableModels = [];
|
||||
this.isInitialized = false;
|
||||
|
||||
|
|
@ -212,6 +224,9 @@ export class GeminiApiService {
|
|||
|
||||
this.codeAssistEndpoint = config.GEMINI_BASE_URL || DEFAULT_CODE_ASSIST_ENDPOINT;
|
||||
this.apiVersion = DEFAULT_CODE_ASSIST_API_VERSION;
|
||||
|
||||
// 保存代理配置供后续使用
|
||||
this.proxyConfig = getProxyConfigForProvider(config, 'gemini-cli-oauth');
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import axios from 'axios';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { configureAxiosProxy } from '../proxy-utils.js';
|
||||
|
||||
// Assumed OpenAI API specification service for interacting with third-party models
|
||||
export class OpenAIApiService {
|
||||
|
|
@ -43,6 +44,9 @@ export class OpenAIApiService {
|
|||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理
|
||||
configureAxiosProxy(axiosConfig, config, 'openai-custom');
|
||||
|
||||
this.axiosInstance = axios.create(axiosConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import axios from 'axios';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { configureAxiosProxy } from '../proxy-utils.js';
|
||||
|
||||
// OpenAI Responses API specification service for interacting with third-party models
|
||||
export class OpenAIResponsesApiService {
|
||||
|
|
@ -42,6 +43,9 @@ export class OpenAIResponsesApiService {
|
|||
if (!this.useSystemProxy) {
|
||||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理 (使用 openai-custom 的代理配置)
|
||||
configureAxiosProxy(axiosConfig, config, 'openai-custom');
|
||||
|
||||
this.axiosInstance = axios.create(axiosConfig);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { EventEmitter } from 'events';
|
|||
import { randomUUID } from 'node:crypto';
|
||||
import { getProviderModels } from '../provider-models.js';
|
||||
import { handleQwenOAuth } from '../oauth-handlers.js';
|
||||
import { configureAxiosProxy } from '../proxy-utils.js';
|
||||
|
||||
// --- Constants ---
|
||||
const QWEN_DIR = '.qwen';
|
||||
|
|
@ -222,6 +223,9 @@ export class QwenApiService {
|
|||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理
|
||||
configureAxiosProxy(axiosConfig, this.config, 'openai-qwen-oauth');
|
||||
|
||||
this.currentAxiosInstance = axios.create(axiosConfig);
|
||||
|
||||
this.isInitialized = true;
|
||||
|
|
@ -512,6 +516,9 @@ export class QwenApiService {
|
|||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
// 配置自定义代理
|
||||
configureAxiosProxy(axiosConfig, this.config, 'openai-qwen-oauth');
|
||||
|
||||
this.currentAxiosInstance = axios.create(axiosConfig);
|
||||
|
||||
// Process message content before sending the request
|
||||
|
|
|
|||
128
src/proxy-utils.js
Normal file
128
src/proxy-utils.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* 代理工具模块
|
||||
* 支持 HTTP、HTTPS 和 SOCKS5 代理
|
||||
*/
|
||||
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { HttpProxyAgent } from 'http-proxy-agent';
|
||||
import { SocksProxyAgent } from 'socks-proxy-agent';
|
||||
|
||||
/**
|
||||
* 解析代理URL并返回相应的代理配置
|
||||
* @param {string} proxyUrl - 代理URL,如 http://127.0.0.1:7890 或 socks5://127.0.0.1:1080
|
||||
* @returns {Object|null} 代理配置对象,包含 httpAgent 和 httpsAgent
|
||||
*/
|
||||
export function parseProxyUrl(proxyUrl) {
|
||||
if (!proxyUrl || typeof proxyUrl !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmedUrl = proxyUrl.trim();
|
||||
if (!trimmedUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(trimmedUrl);
|
||||
const protocol = url.protocol.toLowerCase();
|
||||
|
||||
if (protocol === 'socks5:' || protocol === 'socks4:' || protocol === 'socks:') {
|
||||
// SOCKS 代理
|
||||
const socksAgent = new SocksProxyAgent(trimmedUrl);
|
||||
return {
|
||||
httpAgent: socksAgent,
|
||||
httpsAgent: socksAgent,
|
||||
proxyType: 'socks'
|
||||
};
|
||||
} else if (protocol === 'http:' || protocol === 'https:') {
|
||||
// HTTP/HTTPS 代理
|
||||
return {
|
||||
httpAgent: new HttpProxyAgent(trimmedUrl),
|
||||
httpsAgent: new HttpsProxyAgent(trimmedUrl),
|
||||
proxyType: 'http'
|
||||
};
|
||||
} else {
|
||||
console.warn(`[Proxy] Unsupported proxy protocol: ${protocol}`);
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Proxy] Failed to parse proxy URL: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定的提供商是否启用了代理
|
||||
* @param {Object} config - 配置对象
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {boolean} 是否启用代理
|
||||
*/
|
||||
export function isProxyEnabledForProvider(config, providerType) {
|
||||
if (!config || !config.PROXY_URL || !config.PROXY_ENABLED_PROVIDERS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const enabledProviders = config.PROXY_ENABLED_PROVIDERS;
|
||||
if (!Array.isArray(enabledProviders)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return enabledProviders.includes(providerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定提供商的代理配置
|
||||
* @param {Object} config - 配置对象
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {Object|null} 代理配置对象或 null
|
||||
*/
|
||||
export function getProxyConfigForProvider(config, providerType) {
|
||||
if (!isProxyEnabledForProvider(config, providerType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const proxyConfig = parseProxyUrl(config.PROXY_URL);
|
||||
if (proxyConfig) {
|
||||
console.log(`[Proxy] Using ${proxyConfig.proxyType} proxy for ${providerType}: ${config.PROXY_URL}`);
|
||||
}
|
||||
return proxyConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 axios 配置代理
|
||||
* @param {Object} axiosConfig - axios 配置对象
|
||||
* @param {Object} config - 应用配置对象
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {Object} 更新后的 axios 配置
|
||||
*/
|
||||
export function configureAxiosProxy(axiosConfig, config, providerType) {
|
||||
const proxyConfig = getProxyConfigForProvider(config, providerType);
|
||||
|
||||
if (proxyConfig) {
|
||||
// 使用代理 agent
|
||||
axiosConfig.httpAgent = proxyConfig.httpAgent;
|
||||
axiosConfig.httpsAgent = proxyConfig.httpsAgent;
|
||||
// 禁用 axios 内置的代理配置,使用我们的 agent
|
||||
axiosConfig.proxy = false;
|
||||
}
|
||||
|
||||
return axiosConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 google-auth-library 配置代理
|
||||
* @param {Object} config - 应用配置对象
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {Object|null} transporter 配置对象或 null
|
||||
*/
|
||||
export function getGoogleAuthProxyConfig(config, providerType) {
|
||||
const proxyConfig = getProxyConfigForProvider(config, providerType);
|
||||
|
||||
if (proxyConfig) {
|
||||
return {
|
||||
agent: proxyConfig.httpsAgent
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -701,6 +701,10 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
|||
if (newConfig.PROVIDER_POOLS_FILE_PATH !== undefined) currentConfig.PROVIDER_POOLS_FILE_PATH = newConfig.PROVIDER_POOLS_FILE_PATH;
|
||||
if (newConfig.MAX_ERROR_COUNT !== undefined) currentConfig.MAX_ERROR_COUNT = newConfig.MAX_ERROR_COUNT;
|
||||
if (newConfig.providerFallbackChain !== undefined) currentConfig.providerFallbackChain = newConfig.providerFallbackChain;
|
||||
|
||||
// Proxy settings
|
||||
if (newConfig.PROXY_URL !== undefined) currentConfig.PROXY_URL = newConfig.PROXY_URL;
|
||||
if (newConfig.PROXY_ENABLED_PROVIDERS !== undefined) currentConfig.PROXY_ENABLED_PROVIDERS = newConfig.PROXY_ENABLED_PROVIDERS;
|
||||
|
||||
// Handle system prompt update
|
||||
if (newConfig.systemPrompt !== undefined) {
|
||||
|
|
@ -743,7 +747,9 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
|||
CRON_REFRESH_TOKEN: currentConfig.CRON_REFRESH_TOKEN,
|
||||
PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH,
|
||||
MAX_ERROR_COUNT: currentConfig.MAX_ERROR_COUNT,
|
||||
providerFallbackChain: currentConfig.providerFallbackChain
|
||||
providerFallbackChain: currentConfig.providerFallbackChain,
|
||||
PROXY_URL: currentConfig.PROXY_URL,
|
||||
PROXY_ENABLED_PROVIDERS: currentConfig.PROXY_ENABLED_PROVIDERS
|
||||
};
|
||||
|
||||
writeFileSync(configPath, JSON.stringify(configToSave, null, 2), 'utf-8');
|
||||
|
|
|
|||
|
|
@ -91,6 +91,17 @@ async function loadConfiguration() {
|
|||
}
|
||||
}
|
||||
|
||||
// 加载代理配置
|
||||
const proxyUrlEl = document.getElementById('proxyUrl');
|
||||
if (proxyUrlEl) proxyUrlEl.value = data.PROXY_URL || '';
|
||||
|
||||
// 加载启用代理的提供商
|
||||
const proxyProviderCheckboxes = document.querySelectorAll('input[name="proxyProvider"]');
|
||||
const enabledProviders = data.PROXY_ENABLED_PROVIDERS || [];
|
||||
proxyProviderCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = enabledProviders.includes(checkbox.value);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load configuration:', error);
|
||||
}
|
||||
|
|
@ -149,6 +160,13 @@ async function saveConfiguration() {
|
|||
} else {
|
||||
config.providerFallbackChain = {};
|
||||
}
|
||||
|
||||
// 保存代理配置
|
||||
config.PROXY_URL = document.getElementById('proxyUrl')?.value?.trim() || null;
|
||||
|
||||
// 获取启用代理的提供商列表
|
||||
const proxyProviderCheckboxes = document.querySelectorAll('input[name="proxyProvider"]:checked');
|
||||
config.PROXY_ENABLED_PROVIDERS = Array.from(proxyProviderCheckboxes).map(cb => cb.value);
|
||||
|
||||
try {
|
||||
await window.apiClient.post('/config', config);
|
||||
|
|
|
|||
|
|
@ -211,6 +211,12 @@ const translations = {
|
|||
'config.advanced.adminPassword': '后台登录密码',
|
||||
'config.advanced.adminPasswordPlaceholder': '设置后台登录密码(留空则不修改)',
|
||||
'config.advanced.adminPasswordNote': '用于保护管理控制台的访问,修改后需要重新登录',
|
||||
'config.proxy.title': '代理设置',
|
||||
'config.proxy.url': '代理地址',
|
||||
'config.proxy.urlPlaceholder': '例如: http://127.0.0.1:7890 或 socks5://127.0.0.1:1080',
|
||||
'config.proxy.urlNote': '支持 HTTP、HTTPS 和 SOCKS5 代理,留空则不使用代理',
|
||||
'config.proxy.enabledProviders': '启用代理的提供商',
|
||||
'config.proxy.enabledProvidersNote': '选择需要通过代理访问的提供商,未选中的提供商将直接连接',
|
||||
'config.save': '保存配置',
|
||||
'config.reset': '重置',
|
||||
'config.placeholder.nodeName': '例如: 我的节点1',
|
||||
|
|
@ -620,6 +626,12 @@ const translations = {
|
|||
'config.advanced.adminPassword': 'Admin Password',
|
||||
'config.advanced.adminPasswordPlaceholder': 'Set admin password (leave empty to keep unchanged)',
|
||||
'config.advanced.adminPasswordNote': 'Used to protect management console access, requires re-login after modification',
|
||||
'config.proxy.title': 'Proxy Settings',
|
||||
'config.proxy.url': 'Proxy URL',
|
||||
'config.proxy.urlPlaceholder': 'e.g.: http://127.0.0.1:7890 or socks5://127.0.0.1:1080',
|
||||
'config.proxy.urlNote': 'Supports HTTP, HTTPS and SOCKS5 proxies. Leave empty to disable proxy',
|
||||
'config.proxy.enabledProviders': 'Providers Using Proxy',
|
||||
'config.proxy.enabledProvidersNote': 'Select providers that should use the proxy. Unselected providers will connect directly',
|
||||
'config.save': 'Save Configuration',
|
||||
'config.reset': 'Reset',
|
||||
'config.placeholder.nodeName': 'e.g.: My Node 1',
|
||||
|
|
|
|||
|
|
@ -551,6 +551,65 @@ textarea.form-control {
|
|||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 代理配置区域 */
|
||||
.proxy-config-section {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.proxy-config-section h4 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.proxy-config-section h4 i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 复选框组样式 */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.375rem;
|
||||
background: var(--bg-secondary);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.checkbox-label:hover {
|
||||
border-color: var(--primary-color);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
accent-color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"]:checked + span {
|
||||
color: var(--primary-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.config-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
|
|
|||
|
|
@ -584,6 +584,46 @@
|
|||
<div class="advanced-config-section">
|
||||
<h3 data-i18n="config.advanced.title"><i class="fas fa-cogs"></i> 高级配置</h3>
|
||||
|
||||
<!-- 代理配置 -->
|
||||
<div class="proxy-config-section">
|
||||
<h4 data-i18n="config.proxy.title"><i class="fas fa-globe"></i> 代理设置</h4>
|
||||
<div class="form-group">
|
||||
<label for="proxyUrl" data-i18n="config.proxy.url">代理地址</label>
|
||||
<input type="text" id="proxyUrl" class="form-control" data-i18n-placeholder="config.proxy.urlPlaceholder" placeholder="例如: http://127.0.0.1:7890 或 socks5://127.0.0.1:1080">
|
||||
<small class="form-text" data-i18n="config.proxy.urlNote">支持 HTTP、HTTPS 和 SOCKS5 代理,留空则不使用代理</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label data-i18n="config.proxy.enabledProviders">启用代理的提供商</label>
|
||||
<div class="checkbox-group proxy-providers-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="gemini-cli-oauth">
|
||||
<span>Gemini CLI OAuth</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="gemini-antigravity">
|
||||
<span>Gemini Antigravity</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="claude-kiro-oauth">
|
||||
<span>Claude Kiro OAuth</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="openai-qwen-oauth">
|
||||
<span>Qwen OAuth</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="openai-custom">
|
||||
<span>OpenAI Custom</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="proxyProvider" value="claude-custom">
|
||||
<span>Claude Custom</span>
|
||||
</label>
|
||||
</div>
|
||||
<small class="form-text" data-i18n="config.proxy.enabledProvidersNote">选择需要通过代理访问的提供商,未选中的提供商将直接连接</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-row">
|
||||
<div class="form-group">
|
||||
<label for="systemPromptFilePath" data-i18n="config.advanced.systemPromptFile">系统提示文件路径</label>
|
||||
|
|
|
|||
Loading…
Reference in a new issue