153 lines
4.6 KiB
Python
153 lines
4.6 KiB
Python
"""Rotas Desk — domínios VM112 (Spec 017)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from fastapi.responses import StreamingResponse
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app import auth, vm112_domains
|
|
from app.permissions import can_manage_vm112_domains
|
|
from app.vm112_purge_stream import purge_sse_generator
|
|
from app.vm112_purge_jobs import get_job_public, list_jobs, recover_job, start_job
|
|
|
|
router = APIRouter(prefix="/api/v1/vm112", tags=["vm112-domains"])
|
|
|
|
|
|
class DomainPurgeRequest(BaseModel):
|
|
confirm_domain: str = Field(..., min_length=3)
|
|
root_password: str = Field(..., min_length=1)
|
|
|
|
|
|
def _require_admin(user: auth.DeskUser = Depends(auth.get_current_user)) -> auth.DeskUser:
|
|
if not can_manage_vm112_domains(user.role):
|
|
raise HTTPException(403, "Apenas perfis Admin (super_admin, ops_lead)")
|
|
return user
|
|
|
|
|
|
def _validate_purge_request(domain: str, body: DomainPurgeRequest) -> str:
|
|
domain = domain.lower().strip()
|
|
if domain in vm112_domains.PURGE_BLOCKLIST:
|
|
raise HTTPException(400, f"Domínio {domain} está protegido contra purge")
|
|
if body.confirm_domain.lower().strip() != domain:
|
|
raise HTTPException(400, "Confirmação do domínio não coincide")
|
|
return domain
|
|
|
|
|
|
@router.get("/domains")
|
|
def list_vm112_domains(
|
|
q: str = "",
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
try:
|
|
return vm112_domains.list_domains(q)
|
|
except Exception as e:
|
|
raise HTTPException(502, f"VM112 indisponível: {e}") from e
|
|
|
|
|
|
@router.get("/domains/{domain}")
|
|
def get_vm112_domain(
|
|
domain: str,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
try:
|
|
return vm112_domains.get_domain(domain)
|
|
except Exception as e:
|
|
raise HTTPException(502, f"VM112: {e}") from e
|
|
|
|
|
|
@router.post("/domains/{domain}/purge")
|
|
def purge_vm112_domain(
|
|
domain: str,
|
|
body: DomainPurgeRequest,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
domain = _validate_purge_request(domain, body)
|
|
conn = auth.db()
|
|
try:
|
|
if not vm112_domains.verify_root_password(conn, body.root_password):
|
|
raise HTTPException(403, "Senha Root incorrecta")
|
|
finally:
|
|
conn.close()
|
|
try:
|
|
vm112_result = vm112_domains.purge_vm112(domain)
|
|
except Exception as e:
|
|
raise HTTPException(502, f"Purge VM112 falhou: {e}") from e
|
|
conn = auth.db()
|
|
try:
|
|
desk_counts, desk_timeline = vm112_domains.purge_desk_timeline(conn, domain)
|
|
finally:
|
|
conn.close()
|
|
timeline = vm112_domains.build_purge_timeline(vm112_result, desk_counts, desk_timeline)
|
|
return {
|
|
"ok": True,
|
|
"domain": domain,
|
|
"vm112": vm112_result,
|
|
"desk": desk_counts,
|
|
"timeline": timeline,
|
|
"by": user.username,
|
|
}
|
|
|
|
|
|
@router.post("/domains/{domain}/purge/stream")
|
|
def purge_vm112_domain_stream(
|
|
domain: str,
|
|
body: DomainPurgeRequest,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
"""SSE — progresso purge em tempo real (Fase 2 Spec 017)."""
|
|
domain = _validate_purge_request(domain, body)
|
|
return StreamingResponse(
|
|
purge_sse_generator(domain, body.root_password, user.username),
|
|
media_type="text/event-stream",
|
|
headers={
|
|
"Cache-Control": "no-cache",
|
|
"Connection": "keep-alive",
|
|
"X-Accel-Buffering": "no",
|
|
},
|
|
)
|
|
|
|
|
|
@router.post("/domains/{domain}/purge/jobs")
|
|
def start_purge_job(
|
|
domain: str,
|
|
body: DomainPurgeRequest,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
"""Inicia purge em background; consultar GET /purge/jobs/{id} (recomendado via Traefik)."""
|
|
domain = _validate_purge_request(domain, body)
|
|
job_id = start_job(domain, body.root_password, user.username)
|
|
return {"ok": True, "job_id": job_id, "domain": domain, "status": "running"}
|
|
|
|
|
|
@router.get("/purge/jobs")
|
|
def list_purge_jobs(
|
|
limit: int = 100,
|
|
offset: int = 0,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
return list_jobs(limit=limit, offset=offset)
|
|
|
|
|
|
@router.get("/purge/jobs/{job_id}")
|
|
def get_purge_job_status(
|
|
job_id: str,
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
job = get_job_public(job_id)
|
|
if not job:
|
|
raise HTTPException(404, "Job purge não encontrado")
|
|
return job
|
|
|
|
@router.post("/purge/jobs/{job_id}/recover")
|
|
def recover_purge_job(
|
|
job_id: str,
|
|
domain: str = "",
|
|
user: auth.DeskUser = Depends(_require_admin),
|
|
):
|
|
"""Recupera purge quando job sumiu da memória mas VM112 já removeu o domínio."""
|
|
job = recover_job(job_id, domain or None)
|
|
if not job:
|
|
raise HTTPException(404, "Não foi possível recuperar o job purge")
|
|
return job
|
|
|