From 275a3672d2c75950ec888d394db24ae2f280322a Mon Sep 17 00:00:00 2001 From: kjqwer <2990346238@qq.com> Date: Sun, 24 Aug 2025 20:38:28 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=AB=AF=E5=8F=A3=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E5=8F=AF=E4=BB=A5=E8=AE=BE=E7=BD=AE=E8=87=AA?= =?UTF-8?q?=E4=B9=89=E5=AE=9A=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- backend/server.js | 144 +++++++++------- backend/start.js | 46 +++++- scripts/create-portable.js | 27 +-- start.bat | 18 +- ui/README.md | 2 + ui/src/components/artist/ArtistCard.vue | 49 ++---- ui/src/components/artwork/ArtworkCard.vue | 55 ++----- ui/src/services/api.ts | 22 ++- ui/src/services/download.ts | 4 +- ui/src/views/ArtistView.vue | 15 +- ui/src/views/ArtworkView.vue | 190 +++++++++------------- 12 files changed, 284 insertions(+), 291 deletions(-) diff --git a/README.md b/README.md index 100825a..6042911 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,11 @@ Pixiv 下载浏览管理器是一个基于 Web 的应用程序,提供以下功 4. **启动应用** - 修改代理端口,请用记事本打开 `start.bat` 文件,修改PROXY_PORT端口号 + - 修改服务器端口,请用记事本打开 `start.bat` 文件,修改SERVER_PORT端口号(默认3000) - 双击 `start.bat` 文件启动 5. **访问应用** - - 打开浏览器访问:http://localhost:3000 + - 打开浏览器访问:http://localhost:3000(默认端口,可修改) ## 🌐 代理配置 diff --git a/backend/server.js b/backend/server.js index 8d72164..d2c843a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -23,20 +23,21 @@ const proxyConfig = require('./config'); // 自定义日志中间件 function customLogger(req, res, next) { // 过滤掉静态资源请求和图片代理请求 - const isStaticResource = req.path.startsWith('/assets/') || - req.path.startsWith('/downloads/') || - req.path.includes('.js') || - req.path.includes('.css') || - req.path.includes('.ico') || - req.path.includes('.png') || - req.path.includes('.jpg') || - req.path.includes('.jpeg') || - req.path.includes('.gif') || - req.path.includes('.svg') || - req.path.includes('.woff') || - req.path.includes('.woff2') || - req.path.includes('.ttf') || - req.path.includes('.eot'); + const isStaticResource = + req.path.startsWith('/assets/') || + req.path.startsWith('/downloads/') || + req.path.includes('.js') || + req.path.includes('.css') || + req.path.includes('.ico') || + req.path.includes('.png') || + req.path.includes('.jpg') || + req.path.includes('.jpeg') || + req.path.includes('.gif') || + req.path.includes('.svg') || + req.path.includes('.woff') || + req.path.includes('.woff2') || + req.path.includes('.ttf') || + req.path.includes('.eot'); // 过滤掉图片代理请求 const isImageProxy = req.path === '/api/proxy/image'; @@ -44,17 +45,17 @@ function customLogger(req, res, next) { // 只记录API请求和重要请求,排除静态资源和图片代理 if (!isStaticResource && !isImageProxy) { const start = Date.now(); - + // 原始响应结束方法 const originalEnd = res.end; - + // 重写响应结束方法以获取响应时间 - res.end = function(chunk, encoding) { + res.end = function (chunk, encoding) { const duration = Date.now() - start; const statusCode = res.statusCode; const method = req.method; const url = req.originalUrl; - + // 根据状态码选择颜色和图标 let statusIcon, statusColor; if (statusCode >= 200 && statusCode < 300) { @@ -70,38 +71,49 @@ function customLogger(req, res, next) { statusIcon = '❌'; statusColor = '\x1b[31m'; // 红色 } - + // 根据请求类型选择图标 let methodIcon; switch (method) { - case 'GET': methodIcon = '📥'; break; - case 'POST': methodIcon = '📤'; break; - case 'PUT': methodIcon = '🔄'; break; - case 'DELETE': methodIcon = '🗑️'; break; - case 'PATCH': methodIcon = '🔧'; break; - default: methodIcon = '❓'; + case 'GET': + methodIcon = '📥'; + break; + case 'POST': + methodIcon = '📤'; + break; + case 'PUT': + methodIcon = '🔄'; + break; + case 'DELETE': + methodIcon = '🗑️'; + break; + case 'PATCH': + methodIcon = '🔧'; + break; + default: + methodIcon = '❓'; } - + // 格式化时间 const now = new Date(); - const timeStr = now.toLocaleTimeString('zh-CN', { + const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', - second: '2-digit' + second: '2-digit', }); - + // 构建日志消息 const logMessage = `${statusColor}${statusIcon} ${methodIcon} ${method} ${url} ${statusCode} ${duration}ms\x1b[0m`; - + // 输出日志 console.log(`[${timeStr}] ${logMessage}`); - + // 调用原始的end方法 originalEnd.call(this, chunk, encoding); }; } - + next(); } @@ -109,7 +121,7 @@ class PixivServer { constructor() { this.app = express(); this.backend = null; - this.port = process.env.PORT || 3000; + this.port = 3000; // 默认端口,会在init时重新设置 } /** @@ -117,23 +129,26 @@ class PixivServer { */ async init() { console.log('\x1b[34m🔧 正在初始化 Pixiv 后端服务器...\x1b[0m'); - + + // 重新设置端口(从环境变量获取) + this.port = process.env.PORT || 3000; + // 设置代理 proxyConfig.setEnvironmentVariables(); - + // 初始化 Pixiv 后端 this.backend = new PixivBackend(); await this.backend.init(); - + // 配置中间件 this.setupMiddleware(); - + // 配置路由 this.setupRoutes(); - + // 配置错误处理 - 临时注释掉 this.setupErrorHandling(); - + console.log('\x1b[32m✅ 服务器初始化完成\x1b[0m'); } @@ -143,23 +158,25 @@ class PixivServer { setupMiddleware() { // 自定义日志中间件(替换morgan) this.app.use(customLogger); - + // CORS 中间件 - this.app.use(cors({ - origin: process.env.FRONTEND_URL || 'http://localhost:5173', - credentials: true - })); - + this.app.use( + cors({ + origin: process.env.FRONTEND_URL || true, // 允许所有来源,或者通过环境变量指定 + credentials: true, + }) + ); + // JSON 解析中间件 this.app.use(express.json({ limit: '10mb' })); this.app.use(express.urlencoded({ extended: true, limit: '10mb' })); - + // 静态文件服务 this.app.use('/downloads', express.static(path.join(__dirname, '../downloads'))); - + // 前端静态文件服务 this.app.use(express.static(path.join(__dirname, '../ui/dist'))); - + // 将后端实例注入到请求对象中 this.app.use((req, res, next) => { req.backend = this.backend; @@ -173,13 +190,13 @@ class PixivServer { setupRoutes() { // 健康检查 this.app.get('/health', (req, res) => { - res.json({ - status: 'ok', + res.json({ + status: 'ok', timestamp: new Date().toISOString(), backend: { isLoggedIn: req.backend.isLoggedIn, - user: req.backend.config.user?.account - } + user: req.backend.config.user?.account, + }, }); }); @@ -191,17 +208,17 @@ class PixivServer { this.app.use('/api/ranking', authMiddleware, rankingRoutes); this.app.use('/api/repository', repositoryRoutes); // 仓库管理,不需要认证 this.app.use('/api/proxy', proxyRoutes); // 图片代理,不需要认证 - + // 404 处理 this.app.use((req, res) => { // 如果是API请求,返回JSON格式的404 if (req.path.startsWith('/api/')) { - return res.status(404).json({ - error: 'Not Found', - message: `Route ${req.originalUrl} not found` - }); + return res.status(404).json({ + error: 'Not Found', + message: `Route ${req.originalUrl} not found`, + }); } - + // 否则返回前端页面(SPA路由支持) res.sendFile(path.join(__dirname, '../ui/dist/index.html')); }); @@ -245,13 +262,16 @@ class PixivServer { // 如果直接运行此文件 if (require.main === module) { const server = new PixivServer(); - + // 处理进程信号 process.on('SIGINT', () => server.shutdown()); process.on('SIGTERM', () => server.shutdown()); - + // 启动服务器 - server.init().then(() => server.start()).catch(console.error); + server + .init() + .then(() => server.start()) + .catch(console.error); } -module.exports = PixivServer; \ No newline at end of file +module.exports = PixivServer; diff --git a/backend/start.js b/backend/start.js index 7624eb7..3c42414 100644 --- a/backend/start.js +++ b/backend/start.js @@ -10,15 +10,38 @@ const PixivServer = require('./server'); function parseArguments() { const args = process.argv.slice(2); const options = {}; - + for (let i = 0; i < args.length; i++) { const arg = args[i]; - if (arg === '--proxy-port' && i + 1 < args.length) { - options.proxyPort = parseInt(args[i + 1]); + + // 处理 --key=value 格式 + if (arg.startsWith('--proxy-port=')) { + const port = parseInt(arg.split('=')[1]); + if (!isNaN(port)) { + options.proxyPort = port; + } + } else if (arg.startsWith('--server-port=')) { + const port = parseInt(arg.split('=')[1]); + if (!isNaN(port)) { + options.serverPort = port; + } + } + // 处理 --key value 格式(向后兼容) + else if (arg === '--proxy-port' && i + 1 < args.length) { + const port = parseInt(args[i + 1]); + if (!isNaN(port)) { + options.proxyPort = port; + } + i++; // 跳过下一个参数 + } else if (arg === '--server-port' && i + 1 < args.length) { + const port = parseInt(args[i + 1]); + if (!isNaN(port)) { + options.serverPort = port; + } i++; // 跳过下一个参数 } } - + return options; } @@ -34,6 +57,12 @@ if (cliOptions.proxyPort) { console.log(`\x1b[36m📡 代理端口已设置为: ${cliOptions.proxyPort}\x1b[0m`); } +// 如果提供了服务器端口,设置环境变量 +if (cliOptions.serverPort) { + process.env.PORT = cliOptions.serverPort.toString(); + console.log(`\x1b[36m🌐 服务器端口已设置为: ${cliOptions.serverPort}\x1b[0m`); +} + console.log('\x1b[35m🚀 启动 Pixiv 后端服务器...\x1b[0m'); // 创建服务器实例 @@ -51,7 +80,7 @@ process.on('SIGTERM', async () => { }); // 处理未捕获的异常 -process.on('uncaughtException', (error) => { +process.on('uncaughtException', error => { console.error('\x1b[31m❌ 未捕获的异常:\x1b[0m', error); process.exit(1); }); @@ -62,9 +91,10 @@ process.on('unhandledRejection', (reason, promise) => { }); // 启动服务器 -server.init() +server + .init() .then(() => server.start()) - .catch((error) => { + .catch(error => { console.error('\x1b[31m❌ 服务器启动失败:\x1b[0m', error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/scripts/create-portable.js b/scripts/create-portable.js index c3c9b4d..0b97151 100644 --- a/scripts/create-portable.js +++ b/scripts/create-portable.js @@ -19,14 +19,11 @@ async function createPortable() { // 创建启动脚本 const startScript = `@echo off -chcp 65001 >nul + title Pixiv Manager -:: ======================================== -:: 代理配置 - 请根据你的代理软件修改端口号 -:: 常见端口: Clash=7890, V2Ray=10809, Shadowsocks=1080 -:: ======================================== set PROXY_PORT=7890 +set SERVER_PORT=3000 echo. echo ======================================== @@ -37,17 +34,19 @@ echo. cd /d "%~dp0" echo Current proxy port: %PROXY_PORT% -echo To change proxy port, edit this file and modify line 6 +echo Current server port: %SERVER_PORT% +echo To change proxy port, edit PROXY_PORT=xxxx in this file +echo To change server port, edit SERVER_PORT=xxxx in this file echo. echo Starting backend server... -echo Access URL: http://localhost:3000 +echo Access URL: http://localhost:%SERVER_PORT% echo. echo Tip: Press Ctrl+C to stop server echo. -:: Start server and pass proxy port -pixiv-backend.exe --proxy-port=%PROXY_PORT% +:: Start server and pass proxy port and server port +pixiv-backend.exe --proxy-port=%PROXY_PORT% --server-port=%SERVER_PORT% echo. echo Server stopped @@ -65,13 +64,19 @@ pause 2. 在浏览器中访问 http://localhost:3000 3. 按 Ctrl+C 停止服务器 -## 代理设置(重要) +## 配置设置 -如需使用代理,请用记事本编辑 \`start.bat\` 文件,修改(PROXY_PORT=xxxx)的端口号: +如需修改配置,请用记事本编辑 \`start.bat\` 文件: + +### 代理设置(重要) +修改(PROXY_PORT=xxxx)的端口号: - Clash: 7890 - V2Ray: 10809 - Shadowsocks: 1080 +### 服务器端口设置 +修改(SERVER_PORT=xxxx)的端口号,默认为3000 + ## 注意事项 - 首次运行可能需要几秒钟启动时间 diff --git a/start.bat b/start.bat index d428474..c1627fe 100644 --- a/start.bat +++ b/start.bat @@ -7,6 +7,11 @@ chcp 65001 >nul :: ======================================== set PROXY_PORT= +:: ======================================== +:: 服务器端口配置 - 默认3000 +:: ======================================== +set SERVER_PORT=3000 + echo. echo 🚀 Pixiv Manager 启动中... echo. @@ -14,17 +19,22 @@ echo. cd /d "%~dp0" echo 📡 当前代理端口: %PROXY_PORT% -echo 💡 如需修改代理端口,请用记事本打开此文件,修改第6行的端口号 +echo 🌐 当前服务器端口: %SERVER_PORT% +echo 💡 如需修改端口,请用记事本打开此文件,修改对应的端口号 echo. echo 📊 启动后端服务器... -echo 🌐 访问地址: http://localhost:3000 +echo 🌐 访问地址: http://localhost:%SERVER_PORT% echo. echo 💡 提示: 按 Ctrl+C 停止服务器 echo. -:: 启动服务器并传递代理端口 -node backend/start.js --proxy-port=%PROXY_PORT% +:: 启动服务器并传递代理端口和服务器端口 +if "%PROXY_PORT%"=="" ( + node backend/start.js --server-port=%SERVER_PORT% +) else ( + node backend/start.js --proxy-port=%PROXY_PORT% --server-port=%SERVER_PORT% +) echo. echo ⏹️ 服务器已停止 diff --git a/ui/README.md b/ui/README.md index 341e536..95ec94e 100644 --- a/ui/README.md +++ b/ui/README.md @@ -120,6 +120,8 @@ pnpm lint VITE_API_BASE_URL=http://localhost:3000 ``` +> **注意**: 在生产环境中,前端会自动使用当前域名和端口,无需手动设置此环境变量。 + ### 构建部署 ```bash diff --git a/ui/src/components/artist/ArtistCard.vue b/ui/src/components/artist/ArtistCard.vue index afe9af5..10600c8 100644 --- a/ui/src/components/artist/ArtistCard.vue +++ b/ui/src/components/artist/ArtistCard.vue @@ -1,35 +1,23 @@ \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/components/artwork/ArtworkCard.vue b/ui/src/components/artwork/ArtworkCard.vue index ac5c2e3..82443aa 100644 --- a/ui/src/components/artwork/ArtworkCard.vue +++ b/ui/src/components/artwork/ArtworkCard.vue @@ -1,14 +1,8 @@