feat: 完善 WebUI 功能

This commit is contained in:
foxhui
2025-12-20 18:35:53 +08:00
Unverified
parent c8c7aec0e1
commit 10c96e420f
32 changed files with 1395 additions and 507 deletions
+24 -2
View File
@@ -59,7 +59,7 @@ async function readBody(req) {
* @returns {Function} Admin 路由处理函数
*/
export function createAdminRouter(context) {
const { config, queueManager, tempDir } = context;
const { config, queueManager, tempDir, getSafeMode } = context;
/**
* Admin 路由处理函数
@@ -76,7 +76,8 @@ export function createAdminRouter(context) {
// GET /admin/status - 系统状态
if (method === 'GET' && pathname === '/status') {
const status = getSystemStatus();
sendJson(res, 200, status);
const safeMode = getSafeMode?.() || { enabled: false, reason: null };
sendJson(res, 200, { ...status, safeMode });
return;
}
@@ -175,6 +176,27 @@ export function createAdminRouter(context) {
return;
}
// GET /admin/logs - 读取系统日志
if (method === 'GET' && pathname === '/logs') {
const url = new URL(req.url, `http://${req.headers.host}`);
const lines = parseInt(url.searchParams.get('lines') || '200', 10);
const result = logger.readLogs(lines);
sendJson(res, 200, result);
return;
}
// DELETE /admin/logs - 清除系统日志
if (method === 'DELETE' && pathname === '/logs') {
const success = logger.clearLogs();
if (success) {
logger.info('管理器', '系统日志已清除');
sendJson(res, 200, { success: true, message: '日志已清除' });
} else {
sendJson(res, 500, { success: false, message: '日志清除失败' });
}
return;
}
// GET /admin/data-folders - 列出数据文件夹
if (method === 'GET' && pathname === '/data-folders') {
const workers = config.backend?.pool?.workers || [];
+14 -2
View File
@@ -36,14 +36,14 @@ const WEBUI_DIR = path.join(process.cwd(), 'webui', 'dist');
* @returns {Function} 请求处理函数
*/
export function createGlobalRouter(context) {
const { authToken, config, queueManager, tempDir, loginMode } = context;
const { authToken, config, queueManager, tempDir, loginMode, getSafeMode } = context;
// 创建鉴权中间件
const checkAuth = createAuthMiddleware(authToken);
// 创建子路由处理器
const handleOpenAIRequest = loginMode ? null : createOpenAIRouter(context);
const handleAdminRequest = createAdminRouter({ config, queueManager, tempDir });
const handleAdminRequest = createAdminRouter({ config, queueManager, tempDir, getSafeMode });
/**
* 主路由处理函数
@@ -100,6 +100,18 @@ export function createGlobalRouter(context) {
// OpenAI API (/v1)
if (pathname.startsWith('/v1')) {
// 安全模式下禁用 OpenAI API
const safeMode = getSafeMode?.();
if (safeMode?.enabled) {
res.writeHead(503, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: {
message: `服务运行在安全模式,OpenAI API 不可用。原因: ${safeMode.reason}`,
type: 'service_unavailable'
}
}));
return;
}
// 登录模式下禁用 OpenAI API
if (!handleOpenAIRequest) {
res.writeHead(503, { 'Content-Type': 'application/json' });
+18 -4
View File
@@ -101,6 +101,16 @@ const queueManager = createQueueManager(
*/
const isLoginMode = process.argv.some(arg => arg.startsWith('-login'));
/**
* 安全模式状态
* 当 Pool 初始化失败时进入安全模式,此时:
* - HTTP 服务器正常启动
* - Admin API 和 WebUI 可用
* - OpenAI API 返回 503
*/
let safeMode = false;
let safeModeReason = null;
const handleRequest = createGlobalRouter({
authToken: AUTH_TOKEN,
backendName,
@@ -112,7 +122,8 @@ const handleRequest = createGlobalRouter({
imageLimit: IMAGE_LIMIT,
queueManager,
config,
loginMode: isLoginMode
loginMode: isLoginMode,
getSafeMode: () => ({ enabled: safeMode, reason: safeModeReason })
});
// ==================== 启动服务器 ====================
@@ -128,12 +139,15 @@ async function startServer() {
logger.info('服务器', '完成后可直接关闭浏览器窗口或按 Ctrl+C 退出');
}
// 预先启动 Pool
// 预先启动工作池(失败时进入安全模式)
try {
await queueManager.initializePool();
} catch (err) {
logger.error('服务器', 'Pool 初始化失败', { error: err.message });
process.exit(1);
logger.error('服务器', '工作池初始化失败', { error: err.message });
logger.warn('服务器', '进入安全模式:WebUI 和 Admin API 可用,OpenAI API 不可用');
logger.warn('服务器', '请通过 配置文件或者 WebUI 修改正确的配置后重启服务');
safeMode = true;
safeModeReason = err.message;
}
// 创建并启动 HTTP 服务器