feat: 新增本地存储文件夹管理功能

新功能概述:
- 支持在本地存储模式下创建文件夹
- 支持删除文件夹(递归删除)
- 支持重命名文件夹(已有功能,天然支持)
- 文件夹配额计算正确

后端改动:

1. 新增创建文件夹API (backend/server.js)
   - POST /api/files/mkdir
   - 参数: path(当前路径), folderName(文件夹名称)
   - 安全检查: 禁止特殊字符(/ \ .. :),防止路径遍历攻击
   - 仅限本地存储使用
   - 创建前检查文件夹是否已存在

2. 改进删除功能 (backend/storage.js)
   - LocalStorageClient.delete() 现在支持删除文件夹
   - 使用 fs.rmSync(path, { recursive: true }) 递归删除
   - 新增 calculateFolderSize() 方法计算文件夹总大小
   - 删除文件夹时正确更新配额使用情况

前端改动:

1. 新建文件夹按钮 (frontend/app.html)
   - 在"上传文件"按钮旁新增"新建文件夹"按钮
   - 仅本地存储模式显示

2. 新建文件夹弹窗 (frontend/app.html)
   - 简洁的创建表单
   - 支持回车键快速创建
   - 使用优化的弹窗关闭逻辑(防止拖动选择文本时误关闭)

3. 前端API调用 (frontend/app.js)
   - 新增 createFolderForm 状态
   - 新增 createFolder() 方法
   - 前端参数验证
   - 创建成功后自动刷新文件列表

4. 右键菜单优化 (frontend/app.html)
   - 文件夹不显示"下载"按钮(文件夹暂不支持打包下载)
   - 文件夹不显示"分享"按钮(分享单个文件夹暂不支持)
   - 文件夹支持"重命名"和"删除"操作

安全性:
- 文件夹名称严格验证,禁止包含 / \ .. : 等特殊字符
- 路径安全检查,防止目录遍历攻击
- 仅限本地存储模式使用(SFTP存储使用上传工具管理)

配额管理:
- 空文件夹不占用配额
- 删除文件夹时正确释放配额(计算所有子文件大小)
- 删除非空文件夹会递归删除所有内容

使用方式:
1. 登录后切换到本地存储模式
2. 点击"新建文件夹"按钮
3. 输入文件夹名称,点击创建
4. 双击文件夹进入,支持多级目录
5. 右键文件夹可重命名或删除

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
WanWanYun
2025-11-15 23:46:20 +08:00
parent 3977e72c9e
commit e5c932a351
4 changed files with 144 additions and 7 deletions

View File

@@ -1066,6 +1066,73 @@ app.post('/api/files/rename', authMiddleware, async (req, res) => {
}
});
// 创建文件夹
app.post('/api/files/mkdir', authMiddleware, async (req, res) => {
const { path, folderName } = req.body;
let storage;
// 参数验证
if (!folderName || folderName.trim() === '') {
return res.status(400).json({
success: false,
message: '文件夹名称不能为空'
});
}
// 文件名安全检查 - 防止路径遍历攻击
if (folderName.includes('/') || folderName.includes('\\') || folderName.includes('..') || folderName.includes(':')) {
return res.status(400).json({
success: false,
message: '文件夹名称不能包含特殊字符 (/ \\ .. :)'
});
}
// 只允许本地存储创建文件夹
if (req.user.current_storage_type !== 'local') {
return res.status(400).json({
success: false,
message: '只有本地存储支持创建文件夹'
});
}
try {
const { StorageInterface } = require('./storage');
const storageInterface = new StorageInterface(req.user);
storage = await storageInterface.connect();
// 构造文件夹路径
const basePath = path || '/';
const folderPath = basePath === '/' ? `/${folderName}` : `${basePath}/${folderName}`;
const fullPath = storage.getFullPath(folderPath);
// 检查是否已存在
if (fs.existsSync(fullPath)) {
return res.status(400).json({
success: false,
message: '文件夹已存在'
});
}
// 创建文件夹 (不使用recursive,只创建当前层级)
fs.mkdirSync(fullPath, { mode: 0o755 });
console.log(`[创建文件夹成功] 用户${req.user.id}: ${folderPath}`);
res.json({
success: true,
message: '文件夹创建成功'
});
} catch (error) {
console.error('[创建文件夹失败]', error);
res.status(500).json({
success: false,
message: '创建文件夹失败: ' + error.message
});
} finally {
if (storage) await storage.end();
}
});
// 删除文件
app.post('/api/files/delete', authMiddleware, async (req, res) => {
const { fileName, path } = req.body;
@@ -1090,7 +1157,7 @@ app.post('/api/files/delete', authMiddleware, async (req, res) => {
res.json({
success: true,
message: '文件删除成功'
message: '删除成功'
});
} catch (error) {
console.error('删除文件失败:', error);