Files
Docker-Proxy/hubcmdui/server.js

259 lines
8.3 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.
/**
* Docker 镜像代理加速系统 - 服务器入口点
*/
const express = require('express');
const fs = require('fs').promises;
const path = require('path');
const bodyParser = require('body-parser');
const session = require('express-session');
const cors = require('cors');
const http = require('http');
const logger = require('./logger');
const { ensureDirectoriesExist } = require('./init-dirs');
const { downloadImages } = require('./download-images');
const { gracefulShutdown } = require('./cleanup');
const os = require('os');
const { requireLogin } = require('./middleware/auth');
const compatibilityLayer = require('./compatibility-layer');
const { initializeDatabase } = require('./scripts/init-database');
const database = require('./database/database');
const httpProxyService = require('./services/httpProxyService');
// 设置日志级别 (默认INFO, 可通过环境变量设置)
const logLevel = process.env.LOG_LEVEL || 'WARN';
logger.setLogLevel(logLevel);
logger.info(`日志级别已设置为: ${logLevel}`);
// 导入配置
const config = require('./config');
// 导入中间件
const { sessionActivity, sanitizeRequestBody, securityHeaders } = require('./middleware/auth');
// 导入初始化调度器
const { executeOnce } = require('./lib/initScheduler');
// 初始化Express应用
const app = express();
const server = http.createServer(app);
// 配置中间件
app.use(cors());
app.use(express.json());
app.use(express.static('web'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(session({
secret: config.sessionSecret || 'OhTq3faqSKoxbV%NJV',
resave: true,
saveUninitialized: true,
cookie: {
secure: config.secureSession || false,
maxAge: 7 * 24 * 60 * 60 * 1000 // 7天(一周)
}
}));
// 自定义中间件
app.use(sessionActivity);
app.use(sanitizeRequestBody);
app.use(securityHeaders);
// 请求日志中间件
app.use((req, res, next) => {
const start = Date.now();
// 在响应完成后记录日志
res.on('finish', () => {
const duration = Date.now() - start;
// 增强过滤条件
const isSuccessfulGet = req.method === 'GET' && (res.statusCode === 200 || res.statusCode === 304);
const isStaticResource = req.url.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/i);
const isCommonApiRequest = req.url.startsWith('/api/') &&
(req.url.includes('/check-session') ||
req.url.includes('/system-resources') ||
req.url.includes('/docker/status'));
const isErrorResponse = res.statusCode >= 400;
// 只记录关键API请求和错误响应过滤普通的API请求和静态资源
if ((isErrorResponse ||
(req.url.startsWith('/api/') && !isCommonApiRequest)) &&
!isStaticResource &&
!(isSuccessfulGet && isCommonApiRequest)) {
// 记录简化的请求信息
req.skipDetailedLogging = !isErrorResponse; // 非错误请求跳过详细日志
logger.request(req, res, duration);
}
});
next();
});
// 使用我们的路由注册函数加载所有路由
logger.info('注册所有应用路由...');
const registerRoutes = require('./routes');
registerRoutes(app);
// 提供兼容层以确保旧接口继续工作
require('./compatibility-layer')(app);
// 确保登录路由可用
try {
const loginRouter = require('./routes/login');
app.use('/api', loginRouter);
logger.success('✓ 已添加备用登录路由');
} catch (loginError) {
logger.error('无法加载备用登录路由:', loginError);
}
// 页面路由
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'web', 'index.html'));
});
app.get('/admin', (req, res) => {
res.sendFile(path.join(__dirname, 'web', 'admin.html'));
});
app.get('/docs', (req, res) => {
res.sendFile(path.join(__dirname, 'web', 'docs.html'));
});
// 废弃的登录页面路由 - 该路由未使用且导致404错误现已移除
// app.get('/login', (req, res) => {
// // 检查用户是否已登录
// if (req.session && req.session.user) {
// return res.redirect('/admin'); // 已登录用户重定向到管理页面
// }
//
// res.sendFile(path.join(__dirname, 'web', 'login.html'));
// });
// 404处理
app.use((req, res) => {
res.status(404).json({ error: '请求的资源不存在' });
});
// 错误处理中间件
app.use((err, req, res, next) => {
logger.error('应用错误:', err);
res.status(500).json({ error: '服务器内部错误', details: err.message });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
async function startServer() {
server.listen(PORT, async () => {
logger.info(`服务器已启动并监听端口 ${PORT}`);
try {
// 初始化数据库
try {
await initializeDatabase();
logger.success('数据库初始化完成');
} catch (dbError) {
logger.error('数据库初始化失败:', dbError);
logger.warn('将使用文件存储作为备用方案');
}
// 确保目录存在
await ensureDirectoriesExist();
logger.success('系统目录初始化完成');
// 下载必要资源
await downloadImages();
logger.success('资源下载完成');
// 默认使用SQLite数据库模式
try {
logger.info('正在检查SQLite数据库...');
const { isDatabaseReady } = require('./utils/database-checker');
const dbReady = await isDatabaseReady();
if (!dbReady) {
logger.warn('数据库未完全初始化,正在初始化...');
await initializeDatabase();
} else {
logger.info('SQLite数据库已就绪');
}
logger.success('SQLite数据库初始化完成');
} catch (dbError) {
logger.error('SQLite数据库初始化失败:', dbError.message);
throw dbError; // 数据库初始化失败时直接退出
}
// 初始化系统配置
try {
// 系统配置已在数据库初始化时完成
logger.info('系统配置初始化完成');
} catch (initError) {
logger.warn('系统配置初始化遇到问题:', initError.message);
}
// 初始化HTTP代理服务
try {
await httpProxyService.loadConfig();
// 检查环境变量并自动启动代理
await httpProxyService.checkEnvironmentAndAutoStart();
logger.success('HTTP代理服务配置已加载');
} catch (proxyError) {
logger.warn('HTTP代理服务初始化失败:', proxyError.message);
}
// 尝试启动监控
try {
const monitoringService = require('./services/monitoringService');
await monitoringService.startMonitoring();
logger.success('监控服务已启动');
} catch (monitoringError) {
logger.warn('监控服务启动失败:', monitoringError.message);
logger.warn('监控功能可能不可用');
}
// 尝试设置WebSocket
try {
const dockerRouter = require('./routes/docker');
if (typeof dockerRouter.setupLogWebsocket === 'function') {
dockerRouter.setupLogWebsocket(server);
logger.success('WebSocket服务已启动');
}
} catch (wsError) {
logger.warn('WebSocket服务启动失败:', wsError.message);
logger.warn('容器日志实时流可能不可用');
}
logger.success('服务器初始化完成,系统已准备就绪');
} catch (error) {
logger.error('系统初始化失败,但服务仍将继续运行:', error);
}
});
}
startServer();
// 处理进程终止信号
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
// 捕获未处理的Promise拒绝和未捕获的异常
process.on('unhandledRejection', (reason, promise) => {
logger.error('未处理的Promise拒绝:', reason);
if (reason instanceof Error) {
logger.debug('拒绝原因堆栈:', reason.stack);
}
});
process.on('uncaughtException', (error) => {
logger.error('未捕获的异常:', error);
logger.error('错误堆栈:', error.stack);
// 给日志一些时间写入后退出
setTimeout(() => {
logger.fatal('由于未捕获的异常系统将在3秒后退出');
setTimeout(() => process.exit(1), 3000);
}, 1000);
});
// 导出服务器对象以供测试使用
module.exports = server;