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
+113 -1
View File
@@ -4,9 +4,12 @@
*
* - 环境变量:LOG_LEVEL=debug|info|warn|error
* - 输出格式:YYYY-MM-DD HH:mm:ss.SSS [LEVEL] [模块] 消息 | k=v ...
* - 日志文件:data/temp/system.log(超过 5MB 自动轮转)
*/
import process from 'process';
import fs from 'fs';
import path from 'path';
const LEVELS = ['debug', 'info', 'warn', 'error'];
@@ -19,6 +22,49 @@ const COLORS = {
white: '\x1b[37m'
};
// 日志文件配置
const LOG_DIR = path.join(process.cwd(), 'data', 'temp');
const LOG_FILE = path.join(LOG_DIR, 'system.log');
const LOG_FILE_OLD = path.join(LOG_DIR, 'system.log.old');
const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
// 确保日志目录存在
function ensureLogDir() {
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
}
// 日志轮转:超过 5MB 时重命名为 .old
function rotateLogIfNeeded() {
try {
if (fs.existsSync(LOG_FILE)) {
const stats = fs.statSync(LOG_FILE);
if (stats.size >= MAX_LOG_SIZE) {
// 删除旧的 .old 文件
if (fs.existsSync(LOG_FILE_OLD)) {
fs.unlinkSync(LOG_FILE_OLD);
}
// 重命名当前日志
fs.renameSync(LOG_FILE, LOG_FILE_OLD);
}
}
} catch (e) {
// 忽略轮转错误
}
}
// 写入日志文件
function writeToFile(line) {
try {
ensureLogDir();
rotateLogIfNeeded();
fs.appendFileSync(LOG_FILE, line + '\n', 'utf8');
} catch (e) {
// 忽略写入错误
}
}
// 根据日志级别获取颜色
function getColor(level) {
switch (level.toLowerCase()) {
@@ -94,6 +140,7 @@ export function log(level, mod, msg, meta = {}) {
const color = getColor(level);
const coloredLine = `${color}${line}${COLORS.reset}`;
// 输出到控制台
if (level === 'error') {
console.error(coloredLine);
} else if (level === 'warn') {
@@ -101,6 +148,66 @@ export function log(level, mod, msg, meta = {}) {
} else {
console.log(coloredLine);
}
// 写入日志文件(不带颜色)
writeToFile(line);
}
/**
* 获取日志文件路径
*/
export function getLogPath() {
return LOG_FILE;
}
/**
* 获取旧日志文件路径
*/
export function getOldLogPath() {
return LOG_FILE_OLD;
}
/**
* 清除日志文件
*/
export function clearLogs() {
try {
if (fs.existsSync(LOG_FILE)) {
fs.unlinkSync(LOG_FILE);
}
if (fs.existsSync(LOG_FILE_OLD)) {
fs.unlinkSync(LOG_FILE_OLD);
}
return true;
} catch (e) {
return false;
}
}
/**
* 读取日志文件(返回最后 N 行)
* @param {number} lines - 读取行数
* @returns {{logs: string[], total: number, file: string}}
*/
export function readLogs(lines = 200) {
const result = { logs: [], total: 0, file: LOG_FILE };
try {
if (!fs.existsSync(LOG_FILE)) {
return result;
}
const content = fs.readFileSync(LOG_FILE, 'utf8');
const allLines = content.split('\n').filter(line => line.trim());
result.total = allLines.length;
// 返回最后 N 行
result.logs = allLines.slice(-lines);
} catch (e) {
// 忽略读取错误
}
return result;
}
export const logger = {
@@ -108,5 +215,10 @@ export const logger = {
info: (mod, msg, meta) => log('info', mod, msg, meta),
warn: (mod, msg, meta) => log('warn', mod, msg, meta),
error: (mod, msg, meta) => log('error', mod, msg, meta),
setLevel: setLogLevel
setLevel: setLogLevel,
getLogPath,
getOldLogPath,
clearLogs,
readLogs
};