"""API routes for Backup management.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.deps import get_current_user, require_permission from app.schemas.backup import BackupListResponse, BackupResponse from app.services import backup_service router = APIRouter(prefix="/api/v1/backups", tags=["backups"]) @router.get( "", response_model=BackupListResponse, dependencies=[Depends(require_permission("automation:admin"))], ) async def list_backups( db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """List all backups for the current tenant.""" tenant_id = uuid.UUID(current_user["tenant_id"]) backups = await backup_service.list_backups(db, tenant_id) return BackupListResponse( backups=[BackupResponse.model_validate(b) for b in backups], total=len(backups), ) @router.post( "", response_model=BackupResponse, status_code=201, dependencies=[Depends(require_permission("automation:admin"))], ) async def create_backup( db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Create a new database backup.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) backup = await backup_service.create_backup(db, tenant_id, user_id=user_id) return backup @router.post( "/{backup_id}/restore", response_model=BackupResponse, dependencies=[Depends(require_permission("automation:admin"))], ) async def restore_backup( backup_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Restore a database backup. WARNING: This is a destructive operation. It drops and recreates the database. """ tenant_id = uuid.UUID(current_user["tenant_id"]) try: b_id = uuid.UUID(backup_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid backup_id", "code": "invalid_id"}) from None try: backup = await backup_service.restore_backup(db, tenant_id, b_id) return backup except ValueError as exc: raise HTTPException(400, detail={"detail": str(exc), "code": "invalid_state"}) from exc except FileNotFoundError as exc: raise HTTPException(404, detail={"detail": str(exc), "code": "file_not_found"}) from exc except RuntimeError as exc: raise HTTPException(500, detail={"detail": str(exc), "code": "restore_failed"}) from exc @router.delete( "/{backup_id}", status_code=204, dependencies=[Depends(require_permission("automation:admin"))], ) async def delete_backup( backup_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Delete a backup record and its file.""" tenant_id = uuid.UUID(current_user["tenant_id"]) try: b_id = uuid.UUID(backup_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid backup_id", "code": "invalid_id"}) from None deleted = await backup_service.delete_backup(db, tenant_id, b_id) if not deleted: raise HTTPException(404, detail={"detail": "Backup not found", "code": "not_found"}) return None