feat(phase-4): Folders API (FOLD-01..05), audit helper (flush-not-commit), document sort/FTS/move

- backend/api/folders.py: POST /api/folders (create), GET /api/folders (list),
  GET /api/folders/{id} (breadcrumb), PATCH /api/folders/{id} (rename),
  DELETE /api/folders/{id} (cascade-delete + atomic quota decrement),
  PATCH /api/documents/{id}/folder (move document)
- All folder endpoints use get_regular_user (admin gets 403); 404 for IDOR
- IntegrityError caught -> 409 on duplicate folder name under same parent
- WITH RECURSIVE CTE for subtree collection with SQLite fallback (OperationalError)
- Atomic quota decrement with CASE WHEN pattern (SQLite compat)
- MinIO object deletion best-effort (per-object try/except)
- write_audit_log called after folder.created, folder.renamed, folder.deleted
- backend/api/documents.py: add sort, order, folder_id, q params to list_documents;
  add is_shared field to each document in response using Share subquery
- backend/main.py: register folders_router and document_move_router
This commit is contained in:
curo1305
2026-05-25 18:37:22 +02:00
parent 259a1542d8
commit 33a6f9a290
3 changed files with 540 additions and 6 deletions
+7
View File
@@ -38,6 +38,7 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
)
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
@@ -176,3 +177,9 @@ from api.auth import router as auth_router # noqa: E402
from api.admin import router as admin_router # noqa: E402
app.include_router(auth_router)
app.include_router(admin_router)
# Phase 4: folders router (FOLD-01..05) and document-move endpoint
from api.folders import router as folders_router # noqa: E402
from api.folders import document_move_router as document_move_router # noqa: E402
app.include_router(folders_router)
app.include_router(document_move_router)