Files
aethas-free/on
T
xavier 21348c70aa
Build and Push Docker Image / build-and-push (push) Successful in 36s
des modifications spécifiques sur les droits des utilisateurs
2026-08-29 10:35:26 +02:00

239 lines
13 KiB
Plaintext

diff --git a/main.py b/main.py
index 6115ef5..30b8c0c 100644
--- a/main.py
+++ b/main.py
@@ -8,11 +8,10 @@ from fasthtml.common import *
from starlette.staticfiles import StaticFiles
from starlette.responses import FileResponse, Response, HTMLResponse, RedirectResponse
from starlette.middleware.base import BaseHTTPMiddleware
-from modules.user_db import load_users, save_users, changer_nom_utilisateur, mettre_a_jour_mot_de_passe
-from modules.system_utils import obtenir_infos_serveur
+
from modules.theme import get_theme_hdrs
from modules.database import nettoyer_vieilles_discussions, HISTORY_FILE, load_history, save_history
-from modules.ui_components import render_main_page, render_sidebar, render_login_page, render_rag_upload_modal
+from modules.ui_components import render_main_page, render_sidebar, render_login_page, render_rag_upload_modal, render_backoffice_page
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
@@ -20,6 +19,8 @@ from modules.logger import log_event
from modules.billing_manager import charger_soldes, sauvegarder_soldes
from modules.rag_manager import auto_apprendre_rag
from modules.vector_rag import indexer_dossier_thematique
+from modules.user_db import load_users, save_users, changer_nom_utilisateur, mettre_a_jour_mot_de_passe
+from modules.system_utils import obtenir_infos_serveur

# Lancement du module d'auto-mise à jour en arrière-plan
import modules.model_updater
@@ -27,108 +28,13 @@ import modules.model_updater
dossier_media = "/DATA/AppData/MULTI-IA-AETHAS38/RAG/Media"
os.makedirs(dossier_media, exist_ok=True)

-@rt('/backoffice')
-def get(session, request):
- utilisateur = session.get('user') or request.cookies.get('aethas_user')
- if not utilisateur:
- return RedirectResponse('/login', status_code=303)
- 
- users = load_users()
- infos_user = users.get(utilisateur, {"role": "Membre"})
- role = infos_user.get("role", "Membre")
- 
- # Données spécifiques Super Admin
- stats_serveur = obtenir_infos_serveur() if role == "Super Admin" else None
- 
- return render_backoffice_page(utilisateur, role, users, stats_serveur)
-
-@rt('/backoffice/update_profile')
-async def post(request, session):
- utilisateur = session.get('user') or request.cookies.get('aethas_user')
- if not utilisateur:
- return RedirectResponse('/login', status_code=303)
- 
- form = await request.form()
- nouveau_pseudo = form.get("nouveau_pseudo")
- nouveau_mdp = form.get("nouveau_mdp")
- 
- if nouveau_pseudo and nouveau_pseudo != utilisateur:
- succes = changer_nom_utilisateur(utilisateur, nouveau_pseudo)
- if succes:
- session['user'] = nouveau_pseudo
- resp = RedirectResponse('/backoffice', status_code=303)
- resp.set_cookie("aethas_user", nouveau_pseudo, max_age=86400)
- return resp
- 
- if nouveau_mdp:
- mettre_a_jour_mot_de_passe(utilisateur, nouveau_mdp)
- 
- return RedirectResponse('/backoffice', status_code=303)
-
-# --- WIDGET PAYPAL (RECHARGE DE CRÉDITS) ---
-def render_paypal_recharge_widget(montant_usd: float = 10.00):
- client_id = os.getenv("PAYPAL_CLIENT_ID", "test")
- 
- script_paypal = Script(src=f"https://www.paypal.com/sdk/js?client-id={client_id}&currency=USD")
- container_id = f"paypal-button-container-{str(montant_usd).replace('.', '')}"
- 
- script_logique = Script(f"""
- function chargerBoutonPayPal() {{
- if (typeof paypal !== 'undefined') {{
- paypal.Buttons({{
- style: {{ layout: 'vertical', height: 35, shape: 'rect', label: 'paypal' }},
- createOrder: function(data, actions) {{
- return actions.order.create({{
- purchase_units: [{{ amount: {{ value: '{montant_usd}' }} }}]
- }});
- }},
- onApprove: function(data, actions) {{
- return actions.order.capture().then(function(details) {{
- alert('Paiement validé par ' + details.payer.name.given_name + ' ! Vos crédits vont être ajoutés.');
- 
- fetch('/api/paypal/success', {{
- method: 'POST',
- headers: {{ 'Content-Type': 'application/json' }},
- body: JSON.stringify({{ orderID: data.orderID }})
- }}).then(res => {{
- if(res.ok) {{
- document.getElementById('modal-recharge-paypal').style.display = 'none';
- window.location.reload();
- }} else {{
- alert("Erreur lors de la validation serveur.");
- }}
- }});
- }});
- }}
- }}).render('#{container_id}');
- }} else {{
- setTimeout(chargerBoutonPayPal, 100);
- }}
- }}
- chargerBoutonPayPal();
- """)
-
- paypal_box = Div(
- H4(f"Recharge : {montant_usd} $", style="color: #fff; margin: 0 0 10px 0; font-size: 1.1em; text-align: center;"),
- Div(id=container_id, style="min-height: 100px; width: 100%;"),
- Button("Annuler", type="button", onclick="document.getElementById('modal-recharge-paypal').style.display='none'", 
- style="background: transparent; border: 1px solid #555; color: #aaa; width: 100%; margin-top: 5px; padding: 6px; border-radius: 6px; font-size: 0.85em; cursor: pointer;"),
- style="background: #1a1a1a; padding: 20px; border-radius: 10px; border: 1px solid #333; width: 320px; max-width: 90vw; box-shadow: 0 8px 24px rgba(0,0,0,0.9);"
- )
-
- modal_overlay = Div(
- script_paypal, paypal_box, script_logique,
- id="modal-recharge-paypal",
- style="display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.6); backdrop-filter: blur(3px); align-items: center; justify-content: center; z-index: 10000;"
- )
- return modal_overlay
-
# --- MIDDLEWARE DE SÉCURITÉ & SERVEUR MÉDIA UNIFIÉ ---
class SecurityMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
ip = request.headers.get("X-Forwarded-For", request.client.host).split(",")[0].strip()
utilisateur = request.cookies.get("aethas_user", "visiteur")

+ # --- LECTURE EN RAM ULTRA-DIRECTE PAR LE MIDDLEWARE ---
if request.url.path.startswith("/Media/"):
fname = request.url.path.replace("/Media/", "", 1)
chemin = os.path.join(dossier_media, fname)
@@ -140,10 +46,12 @@ class SecurityMiddleware(BaseHTTPMiddleware):
return HTMLResponse(f"Erreur : {e}", status_code=500)
return HTMLResponse("Fichier introuvable", status_code=404)

+ # EXCLUSION DE /Media POUR NE PAS BLOQUER LE RESTE
if not request.url.path.startswith(("/Logo", "/login", "/Media")):
if verifier_et_bloquer_vpn(ip, utilisateur):
return HTMLResponse("<h1 style='color:red; text-align:center;'>Accès refusé</h1><p style='text-align:center;'>VPN ou Proxy strictement interdit.</p>", status_code=403)

+ # Exclusion stricte du spam de logs (Silence pour /ping et /favicon)
if request.url.path not in ["/ping", "/favicon.ico"]:
journaliser_ip(ip, f"Accès à {request.url.path}")

@@ -182,6 +90,44 @@ def get(session):
resp.delete_cookie("aethas_user")
return resp

+# --- ROUTES DU BACK OFFICE & PROFIL ---
+@rt('/backoffice')
+def get(session, request):
+ utilisateur = session.get('user') or request.cookies.get('aethas_user')
+ if not utilisateur:
+ return RedirectResponse('/login', status_code=303)
+ 
+ users = load_users()
+ infos_user = users.get(utilisateur, {"role": "Membre"})
+ role = infos_user.get("role", "Membre")
+ 
+ stats_serveur = obtenir_infos_serveur() if role == "Super Admin" else None
+ 
+ return render_backoffice_page(utilisateur, role, users, stats_serveur)
+
+@rt('/backoffice/update_profile')
+async def post(request, session):
+ utilisateur = session.get('user') or request.cookies.get('aethas_user')
+ if not utilisateur:
+ return RedirectResponse('/login', status_code=303)
+ 
+ form = await request.form()
+ nouveau_pseudo = form.get("nouveau_pseudo")
+ nouveau_mdp = form.get("nouveau_mdp")
+ 
+ if nouveau_pseudo and nouveau_pseudo != utilisateur:
+ succes = changer_nom_utilisateur(utilisateur, nouveau_pseudo)
+ if succes:
+ session['user'] = nouveau_pseudo
+ resp = RedirectResponse('/backoffice', status_code=303)
+ resp.set_cookie("aethas_user", nouveau_pseudo, max_age=86400)
+ return resp
+ 
+ if nouveau_mdp:
+ mettre_a_jour_mot_de_passe(utilisateur, nouveau_mdp)
+ 
+ return RedirectResponse('/backoffice', status_code=303)
+
# --- ROUTES PROTÉGÉES ---
@rt('/')
def get(session, request):
@@ -192,11 +138,7 @@ def get(session, request):
session['user'] = utilisateur
if 'current_chat_id' not in session or not session['current_chat_id']:
session['current_chat_id'] = str(uuid.uuid4())
- 
- main_page = render_main_page(session['current_chat_id'], utilisateur_nom=session['user'])
- paypal_modal = render_paypal_recharge_widget(10.00)
- 
- return paypal_modal, main_page
+ return render_main_page(session['current_chat_id'], utilisateur_nom=session['user'])

@rt('/chat')
async def post(request, session):
@@ -245,18 +187,14 @@ def post(cid: str):
if cid_str in h: del h[cid_str]; save_history(h)
return render_sidebar()

-@rt('/api/paypal/success')
-async def post(request):
- data = await request.json()
- order_id = data.get("orderID")
- log_event("INFO", "FINANCE", f"Recharge PayPal validée - OrderID: {order_id}")
- return {"status": "success"}
-
+# --- NOUVELLE ROUTE POUR LE RAG (BOUTON MANUEL) ---
@rt('/api/forcer_rag')
def post(prompt: str, reponse: str, theme: str):
+ """Intercepte la demande manuelle, lance l'indexation et renvoie un badge vert."""
auto_apprendre_rag(prompt, reponse, theme)
return Span("✅ Indexé", cls="msg-action-btn", style="background-color: #4caf50; color: white; border: none; cursor: default;")

+# --- NOUVELLES ROUTES POUR L'UPLOAD MODAL DU RAG ---
@rt('/admin/rag_modal')
def get(session):
if session.get('user', '') != 'xavier': 
@@ -295,6 +233,7 @@ async def post(request, session):

from modules.export_utils import generer_export_comptable

+# --- NOUVELLE ROUTE POUR L'EXPORT COMPTABLE EXCEL ---
@rt('/admin/export_comptable')
def get(session):
if session.get('user', '') != 'xavier':