"""Rotas project identity — Spec 037-C.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel, Field from app.deps import bind_onboarding_session, get_session_from_request from app.services import activity_log, ops_webhook, project_identity from app.services.carbonio import CarbonioError from app.services.domain_format import normalize_domain, validate_primary_domain, invalid_domain_http_detail router = APIRouter( prefix="/onboarding/project", tags=["onboarding-project"], dependencies=[Depends(bind_onboarding_session)], ) class ProvisionProjectEmailRequest(BaseModel): domain: str = Field(..., min_length=3, max_length=253) display_name: str | None = Field(None, max_length=120) @router.post("/provision-email") def provision_project_email(body: ProvisionProjectEmailRequest, request: Request): """Cria proj_{id}@ligbox.com.br e regista no domain_registry.""" get_session_from_request(request) domain = normalize_domain(body.domain) try: validate_primary_domain(domain) except ValueError as e: raise HTTPException(status_code=400, detail=invalid_domain_http_detail()) from e try: activity_log.info(f"Provision e-mail projeto Ligbox: {domain}", source="project") result = project_identity.provision_project_email( domain, display_name=body.display_name, ) if result.get("already_exists"): activity_log.ok(f"E-mail projeto já existe: {result['ligbox_project_email']}", source="project") else: activity_log.ok(f"E-mail projeto criado: {result['ligbox_project_email']}", source="project") ops_webhook.emit_event( "onboard.project.email.created", domain=domain, session_id=get_session_from_request(request), data={ "ligbox_project_email": result.get("ligbox_project_email"), "project_id": result.get("project_id"), "already_exists": result.get("already_exists", False), }, ) # Nunca logar password safe = {k: v for k, v in result.items() if k != "password"} if result.get("password"): safe["password_available"] = True return safe except CarbonioError as e: activity_log.error(f"Carbonio projeto: {e}", source="project") raise HTTPException(status_code=502, detail=str(e)) from e @router.get("/{domain}") def get_project_identity(domain: str): """Estado identidade projeto (sem senha).""" domain = normalize_domain(domain) data = project_identity.get_project_identity(domain) if not data: raise HTTPException(404, f"Sem identidade projeto para {domain}") return data @router.post("/{domain}/reveal-password") def reveal_project_password(domain: str, request: Request): """Uma vez por sessão — devolve senha do vault.""" get_session_from_request(request) domain = normalize_domain(domain) vault = project_identity.load_password_vault(domain) if not vault or not vault.get("password"): raise HTTPException(404, "Senha não disponível ou já entregue.") project_identity.mark_password_delivered(domain) return { "domain": domain, "ligbox_project_email": vault.get("ligbox_project_email"), "password": vault["password"], "webmail": f"https://mail.ligbox.com.br/", "message": "Guarde a senha — não será mostrada novamente.", }