"""Rotas internas VM112 — Desk/FOSS (Spec 035/043). Auth: X-Ops-Internal-Token.""" from __future__ import annotations import os from fastapi import APIRouter, Depends, Header, HTTPException from pydantic import BaseModel, Field from app.services import activity_log, mail_bundle from app.services.carbonio import CarbonioError from app.services.domain_format import invalid_domain_http_detail, normalize_domain, validate_primary_domain router = APIRouter(prefix="/internal/provision", tags=["internal-provision"]) OPS_INTERNAL_TOKEN = os.getenv("OPS_INTERNAL_TOKEN", "") def _require_internal(x_ops_internal_token: str | None = Header(default=None, alias="X-Ops-Internal-Token")): if not OPS_INTERNAL_TOKEN: raise HTTPException(503, "OPS_INTERNAL_TOKEN não configurado no wizard") if x_ops_internal_token != OPS_INTERNAL_TOKEN: raise HTTPException(401, "token interno inválido") return True class MailBundleRequest(BaseModel): domain: str = Field(..., min_length=3, max_length=253) admin_email: str = Field(..., min_length=5) admin_name: str | None = Field(None, max_length=120) seats: int = Field(25, ge=1, le=500) mail_gb_per_seat: int = Field(30, ge=1, le=500) files_gb_per_seat: int = Field(200, ge=1, le=2000) foss_order_id: str | int | None = None @router.post("/mail-bundle", dependencies=[Depends(_require_internal)]) def post_mail_bundle(body: MailBundleRequest): """ Provisiona domínio + conta gerente Carbonio com quotas do bundle mail. Chamado pelo Desk (Spec 043) após FOSS/OpenPanel/Odoo. """ domain = normalize_domain(body.domain) try: validate_primary_domain(domain) except ValueError as e: raise HTTPException(400, detail=invalid_domain_http_detail()) from e try: result = mail_bundle.provision_mail_bundle( domain=domain, admin_email=body.admin_email.strip().lower(), admin_name=body.admin_name, seats=body.seats, mail_gb_per_seat=body.mail_gb_per_seat, files_gb_per_seat=body.files_gb_per_seat, foss_order_id=body.foss_order_id, ) safe = {k: v for k, v in result.items() if k not in ("password",)} return {"ok": True, **safe} except ValueError as e: raise HTTPException(400, str(e)) from e except CarbonioError as e: activity_log.error(f"mail-bundle falhou: {e}", source="internal") raise HTTPException(502, str(e)) from e @router.get("/mail-bundle/{domain}", dependencies=[Depends(_require_internal)]) def get_mail_bundle(domain: str): """Estado do bundle (sem senhas).""" domain = normalize_domain(domain) data = mail_bundle.load_bundle(domain) if not data: raise HTTPException(404, "bundle não provisionado") return {"ok": True, **{k: v for k, v in data.items() if k != "password"}}