From ba4078b66b541c7148bb087f50549ae71d9ff1aa Mon Sep 17 00:00:00 2001 From: kjqwer <2990346238@qq.com> Date: Thu, 9 Oct 2025 10:21:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E5=90=AF=E6=B2=A1?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E4=BB=A3=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/config.js | 41 ++++++++++++++++++++++++++++++-- backend/routes/system.js | 5 +++- backend/server.js | 51 +++++++++++++++++++++++++++++++++++----- backend/utils/logger.js | 34 ++++++++++++++++++++++++--- 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/backend/config.js b/backend/config.js index a933180..ea2f0d4 100644 --- a/backend/config.js +++ b/backend/config.js @@ -1,14 +1,51 @@ const { defaultLogger } = require('./utils/logger'); +const Fse = require('fs-extra'); +const Path = require('path'); // 创建logger实例 const logger = defaultLogger.child('ProxyConfig'); +// 配置文件路径 +const CONFIG_FILE_DIR = require('appdata-path').getAppDataPath('pmanager'); +const CONFIG_FILE = Path.resolve(CONFIG_FILE_DIR, 'config.json'); + +// 读取用户配置中的代理设置 +function getUserProxyConfig() { + try { + if (Fse.existsSync(CONFIG_FILE)) { + const config = Fse.readJsonSync(CONFIG_FILE); + if (config.proxy) { + // 解析代理URL获取端口 + const proxyUrl = config.proxy; + const match = proxyUrl.match(/http:\/\/127\.0\.0\.1:(\d+)/); + if (match) { + return parseInt(match[1]); + } + } + } + } catch (error) { + logger.debug('读取用户代理配置失败:', error.message); + } + return null; +} + // 代理配置 const proxyConfig = { // 系统代理配置 system: { host: '127.0.0.1', - port: process.env.PROXY_PORT ? parseInt(process.env.PROXY_PORT) : 7890, + port: (() => { + // 优先级:环境变量 > 用户配置 > 默认值 + if (process.env.PROXY_PORT) { + return parseInt(process.env.PROXY_PORT); + } + const userPort = getUserProxyConfig(); + if (userPort) { + logger.info('从用户配置读取代理端口:', userPort); + return userPort; + } + return 7890; + })(), protocol: 'http' }, @@ -38,4 +75,4 @@ const proxyConfig = { } }; -module.exports = proxyConfig; \ No newline at end of file +module.exports = proxyConfig; \ No newline at end of file diff --git a/backend/routes/system.js b/backend/routes/system.js index db9b85d..e9fae06 100644 --- a/backend/routes/system.js +++ b/backend/routes/system.js @@ -22,18 +22,21 @@ router.post('/restart', async (req, res) => { // 延迟执行重启,给响应时间发送 setTimeout(async () => { try { - // 获取服务器实例(通过全局变量或其他方式) + // 获取服务器实例 const server = req.app.locals.serverInstance; if (server && typeof server.restart === 'function') { + logger.info('开始执行服务器重启...'); await server.restart(); } else { logger.error('无法获取服务器实例或重启方法'); // 如果无法优雅重启,则退出进程让进程管理器重启 + logger.info('尝试直接退出进程进行重启'); process.exit(1); } } catch (error) { logger.error('重启失败:', error); // 强制退出进程 + logger.info('重启失败,强制退出进程'); process.exit(1); } }, 1000); // 延迟1秒执行重启 diff --git a/backend/server.js b/backend/server.js index 4a8a496..0527088 100644 --- a/backend/server.js +++ b/backend/server.js @@ -31,6 +31,19 @@ class PixivServer { 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 + } + }; } /** @@ -203,15 +216,41 @@ class PixivServer { await this.backend.cleanup?.(); } - logger.info('正在重新初始化服务器...'); + logger.info('正在使用原始启动参数重新启动服务器...'); - // 重新初始化 - await this.init(); + // 使用spawn重新启动进程,保持原始参数 + const { spawn } = require('child_process'); + const path = require('path'); - // 重新启动 - this.start(); + // 构建启动命令 + 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); - logger.info('服务器重启完成'); return { success: true, message: '服务器重启成功' }; } catch (error) { diff --git a/backend/utils/logger.js b/backend/utils/logger.js index cceeb07..dfcd4ff 100644 --- a/backend/utils/logger.js +++ b/backend/utils/logger.js @@ -147,6 +147,22 @@ class Logger { /** * 格式化日志消息 */ + /** + * 安全的JSON序列化,避免循环引用 + */ + safeStringify(obj, space = 2) { + const seen = new WeakSet(); + return JSON.stringify(obj, (key, val) => { + if (val != null && typeof val === 'object') { + if (seen.has(val)) { + return '[Circular Reference]'; + } + seen.add(val); + } + return val; + }, space); + } + formatMessage(level, message, data = null) { const timeStr = this.getTimeString(); const levelName = LogLevelTexts[level]; @@ -167,12 +183,24 @@ class Logger { if (errorProps.length > 0) { const additionalProps = {}; errorProps.forEach(prop => { - additionalProps[prop] = data[prop]; + try { + additionalProps[prop] = data[prop]; + } catch (e) { + additionalProps[prop] = '[Unable to serialize]'; + } }); - formattedMessage += `\n Additional: ${JSON.stringify(additionalProps, null, 2)}`; + try { + formattedMessage += `\n Additional: ${this.safeStringify(additionalProps)}`; + } catch (e) { + formattedMessage += `\n Additional: [Serialization failed]`; + } } } else if (typeof data === 'object') { - formattedMessage += ` ${JSON.stringify(data, null, 2)}`; + try { + formattedMessage += ` ${this.safeStringify(data)}`; + } catch (e) { + formattedMessage += ` [Object serialization failed]`; + } } else { formattedMessage += ` ${data}`; }