diff --git a/api.py b/api.py index 9cfd319..3c19447 100644 --- a/api.py +++ b/api.py @@ -25,7 +25,7 @@ from fastapi.responses import FileResponse, JSONResponse from db import ServerDB from receiver import Receiver, ReceiverError -from settings import STORAGE_ROOT, TMP_ROOT, TOKEN +from settings import QUOTA_BYTES, STORAGE_ROOT, TMP_ROOT, TOKEN from storage import Storage from task_manager import TaskManager @@ -63,6 +63,19 @@ async def list_files(): return {"files": db.list_files()} +@app.get("/api/quota", dependencies=[Depends(verify_token)]) +async def quota(): + """用户空间配额: 已用 (files size 总和) / 配额 / 剩余""" + used = db.sum_files_size() + remain = max(QUOTA_BYTES - used, 0) + return { + "used_bytes": used, + "quota_bytes": QUOTA_BYTES, + "remain_bytes": remain, + "percent": round(used * 100 / QUOTA_BYTES, 1) if QUOTA_BYTES else 0, + } + + @app.delete("/api/files/{file_id}", dependencies=[Depends(verify_token)]) async def delete_file(file_id: str): """删除文件 (卷目录 + 入库记录)""" diff --git a/db.py b/db.py index bacd781..ee4ec25 100644 --- a/db.py +++ b/db.py @@ -201,6 +201,12 @@ class ServerDB: cur = conn.execute("DELETE FROM files WHERE file_id = ?", (file_id,)) return cur.rowcount > 0 + def sum_files_size(self) -> int: + """全部文件 size 总和 (配额统计用)""" + with self._conn() as conn: + row = conn.execute("SELECT COALESCE(SUM(size), 0) FROM files").fetchone() + return int(row[0]) + def get_file(self, file_id: str) -> dict[str, Any] | None: """单文件记录 (下载端点, 含解密参数 enc_params + 卷数)""" with self._conn() as conn: diff --git a/settings.py b/settings.py index 15196f5..19c9b5c 100644 --- a/settings.py +++ b/settings.py @@ -17,6 +17,9 @@ STORAGE_ROOT = Path(os.environ.get("SZ_STORAGE_ROOT", "data/storage")) # 临时目录 (分卷/合并中间产物) TMP_ROOT = Path(os.environ.get("SZ_TMP_ROOT", "data/tmp")) +# 用户空间配额 (已用 = files 表 size 总和, 超配额拒绝新上传? 暂只读展示) +QUOTA_BYTES = int(os.environ.get("SZ_QUOTA", str(10 * 1024**3))) # 默认 10GB + # API 服务 HOST = os.environ.get("SZ_HOST", "0.0.0.0") PORT = int(os.environ.get("SZ_PORT", "8000")) diff --git a/tests/test_api.py b/tests/test_api.py index b92c22c..33954f2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -237,6 +237,16 @@ class ServerPipelineTest(unittest.TestCase): len(self.client.get("/api/files", headers=AUTH).json()["files"]), before ) + # 配额: 返回结构 + 已用 = files size 总和 + r = self.client.get("/api/quota", headers=AUTH) + self.assertEqual(r.status_code, 200) + q = r.json() + self.assertIn("used_bytes", q) + self.assertIn("quota_bytes", q) + self.assertIn("remain_bytes", q) + self.assertEqual(q["used_bytes"], q["quota_bytes"] - q["remain_bytes"]) + self.assertGreater(q["quota_bytes"], 0) + # del: 删除后列表减少, 再删 404 r = self.client.delete(f"/api/files/{file_id}", headers=AUTH) self.assertEqual(r.status_code, 200, r.text)