import { existsSync, readFileSync } from 'fs'; import path from 'path'; // Import UI modules import * as auth from '../ui-modules/auth.js'; import * as configApi from '../ui-modules/config-api.js'; import * as providerApi from '../ui-modules/provider-api.js'; import * as usageApi from '../ui-modules/usage-api.js'; import * as pluginApi from '../ui-modules/plugin-api.js'; import * as uploadConfigApi from '../ui-modules/upload-config-api.js'; import * as systemApi from '../ui-modules/system-api.js'; import * as updateApi from '../ui-modules/update-api.js'; import * as oauthApi from '../ui-modules/oauth-api.js'; import * as eventBroadcast from '../ui-modules/event-broadcast.js'; // Re-export from event-broadcast module export { broadcastEvent, initializeUIManagement, handleUploadOAuthCredentials, upload } from '../ui-modules/event-broadcast.js'; /** * Serve static files for the UI * @param {string} path - The request path * @param {http.ServerResponse} res - The HTTP response object */ export async function serveStaticFiles(pathParam, res) { const filePath = path.join(process.cwd(), 'static', pathParam === '/' || pathParam === '/index.html' ? 'index.html' : pathParam.replace('/static/', '')); if (existsSync(filePath)) { const ext = path.extname(filePath); const contentType = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', '.png': 'image/png', '.jpg': 'image/jpeg', '.ico': 'image/x-icon' }[ext] || 'text/plain'; res.writeHead(200, { 'Content-Type': contentType }); res.end(readFileSync(filePath)); return true; } return false; } /** * Handle UI management API requests * @param {string} method - The HTTP method * @param {string} path - The request path * @param {http.IncomingMessage} req - The HTTP request object * @param {http.ServerResponse} res - The HTTP response object * @param {Object} currentConfig - The current configuration object * @param {Object} providerPoolManager - The provider pool manager instance * @returns {Promise} - True if the request was handled by UI API */ export async function handleUIApiRequests(method, pathParam, req, res, currentConfig, providerPoolManager) { // 处理登录接口 if (method === 'POST' && pathParam === '/api/login') { return await auth.handleLoginRequest(req, res); } // 健康检查接口(用于前端token验证) if (method === 'GET' && pathParam === '/api/health') { return await systemApi.handleHealthCheck(req, res); } // Handle UI management API requests (需要token验证,除了登录接口、健康检查和Events接口) if (pathParam.startsWith('/api/') && pathParam !== '/api/login' && pathParam !== '/api/health' && pathParam !== '/api/events' ) { // 检查token验证 const isAuth = await auth.checkAuth(req); if (!isAuth) { res.writeHead(401, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' }); res.end(JSON.stringify({ error: { message: 'Unauthorized access, please login first', code: 'UNAUTHORIZED' } })); return true; } } // 文件上传API if (method === 'POST' && pathParam === '/api/upload-oauth-credentials') { return await eventBroadcast.handleUploadOAuthCredentials(req, res); } // Update admin password if (method === 'POST' && pathParam === '/api/admin-password') { return await configApi.handleUpdateAdminPassword(req, res); } // Get configuration if (method === 'GET' && pathParam === '/api/config') { return await configApi.handleGetConfig(req, res, currentConfig); } // Update configuration if (method === 'POST' && pathParam === '/api/config') { return await configApi.handleUpdateConfig(req, res, currentConfig); } // Get system information if (method === 'GET' && pathParam === '/api/system') { return await systemApi.handleGetSystem(req, res); } // Get provider pools summary if (method === 'GET' && pathParam === '/api/providers') { return await providerApi.handleGetProviders(req, res, currentConfig, providerPoolManager); } // Get specific provider type details const providerTypeMatch = pathParam.match(/^\/api\/providers\/([^\/]+)$/); if (method === 'GET' && providerTypeMatch) { const providerType = decodeURIComponent(providerTypeMatch[1]); return await providerApi.handleGetProviderType(req, res, currentConfig, providerPoolManager, providerType); } // Get available models for all providers or specific provider type if (method === 'GET' && pathParam === '/api/provider-models') { return await providerApi.handleGetProviderModels(req, res); } // Get available models for a specific provider type const providerModelsMatch = pathParam.match(/^\/api\/provider-models\/([^\/]+)$/); if (method === 'GET' && providerModelsMatch) { const providerType = decodeURIComponent(providerModelsMatch[1]); return await providerApi.handleGetProviderTypeModels(req, res, providerType); } // Add new provider configuration if (method === 'POST' && pathParam === '/api/providers') { return await providerApi.handleAddProvider(req, res, currentConfig, providerPoolManager); } // Reset all providers health status for a specific provider type // NOTE: This must be before the generic /{providerType}/{uuid} route to avoid matching 'reset-health' as UUID const resetHealthMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/reset-health$/); if (method === 'POST' && resetHealthMatch) { const providerType = decodeURIComponent(resetHealthMatch[1]); return await providerApi.handleResetProviderHealth(req, res, currentConfig, providerPoolManager, providerType); } // Perform health check for all providers of a specific type // NOTE: This must be before the generic /{providerType}/{uuid} route to avoid matching 'health-check' as UUID const healthCheckMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/health-check$/); if (method === 'POST' && healthCheckMatch) { const providerType = decodeURIComponent(healthCheckMatch[1]); return await providerApi.handleHealthCheck(req, res, currentConfig, providerPoolManager, providerType); } // Delete all unhealthy providers for a specific type // NOTE: This must be before the generic /{providerType}/{uuid} route to avoid matching 'delete-unhealthy' as UUID const deleteUnhealthyMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/delete-unhealthy$/); if (method === 'DELETE' && deleteUnhealthyMatch) { const providerType = decodeURIComponent(deleteUnhealthyMatch[1]); return await providerApi.handleDeleteUnhealthyProviders(req, res, currentConfig, providerPoolManager, providerType); } // Refresh UUIDs for all unhealthy providers of a specific type // NOTE: This must be before the generic /{providerType}/{uuid} route to avoid matching 'refresh-unhealthy-uuids' as UUID const refreshUnhealthyUuidsMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/refresh-unhealthy-uuids$/); if (method === 'POST' && refreshUnhealthyUuidsMatch) { const providerType = decodeURIComponent(refreshUnhealthyUuidsMatch[1]); return await providerApi.handleRefreshUnhealthyUuids(req, res, currentConfig, providerPoolManager, providerType); } // Disable/Enable specific provider configuration const disableEnableProviderMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/([^\/]+)\/(disable|enable)$/); if (disableEnableProviderMatch) { const providerType = decodeURIComponent(disableEnableProviderMatch[1]); const providerUuid = disableEnableProviderMatch[2]; const action = disableEnableProviderMatch[3]; return await providerApi.handleDisableEnableProvider(req, res, currentConfig, providerPoolManager, providerType, providerUuid, action); } // Refresh UUID for specific provider configuration const refreshUuidMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/([^\/]+)\/refresh-uuid$/); if (method === 'POST' && refreshUuidMatch) { const providerType = decodeURIComponent(refreshUuidMatch[1]); const providerUuid = refreshUuidMatch[2]; return await providerApi.handleRefreshProviderUuid(req, res, currentConfig, providerPoolManager, providerType, providerUuid); } // Update specific provider configuration // NOTE: This generic route must be after all specific routes like /reset-health, /health-check, /delete-unhealthy const updateProviderMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/([^\/]+)$/); if (method === 'PUT' && updateProviderMatch) { const providerType = decodeURIComponent(updateProviderMatch[1]); const providerUuid = updateProviderMatch[2]; return await providerApi.handleUpdateProvider(req, res, currentConfig, providerPoolManager, providerType, providerUuid); } // Delete specific provider configuration if (method === 'DELETE' && updateProviderMatch) { const providerType = decodeURIComponent(updateProviderMatch[1]); const providerUuid = updateProviderMatch[2]; return await providerApi.handleDeleteProvider(req, res, currentConfig, providerPoolManager, providerType, providerUuid); } // Generate OAuth authorization URL for providers const generateAuthUrlMatch = pathParam.match(/^\/api\/providers\/([^\/]+)\/generate-auth-url$/); if (method === 'POST' && generateAuthUrlMatch) { const providerType = decodeURIComponent(generateAuthUrlMatch[1]); return await oauthApi.handleGenerateAuthUrl(req, res, currentConfig, providerType); } // Handle manual OAuth callback if (method === 'POST' && pathParam === '/api/oauth/manual-callback') { return await oauthApi.handleManualOAuthCallback(req, res); } // Server-Sent Events for real-time updates if (method === 'GET' && pathParam === '/api/events') { return await eventBroadcast.handleEvents(req, res); } // Get upload configuration files list if (method === 'GET' && pathParam === '/api/upload-configs') { return await uploadConfigApi.handleGetUploadConfigs(req, res, currentConfig, providerPoolManager); } // View specific configuration file const viewConfigMatch = pathParam.match(/^\/api\/upload-configs\/view\/(.+)$/); if (method === 'GET' && viewConfigMatch) { const filePath = decodeURIComponent(viewConfigMatch[1]); return await uploadConfigApi.handleViewConfigFile(req, res, filePath); } // Delete specific configuration file const deleteConfigMatch = pathParam.match(/^\/api\/upload-configs\/delete\/(.+)$/); if (method === 'DELETE' && deleteConfigMatch) { const filePath = decodeURIComponent(deleteConfigMatch[1]); return await uploadConfigApi.handleDeleteConfigFile(req, res, filePath); } // Download all configs as zip if (method === 'GET' && pathParam === '/api/upload-configs/download-all') { return await uploadConfigApi.handleDownloadAllConfigs(req, res); } // Delete all unbound config files if (method === 'DELETE' && pathParam === '/api/upload-configs/delete-unbound') { return await uploadConfigApi.handleDeleteUnboundConfigs(req, res, currentConfig, providerPoolManager); } // Quick link config to corresponding provider based on directory if (method === 'POST' && pathParam === '/api/quick-link-provider') { return await providerApi.handleQuickLinkProvider(req, res, currentConfig, providerPoolManager); } // Get usage limits for all providers if (method === 'GET' && pathParam === '/api/usage') { return await usageApi.handleGetUsage(req, res, currentConfig, providerPoolManager); } // Get usage limits for a specific provider type const usageProviderMatch = pathParam.match(/^\/api\/usage\/([^\/]+)$/); if (method === 'GET' && usageProviderMatch) { const providerType = decodeURIComponent(usageProviderMatch[1]); return await usageApi.handleGetProviderUsage(req, res, currentConfig, providerPoolManager, providerType); } // Check for updates - compare local VERSION with latest git tag if (method === 'GET' && pathParam === '/api/check-update') { return await updateApi.handleCheckUpdate(req, res); } // Perform update - git fetch and checkout to latest tag if (method === 'POST' && pathParam === '/api/update') { return await updateApi.handlePerformUpdate(req, res); } // Reload configuration files if (method === 'POST' && pathParam === '/api/reload-config') { return await configApi.handleReloadConfig(req, res, providerPoolManager); } // Restart service (worker process) if (method === 'POST' && pathParam === '/api/restart-service') { return await systemApi.handleRestartService(req, res); } // Get service mode information if (method === 'GET' && pathParam === '/api/service-mode') { return await systemApi.handleGetServiceMode(req, res); } // Batch import Kiro refresh tokens with SSE (real-time progress) if (method === 'POST' && pathParam === '/api/kiro/batch-import-tokens') { return await oauthApi.handleBatchImportKiroTokens(req, res); } // Import AWS SSO credentials for Kiro if (method === 'POST' && pathParam === '/api/kiro/import-aws-credentials') { return await oauthApi.handleImportAwsCredentials(req, res); } // Get plugins list if (method === 'GET' && pathParam === '/api/plugins') { return await pluginApi.handleGetPlugins(req, res); } // Toggle plugin status const togglePluginMatch = pathParam.match(/^\/api\/plugins\/(.+)\/toggle$/); if (method === 'POST' && togglePluginMatch) { const pluginName = decodeURIComponent(togglePluginMatch[1]); return await pluginApi.handleTogglePlugin(req, res, pluginName); } return false; }