Spaces:
Sleeping
Sleeping
| 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<boolean>} - 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; | |
| } |