diff --git a/main.py b/main.py index 4d10b87..adeacfe 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ CG3: static-mappe serveres fra /static/*. AUTH: /auth/login + /auth/callback for klient OAuth2 onboarding. CG5a: POST /notify/webhook — push til Slack/Teams/Chat/Discord/etc. CG5b: POST /notify/email — SendGrid digest til kunde-e-post. +CG5-onboard: GET /onboard + POST /onboard/complete — klient-onboarding wizard. """ import os @@ -56,7 +57,7 @@ app = FastAPI( # Paths exempt from IAP enforcement (health/readiness probes reach Cloud Run # directly without the IAP-injected x-goog-authenticated-user-email header). -IAP_EXEMPT_PATHS = {"/health", "/healthz", "/readiness", "/liveness"} +IAP_EXEMPT_PATHS = {"/health", "/healthz", "/readiness", "/liveness", "/onboard"} @app.middleware("http") async def require_iap(request: Request, call_next): @@ -183,6 +184,18 @@ class EmailNotifyRequest(BaseModel): payload: Optional[Dict[str, Any]] = Field(None, description="Valgfri rådata å inkludere i e-post") +# ── ONBOARDING MODEL (CG5-onboard) ──────────────────────────────────────────── +class OnboardCompleteRequest(BaseModel): + company: str + contact: str + email: str + gcp_project: str + tier: str + channels: List[str] + webhook_url: Optional[str] = None + email_to: Optional[str] = None + + # ── NOTIFICATION SERVICE (CG5a + CG5b) ─────────────────────────────────────── class NotificationService: """ @@ -350,6 +363,144 @@ async def _notify_email(request: Request, req: EmailNotifyRequest): app.add_api_route("/notify/email", endpoint=require_auth(_notify_email), methods=["POST"]) +# ── ONBOARDING ENDPOINTS (CG5-onboard) ─────────────────────────────────────── + +@app.get("/onboard") +def onboard_wizard(): + """ + CG5-onboard: Server onboarding-wizard HTML. + IAP-exempt — kunder når denne uten eksisterende sesjon. + """ + return FileResponse("static/onboard.html") + + +@app.post("/onboard/complete") +async def onboard_complete(req: OnboardCompleteRequest): + """ + CG5-onboard: Lagrer kundeprofil i Firestore og sender velkomstvarsler + via eksisterende POST /notify/webhook og POST /notify/email. + Ingen nye notification-endepunkter — gjenbruker _notifier singleton. + """ + # 1. Lagre i Firestore (best-effort — feiler ikke onboarding ved DB-feil) + if db: + try: + db.collection("onboarded_customers").document(req.gcp_project).set({ + "company": req.company, + "contact": req.contact, + "email": req.email, + "gcp_project": req.gcp_project, + "tier": req.tier, + "channels": req.channels, + "webhook_url": req.webhook_url, + "email_to": req.email_to, + "onboarded_at": firestore.SERVER_TIMESTAMP, + }) + print(f"[Onboard] {req.gcp_project} lagret i Firestore") + except Exception as e: + print(f"[Onboard] Firestore-feil (ikke kritisk): {e}", file=sys.stderr) + + notify_results: Dict[str, Any] = {} + + # 2. Webhook-velkomstmelding (via eksisterende /notify/webhook-logikk) + if req.webhook_url and "webhook" in req.channels: + wh_result = await _notifier.send_webhook(WebhookNotifyRequest( + url=req.webhook_url, + event="custom", + title=f"🎉 {req.company} er koblet til CostGuard!", + body=( + f"Hei {req.contact}! CostGuard overvåker nå *{req.gcp_project}*. " + f"Du mottar første kostnadsrapport innen 24 timer." + ), + payload={ + "bedrift": req.company, + "tier": req.tier, + "prosjekt": req.gcp_project, + "dato": datetime.date.today().isoformat(), + }, + )) + notify_results["webhook"] = wh_result + + # 3. Velkomst-e-post (via eksisterende /notify/email-logikk) + if req.email_to and "email" in req.channels: + TIER_LABELS = { + "starter": "Starter ($499/mnd)", + "guard": "Guard ($999/mnd)", + "shield": "Shield ($1.999/mnd)", + "enterprise": "Enterprise ($3.500+/mnd)", + } + tier_label = TIER_LABELS.get(req.tier, req.tier) + em_result = await _notifier.send_email(EmailNotifyRequest( + to=req.email_to, + event="custom", + subject=f"✅ Velkommen til CostGuard — {req.company}", + body_html=f""" +
Hei {req.contact},
+CostGuard overvåker nå ditt GCP-prosjekt og vil varsle deg ved kostnadsspikes, + anomalier og budsjettoverskridelser.
+| Bedrift | +{req.company} | +
| GCP-prosjekt | +{req.gcp_project} |
+
| Tier | +{tier_label} | +
| Kanaler | +{', '.join(req.channels)} | +
Du mottar din første kostnadsrapport innen 24 timer.
+ +CostGuard by Vauco AS · opax.vauco.no
+