feat(admin): 添加系统健康检测功能

新增管理员健康检测面板,可检测以下配置项:
- 安全配置:JWT密钥、CORS、HTTPS、管理员账号、登录防爆破
- 服务状态:SMTP邮件、数据库连接、存储目录
- 运行配置:反向代理支持、Node环境

修改文件:
- backend/auth.js: 新增 isJwtSecretSecure() 函数
- backend/server.js: 新增 /api/admin/health-check API
- frontend/app.js: 新增健康检测数据和方法
- frontend/app.html: 新增健康检测UI界面

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-27 19:37:42 +08:00
parent d2aa115b5b
commit 1dde17bb04
4 changed files with 385 additions and 3 deletions

View File

@@ -21,7 +21,7 @@ const execAsync = util.promisify(exec);
const execFileAsync = util.promisify(execFile);
const { db, UserDB, ShareDB, SettingsDB, VerificationDB, PasswordResetTokenDB } = require('./database');
const { generateToken, authMiddleware, adminMiddleware } = require('./auth');
const { generateToken, authMiddleware, adminMiddleware, isJwtSecretSecure } = require('./auth');
const app = express();
const PORT = process.env.PORT || 40001;
@@ -3382,6 +3382,184 @@ app.post('/api/admin/settings/test-smtp', authMiddleware, adminMiddleware, async
}
});
// 系统健康检测API
app.get('/api/admin/health-check', authMiddleware, adminMiddleware, async (req, res) => {
try {
const checks = [];
let overallStatus = 'healthy'; // healthy, warning, critical
// 1. JWT密钥安全检查
const jwtSecure = isJwtSecretSecure();
checks.push({
name: 'JWT密钥',
category: 'security',
status: jwtSecure ? 'pass' : 'fail',
message: jwtSecure ? 'JWT密钥已正确配置随机生成' : 'JWT密钥使用默认值或长度不足存在安全风险',
suggestion: jwtSecure ? null : '请在.env中设置随机生成的JWT_SECRET至少32字符'
});
if (!jwtSecure) overallStatus = 'critical';
// 2. CORS配置检查
const allowedOrigins = process.env.ALLOWED_ORIGINS;
const corsConfigured = allowedOrigins && allowedOrigins.trim().length > 0;
checks.push({
name: 'CORS跨域配置',
category: 'security',
status: corsConfigured ? 'pass' : 'warning',
message: corsConfigured
? `已配置允许的域名: ${allowedOrigins}`
: 'CORS未配置允许所有来源仅适合开发环境',
suggestion: corsConfigured ? null : '生产环境建议配置ALLOWED_ORIGINS环境变量'
});
if (!corsConfigured && overallStatus === 'healthy') overallStatus = 'warning';
// 3. HTTPS/Cookie安全配置
const enforceHttps = process.env.ENFORCE_HTTPS === 'true';
const cookieSecure = process.env.COOKIE_SECURE === 'true';
const httpsConfigured = enforceHttps && cookieSecure;
checks.push({
name: 'HTTPS安全配置',
category: 'security',
status: httpsConfigured ? 'pass' : 'warning',
message: httpsConfigured
? 'HTTPS强制开启Cookie安全标志已设置'
: `ENFORCE_HTTPS=${enforceHttps}, COOKIE_SECURE=${cookieSecure}`,
suggestion: httpsConfigured ? null : '生产环境建议开启ENFORCE_HTTPS和COOKIE_SECURE'
});
// 4. 管理员密码强度检查(检查是否为默认值)
const adminUsername = process.env.ADMIN_USERNAME;
const adminConfigured = adminUsername && adminUsername !== 'admin';
checks.push({
name: '管理员账号配置',
category: 'security',
status: adminConfigured ? 'pass' : 'warning',
message: adminConfigured
? '管理员用户名已自定义'
: '管理员使用默认用户名"admin"',
suggestion: adminConfigured ? null : '建议使用自定义管理员用户名'
});
// 5. SMTP邮件配置检查
const smtpHost = SettingsDB.get('smtp_host') || process.env.SMTP_HOST;
const smtpUser = SettingsDB.get('smtp_user') || process.env.SMTP_USER;
const smtpPassword = SettingsDB.get('smtp_password') || process.env.SMTP_PASSWORD;
const smtpConfigured = smtpHost && smtpUser && smtpPassword;
checks.push({
name: 'SMTP邮件服务',
category: 'service',
status: smtpConfigured ? 'pass' : 'warning',
message: smtpConfigured
? `已配置: ${smtpHost}`
: '未配置SMTP邮箱验证和密码重置功能不可用',
suggestion: smtpConfigured ? null : '配置SMTP以启用邮箱验证功能'
});
// 6. 数据库连接检查
let dbStatus = 'pass';
let dbMessage = '数据库连接正常';
try {
const testQuery = db.prepare('SELECT 1').get();
if (!testQuery) throw new Error('查询返回空');
} catch (dbError) {
dbStatus = 'fail';
dbMessage = '数据库连接异常: ' + dbError.message;
overallStatus = 'critical';
}
checks.push({
name: '数据库连接',
category: 'service',
status: dbStatus,
message: dbMessage,
suggestion: dbStatus === 'fail' ? '检查数据库文件权限和路径配置' : null
});
// 7. 存储目录检查
const storageRoot = process.env.STORAGE_ROOT || path.join(__dirname, 'storage');
let storageStatus = 'pass';
let storageMessage = `存储目录正常: ${storageRoot}`;
try {
if (!fs.existsSync(storageRoot)) {
fs.mkdirSync(storageRoot, { recursive: true });
storageMessage = `存储目录已创建: ${storageRoot}`;
}
// 检查写入权限
const testFile = path.join(storageRoot, '.health-check-test');
fs.writeFileSync(testFile, 'test');
fs.unlinkSync(testFile);
} catch (storageError) {
storageStatus = 'fail';
storageMessage = '存储目录不可写: ' + storageError.message;
overallStatus = 'critical';
}
checks.push({
name: '存储目录',
category: 'service',
status: storageStatus,
message: storageMessage,
suggestion: storageStatus === 'fail' ? '检查存储目录权限确保Node进程有写入权限' : null
});
// 8. 限流器状态
const rateLimiterActive = typeof loginLimiter !== 'undefined' && loginLimiter !== null;
checks.push({
name: '登录防爆破',
category: 'security',
status: rateLimiterActive ? 'pass' : 'warning',
message: rateLimiterActive
? '限流器已启用5次/15分钟封锁30分钟'
: '限流器未正常初始化',
suggestion: null
});
// 9. 信任代理配置(反向代理环境)
const trustProxy = app.get('trust proxy');
checks.push({
name: '反向代理支持',
category: 'config',
status: trustProxy ? 'pass' : 'info',
message: trustProxy
? '已启用trust proxy支持X-Forwarded-For'
: '未启用trust proxy直接暴露时无影响',
suggestion: null
});
// 10. Node环境
const nodeEnv = process.env.NODE_ENV || 'development';
checks.push({
name: '运行环境',
category: 'config',
status: nodeEnv === 'production' ? 'pass' : 'info',
message: `当前环境: ${nodeEnv}`,
suggestion: nodeEnv !== 'production' ? '生产部署建议设置NODE_ENV=production' : null
});
// 统计
const summary = {
total: checks.length,
pass: checks.filter(c => c.status === 'pass').length,
warning: checks.filter(c => c.status === 'warning').length,
fail: checks.filter(c => c.status === 'fail').length,
info: checks.filter(c => c.status === 'info').length
};
res.json({
success: true,
overallStatus,
summary,
checks,
timestamp: new Date().toISOString(),
version: process.env.npm_package_version || '1.1.0'
});
} catch (error) {
console.error('健康检测失败:', error);
res.status(500).json({
success: false,
message: '健康检测失败: ' + error.message
});
}
});
// 获取服务器存储统计信息
app.get('/api/admin/storage-stats', authMiddleware, adminMiddleware, async (req, res) => {
try {