archive_purge.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. """Archive auto-purge endpoints (#1008 follow-up).
  2. Admin-only (``ARCHIVES_PURGE``). Provides:
  3. * ``GET /archives/purge/preview`` — live count for the admin slider
  4. * ``POST /archives/purge`` — one-shot manual bulk delete
  5. * ``GET/PUT /archives/purge/settings`` — auto-purge toggle + threshold
  6. """
  7. from __future__ import annotations
  8. import logging
  9. from fastapi import APIRouter, Depends, HTTPException, Query
  10. from sqlalchemy.ext.asyncio import AsyncSession
  11. from backend.app.core.auth import require_permission_if_auth_enabled
  12. from backend.app.core.database import get_db
  13. from backend.app.core.permissions import Permission
  14. from backend.app.models.user import User
  15. from backend.app.schemas.archive_purge import (
  16. ArchivePurgePreviewResponse,
  17. ArchivePurgeRequest,
  18. ArchivePurgeResponse,
  19. ArchivePurgeSettings,
  20. )
  21. from backend.app.services.archive_purge import (
  22. MAX_AUTO_PURGE_DAYS,
  23. MIN_AUTO_PURGE_DAYS,
  24. archive_purge_service,
  25. )
  26. logger = logging.getLogger(__name__)
  27. router = APIRouter(prefix="/archives", tags=["archives-purge"])
  28. @router.get("/purge/preview", response_model=ArchivePurgePreviewResponse)
  29. async def preview_archive_purge(
  30. older_than_days: int = Query(ge=1, le=3650),
  31. db: AsyncSession = Depends(get_db),
  32. _: User | None = Depends(require_permission_if_auth_enabled(Permission.ARCHIVES_PURGE)),
  33. ):
  34. """Count + size of archives eligible for purge. Read-only."""
  35. result = await archive_purge_service.preview_purge(db, older_than_days=older_than_days)
  36. return ArchivePurgePreviewResponse(**result)
  37. @router.post("/purge", response_model=ArchivePurgeResponse)
  38. async def execute_archive_purge(
  39. body: ArchivePurgeRequest,
  40. db: AsyncSession = Depends(get_db),
  41. _: User | None = Depends(require_permission_if_auth_enabled(Permission.ARCHIVES_PURGE)),
  42. ):
  43. """Hard-delete archives older than the threshold. Irreversible."""
  44. deleted = await archive_purge_service.purge_older_than(db, older_than_days=body.older_than_days)
  45. return ArchivePurgeResponse(deleted=deleted)
  46. @router.get("/purge/settings", response_model=ArchivePurgeSettings)
  47. async def get_archive_purge_settings(
  48. db: AsyncSession = Depends(get_db),
  49. _: User | None = Depends(require_permission_if_auth_enabled(Permission.ARCHIVES_PURGE)),
  50. ):
  51. cfg = await archive_purge_service.get_settings(db)
  52. return ArchivePurgeSettings(enabled=cfg["enabled"], days=cfg["days"])
  53. @router.put("/purge/settings", response_model=ArchivePurgeSettings)
  54. async def update_archive_purge_settings(
  55. body: ArchivePurgeSettings,
  56. db: AsyncSession = Depends(get_db),
  57. _: User | None = Depends(require_permission_if_auth_enabled(Permission.ARCHIVES_PURGE)),
  58. ):
  59. if body.days < MIN_AUTO_PURGE_DAYS or body.days > MAX_AUTO_PURGE_DAYS:
  60. raise HTTPException(
  61. status_code=400,
  62. detail=f"days must be between {MIN_AUTO_PURGE_DAYS} and {MAX_AUTO_PURGE_DAYS}",
  63. )
  64. saved = await archive_purge_service.set_settings(db, enabled=body.enabled, days=body.days)
  65. return ArchivePurgeSettings(enabled=saved["enabled"], days=saved["days"])