import io import zipfile from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse from sqlalchemy.orm import Session from app.api.deps import get_current_user, get_db from app.models.user import User from app.schemas.backup import BackupDiffResponse, BackupResponse, BulkActionRequest from app.services.backup_service import backup_service router = APIRouter() @router.get("", response_model=list[BackupResponse]) def list_backups( search: str | None = Query(default=None), backup_type: str | None = Query(default=None, pattern="^(export|binary)$"), router_id: int | None = Query(default=None), sort_by: str = Query(default="created_at"), order: str = Query(default="desc", pattern="^(asc|desc)$"), current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ): return backup_service.list_backups( db, current_user, search=search, backup_type=backup_type, router_id=router_id, sort_by=sort_by, order=order, ) @router.get("/router/{router_id}", response_model=list[BackupResponse]) def list_router_backups(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.list_router_backups(db, current_user, router_id) @router.post("/routers/export-all") def export_all(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.export_all(db, current_user) @router.post("/routers/binary-all") def binary_all(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.binary_all(db, current_user) @router.post("/router/{router_id}/export", response_model=BackupResponse) def export_router(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.export_router(db, current_user, router_id) @router.post("/router/{router_id}/binary", response_model=BackupResponse) def binary_backup(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.binary_backup(db, current_user, router_id) @router.post("/router/{router_id}/upload/{backup_id}") def upload_to_router(router_id: int, backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backup_service.upload_backup_to_router(db, current_user, router_id, backup_id) return {"message": "Backup uploaded to router"} @router.delete("/{backup_id}") def delete_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backup_service.delete_backup(db, current_user, backup_id) return {"message": "Backup deleted"} @router.get("/{backup_id}/download") def download_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backup = backup_service.get_backup_for_user(db, current_user, backup_id) return FileResponse(path=backup.file_path, filename=backup.file_name) @router.get("/{backup_id}/view") def view_export(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backup = backup_service.get_backup_for_user(db, current_user, backup_id) if backup.backup_type != "export": raise HTTPException(status_code=400, detail="Only export backups can be viewed") with open(backup.file_path, "r", encoding="utf-8", errors="ignore") as handle: return {"content": handle.read(), "backup": BackupResponse.model_validate(backup_service._serialize_backup(backup))} @router.post("/{backup_id}/email") def email_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backup_service.email_backup(db, current_user, backup_id) return {"message": "Backup sent by email"} @router.get("/{left_id}/diff/{right_id}", response_model=BackupDiffResponse) def diff_backups(left_id: int, right_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): return backup_service.diff_backups(db, current_user, left_id, right_id) @router.get("/{left_id}/diff/{right_id}/html", response_class=HTMLResponse) def diff_backups_html(left_id: int, right_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): result = backup_service.diff_backups(db, current_user, left_id, right_id) return HTMLResponse(result["diff_html"]) @router.post("/bulk") def bulk_action(payload: BulkActionRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): backups = [backup_service.get_backup_for_user(db, current_user, backup_id) for backup_id in payload.backup_ids] if payload.action == "delete": for backup in backups: backup_service.delete_backup(db, current_user, backup.id, commit=False) db.commit() return {"message": f"Deleted {len(backups)} backups"} if payload.action == "download": stream = io.BytesIO() with zipfile.ZipFile(stream, "w") as archive: for backup in backups: archive.write(backup.file_path, backup.file_name) stream.seek(0) return StreamingResponse( stream, media_type="application/zip", headers={"Content-Disposition": 'attachment; filename="backups.zip"'}, ) raise HTTPException(status_code=400, detail="Unsupported bulk action")