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() {
// 阻止全局拖拽默认行为(防止拖到区域外打开新页面)