import { existsSync, readFileSync, writeFileSync } from 'fs'; import { promises as fs } from 'fs'; import path from 'path'; import { CONFIG } from '../core/config-manager.js'; import { serviceInstances } from '../providers/adapter.js'; import { initApiService } from '../services/service-manager.js'; import { getRequestBody } from '../utils/common.js'; import { broadcastEvent } from '../ui-modules/event-broadcast.js'; /** * 重载配置文件 * 动态导入config-manager并重新初始化配置 * @returns {Promise} 返回重载后的配置对象 */ export async function reloadConfig(providerPoolManager) { try { // Import config manager dynamically const { initializeConfig } = await import('../core/config-manager.js'); // Reload main config const newConfig = await initializeConfig(process.argv.slice(2), 'configs/config.json'); // Update provider pool manager if available if (providerPoolManager) { providerPoolManager.providerPools = newConfig.providerPools; providerPoolManager.initializeProviderStatus(); } // Update global CONFIG Object.assign(CONFIG, newConfig); console.log('[UI API] Configuration reloaded:'); // Update initApiService - 清空并重新初始化服务实例 Object.keys(serviceInstances).forEach(key => delete serviceInstances[key]); initApiService(CONFIG); console.log('[UI API] Configuration reloaded successfully'); return newConfig; } catch (error) { console.error('[UI API] Failed to reload configuration:', error); throw error; } } /** * 获取配置 */ export async function handleGetConfig(req, res, currentConfig) { let systemPrompt = ''; if (currentConfig.SYSTEM_PROMPT_FILE_PATH && existsSync(currentConfig.SYSTEM_PROMPT_FILE_PATH)) { try { systemPrompt = readFileSync(currentConfig.SYSTEM_PROMPT_FILE_PATH, 'utf-8'); } catch (e) { console.warn('[UI API] Failed to read system prompt file:', e.message); } } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ...currentConfig, systemPrompt })); return true; } /** * 更新配置 */ export async function handleUpdateConfig(req, res, currentConfig) { try { const body = await getRequestBody(req); const newConfig = body; // Update config values in memory if (newConfig.REQUIRED_API_KEY !== undefined) currentConfig.REQUIRED_API_KEY = newConfig.REQUIRED_API_KEY; if (newConfig.HOST !== undefined) currentConfig.HOST = newConfig.HOST; if (newConfig.SERVER_PORT !== undefined) currentConfig.SERVER_PORT = newConfig.SERVER_PORT; if (newConfig.MODEL_PROVIDER !== undefined) currentConfig.MODEL_PROVIDER = newConfig.MODEL_PROVIDER; if (newConfig.SYSTEM_PROMPT_FILE_PATH !== undefined) currentConfig.SYSTEM_PROMPT_FILE_PATH = newConfig.SYSTEM_PROMPT_FILE_PATH; if (newConfig.SYSTEM_PROMPT_MODE !== undefined) currentConfig.SYSTEM_PROMPT_MODE = newConfig.SYSTEM_PROMPT_MODE; if (newConfig.PROMPT_LOG_BASE_NAME !== undefined) currentConfig.PROMPT_LOG_BASE_NAME = newConfig.PROMPT_LOG_BASE_NAME; if (newConfig.PROMPT_LOG_MODE !== undefined) currentConfig.PROMPT_LOG_MODE = newConfig.PROMPT_LOG_MODE; if (newConfig.REQUEST_MAX_RETRIES !== undefined) currentConfig.REQUEST_MAX_RETRIES = newConfig.REQUEST_MAX_RETRIES; if (newConfig.REQUEST_BASE_DELAY !== undefined) currentConfig.REQUEST_BASE_DELAY = newConfig.REQUEST_BASE_DELAY; if (newConfig.CREDENTIAL_SWITCH_MAX_RETRIES !== undefined) currentConfig.CREDENTIAL_SWITCH_MAX_RETRIES = newConfig.CREDENTIAL_SWITCH_MAX_RETRIES; if (newConfig.CRON_NEAR_MINUTES !== undefined) currentConfig.CRON_NEAR_MINUTES = newConfig.CRON_NEAR_MINUTES; if (newConfig.CRON_REFRESH_TOKEN !== undefined) currentConfig.CRON_REFRESH_TOKEN = newConfig.CRON_REFRESH_TOKEN; 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.WARMUP_TARGET !== undefined) currentConfig.WARMUP_TARGET = newConfig.WARMUP_TARGET; if (newConfig.REFRESH_CONCURRENCY_PER_PROVIDER !== undefined) currentConfig.REFRESH_CONCURRENCY_PER_PROVIDER = newConfig.REFRESH_CONCURRENCY_PER_PROVIDER; if (newConfig.providerFallbackChain !== undefined) currentConfig.providerFallbackChain = newConfig.providerFallbackChain; if (newConfig.modelFallbackMapping !== undefined) currentConfig.modelFallbackMapping = newConfig.modelFallbackMapping; // 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) { const promptPath = currentConfig.SYSTEM_PROMPT_FILE_PATH || 'configs/input_system_prompt.txt'; try { const relativePath = path.relative(process.cwd(), promptPath); writeFileSync(promptPath, newConfig.systemPrompt, 'utf-8'); // 广播更新事件 broadcastEvent('config_update', { action: 'update', filePath: relativePath, type: 'system_prompt', timestamp: new Date().toISOString() }); console.log('[UI API] System prompt updated'); } catch (e) { console.warn('[UI API] Failed to write system prompt:', e.message); } } // Update config.json file try { const configPath = 'configs/config.json'; // Create a clean config object for saving (exclude runtime-only properties) const configToSave = { REQUIRED_API_KEY: currentConfig.REQUIRED_API_KEY, SERVER_PORT: currentConfig.SERVER_PORT, HOST: currentConfig.HOST, MODEL_PROVIDER: currentConfig.MODEL_PROVIDER, SYSTEM_PROMPT_FILE_PATH: currentConfig.SYSTEM_PROMPT_FILE_PATH, SYSTEM_PROMPT_MODE: currentConfig.SYSTEM_PROMPT_MODE, PROMPT_LOG_BASE_NAME: currentConfig.PROMPT_LOG_BASE_NAME, PROMPT_LOG_MODE: currentConfig.PROMPT_LOG_MODE, REQUEST_MAX_RETRIES: currentConfig.REQUEST_MAX_RETRIES, REQUEST_BASE_DELAY: currentConfig.REQUEST_BASE_DELAY, CREDENTIAL_SWITCH_MAX_RETRIES: currentConfig.CREDENTIAL_SWITCH_MAX_RETRIES, CRON_NEAR_MINUTES: currentConfig.CRON_NEAR_MINUTES, CRON_REFRESH_TOKEN: currentConfig.CRON_REFRESH_TOKEN, PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH, MAX_ERROR_COUNT: currentConfig.MAX_ERROR_COUNT, POOL_SIZE_LIMIT: currentConfig.POOL_SIZE_LIMIT, WARMUP_TARGET: currentConfig.WARMUP_TARGET, REFRESH_CONCURRENCY_PER_PROVIDER: currentConfig.REFRESH_CONCURRENCY_PER_PROVIDER, providerFallbackChain: currentConfig.providerFallbackChain, modelFallbackMapping: currentConfig.modelFallbackMapping, PROXY_URL: currentConfig.PROXY_URL, PROXY_ENABLED_PROVIDERS: currentConfig.PROXY_ENABLED_PROVIDERS }; writeFileSync(configPath, JSON.stringify(configToSave, null, 2), 'utf-8'); console.log('[UI API] Configuration saved to configs/config.json'); // 广播更新事件 broadcastEvent('config_update', { action: 'update', filePath: 'configs/config.json', type: 'main_config', timestamp: new Date().toISOString() }); } catch (error) { console.error('[UI API] Failed to save configuration to file:', error.message); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: { message: 'Failed to save configuration to file: ' + error.message, partial: true // Indicate that memory config was updated but not saved } })); return true; } // Update the global CONFIG object to reflect changes immediately Object.assign(CONFIG, currentConfig); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, message: 'Configuration updated successfully', details: 'Configuration has been updated in both memory and config.json file' })); return true; } catch (error) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: { message: error.message } })); return true; } } /** * 重载配置文件 */ export async function handleReloadConfig(req, res, providerPoolManager) { try { // 调用重载配置函数 const newConfig = await reloadConfig(providerPoolManager); // 广播更新事件 broadcastEvent('config_update', { action: 'reload', filePath: 'configs/config.json', providerPoolsPath: newConfig.PROVIDER_POOLS_FILE_PATH || null, timestamp: new Date().toISOString() }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, message: 'Configuration files reloaded successfully', details: { configReloaded: true, configPath: 'configs/config.json', providerPoolsPath: newConfig.PROVIDER_POOLS_FILE_PATH || null } })); return true; } catch (error) { console.error('[UI API] Failed to reload config files:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: { message: 'Failed to reload configuration files: ' + error.message } })); return true; } } /** * 更新管理员密码 */ export async function handleUpdateAdminPassword(req, res) { try { const body = await getRequestBody(req); const { password } = body; if (!password || password.trim() === '') { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: { message: 'Password cannot be empty' } })); return true; } // 写入密码到 pwd 文件 const pwdFilePath = path.join(process.cwd(), 'configs', 'pwd'); await fs.writeFile(pwdFilePath, password.trim(), 'utf-8'); console.log('[UI API] Admin password updated successfully'); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, message: 'Admin password updated successfully' })); return true; } catch (error) { console.error('[UI API] Failed to update admin password:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: { message: 'Failed to update password: ' + error.message } })); return true; } }