#!/usr/bin/env python3 """Deploy 037 construction on VM112 — run ON the host.""" from pathlib import Path import ast WIZARD = Path("/opt/ligbox-wizard") TMP = Path("/tmp") for name in ["project_identity.py", "cf_client_accounts.py"]: dst = WIZARD / "backend/app/services" / name dst.write_text((TMP / name).read_text(encoding="utf-8"), encoding="utf-8") print("copied", name) (WIZARD / "backend/app/routers/project.py").write_text( (TMP / "project_router.py").read_text(encoding="utf-8"), encoding="utf-8" ) print("copied project.py") main = WIZARD / "backend/app/main.py" text = main.read_text(encoding="utf-8") old_imp = ( "from app.routers import admin_accounts, admin_domains, assist, corporate, " "domain_admin, onboarding, portal_auth, telemetry" ) new_imp = old_imp.replace(", telemetry", ", project, telemetry") if "project, telemetry" not in text: text = text.replace(old_imp, new_imp) if "project.router" not in text: text = text.replace( 'app.include_router(onboarding.router, prefix="/api")', 'app.include_router(onboarding.router, prefix="/api")\n' 'app.include_router(project.router, prefix="/api")', ) main.write_text(text, encoding="utf-8") print("main.py ok") onb = WIZARD / "backend/app/routers/onboarding.py" ot = onb.read_text(encoding="utf-8") if "cf_client_accounts" not in ot: ot = ot.replace( "from app.services import dns_byo_vault", "from app.services import dns_byo_vault, cf_client_accounts, project_identity", ) PHASE_D = ''' class ProvisionClientAccountRequest(BaseModel): domain: str = Field(..., min_length=3, max_length=253) display_name: str | None = None class HandoffManagerRequest(BaseModel): domain: str manager_email: str = Field(..., min_length=5) def _project_email_for_domain(domain: str) -> str | None: ident = project_identity.get_project_identity(domain.lower().strip()) return ident.get("ligbox_project_email") if ident else None @router.post("/dns/cloudflare/provision-client-account") def provision_client_cf_account(body: ProvisionClientAccountRequest, request: Request): 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: ligbox_email = _project_email_for_domain(domain) result = cf_client_accounts.provision_client_account( domain, ligbox_project_email=ligbox_email, account_name=body.display_name or domain, ) ops_webhook.emit_event( "onboard.cf_account.provisioned", domain=domain, session_id=get_session_from_request(request), data={ "ligbox_project_email": ligbox_email, "zone_id": result.get("zone", {}).get("id"), "provision_mode": result.get("record", {}).get("provision_mode"), }, ) return result except CloudflareError as e: raise HTTPException(502, str(e)) from e @router.post("/dns/cloudflare/handoff-manager") def handoff_cf_manager(body: HandoffManagerRequest, request: Request): get_session_from_request(request) domain = normalize_domain(body.domain) result = cf_client_accounts.handoff_manager(domain, body.manager_email) ops_webhook.emit_event( "onboard.cf_handoff.requested", domain=domain, session_id=get_session_from_request(request), data=result, ) return result ''' if "/dns/cloudflare/provision-client-account" not in ot: ot = ot.replace('@router.get("/session/password-status")', PHASE_D + '@router.get("/session/password-status")') if "onboard.dns.applied" not in ot: ot = ot.replace( ' verification = dns_verify.verify_mail_dns(domain, settings.mail_public_ip)\n return {\n "domain": domain,', ' verification = dns_verify.verify_mail_dns(domain, settings.mail_public_ip)\n ops_webhook.emit_event(\n "onboard.dns.applied",\n domain=domain,\n session_id=sid,\n data={\n "dns_mode": dns_mode,\n "ligbox_project_email": _project_email_for_domain(domain),\n "ligbox_account_id": ligbox_acct,\n "zone_id": zone_id,\n "verification_ready": verification.get("ready"),\n },\n )\n return {\n "domain": domain,', ) if "onboard.account.created" not in ot: ot = ot.replace( ' activity_log.ok("Processo de onboarding finalizado", source="portal")', ' ops_webhook.emit_event(\n "onboard.account.created",\n domain=domain,\n session_id=sid,\n data={\n "email": email,\n "dns_mode": dns_mode,\n "ligbox_project_email": _project_email_for_domain(domain),\n "verified": verified,\n },\n )\n activity_log.ok("Processo de onboarding finalizado", source="portal")', ) onb.write_text(ot, encoding="utf-8") ast.parse(main.read_text()) ast.parse(onb.read_text()) print("deploy-037-vm112 OK")