Files
pixiv/backend/server.js
T

289 lines
7.8 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const path = require('path');
// 导入logger
const { defaultLogger } = require('./utils/logger');
// 导入中间件
const { errorHandler } = require('./middleware/errorHandler');
const { loggerMiddleware } = require('./middleware/logger');
const { corsMiddleware } = require('./middleware/cors');
const { bodyParserMiddleware } = require('./middleware/bodyParser');
const { staticFilesMiddleware } = require('./middleware/staticFiles');
const { backendInjector } = require('./middleware/backendInjector');
// 导入路由配置
const { setupRoutes } = require('./routes');
// 导入核心模块
const PixivBackend = require('./core');
const proxyConfig = require('./config');
// 创建logger实例
const logger = defaultLogger.child('Server');
class PixivServer {
constructor() {
this.app = express();
this.backend = null;
this.server = null; // 添加server实例引用
this.port = 3000; // 默认端口,会在init时重新设置
this.logLevel = process.env.LOG_LEVEL || 'info'; // 获取日志级别
this.isVerboseMode = ['debug', 'trace'].includes(this.logLevel.toLowerCase()); // 检查是否为详细模式
// 保存启动时的命令行参数和环境变量
this.startupArgs = {
argv: [...process.argv], // 复制命令行参数
env: {
PORT: process.env.PORT,
PROXY_PORT: process.env.PROXY_PORT,
LOG_LEVEL: process.env.LOG_LEVEL,
NODE_ENV: process.env.NODE_ENV,
AUTO_OPEN_BROWSER: process.env.AUTO_OPEN_BROWSER,
UV_THREADPOOL_SIZE: process.env.UV_THREADPOOL_SIZE
}
};
}
/**
* 初始化服务器
*/
async init() {
logger.info('正在初始化 Pixiv 后端服务器...');
// 重新设置端口(从环境变量获取)
this.port = process.env.PORT || 3000;
// 如果启用了详细模式,输出调试信息
if (this.isVerboseMode) {
logger.info(`详细模式已启用 (日志级别: ${this.logLevel.toUpperCase()})`);
logger.debug('环境变量:', {
NODE_ENV: process.env.NODE_ENV,
PORT: process.env.PORT,
PROXY_PORT: process.env.PROXY_PORT,
LOG_LEVEL: process.env.LOG_LEVEL
});
}
// 设置代理
proxyConfig.setEnvironmentVariables();
// 初始化 Pixiv 后端
this.backend = new PixivBackend();
await this.backend.init();
// 配置中间件
this.setupMiddleware();
// 配置路由
this.setupRoutes();
// 配置错误处理
this.setupErrorHandling();
logger.info('服务器初始化完成');
}
/**
* 配置中间件
*/
setupMiddleware() {
// 自定义日志中间件
this.app.use(loggerMiddleware);
// CORS 中间件
this.app.use(corsMiddleware());
// Body Parser 中间件
this.app.use(bodyParserMiddleware());
// 静态文件服务中间件
const staticMiddlewares = staticFilesMiddleware();
this.app.use('/downloads', staticMiddlewares[0]); // 下载文件静态服务
this.app.use(staticMiddlewares[1]); // 前端静态文件服务
// 后端实例注入中间件
this.app.use(backendInjector(this.backend));
// 将服务器实例保存到app.locals中,供路由使用
this.app.locals.serverInstance = this;
}
/**
* 配置路由
*/
setupRoutes() {
setupRoutes(this.app, this.backend);
}
/**
* 配置错误处理
*/
setupErrorHandling() {
this.app.use(errorHandler);
}
/**
* 启动服务器
*/
start() {
this.server = this.app.listen(this.port, () => {
logger.info('Pixiv 后端服务器已启动');
logger.info(`服务地址: http://localhost:${this.port}`);
logger.info(`健康检查: http://localhost:${this.port}/health`);
logger.info(`登录状态: ${this.backend.isLoggedIn ? '已登录' : '未登录'}`);
if (this.backend.isLoggedIn) {
logger.info(`用户: ${this.backend.config.user?.account}`);
}
if (this.isVerboseMode) {
logger.info(`日志级别: ${this.logLevel.toUpperCase()}`);
logger.debug(`服务器端口: ${this.port}`);
logger.debug(`代理端口: ${process.env.PROXY_PORT || '未设置'}`);
}
logger.info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
// 自动打开浏览器
if (process.env.AUTO_OPEN_BROWSER === 'true') {
this.openBrowser();
}
});
}
/**
* 打开浏览器
*/
openBrowser() {
const url = `http://localhost:${this.port}`;
logger.info(`正在打开浏览器: ${url}`);
const { spawn } = require('child_process');
const os = require('os');
let command;
let args = [url];
switch (os.platform()) {
case 'win32':
command = 'cmd';
args = ['/c', 'start', '""', url];
break;
case 'darwin':
command = 'open';
break;
case 'linux':
command = 'xdg-open';
break;
default:
logger.warn('不支持的操作系统,无法自动打开浏览器');
return;
}
try {
const child = spawn(command, args, {
detached: true,
stdio: 'ignore'
});
child.unref();
logger.info('浏览器已打开');
} catch (error) {
logger.warn('打开浏览器失败:', error.message);
}
}
/**
* 重启服务器
*/
async restart() {
logger.info('正在重启服务器...');
try {
// 清理代理环境变量
proxyConfig.clearEnvironmentVariables();
// 关闭当前服务器
if (this.server) {
await new Promise((resolve) => {
this.server.close(() => {
logger.info('HTTP服务器已关闭');
resolve();
});
});
}
// 清理后端实例
if (this.backend) {
await this.backend.cleanup?.();
}
logger.info('正在使用原始启动参数重新启动服务器...');
// 使用spawn重新启动进程,保持原始参数
const { spawn } = require('child_process');
const path = require('path');
// 构建启动命令
const nodeExecutable = process.execPath;
const startScript = path.join(__dirname, 'start.js');
// 获取原始命令行参数(排除node和脚本路径)
const originalArgs = this.startupArgs.argv.slice(2);
logger.info('重启命令:', nodeExecutable, [startScript, ...originalArgs]);
// 启动新进程
const child = spawn(nodeExecutable, [startScript, ...originalArgs], {
detached: true,
stdio: 'inherit',
env: {
...process.env,
...this.startupArgs.env // 恢复原始环境变量
}
});
// 分离子进程
child.unref();
logger.info('新进程已启动,当前进程即将退出');
// 延迟退出当前进程
setTimeout(() => {
process.exit(0);
}, 500);
return { success: true, message: '服务器重启成功' };
} catch (error) {
logger.error('服务器重启失败:', error);
throw error;
}
}
/**
* 优雅关闭
*/
async shutdown() {
logger.info('正在关闭服务器...');
// 清理代理环境变量
proxyConfig.clearEnvironmentVariables();
logger.info('服务器已关闭');
process.exit(0);
}
}
// 如果直接运行此文件
if (require.main === module) {
const server = new PixivServer();
// 处理进程信号
process.on('SIGINT', () => server.shutdown());
process.on('SIGTERM', () => server.shutdown());
// 启动服务器
server
.init()
.then(() => server.start())
.catch((error) => logger.error('服务器启动失败', error));
}
module.exports = PixivServer;