feat(abonnements): paliers LLM Essentielle/Premium, admin modeles par forfait, statistiques revenus, emails marketing
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s

- Gamme officielle : Essentielle (Pro) = deepseek-v4-flash, glm-5.3-flash,
  minimax-m3 ; Premium (Business) = claude-sonnet-5, deepseek-v4-pro, glm-5.3
- Routage reel : le modele employe suit le palier IA du forfait (reglages
  admin > defaut du plan), garde anti-croisement de palier
- Nouvelle page admin « Modeles & abonnements » (matrice forfaits/paliers,
  modele par defaut, catalogue OpenRouter)
- Statistiques enrichies : revenus (30 j + total), MRR estime, credits,
  liste d'attente, paliers utilises
- Nouvelle page admin « Marketing » : audiences avec compteurs, apercu,
  envoi test obligatoire, historique, desabonnement public + en-tetes
  List-Unsubscribe ; .gitignore pour les donnees d'execution
- Interface (accueil + tarifs) alignee sur la gamme, 13 langues completes
- Tests : routage par palier (fonction + tache), marketing, revenus en base
This commit is contained in:
2026-09-05 12:51:49 +02:00
parent 4e6850d6b1
commit 6c26712687
76 changed files with 5289 additions and 271 deletions

View File

@@ -51,9 +51,13 @@ async def get_available_providers(
"""
from routes.admin_routes import load_settings
from models.subscription import PlanType, PLANS
from routes.translate_routes import _resolve_tier_model
settings = load_settings()
is_dev = os.getenv("APP_ENV", "production").lower() == "development"
# Le modèle affiché est celui réellement routé pour le plan de l'appelant
# (réglages admin « ai_tiers » > défaut du plan — gamme officielle).
user_plan = str(current_user.plan) if current_user is not None else None
def _key_ready(key_var: str) -> bool:
return bool(os.getenv(key_var, "").strip())
@@ -84,12 +88,7 @@ async def get_available_providers(
# AI Essentielle (OpenRouter — cheap model / Eco)
if _is_enabled("openrouter", key_var="OPENROUTER_API_KEY"):
or_cfg = getattr(settings, "openrouter", None)
model = _resolve_model(
or_cfg.model if or_cfg else None,
"OPENROUTER_MODEL",
"google/gemini-3.5-flash",
)
model = _resolve_tier_model(settings, user_plan, premium=False)
available.append({
"id": "openrouter",
"label": "Traduction IA Éco",
@@ -118,12 +117,7 @@ async def get_available_providers(
# AI Premium (OpenRouter — premium model)
if _is_enabled("openrouter_premium", key_var="OPENROUTER_API_KEY") or _is_enabled("openrouter", key_var="OPENROUTER_API_KEY"):
orp_cfg = getattr(settings, "openrouter_premium", None)
model = _resolve_model(
orp_cfg.model if orp_cfg else None,
"OPENROUTER_PREMIUM_MODEL",
"anthropic/claude-sonnet-4.6",
)
model = _resolve_tier_model(settings, user_plan, premium=True)
available.append({
"id": "openrouter_premium",
"label": "Traduction IA Premium",