diff --git a/.env.master b/.env.master index 0edf599..da4fce4 100644 --- a/.env.master +++ b/.env.master @@ -100,7 +100,7 @@ MAILCHIMP_LIST_ID=a48d9ad852 # Configuration Stripe STRIPE_SECRET_KEY=sk_test_51OwKmMP5xh1u9BqSeFpqw0Yr15hHtFsh0pvRGaE0VERhlYtvw33ND1qiGA6Dy1DPmmV61B6BqIimlhuv7bwElhjF00PLQwD60n -STRIPE_PUBLISHABLE_KEY= +STRIPE_PUBLISHABLE_KEY=pk_test_51OwKmMP5xh1u9BqSOCBC1H40ACnGE4qH5ZERZ4tKktQuTcqRTPY26QqKeM6FfghdnbcC7nPbtMtnB6jemInrs67Z008RxkUGcn STRIPE_WEBHOOK_SECRET= STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID=price_1P66fuP5xh1u9BqSHj0O6Uy3 STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID=price_1P9NsRP5xh1u9BqSFgkUDbQY diff --git a/conf/nginx/dev4.4nkweb.com-https.conf b/conf/nginx/dev4.4nkweb.com-https.conf index dbaaf8a..b1cdd25 100644 --- a/conf/nginx/dev4.4nkweb.com-https.conf +++ b/conf/nginx/dev4.4nkweb.com-https.conf @@ -81,6 +81,11 @@ server { add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; + # Désactiver le cache côté client + add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; + add_header Pragma "no-cache" always; + expires -1; + # Cache pour les assets statiques location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { expires 1h; @@ -101,6 +106,12 @@ server { add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization"; + # Désactiver le cache proxy/client + proxy_no_cache 1; + proxy_cache_bypass 1; + add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; + add_header Pragma "no-cache" always; + # Timeouts proxy_connect_timeout 10s; proxy_send_timeout 10s; diff --git a/web/status/api.py b/web/status/api.py index 9ea7aa9..def7f24 100644 --- a/web/status/api.py +++ b/web/status/api.py @@ -423,33 +423,77 @@ class StatusAPIHandler(BaseHTTPRequestHandler): except Exception: mailchimp_test = {"provider": "Mailchimp", "status": "error"} - stripe_by_offer = {"STANDARD": 0, "UNLIMITED": 0} + # Stripe: lister prices et agréger en balayant les subscriptions (sans filtre price) + stripe_by_offer = {"CREATORS": 0, "STARTER": 0, "STANDARD": 0, "UNLIMITED": 0, "TOTAL": 0} + stripe_prices_map = {} if env_map.get("STRIPE_SECRET_KEY"): try: auth_h = f"Authorization: Bearer {env_map.get('STRIPE_SECRET_KEY')}" - code_st, out_st, _ = run_cmd([ - "curl", "-fsS", "https://api.stripe.com/v1/subscriptions?limit=100&status=active", + # 1) Lister les prices actifs (<=100) pour mapper price.id -> nickname + code_p, out_p, _ = run_cmd([ + "curl", "-fsS", "https://api.stripe.com/v1/prices?limit=100&active=true", "-H", auth_h ], timeout_seconds=6) - if code_st == 0 and out_st: - data_st = json.loads(out_st) - price_ids = { - "STANDARD": { - env_map.get("STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID"), - env_map.get("STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID"), - }, - "UNLIMITED": { - env_map.get("STRIPE_UNLIMITED_SUBSCRIPTION_PRICE_ID"), - env_map.get("STRIPE_UNLIMITED_ANNUAL_SUBSCRIPTION_PRICE_ID"), - }, - } - for sub in (data_st.get("data") or []): - for it in ((sub.get("items") or {}).get("data") or []): - pid = ((it.get("price") or {}).get("id")) - if pid and pid in price_ids["STANDARD"]: - stripe_by_offer["STANDARD"] += 1 - if pid and pid in price_ids["UNLIMITED"]: - stripe_by_offer["UNLIMITED"] += 1 + if code_p == 0 and out_p: + prices = (json.loads(out_p) or {}).get("data") or [] + for pr in prices: + pid = pr.get('id') + stripe_prices_map[pid] = pr.get('nickname') or '' + # Déterminer les familles par ID connus (si présents dans l'env) sinon par nickname + creators_ids = set(filter(None, [env_map.get("STRIPE_CREATORS_PRICE_ID")])) + standard_ids = set(filter(None, [ + env_map.get("STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID"), + env_map.get("STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID"), + env_map.get("STRIPE_STANDARD_MONTHLY_YEAR_PRICE_ID"), + env_map.get("STRIPE_STANDARD_MONTHLY_MONTH_PRICE_ID"), + ])) + starter_ids = set(filter(None, [ + env_map.get("STRIPE_STARTER_ANNUAL_PRICE_ID"), + env_map.get("STRIPE_STARTER_MONTHLY_YEAR_PRICE_ID"), + env_map.get("STRIPE_STARTER_MONTHLY_MONTH_PRICE_ID"), + ])) + unlimited_ids = set(filter(None, [ + env_map.get("STRIPE_UNLIMITED_SUBSCRIPTION_PRICE_ID"), + env_map.get("STRIPE_UNLIMITED_ANNUAL_SUBSCRIPTION_PRICE_ID"), + ])) + + def family_for(pid: str, nickname: str) -> str: + if pid in creators_ids or (nickname and 'createur' in nickname.lower()): + return 'CREATORS' + if pid in starter_ids or (nickname and 'starter' in nickname.lower()): + return 'STARTER' + if pid in standard_ids or (nickname and 'standard' in nickname.lower()): + return 'STANDARD' + if pid in unlimited_ids or (nickname and 'unlimit' in nickname.lower()): + return 'UNLIMITED' + return '' + # 2) Lister subscriptions (active + trialing) et agréger par famille du price + starting_after = None + pages = 0 + while pages < 3: # limite de pagination pour éviter les boucles longues + url = "https://api.stripe.com/v1/subscriptions?limit=100&status=active&status=trialing" + if starting_after: + url += f"&starting_after={starting_after}" + code_s, out_s, _ = run_cmd(["curl", "-fsS", url, "-H", auth_h], timeout_seconds=8) + if code_s != 0 or not out_s: + break + d = json.loads(out_s) or {} + subs = d.get("data") or [] + for sub in subs: + items = ((sub.get("items") or {}).get("data") or []) + for it in items: + pid = ((it.get("price") or {}).get("id")) + nick = stripe_prices_map.get(pid, '') + fam = family_for(pid or '', nick) + if not fam: + continue + stripe_by_offer[fam] = stripe_by_offer.get(fam, 0) + 1 + stripe_by_offer["TOTAL"] += 1 + if d.get("has_more") and subs: + starting_after = subs[-1].get('id') + pages += 1 + continue + break except Exception: pass diff --git a/web/status/index.html b/web/status/index.html index cce7968..02523f2 100644 --- a/web/status/index.html +++ b/web/status/index.html @@ -119,7 +119,7 @@
- +
@@ -250,7 +250,7 @@ idbGrid.innerHTML = ''; try { - const res = await fetch('/status/api?ts=' + Date.now()); + const res = await fetch('/status/api'); if (!res.ok) throw new Error('API status indisponible'); const data = await res.json(); // Summary banner @@ -295,6 +295,7 @@ window.__integrations = { mailchimp: data.integrations_test?.mailchimp, stripe: data.integrations_test?.stripe_subscriptions_by_offer, + stripe_prices: data.integrations_test?.stripe_prices || {}, ovh: data.integrations_test?.ovh, }; const integCards = createIntegrationsCards(data.integrations_configured); @@ -327,8 +328,11 @@ } } + // Wire button (avoid inline handler for CSP) + document.getElementById('refresh-btn').addEventListener('click', () => { + refreshStatus(); + }); refreshStatus(); - setInterval(refreshStatus, 30000); // ---- IndexedDB helpers ---- function openDb(name, version) {