Files
WebAI2API/src/server/http/adminRoutes.js
T
2025-12-19 04:43:59 +08:00

357 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @fileoverview Admin API 路由模块
* @description 提供管理接口,包括系统状态、配置管理、适配器元数据等
*/
import { sendJson, sendApiError } from './respond.js';
import { ERROR_CODES } from '../errors.js';
import { logger } from '../../utils/logger.js';
import {
getSystemStatus,
getDataFolders,
deleteDataFolders,
clearTempFiles
} from '../../utils/systemInfo.js';
import {
getServerConfig,
saveServerConfig,
getBrowserConfig,
saveBrowserConfig,
getQueueConfig,
saveQueueConfig,
getInstancesConfig,
saveInstancesConfig,
getAdaptersConfig,
saveAdaptersConfig,
getPoolConfig,
savePoolConfig
} from '../../utils/configManager.js';
import {
validateServerConfig,
validateBrowserConfig,
validateInstancesConfig,
validatePoolConfig,
validateAdaptersConfig
} from '../../utils/configValidator.js';
import { registry } from '../../backend/registry.js';
/**
* 读取请求体
* @param {import('http').IncomingMessage} req
* @returns {Promise<object>}
*/
async function readBody(req) {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const body = Buffer.concat(chunks).toString();
return body ? JSON.parse(body) : {};
}
/**
* 创建 Admin 路由处理器
* @param {object} context - 路由上下文
* @param {object} context.config - 完整配置对象
* @param {object} context.queueManager - 队列管理器
* @param {string} context.tempDir - 临时目录
* @returns {Function} Admin 路由处理函数
*/
export function createAdminRouter(context) {
const { config, queueManager, tempDir } = context;
/**
* Admin 路由处理函数
* @param {import('http').IncomingMessage} req
* @param {import('http').ServerResponse} res
* @param {string} pathname - 去除 /admin 前缀后的路径
*/
return async function handleAdminRequest(req, res, pathname) {
const method = req.method;
try {
// ==================== 系统管理 ====================
// GET /admin/status - 系统状态
if (method === 'GET' && pathname === '/status') {
const status = getSystemStatus();
sendJson(res, 200, status);
return;
}
// POST /admin/restart - 重启服务(使用子进程分离)
if (method === 'POST' && pathname === '/restart') {
const { spawn } = await import('child_process');
sendJson(res, 200, { success: true, message: '服务正在重启...' });
logger.info('管理器', '收到重启请求,将在 1 秒后重启');
setTimeout(() => {
// 启动新进程(完全独立)
const child = spawn(process.execPath, process.argv.slice(1), {
cwd: process.cwd(),
detached: true,
stdio: 'ignore',
env: process.env
});
child.unref();
// 退出当前进程
setTimeout(() => process.exit(0), 500);
}, 1000);
return;
}
// POST /admin/stop - 停止服务
if (method === 'POST' && pathname === '/stop') {
sendJson(res, 200, { success: true, message: '服务正在停止...' });
logger.info('管理器', '收到停止请求,将在 1 秒后退出');
setTimeout(() => process.exit(0), 1000);
return;
}
// ==================== 缓存与数据管理 ====================
// POST /admin/cache/clear - 清理缓存
if (method === 'POST' && pathname === '/cache/clear') {
const result = clearTempFiles(tempDir);
sendJson(res, 200, { success: true, cleaned: result.cleaned });
return;
}
// GET /admin/data-folders - 列出数据文件夹
if (method === 'GET' && pathname === '/data-folders') {
const workers = config.backend?.pool?.workers || [];
const folders = getDataFolders(workers);
sendJson(res, 200, folders);
return;
}
// POST /admin/data-folders/delete - 删除数据文件夹
if (method === 'POST' && pathname === '/data-folders/delete') {
const body = await readBody(req);
if (!body.folders || !Array.isArray(body.folders)) {
sendApiError(res, { code: ERROR_CODES.INVALID_REQUEST_BODY, message: '缺少 folders 数组' });
return;
}
const workers = config.backend?.pool?.workers || [];
const result = deleteDataFolders(body.folders, workers);
if (result.errors.length > 0) {
sendJson(res, 207, result); // 207 Multi-Status
} else {
sendJson(res, 200, result);
}
return;
}
// ==================== 配置管理 ====================
// GET/POST /admin/config/server
if (pathname === '/config/server') {
if (method === 'GET') {
const serverConfig = getServerConfig();
const queueConfig = getQueueConfig();
sendJson(res, 200, {
...serverConfig,
queueBuffer: queueConfig.queueBuffer,
imageLimit: queueConfig.imageLimit
});
} else if (method === 'POST') {
const body = await readBody(req);
// 校验配置
const validation = validateServerConfig(body);
if (!validation.valid) {
sendApiError(res, {
code: ERROR_CODES.INVALID_REQUEST_BODY,
message: `配置校验失败: ${validation.errors.join('; ')}`
});
return;
}
// 分别保存 server 和 queue 配置
saveServerConfig(body);
if (body.queueBuffer !== undefined || body.imageLimit !== undefined) {
saveQueueConfig(body);
}
sendJson(res, 200, { success: true, message: '配置已保存,请重启服务生效' });
} else {
res.writeHead(405);
res.end();
}
return;
}
// GET/POST /admin/config/browser
if (pathname === '/config/browser') {
if (method === 'GET') {
sendJson(res, 200, getBrowserConfig());
} else if (method === 'POST') {
const body = await readBody(req);
// 校验配置
const validation = validateBrowserConfig(body);
if (!validation.valid) {
sendApiError(res, {
code: ERROR_CODES.INVALID_REQUEST_BODY,
message: `配置校验失败: ${validation.errors.join('; ')}`
});
return;
}
saveBrowserConfig(body);
sendJson(res, 200, { success: true, message: '配置已保存,请重启服务生效' });
} else {
res.writeHead(405);
res.end();
}
return;
}
// GET/POST /admin/config/instances (对应原设计的 workers)
if (pathname === '/config/instances' || pathname === '/config/workers') {
if (method === 'GET') {
sendJson(res, 200, getInstancesConfig());
} else if (method === 'POST') {
const body = await readBody(req);
// 校验配置(包括 Instance/Worker 名称唯一性)
const validation = validateInstancesConfig(body);
if (!validation.valid) {
sendApiError(res, {
code: ERROR_CODES.INVALID_REQUEST_BODY,
message: `配置校验失败: ${validation.errors.join('; ')}`
});
return;
}
saveInstancesConfig(body);
sendJson(res, 200, { success: true, message: '配置已保存,请重启服务生效' });
} else {
res.writeHead(405);
res.end();
}
return;
}
// GET/POST /admin/config/adapters
if (pathname === '/config/adapters') {
if (method === 'GET') {
sendJson(res, 200, getAdaptersConfig());
} else if (method === 'POST') {
const body = await readBody(req);
// 校验配置
const validation = validateAdaptersConfig(body);
if (!validation.valid) {
sendApiError(res, {
code: ERROR_CODES.INVALID_REQUEST_BODY,
message: `配置校验失败: ${validation.errors.join('; ')}`
});
return;
}
saveAdaptersConfig(body);
sendJson(res, 200, { success: true, message: '配置已保存,请重启服务生效' });
} else {
res.writeHead(405);
res.end();
}
return;
}
// GET/POST /admin/config/pool - 负载均衡和故障转移配置
if (pathname === '/config/pool') {
if (method === 'GET') {
sendJson(res, 200, getPoolConfig());
} else if (method === 'POST') {
const body = await readBody(req);
// 校验配置
const validation = validatePoolConfig(body);
if (!validation.valid) {
sendApiError(res, {
code: ERROR_CODES.INVALID_REQUEST_BODY,
message: `配置校验失败: ${validation.errors.join('; ')}`
});
return;
}
savePoolConfig(body);
sendJson(res, 200, { success: true, message: '配置已保存,请重启服务生效' });
} else {
res.writeHead(405);
res.end();
}
return;
}
// ==================== 元数据 ====================
// GET /admin/adapters - 获取适配器列表(含 configSchema
if (method === 'GET' && pathname === '/adapters') {
const adapters = [];
const adapterIds = registry.getAdapterIds();
for (const id of adapterIds) {
const adapter = registry.getAdapter(id);
if (adapter) {
adapters.push({
id: adapter.id,
displayName: adapter.displayName || adapter.id,
modelCount: adapter.models?.length || 0,
models: (adapter.models || []).map(m => m.id),
configSchema: adapter.configSchema || []
});
}
}
sendJson(res, 200, adapters);
return;
}
// ==================== 统计与监控 ====================
// GET /admin/stats - 基本统计
if (method === 'GET' && pathname === '/stats') {
const instances = config.backend?.pool?.instances || [];
const workers = config.backend?.pool?.workers || [];
sendJson(res, 200, {
instances: instances.length,
workers: workers.length
});
return;
}
// GET /admin/queue - 任务队列状态
if (method === 'GET' && pathname === '/queue') {
const queueStatus = queueManager.getStatus();
const detailedStatus = queueManager.getDetailedStatus();
sendJson(res, 200, {
processing: queueStatus.processing,
waiting: queueStatus.queueLength,
total: queueStatus.total,
processingTasks: detailedStatus.processing,
waitingTasks: detailedStatus.waiting
});
return;
}
// 404
res.writeHead(404);
res.end(JSON.stringify({ error: 'Not Found' }));
} catch (err) {
logger.error('管理器', `请求处理失败: ${err.message}`);
sendApiError(res, {
code: ERROR_CODES.INTERNAL_ERROR,
message: err.message
});
}
};
}