fe418bf3f7
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m10s
Gate the demo chat behind Supabase auth: the widget now fetches Supabase config on open, shows a compact sign-in pane (Google OAuth or email/password) when the user is unauthenticated, and passes the access_token to /api/demo. The server verifies the token via supabase_auth_user() before any processing and uses the verified user ID as the rate-limit key. Add a budget kill switch: a demo_config table in Supabase holds a demo_enabled flag that /api/demo polls every 60s (cached, fails open). A Cloud Function (demo-budget-guard) is triggered by a GCP Pub/Sub budget alert and sets demo_enabled = 'false' when spend crosses 90% of the $150 daily budget. Budget and topic are provisioned; function is live in us-central1.
36 lines
1.2 KiB
Python
36 lines
1.2 KiB
Python
import json
|
|
import base64
|
|
import os
|
|
import requests
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def budget_alert(event, context):
|
|
"""Triggered by a Pub/Sub budget alert. Disables demo if threshold exceeded."""
|
|
data = base64.b64decode(event['data']).decode('utf-8')
|
|
alert = json.loads(data)
|
|
|
|
# Only act on threshold exceeded alerts (not forecasts)
|
|
cost_amount = alert.get('costAmount', 0)
|
|
budget_amount = alert.get('budgetAmount', 1)
|
|
threshold = cost_amount / budget_amount if budget_amount else 0
|
|
|
|
if threshold < 0.9:
|
|
print(f"Threshold {threshold:.1%} below 90%, no action")
|
|
return
|
|
|
|
supabase_url = os.environ['SUPABASE_URL']
|
|
service_key = os.environ['SUPABASE_SERVICE_KEY']
|
|
|
|
resp = requests.patch(
|
|
f"{supabase_url}/rest/v1/demo_config?key=eq.demo_enabled",
|
|
headers={
|
|
'Authorization': f'Bearer {service_key}',
|
|
'apikey': service_key,
|
|
'Content-Type': 'application/json',
|
|
'Prefer': 'return=minimal',
|
|
},
|
|
json={'value': 'false', 'updated_at': datetime.now(timezone.utc).isoformat()}
|
|
)
|
|
print(f"Demo disabled — budget at {threshold:.1%}. Supabase: {resp.status_code}")
|