From e5ba17329cd00f4bf8bdd42204a462573ebe4c4b Mon Sep 17 00:00:00 2001 From: WanWanYun Date: Wed, 12 Nov 2025 20:44:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E4=B8=8A=E4=BC=A0=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=92=8C=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端: 添加 GET /api/admin/check-upload-tool 检测工具是否存在 - 后端: 添加 POST /api/admin/upload-tool 允许管理员手动上传exe - 前端: 管理员界面新增上传工具管理卡片 - 前端: 支持检测工具状态、显示文件信息、手动上传 - 验证: 文件必须是.exe格式且大小>20MB --- backend/server.js | 109 ++++++++++++++++++++++++++++++++++++++++++++++ frontend/app.html | 67 ++++++++++++++++++++++++++++ frontend/app.js | 94 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 268 insertions(+), 2 deletions(-) diff --git a/backend/server.js b/backend/server.js index 8d47d63..9efcbaf 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2333,6 +2333,115 @@ app.delete('/api/admin/shares/:id', authMiddleware, adminMiddleware, (req, res) } }); +// ============================================ +// 管理员:上传工具管理 +// ============================================ + +// 检查上传工具是否存在 +app.get('/api/admin/check-upload-tool', authMiddleware, adminMiddleware, (req, res) => { + try { + const toolPath = path.join(__dirname, '..', 'upload-tool', 'dist', '玩玩云上传工具.exe'); + + if (fs.existsSync(toolPath)) { + const stats = fs.statSync(toolPath); + const sizeMB = (stats.size / (1024 * 1024)).toFixed(2); + + res.json({ + success: true, + exists: true, + fileInfo: { + path: toolPath, + size: stats.size, + sizeMB: sizeMB, + modifiedAt: stats.mtime + } + }); + } else { + res.json({ + success: true, + exists: false, + message: '上传工具不存在' + }); + } + } catch (error) { + console.error('检查上传工具失败:', error); + res.status(500).json({ + success: false, + message: '检查失败: ' + error.message + }); + } +}); + +// 上传工具文件 +const uploadToolStorage = multer.diskStorage({ + destination: (req, file, cb) => { + const uploadDir = path.join(__dirname, '..', 'upload-tool', 'dist'); + // 确保目录存在 + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + cb(null, uploadDir); + }, + filename: (req, file, cb) => { + // 固定文件名 + cb(null, '玩玩云上传工具.exe'); + } +}); + +const uploadTool = multer({ + storage: uploadToolStorage, + limits: { + fileSize: 100 * 1024 * 1024 // 限制100MB + }, + fileFilter: (req, file, cb) => { + // 只允许.exe文件 + if (!file.originalname.toLowerCase().endsWith('.exe')) { + return cb(new Error('只允许上传.exe文件')); + } + cb(null, true); + } +}); + +app.post('/api/admin/upload-tool', authMiddleware, adminMiddleware, uploadTool.single('file'), (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + message: '请选择要上传的文件' + }); + } + + const fileSizeMB = (req.file.size / (1024 * 1024)).toFixed(2); + + // 验证文件大小(至少20MB,上传工具通常很大) + if (req.file.size < 20 * 1024 * 1024) { + // 删除上传的文件 + fs.unlinkSync(req.file.path); + return res.status(400).json({ + success: false, + message: '文件大小异常,上传工具通常大于20MB' + }); + } + + console.log(`[上传工具] 管理员上传成功: ${fileSizeMB}MB`); + + res.json({ + success: true, + message: '上传工具已上传', + fileInfo: { + size: req.file.size, + sizeMB: fileSizeMB + } + }); + } catch (error) { + console.error('上传工具失败:', error); + res.status(500).json({ + success: false, + message: '上传失败: ' + error.message + }); + } +}); + // 分享页面访问路由 app.get("/s/:code", (req, res) => { const shareCode = req.params.code; diff --git a/frontend/app.html b/frontend/app.html index 3d163c7..7bd7339 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -1381,6 +1381,73 @@ + + +
+

+ 上传工具管理 +

+ + +
+
+
+
+
+ 上传工具已存在 +
+
+ 文件大小: {{ uploadToolStatus.fileInfo.sizeMB }} MB +
+
+ 最后修改: {{ formatDate(uploadToolStatus.fileInfo.modifiedAt) }} +
+
+ +
+
+ +
+
+ 上传工具不存在 +
+
+ 普通用户将无法下载上传工具,请上传工具文件 +
+
+
+ + +
+ + + + + +
+ + +
+
+ 说明: +
    +
  • 上传工具文件应为 .exe 格式,大小通常在 20-50 MB
  • +
  • 上传后,普通用户可以在设置页面下载该工具
  • +
  • 如果安装脚本下载失败,可以在这里手动上传
  • +
+
+
+
diff --git a/frontend/app.js b/frontend/app.js index 6a46f76..57170d1 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -171,7 +171,12 @@ createApp({ }, // 定期检查用户配置更新的定时器 - profileCheckInterval: null + profileCheckInterval: null, + + // 上传工具管理 + uploadToolStatus: null, // 上传工具状态 { exists, fileInfo: { size, sizeMB, modifiedAt } } + checkingUploadTool: false, // 是否正在检测上传工具 + uploadingTool: false // 是否正在上传工具 }; }, @@ -1762,7 +1767,92 @@ handleDragLeave(e) { this.showToast('error', '错误', '更新系统设置失败'); } } - }, +, + + // ===== 上传工具管理 ===== + + // 检测上传工具是否存在 + async checkUploadTool() { + this.checkingUploadTool = true; + try { + const response = await axios.get( + `${this.apiBase}/api/admin/check-upload-tool`, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.uploadToolStatus = response.data; + if (response.data.exists) { + this.showToast('success', '检测完成', '上传工具文件存在'); + } else { + this.showToast('warning', '提示', '上传工具文件不存在,请上传'); + } + } + } catch (error) { + console.error('检测上传工具失败:', error); + this.showToast('error', '错误', '检测失败: ' + (error.response?.data?.message || error.message)); + } finally { + this.checkingUploadTool = false; + } + }, + + // 处理上传工具文件 + async handleUploadToolFile(event) { + const file = event.target.files[0]; + if (!file) return; + + // 验证文件类型 + if (!file.name.toLowerCase().endsWith('.exe')) { + this.showToast('error', '错误', '只能上传 .exe 文件'); + event.target.value = ''; + return; + } + + // 验证文件大小(至少20MB) + const minSizeMB = 20; + const fileSizeMB = file.size / (1024 * 1024); + if (fileSizeMB < minSizeMB) { + this.showToast('error', '错误', `文件大小过小(${fileSizeMB.toFixed(2)}MB),上传工具通常大于${minSizeMB}MB`); + event.target.value = ''; + return; + } + + // 确认上传 + if (!confirm(`确定要上传 ${file.name} (${fileSizeMB.toFixed(2)}MB) 吗?`)) { + event.target.value = ''; + return; + } + + this.uploadingTool = true; + + try { + const formData = new FormData(); + formData.append('file', file); + + const response = await axios.post( + `${this.apiBase}/api/admin/upload-tool`, + formData, + { + headers: { + Authorization: `Bearer ${this.token}`, + 'Content-Type': 'multipart/form-data' + } + } + ); + + if (response.data.success) { + this.showToast('success', '成功', '上传工具已上传'); + // 重新检测 + await this.checkUploadTool(); + } + } catch (error) { + console.error('上传工具失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '上传失败'); + } finally { + this.uploadingTool = false; + event.target.value = ''; // 清空input,允许重复上传 + } + } }, mounted() { // 阻止全局拖拽默认行为(防止拖到区域外打开新页面)