diff --git a/main.py b/main.py index 4eee3d4..6115ef5 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,8 @@ 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 @@ -26,6 +27,44 @@ 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") diff --git a/modules/system_utils.py b/modules/system_utils.py new file mode 100644 index 0000000..d10685c --- /dev/null +++ b/modules/system_utils.py @@ -0,0 +1,10 @@ +import shutil + +def obtenir_infos_serveur(): + total, used, free = shutil.disk_usage("/") + return { + "disque_total_gb": round(total / (2**30), 2), + "disque_utilise_gb": round(used / (2**30), 2), + "disque_libre_gb": round(free / (2**30), 2), + "pourcentage_utilise": round((used / total) * 100, 1) + } \ No newline at end of file diff --git a/modules/ui_components.py b/modules/ui_components.py index 717a235..2b7d654 100644 --- a/modules/ui_components.py +++ b/modules/ui_components.py @@ -294,4 +294,40 @@ def render_rag_upload_modal(): ), id="rag-modal", style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 9999;" + ) + +def render_backoffice_page(utilisateur, role, tous_les_utilisateurs, stats_serveur): + sections = [ + H2(f"⚙️ Paramètres du compte - {utilisateur.capitalize()} ({role})", style="color: #00e5ff;"), + Form( + Div(Label("Modifier mon pseudonyme :"), Input(type="text", name="nouveau_pseudo", value=utilisateur, cls="input-field"), style="margin-bottom: 10px;"), + Div(Label("Nouveau mot de passe :"), Input(type="password", name="nouveau_mdp", placeholder="Laisser vide si inchangé", cls="input-field"), style="margin-bottom: 15px;"), + Button("Enregistrer les modifications", type="submit", cls="btn-submit"), + method="post", action="/backoffice/update_profile", style="background: #1a1a1a; padding: 20px; border-radius: 8px; border: 1px solid #333; margin-bottom: 20px;" + ) + ] + + # Spécificités Admin & Super Admin (Héritage des privilèges inférieurs) + if role in ["Admin", "Super Admin"]: + lignes_utilisateurs = [Li(f"{pseudo} — Rôle : {data.get('role', 'Membre')}") for pseudo, data in tous_les_utilisateurs.items()] + sections.append(Div( + H3("👥 Gestion des utilisateurs", style="color: #4caf50;"), + Ul(*lignes_utilisateurs), + style="background: #1a1a1a; padding: 20px; border-radius: 8px; border: 1px solid #333; margin-bottom: 20px;" + )) + + # Spécificités Super Admin uniquement + if role == "Super Admin" and stats_serveur: + sections.append(Div( + H3("🖥️ Supervision Système & Serveur", style="color: #ff9800;"), + P(f"💾 Espace disque utilisé : {stats_serveur['disque_utilise_gb']} Go / {stats_serveur['disque_total_gb']} Go ({stats_serveur['pourcentage_utilise']}%)"), + P(f"📂 Espace libre : {stats_serveur['disque_libre_gb']} Go"), + style="background: #1a1a1a; padding: 20px; border-radius: 8px; border: 1px solid #333;" + )) + + sections.append(A("← Retour au Chat", href="/", style="display: block; margin-top: 20px; color: #00e5ff; text-decoration: none;")) + + return Title("Back Office - Aethas38"), Body( + Div(*sections, style="max-width: 600px; margin: 50px auto; font-family: monospace; color: #fff;"), + style="background-color: #0d0d0d; padding: 20px;" ) \ No newline at end of file diff --git a/modules/user_db.py b/modules/user_db.py new file mode 100644 index 0000000..34f9b13 --- /dev/null +++ b/modules/user_db.py @@ -0,0 +1,48 @@ +import os +import json +from modules.auth_manager import hasher_mot_de_passe, verifier_mot_de_passe + +USERS_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/users.json" + +def init_users(): + if not os.path.exists(USERS_FILE): + default_users = { + "xavier": { + "email": "admin@mail.aethas38.duckdns.org", + "password_hash": hasher_mot_de_passe("Xavier2026!"), + "role": "Super Admin" + }, + "celine": { + "email": "cnoirard@ac-lyon.fr", + "password_hash": hasher_mot_de_passe("Celine2026!"), + "role": "Admin" + } + } + os.makedirs(os.path.dirname(USERS_FILE), exist_ok=True) + with open(USERS_FILE, 'w', encoding='utf-8') as f: + json.dump(default_users, f, indent=4) + +def load_users(): + init_users() + with open(USERS_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + +def save_users(users): + with open(USERS_FILE, 'w', encoding='utf-8') as f: + json.dump(users, f, indent=4) + +def changer_nom_utilisateur(ancien_nom, nouveau_nom): + users = load_users() + if ancien_nom in users and nouveau_nom not in users: + users[nouveau_nom] = users.pop(ancien_nom) + save_users(users) + return True + return False + +def mettre_a_jour_mot_de_passe(nom_utilisateur, nouveau_mdp): + users = load_users() + if nom_utilisateur in users: + users[nom_utilisateur]["password_hash"] = hasher_mot_de_passe(nouveau_mdp) + save_users(users) + return True + return False \ No newline at end of file