quota: GET /api/quota (used=files size 总和 / quota 默认 10GB SZ_QUOTA env / remain / percent); 测试 7 项

This commit is contained in:
lou
2026-08-10 01:42:51 +08:00
parent dbf32fe55e
commit 95ced31222
4 changed files with 33 additions and 1 deletions
+14 -1
View File
@@ -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):
"""删除文件 (卷目录 + 入库记录)"""
+6
View File
@@ -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:
+3
View File
@@ -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"))
+10
View File
@@ -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)