aiclient-2-api / src /services /ui-manager.js
Jaasomn
Initial deployment
ceb3821
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;
}