diff --git a/backend/server.js b/backend/server.js index 7c75fa4..fad85ad 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1571,6 +1571,88 @@ app.post('/api/user/update-ftp', } ); +// 获取SFTP存储空间使用情况 +app.get('/api/user/sftp-usage', authMiddleware, async (req, res) => { + let sftp = null; + + try { + // 检查用户是否配置了SFTP + if (!req.user.has_ftp_config) { + return res.status(400).json({ + success: false, + message: '未配置SFTP服务器' + }); + } + + // 连接SFTP + sftp = await connectToSFTP(req.user); + + // 递归计算目录大小的函数 + async function calculateDirSize(dirPath) { + let totalSize = 0; + let fileCount = 0; + let dirCount = 0; + + try { + const list = await sftp.list(dirPath); + + for (const item of list) { + // 跳过 . 和 .. 目录 + if (item.name === '.' || item.name === '..') continue; + + const itemPath = dirPath === '/' ? `/${item.name}` : `${dirPath}/${item.name}`; + + if (item.type === 'd') { + // 是目录,递归计算 + dirCount++; + const subResult = await calculateDirSize(itemPath); + totalSize += subResult.totalSize; + fileCount += subResult.fileCount; + dirCount += subResult.dirCount; + } else { + // 是文件,累加大小 + fileCount++; + totalSize += item.size || 0; + } + } + } catch (err) { + // 跳过无法访问的目录 + console.warn(`[SFTP统计] 无法访问目录 ${dirPath}: ${err.message}`); + } + + return { totalSize, fileCount, dirCount }; + } + + // 从根目录开始计算 + const result = await calculateDirSize('/'); + + res.json({ + success: true, + usage: { + totalSize: result.totalSize, + totalSizeFormatted: formatFileSize(result.totalSize), + fileCount: result.fileCount, + dirCount: result.dirCount + } + }); + + } catch (error) { + console.error('[SFTP统计] 获取失败:', error); + res.status(500).json({ + success: false, + message: '获取SFTP空间使用情况失败: ' + error.message + }); + } finally { + if (sftp) { + try { + await sftp.end(); + } catch (e) { + // 忽略关闭错误 + } + } + } +}); + // 修改管理员账号信息(仅管理员可修改用户名) app.post('/api/admin/update-profile', authMiddleware, diff --git a/frontend/app.html b/frontend/app.html index 347254c..21d52c3 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -1281,6 +1281,29 @@
先填写 SFTP 连接信息再切换
+ +
+
+ 空间统计 + +
+
+ 统计中... +
+
+ {{ sftpUsageError }} +
+
+ {{ sftpUsage.totalSizeFormatted }} + ({{ sftpUsage.fileCount }} 文件) +
+
点击刷新查看
+
- -
-

- SFTP存储 -

- -
-
- 存储方式: - SFTP存储 - - 仅SFTP - -
- -
- 服务器: - {{ user.ftp_host }}:{{ user.ftp_port }} -
- -
- - 说明: 管理员已将您的存储权限设置为"仅SFTP存储",您的文件存储在远程SFTP服务器上。如需使用本地存储,请联系管理员修改权限设置。 -
-
-
- - +

SFTP存储 @@ -1389,6 +1385,68 @@ 配置 / 修改 SFTP

+ + +
+
+ 服务器信息 +
+
+ {{ user.ftp_host }}:{{ user.ftp_port }} +
+
+ + +
+
+
+ 空间使用统计 +
+ +
+ + +
+ +
正在统计 SFTP 空间使用情况...
+
(文件较多时可能需要一些时间)
+
+ + +
+ {{ sftpUsageError }} +
+ + +
+
+
{{ sftpUsage.totalSizeFormatted }}
+
总使用空间
+
+
+
{{ sftpUsage.fileCount }}
+
文件数
+
+
+
{{ sftpUsage.dirCount }}
+
文件夹数
+
+
+ + +
+ + 点击"刷新"按钮统计 SFTP 空间使用情况 +
+
+
数据存储在你的 SFTP 服务器上,如需切换回本地请联系管理员调整权限。 diff --git a/frontend/app.js b/frontend/app.js index 3c5a4a2..7b31004 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -220,7 +220,12 @@ createApp({ // SFTP配置引导弹窗 showSftpGuideModal: false, - showSftpConfigModal: false + showSftpConfigModal: false, + + // SFTP空间使用统计 + sftpUsage: null, // { totalSize, totalSizeFormatted, fileCount, dirCount } + sftpUsageLoading: false, + sftpUsageError: null }; }, @@ -1840,6 +1845,35 @@ handleDragLeave(e) { console.error('加载用户资料失败:', error); } }, + + // 加载SFTP空间使用统计 + async loadSftpUsage() { + // 仅在用户已配置SFTP时才加载 + if (!this.user?.has_ftp_config) { + this.sftpUsage = null; + return; + } + + this.sftpUsageLoading = true; + this.sftpUsageError = null; + + try { + const response = await axios.get( + `${this.apiBase}/api/user/sftp-usage`, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.sftpUsage = response.data.usage; + } + } catch (error) { + console.error('获取SFTP空间使用情况失败:', error); + this.sftpUsageError = error.response?.data?.message || '获取失败'; + } finally { + this.sftpUsageLoading = false; + } + }, + // 启动定期检查用户配置 startProfileSync() { // 清除已有的定时器