Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """Sync backend/.env vers Render.com (API gratuite 24/7).""" | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| from pathlib import Path | |
| import httpx | |
| from dotenv import load_dotenv | |
| ROOT = Path(__file__).resolve().parents[1] | |
| load_dotenv(ROOT / "emo" / "backend" / ".env", override=True) | |
| RENDER_API = "https://api.render.com/v1" | |
| SERVICE_NAME = os.environ.get("RENDER_SERVICE_NAME", "emo-online-api") | |
| BACKEND_URL = os.environ.get( | |
| "EMO_PUBLIC_BACKEND_URL", "https://xroxx-emo-online-api.hf.space" | |
| ).rstrip("/") | |
| SECRET_KEYS = [ | |
| "MONGO_URL", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", | |
| "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", | |
| "GROQ_API_KEY", "OPENROUTER_API_KEY", "DEEPSEEK_API_KEY", | |
| "HF_TOKEN", "HUGGINGFACE_API_KEY", "EMERGENT_LLM_KEY", | |
| "STRIPE_API_KEY", "STRIPE_BASIC_LINK", "STRIPE_PREMIUM_LINK", "STRIPE_ULTRA_LINK", | |
| "EMO_PRODUCT_KEYS", | |
| ] | |
| VARIABLES = { | |
| "DB_NAME": "emo", | |
| "EMO_DEV_MODE": "false", | |
| "EMO_SERVE_FRONTEND": "false", | |
| "EMO_BROWSER_ENABLED": "false", | |
| "EMO_USE_SALES_LLM_KEYS": "true", | |
| "EMO_PUBLIC_BACKEND_URL": BACKEND_URL, | |
| "EMO_FRONTEND_URL": "https://xeroxytb.com", | |
| "CORS_ORIGINS": "https://xeroxytb.com,https://www.xeroxytb.com,https://xeroxytb.github.io", | |
| "GOOGLE_REDIRECT_URI": f"{BACKEND_URL}/api/auth/google/callback", | |
| "PORT": "8010", | |
| "EMO_ADMIN_EMAILS": os.environ.get("EMO_ADMIN_EMAILS", "huglostalatac@gmail.com"), | |
| } | |
| SALES_DUP = { | |
| "SALES_OPENAI_API_KEY": "OPENAI_API_KEY", | |
| "SALES_ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY", | |
| "SALES_GEMINI_API_KEY": "GEMINI_API_KEY", | |
| "SALES_GROQ_API_KEY": "GROQ_API_KEY", | |
| "SALES_OPENROUTER_API_KEY": "OPENROUTER_API_KEY", | |
| "SALES_DEEPSEEK_API_KEY": "DEEPSEEK_API_KEY", | |
| "SALES_HF_TOKEN": "HF_TOKEN", | |
| } | |
| def _headers(token: str) -> dict: | |
| return {"Authorization": f"Bearer {token}", "Accept": "application/json"} | |
| def find_service(client: httpx.Client, token: str) -> dict | None: | |
| cursor = None | |
| while True: | |
| params = {"limit": 20} | |
| if cursor: | |
| params["cursor"] = cursor | |
| r = client.get(f"{RENDER_API}/services", headers=_headers(token), params=params, timeout=30) | |
| r.raise_for_status() | |
| data = r.json() | |
| for item in data: | |
| svc = item.get("service") or item | |
| if svc.get("name") == SERVICE_NAME: | |
| return svc | |
| cursor = r.headers.get("X-Next-Cursor") or r.headers.get("x-next-cursor") | |
| if not cursor: | |
| break | |
| return None | |
| def put_env(client: httpx.Client, token: str, service_id: str, key: str, value: str) -> None: | |
| r = client.put( | |
| f"{RENDER_API}/services/{service_id}/env-vars/{key}", | |
| headers={**_headers(token), "Content-Type": "application/json"}, | |
| json={"value": value}, | |
| timeout=30, | |
| ) | |
| if r.status_code == 404: | |
| r = client.post( | |
| f"{RENDER_API}/services/{service_id}/env-vars", | |
| headers={**_headers(token), "Content-Type": "application/json"}, | |
| json={"envVar": {"key": key, "value": value}}, | |
| timeout=30, | |
| ) | |
| r.raise_for_status() | |
| def main() -> int: | |
| token = os.environ.get("RENDER_API_KEY", "").strip() | |
| if not token: | |
| print("RENDER_API_KEY manquant — crée une clé sur https://dashboard.render.com/u/settings#api-keys") | |
| print("Deploy 1-clic (gratuit) : https://render.com/deploy?repo=https://github.com/XeroxYTB/emo-online") | |
| return 1 | |
| with httpx.Client() as client: | |
| svc = find_service(client, token) | |
| if not svc: | |
| print(f"Service '{SERVICE_NAME}' introuvable sur Render.") | |
| print("Deploy 1-clic : https://render.com/deploy?repo=https://github.com/XeroxYTB/emo-online") | |
| return 1 | |
| sid = svc["id"] | |
| print(f"Sync env → {svc.get('name')} ({svc.get('serviceDetails', {}).get('url', BACKEND_URL)})") | |
| count = 0 | |
| for key in SECRET_KEYS: | |
| val = os.environ.get(key, "").strip() | |
| if val: | |
| put_env(client, token, sid, key, val) | |
| print(f" secret {key} OK") | |
| count += 1 | |
| for key, val in VARIABLES.items(): | |
| put_env(client, token, sid, key, val) | |
| print(f" var {key} OK") | |
| count += 1 | |
| for sales, src in SALES_DUP.items(): | |
| val = os.environ.get(sales, "").strip() or os.environ.get(src, "").strip() | |
| if val: | |
| put_env(client, token, sid, sales, val) | |
| print(f" sales {sales} OK") | |
| count += 1 | |
| # Trigger redeploy | |
| dr = client.post( | |
| f"{RENDER_API}/services/{sid}/deploys", | |
| headers={**_headers(token), "Content-Type": "application/json"}, | |
| json={"clearCache": "do_not_clear"}, | |
| timeout=60, | |
| ) | |
| if dr.status_code in (200, 201): | |
| print("Redeploy declenche.") | |
| else: | |
| print(f"Redeploy: HTTP {dr.status_code} (env sync OK, {count} vars)") | |
| print("Done.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |