"""恢复 API。""" import asyncio from datetime import datetime from fastapi import APIRouter, HTTPException from app.core.restore import execute_restore from app.deps import AdminUser, DBSession from app.models.job import Job from app.models.restore import RestoreHistory from app.models.run import Run from app.schemas.restore import RestoreOut, RestoreRequest, RestoreTargetsResponse from app.utils.crypto import decrypt_dict router = APIRouter(prefix="/restore", tags=["restore"]) @router.get("/targets", response_model=RestoreTargetsResponse) def get_restore_targets(run_id: int, db: DBSession, _: AdminUser) -> RestoreTargetsResponse: run = db.get(Run, run_id) if not run: raise HTTPException(status_code=404, detail="Run not found") job = db.get(Job, run.job_id) if not job: raise HTTPException(status_code=404, detail="Job not found") src = decrypt_dict(job.source_config_json) suggested: list[str] = [] if job.type == "mysql": suggested.append(src.get("database", "")) elif job.type == "directory": suggested.append(src.get("path", "")) return RestoreTargetsResponse( run_id=run.id, job_type=job.type, artifact_size=run.artifact_size, artifact_checksum=run.artifact_checksum, suggested_targets=[s for s in suggested if s], ) @router.post("", response_model=RestoreOut, status_code=201) async def trigger_restore(payload: RestoreRequest, db: DBSession, _: AdminUser) -> RestoreHistory: run = db.get(Run, payload.run_id) if not run or not run.artifact_path: raise HTTPException(status_code=404, detail="Run not found or no artifact") job = db.get(Job, run.job_id) if not job: raise HTTPException(status_code=404, detail="Job not found") # 校验 target_type 与 job.type 匹配 if job.type == "mysql" and payload.target_type != "mysql_db": raise HTTPException(status_code=400, detail="target_type 与源类型不匹配(应为 mysql_db)") if job.type == "directory" and payload.target_type != "directory_path": raise HTTPException(status_code=400, detail="target_type 与源类型不匹配(应为 directory_path)") rh = RestoreHistory( run_id=run.id, target=payload.target, status="pending", ) db.add(rh) db.commit() db.refresh(rh) restore_id = rh.id asyncio.create_task(execute_restore(restore_id)) return rh @router.get("/history", response_model=list[RestoreOut]) def restore_history(db: DBSession, _: AdminUser, run_id: int | None = None) -> list[RestoreHistory]: q = db.query(RestoreHistory) if run_id is not None: q = q.filter(RestoreHistory.run_id == run_id) return q.order_by(RestoreHistory.id.desc()).limit(200).all()