diff --git a/README.md b/README.md index c5e25e3..af71afa 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,10 @@ tail -f /tmp/sz-server.log # 实时看 ``` data/ - ├─ app.db SQLite (transfers / files / chunks) - ├─ storage/日期/任务ID/ 密文卷目录 (chunk_0001..N, 不合并) + ├─ app.db SQLite (users / transfers / files / chunks) + ├─ storage/用户名/日期/任务ID/ 密文卷目录 (每用户独立目录, chunk_0001..N, 不合并) + │ admin/ SZ_TOKEN 上传的文件 + │ 用户名/ 注册用户上传的文件 └─ tmp/任务ID/ 上传中的卷 (complete 后 move 入 storage) ``` diff --git a/api.py b/api.py index 043cd0d..55d51cd 100644 --- a/api.py +++ b/api.py @@ -250,7 +250,7 @@ async def complete(transfer_id: str, request: Request, username: str = Depends(v "error": f"缺卷: {transfer['chunk_count'] - len(got)} 卷未上传", }) tasks.set_status(transfer_id, "storing") - final_dir = storage.store_chunks(transfer_id, TMP_ROOT / transfer_id) + final_dir = storage.store_chunks(transfer_id, TMP_ROOT / transfer_id, username) file_id = db.insert_file( transfer_id, transfer["file_name"], diff --git a/storage.py b/storage.py index 1016237..02d0320 100644 --- a/storage.py +++ b/storage.py @@ -20,12 +20,14 @@ class Storage: self.tasks = tasks self.storage_root = Path(storage_root) - def store_chunks(self, transfer_id: str, chunk_dir: Path) -> Path: - """卷目录 -> 最终存储目录 (整体 move, 返回目标目录路径) + def store_chunks(self, transfer_id: str, chunk_dir: Path, user_id: str) -> Path: + """卷目录 -> 用户目录/日期/transfer_id (整体 move, 返回目标目录路径) + 每用户独立目录 (user_id 已过注册校验: 3-32 位字母数字/下划线, 无路径风险)。 注意: 目标目录不能预创建, 否则 shutil.move 会嵌套成 dst/dst/。 """ - day_dir = self.storage_root / datetime.now().strftime("%Y-%m-%d") + user_dir = self.storage_root / user_id + day_dir = user_dir / datetime.now().strftime("%Y-%m-%d") final_dir = day_dir / transfer_id if final_dir.exists(): raise OSError(f"存储目录已存在: {final_dir}") diff --git a/tests/test_api.py b/tests/test_api.py index 4ff9a8d..860b241 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -117,6 +117,8 @@ class ServerPipelineTest(unittest.TestCase): self.assertIsNotNone(row) chunk_dir = Path(row["path"]) self.assertTrue(chunk_dir.is_dir()) + # 每用户独立目录: admin 上传 -> storage/admin/日期/transfer_id + self.assertIn("admin", chunk_dir.parts) chunk_files_on_disk = sorted(chunk_dir.glob("chunk_*")) self.assertEqual(len(chunk_files_on_disk), len(chunk_files)) # 卷内容与客户端上传的卷一致