align for IA agents + grafana
This commit is contained in:
parent
18ca8a95db
commit
3367c53963
@ -100,7 +100,7 @@ MAILCHIMP_LIST_ID=a48d9ad852
|
|||||||
|
|
||||||
# Configuration Stripe
|
# Configuration Stripe
|
||||||
STRIPE_SECRET_KEY=sk_test_51OwKmMP5xh1u9BqSeFpqw0Yr15hHtFsh0pvRGaE0VERhlYtvw33ND1qiGA6Dy1DPmmV61B6BqIimlhuv7bwElhjF00PLQwD60n
|
STRIPE_SECRET_KEY=sk_test_51OwKmMP5xh1u9BqSeFpqw0Yr15hHtFsh0pvRGaE0VERhlYtvw33ND1qiGA6Dy1DPmmV61B6BqIimlhuv7bwElhjF00PLQwD60n
|
||||||
STRIPE_PUBLISHABLE_KEY=
|
STRIPE_PUBLISHABLE_KEY=pk_test_51OwKmMP5xh1u9BqSOCBC1H40ACnGE4qH5ZERZ4tKktQuTcqRTPY26QqKeM6FfghdnbcC7nPbtMtnB6jemInrs67Z008RxkUGcn
|
||||||
STRIPE_WEBHOOK_SECRET=
|
STRIPE_WEBHOOK_SECRET=
|
||||||
STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID=price_1P66fuP5xh1u9BqSHj0O6Uy3
|
STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID=price_1P66fuP5xh1u9BqSHj0O6Uy3
|
||||||
STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID=price_1P9NsRP5xh1u9BqSFgkUDbQY
|
STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID=price_1P9NsRP5xh1u9BqSFgkUDbQY
|
||||||
|
@ -81,6 +81,11 @@ server {
|
|||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" 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
|
# Cache pour les assets statiques
|
||||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
|
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
|
||||||
expires 1h;
|
expires 1h;
|
||||||
@ -101,6 +106,12 @@ server {
|
|||||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
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
|
# Timeouts
|
||||||
proxy_connect_timeout 10s;
|
proxy_connect_timeout 10s;
|
||||||
proxy_send_timeout 10s;
|
proxy_send_timeout 10s;
|
||||||
|
@ -423,33 +423,77 @@ class StatusAPIHandler(BaseHTTPRequestHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
mailchimp_test = {"provider": "Mailchimp", "status": "error"}
|
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"):
|
if env_map.get("STRIPE_SECRET_KEY"):
|
||||||
try:
|
try:
|
||||||
auth_h = f"Authorization: Bearer {env_map.get('STRIPE_SECRET_KEY')}"
|
auth_h = f"Authorization: Bearer {env_map.get('STRIPE_SECRET_KEY')}"
|
||||||
code_st, out_st, _ = run_cmd([
|
# 1) Lister les prices actifs (<=100) pour mapper price.id -> nickname
|
||||||
"curl", "-fsS", "https://api.stripe.com/v1/subscriptions?limit=100&status=active",
|
code_p, out_p, _ = run_cmd([
|
||||||
|
"curl", "-fsS", "https://api.stripe.com/v1/prices?limit=100&active=true",
|
||||||
"-H", auth_h
|
"-H", auth_h
|
||||||
], timeout_seconds=6)
|
], timeout_seconds=6)
|
||||||
if code_st == 0 and out_st:
|
if code_p == 0 and out_p:
|
||||||
data_st = json.loads(out_st)
|
prices = (json.loads(out_p) or {}).get("data") or []
|
||||||
price_ids = {
|
for pr in prices:
|
||||||
"STANDARD": {
|
pid = pr.get('id')
|
||||||
env_map.get("STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID"),
|
stripe_prices_map[pid] = pr.get('nickname') or ''
|
||||||
env_map.get("STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID"),
|
# 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")]))
|
||||||
"UNLIMITED": {
|
standard_ids = set(filter(None, [
|
||||||
env_map.get("STRIPE_UNLIMITED_SUBSCRIPTION_PRICE_ID"),
|
env_map.get("STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID"),
|
||||||
env_map.get("STRIPE_UNLIMITED_ANNUAL_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"),
|
||||||
for sub in (data_st.get("data") or []):
|
]))
|
||||||
for it in ((sub.get("items") or {}).get("data") or []):
|
starter_ids = set(filter(None, [
|
||||||
pid = ((it.get("price") or {}).get("id"))
|
env_map.get("STRIPE_STARTER_ANNUAL_PRICE_ID"),
|
||||||
if pid and pid in price_ids["STANDARD"]:
|
env_map.get("STRIPE_STARTER_MONTHLY_YEAR_PRICE_ID"),
|
||||||
stripe_by_offer["STANDARD"] += 1
|
env_map.get("STRIPE_STARTER_MONTHLY_MONTH_PRICE_ID"),
|
||||||
if pid and pid in price_ids["UNLIMITED"]:
|
]))
|
||||||
stripe_by_offer["UNLIMITED"] += 1
|
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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<button class="refresh-btn" onclick="refreshStatus()">🔄 Actualiser</button>
|
<button class="refresh-btn" id="refresh-btn">🔄 Actualiser</button>
|
||||||
<div class="timestamp" id="timestamp"></div>
|
<div class="timestamp" id="timestamp"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -250,7 +250,7 @@
|
|||||||
idbGrid.innerHTML = '';
|
idbGrid.innerHTML = '';
|
||||||
|
|
||||||
try {
|
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');
|
if (!res.ok) throw new Error('API status indisponible');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
// Summary banner
|
// Summary banner
|
||||||
@ -295,6 +295,7 @@
|
|||||||
window.__integrations = {
|
window.__integrations = {
|
||||||
mailchimp: data.integrations_test?.mailchimp,
|
mailchimp: data.integrations_test?.mailchimp,
|
||||||
stripe: data.integrations_test?.stripe_subscriptions_by_offer,
|
stripe: data.integrations_test?.stripe_subscriptions_by_offer,
|
||||||
|
stripe_prices: data.integrations_test?.stripe_prices || {},
|
||||||
ovh: data.integrations_test?.ovh,
|
ovh: data.integrations_test?.ovh,
|
||||||
};
|
};
|
||||||
const integCards = createIntegrationsCards(data.integrations_configured);
|
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();
|
refreshStatus();
|
||||||
setInterval(refreshStatus, 30000);
|
|
||||||
|
|
||||||
// ---- IndexedDB helpers ----
|
// ---- IndexedDB helpers ----
|
||||||
function openDb(name, version) {
|
function openDb(name, version) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user