app.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/env node
  2. /**
  3. * 应用主入口文件 - 启动服务器并初始化所有组件
  4. */
  5. // 记录服务器启动时间 - 最先执行这行代码,确保第一时间记录
  6. global.serverStartTime = Date.now();
  7. const express = require('express');
  8. const session = require('express-session');
  9. const path = require('path');
  10. const http = require('http');
  11. const logger = require('./logger');
  12. const { ensureDirectoriesExist } = require('./init-dirs');
  13. const registerRoutes = require('./routes');
  14. const { requireLogin, sessionActivity, sanitizeRequestBody, securityHeaders } = require('./middleware/auth');
  15. // 记录服务器启动时间到日志
  16. console.log(`服务器启动,时间戳: ${global.serverStartTime}`);
  17. logger.warn(`服务器启动,时间戳: ${global.serverStartTime}`);
  18. // 添加 session 文件存储模块 - 先导入session-file-store并创建对象
  19. const FileStore = require('session-file-store')(session);
  20. // 确保目录结构存在
  21. ensureDirectoriesExist().catch(err => {
  22. logger.error('创建必要目录失败:', err);
  23. process.exit(1);
  24. });
  25. // 初始化Express应用 - 确保正确初始化
  26. const app = express();
  27. const server = http.createServer(app);
  28. // 基本中间件配置
  29. app.use(express.json());
  30. app.use(express.urlencoded({ extended: true }));
  31. app.use(express.static(path.join(__dirname, 'web')));
  32. // 添加对documentation目录的静态访问
  33. app.use('/documentation', express.static(path.join(__dirname, 'documentation')));
  34. app.use(sessionActivity);
  35. app.use(sanitizeRequestBody);
  36. app.use(securityHeaders);
  37. // 会话配置
  38. app.use(session({
  39. secret: process.env.SESSION_SECRET || 'hubcmdui-secret-key',
  40. resave: false,
  41. saveUninitialized: false,
  42. cookie: {
  43. secure: process.env.NODE_ENV === 'production',
  44. maxAge: 24 * 60 * 60 * 1000 // 24小时
  45. },
  46. store: new FileStore({
  47. path: path.join(__dirname, 'data', 'sessions'),
  48. ttl: 86400
  49. })
  50. }));
  51. // 添加一个中间件来检查API请求的会话状态
  52. app.use('/api', (req, res, next) => {
  53. // 这些API端点不需要登录
  54. const publicEndpoints = [
  55. '/api/login',
  56. '/api/logout',
  57. '/api/check-session',
  58. '/api/health',
  59. '/api/system-status',
  60. '/api/system-resource-details',
  61. '/api/menu-items',
  62. '/api/config',
  63. '/api/monitoring-config',
  64. '/api/documentation',
  65. '/api/documentation/file'
  66. ];
  67. // 如果是公共API或用户已登录,则继续
  68. if (publicEndpoints.includes(req.path) ||
  69. publicEndpoints.some(endpoint => req.path.startsWith(endpoint)) ||
  70. (req.session && req.session.user)) {
  71. return next();
  72. }
  73. // 否则返回401未授权
  74. logger.warn(`未授权访问: ${req.path}`);
  75. return res.status(401).json({ error: 'Unauthorized' });
  76. });
  77. // 导入并注册所有路由
  78. registerRoutes(app);
  79. // 默认路由
  80. app.get('/', (req, res) => {
  81. res.sendFile(path.join(__dirname, 'web', 'index.html'));
  82. });
  83. app.get('/admin', (req, res) => {
  84. res.sendFile(path.join(__dirname, 'web', 'admin.html'));
  85. });
  86. // 404处理
  87. app.use((req, res) => {
  88. res.status(404).json({ error: 'Not Found' });
  89. });
  90. // 错误处理中间件
  91. app.use((err, req, res, next) => {
  92. logger.error('应用错误:', err);
  93. res.status(500).json({ error: '服务器内部错误', details: err.message });
  94. });
  95. // 启动服务器
  96. const PORT = process.env.PORT || 3000;
  97. server.listen(PORT, async () => {
  98. logger.info(`服务器已启动并监听端口 ${PORT}`);
  99. try {
  100. // 确保目录存在
  101. await ensureDirectoriesExist();
  102. logger.success('系统初始化完成');
  103. } catch (error) {
  104. logger.error('系统初始化失败:', error);
  105. }
  106. });
  107. // 注册进程事件处理
  108. process.on('SIGINT', () => {
  109. logger.info('接收到中断信号,正在关闭服务...');
  110. server.close(() => {
  111. logger.info('服务器已关闭');
  112. process.exit(0);
  113. });
  114. });
  115. process.on('SIGTERM', () => {
  116. logger.info('接收到终止信号,正在关闭服务...');
  117. server.close(() => {
  118. logger.info('服务器已关闭');
  119. process.exit(0);
  120. });
  121. });
  122. module.exports = { app, server };
  123. // 路由注册函数
  124. function registerRoutes(app) {
  125. try {
  126. logger.info('开始注册路由...');
  127. // API端点
  128. app.use('/api', [
  129. require('./routes/index'),
  130. require('./routes/docker'),
  131. require('./routes/docs'),
  132. require('./routes/users'),
  133. require('./routes/menu'),
  134. require('./routes/server')
  135. ]);
  136. logger.info('基本API路由已注册');
  137. // 系统路由 - 函数式注册
  138. const systemRouter = require('./routes/system');
  139. app.use('/api/system', systemRouter);
  140. logger.info('系统路由已注册');
  141. // 认证路由 - 直接使用Router实例
  142. const authRouter = require('./routes/auth');
  143. app.use('/api', authRouter);
  144. logger.info('认证路由已注册');
  145. // 配置路由 - 函数式注册
  146. const configRouter = require('./routes/config');
  147. if (typeof configRouter === 'function') {
  148. logger.info('配置路由是一个函数,正在注册...');
  149. configRouter(app);
  150. logger.info('配置路由已注册');
  151. } else {
  152. logger.error('配置路由不是一个函数,无法注册', typeof configRouter);
  153. }
  154. logger.success('✓ 所有路由已注册');
  155. } catch (error) {
  156. logger.error('路由注册失败:', error);
  157. }
  158. }