diff --git a/aethas_balances.json b/aethas_balances.json
deleted file mode 100644
index d8b69d3..0000000
--- a/aethas_balances.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "cagnotte_commune_vip": 34.988855
-}
\ No newline at end of file
diff --git a/aethas_ip_logs.json b/aethas_ip_logs.json
index 536ddd8..3aca4a8 100644
--- a/aethas_ip_logs.json
+++ b/aethas_ip_logs.json
@@ -11650,5 +11650,179 @@
"action": "Acc\u00e8s \u00e0 /",
"date": "2026-08-23 09:39:21",
"timestamp": 1787470761.3066163
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:29:03",
+ "timestamp": 1787473743.7729807
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:29:07",
+ "timestamp": 1787473747.1458204
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /api/paypal/success",
+ "date": "2026-08-23 10:29:29",
+ "timestamp": 1787473769.173476
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:29:29",
+ "timestamp": 1787473769.326424
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:38:09",
+ "timestamp": 1787474289.483428
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /api/paypal/success",
+ "date": "2026-08-23 10:38:30",
+ "timestamp": 1787474310.2446322
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:38:30",
+ "timestamp": 1787474310.3879251
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:40:40",
+ "timestamp": 1787474440.6522577
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /api/paypal/success",
+ "date": "2026-08-23 10:41:17",
+ "timestamp": 1787474477.0745878
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:45:53",
+ "timestamp": 1787474753.4862194
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /api/paypal/success",
+ "date": "2026-08-23 10:46:11",
+ "timestamp": 1787474771.704107
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 10:46:11",
+ "timestamp": 1787474771.8661208
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:09:04",
+ "timestamp": 1787476144.4969127
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:09:05",
+ "timestamp": 1787476145.455936
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:09:07",
+ "timestamp": 1787476147.144958
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:09:20",
+ "timestamp": 1787476160.3453426
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:13:18",
+ "timestamp": 1787476398.5733256
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:13:25",
+ "timestamp": 1787476405.3547883
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:16:45",
+ "timestamp": 1787476605.5372877
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:16:54",
+ "timestamp": 1787476614.6163135
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:17:17",
+ "timestamp": 1787476637.6742752
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:17:28",
+ "timestamp": 1787476648.5071766
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:21:45",
+ "timestamp": 1787476905.9197671
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:23:15",
+ "timestamp": 1787476995.713237
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:26:20",
+ "timestamp": 1787477180.2584555
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:27:10",
+ "timestamp": 1787477230.102091
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:27:39",
+ "timestamp": 1787477259.2473674
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:32:08",
+ "timestamp": 1787477528.771442
+ },
+ {
+ "ip": "127.0.0.1",
+ "action": "Acc\u00e8s \u00e0 /",
+ "date": "2026-08-23 11:36:33",
+ "timestamp": 1787477793.7782843
}
]
\ No newline at end of file
diff --git a/main.py b/main.py
index 05025da..8b47c29 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,8 @@
+import os
+from dotenv import load_dotenv
+load_dotenv() # Force la lecture du fichier .env en local
import uuid
+import json
from fasthtml.common import *
from starlette.staticfiles import StaticFiles
from starlette.responses import FileResponse, Response, HTMLResponse, RedirectResponse
@@ -10,6 +14,8 @@ from modules.ui_components import render_main_page, render_sidebar, render_login
from modules.chat_handler import handle_chat_request
from modules.security import verifier_et_bloquer_vpn, journaliser_ip
from modules.auth_manager import IDENTIFIANTS_ADMIN, verifier_mot_de_passe
+from modules.logger import log_event
+from modules.billing_manager import charger_soldes, sauvegarder_soldes
# Lancement du module d'auto-mise à jour en arrière-plan
import modules.model_updater
diff --git a/modules/billing_manager.py b/modules/billing_manager.py
index a048f83..b47edf4 100644
--- a/modules/billing_manager.py
+++ b/modules/billing_manager.py
@@ -16,7 +16,6 @@ TARIFS_FILE = os.path.join(BASE_DIR, "tarifs_openrouter.json")
VIP_ADMIN_USERS = ["xavier", "céline", "celine", "anaëlle", "anaelle", "renan", "audric"]
def obtenir_tarifs_dynamiques() -> dict:
- """Récupère les tarifs via API toutes les 2 heures maximum pour éviter le hardcoding."""
maintenant = time.time()
if os.path.exists(TARIFS_FILE):
@@ -36,7 +35,6 @@ def obtenir_tarifs_dynamiques() -> dict:
pricing = m.get("pricing", {})
p_prompt = float(pricing.get("prompt", 0.0))
p_comp = float(pricing.get("completion", 0.0))
- # On sécurise l'estimation en prenant la valeur la plus haute entre tokens in et out
tarifs[m["id"]] = max(p_prompt, p_comp)
with open(TARIFS_FILE, "w", encoding="utf-8") as f:
@@ -46,7 +44,6 @@ def obtenir_tarifs_dynamiques() -> dict:
except Exception as e:
log_event("ERROR", "BILLING", f"Échec récupération des tarifs : {e}")
- # Repli sur le cache si l'API est injoignable
if os.path.exists(TARIFS_FILE):
try:
with open(TARIFS_FILE, "r", encoding="utf-8") as f:
@@ -150,6 +147,31 @@ def verifier_et_valider_budget(prompt: str, modele_id: str, utilisateur_nom: str
return True, prix_facture
def obtenir_solde(utilisateur_nom: str) -> float:
+ # --- SYNCHRONISATION DIRECTE POUR L'ADMINISTRATEUR PRINCIPAL ---
+ if utilisateur_nom.lower() in ["xavier"]:
+ api_key = os.getenv("OPENROUTER_API_KEY")
+ if api_key:
+ try:
+ resp = requests.get("https://openrouter.ai/api/v1/auth/key", headers={"Authorization": f"Bearer {api_key}"}, timeout=3)
+ if resp.status_code == 200:
+ data = resp.json().get("data", {})
+
+ credit_total = data.get('usage', 0.0)
+ limite_restante = data.get('limit_remaining')
+ balance = data.get('balance', 0.0)
+
+ vrai_solde = float(limite_restante) if limite_restante is not None else float(balance)
+
+ if vrai_solde > 0 or limite_restante is not None:
+ soldes = charger_soldes()
+ soldes["cagnotte_commune_vip"] = vrai_solde
+ soldes["xavier"] = vrai_solde
+ sauvegarder_soldes(soldes)
+ return vrai_solde
+ except Exception as e:
+ log_event("WARNING", "BILLING", f"Échec de synchronisation OpenRouter : {e}")
+
+ # --- COMPORTEMENT STANDARD POUR LES AUTRES UTILISATEURS ---
soldes = charger_soldes()
if utilisateur_nom.lower() in VIP_ADMIN_USERS:
return soldes.get("cagnotte_commune_vip", 0.0)
@@ -177,27 +199,4 @@ def generer_dossier_litige(utilisateur_nom: str, est_admin: bool = False) -> lis
"tokens": item.get("tokens"),
"montant_facture_ttc": item.get("prix_facture_client")
})
- return dossier
-
-def calculer_prix_recharge_ttc(montant_credits_usd: float, methode: str = "paypal") -> float:
- """
- Calcule le coût final pour l'utilisateur qui souhaite acheter une recharge de crédits.
- Le coût final répercute les frais bancaires pour que l'entreprise soit à l'équilibre.
-
- Frais estimés (Variables selon devises, basés sur des profils Sandbox/Standard) :
- - PayPal : 2.9% + 0.35$ fixes par transaction
- - Revolut Business : 1.0% + 0.20$ fixes par transaction en ligne
-
- Formule d'absorption des frais : Montant_Final = (Credits + Frais_Fixes) / (1 - Pourcentage_Frais)
- """
- if methode.lower() == "paypal":
- frais_fixes = 0.35
- frais_variable = 0.029
- elif methode.lower() == "revolut":
- frais_fixes = 0.20
- frais_variable = 0.010
- else:
- return montant_credits_usd # Aucune méthode bancaire spécifiée
-
- montant_a_payer = (montant_credits_usd + frais_fixes) / (1 - frais_variable)
- return round(montant_a_payer, 2)
\ No newline at end of file
+ return dossier
\ No newline at end of file
diff --git a/modules/debug_mailer.py b/modules/debug_mailer.py
index 7dbc580..e7e02b5 100644
--- a/modules/debug_mailer.py
+++ b/modules/debug_mailer.py
@@ -3,25 +3,24 @@ import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
-# Option de désactivation depuis l'interface (Phase Debug)
DEBUG_MAIL_ACTIVE = True
def envoyer_alerte_debug(niveau, source, message_erreur):
"""
- Envoie un e-mail à l'administrateur en cas d'erreur serveur (Phase Debug).
- Utilise le serveur SMTP défini dans le .env
+ Envoie un e-mail à l'administrateur.
+ Gère le chiffrement (Port 587) et le relai interne sans mot de passe (Port 25).
"""
if not DEBUG_MAIL_ACTIVE:
return
email_admin = os.getenv("ADMIN_EMAIL", "votre-email@domaine.com")
- smtp_serveur = os.getenv("SMTP_SERVER")
+ smtp_serveur = os.getenv("SMTP_SERVER", "192.168.1.20")
smtp_port = int(os.getenv("SMTP_PORT", 587))
- smtp_user = os.getenv("SMTP_USER")
- smtp_pass = os.getenv("SMTP_PASS")
+ smtp_user = os.getenv("SMTP_USER", "")
+ smtp_pass = os.getenv("SMTP_PASS", "")
- if not all([smtp_serveur, smtp_user, smtp_pass]):
- print(f"[{niveau}] [DEBUG_MAILER] Configuration SMTP absente. Impossible d'envoyer l'alerte pour : {message_erreur}")
+ if not smtp_serveur:
+ print(f"[{niveau}] [DEBUG_MAILER] Serveur SMTP absent. Alerte annulée.")
return
sujet = f"[ALERTE {niveau}] Aethas38_Multi_IA - Erreur dans {source}"
@@ -36,22 +35,32 @@ def envoyer_alerte_debug(niveau, source, message_erreur):
{message_erreur}
-
Notification automatique de l'Orchestrateur (Phase Debug)
+Notification automatique de l'Orchestrateur