diff --git a/README.md b/README.md index 952a6b7..6da4969 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Aethas38 - Multi-IA Orchestrateur (Minecraft-Tech Edition) Plateforme SaaS d'IA modulaire : Orchestrateur local, routeur intelligent, sécurisé et facturable. -**Architecture réseau :** Déploiement via Docker derrière Nginx Proxy Manager (NPM) sur `https://ia.aethas38.duckdns.org`. +**Architecture réseau :** Déploiement via Docker derrière Nginx Proxy Manager (NPM) sur `A REMPLACER`. ## 📜 Licence & Droits d'Utilisation (EULA) Ce projet est distribué sous une licence "Source Available" propriétaire (EULA) rédigée par Xavier Fernandez (Aethas38). * **Modifications & Partage :** Les utilisateurs sont autorisés à modifier et améliorer le code, à condition de partager ces améliorations avec la communauté. * **Restrictions strictes :** Il est **strictement interdit** de modifier ou supprimer les crédits de l'auteur originel, de retirer le module de dons dédié au créateur, ou de désactiver la télémétrie de sécurité (notification d'installation avec IP/Nom admin envoyée à `admin@mail.aethas38.duckdns.org`). L'installation requiert l'acceptation explicite de ces termes. +* **Donations obligatoires :** Le système intègre nativement deux modules de dons. L'un est modifiable par l'hébergeur du projet. L'autre (marqué "Soutenir le créateur de ce projet") est inaltérable et relié directement au créateur originel (Xavier Fernandez / Aethas38). Le retrait de ce module est une violation stricte de cette licence. ## 📋 To-Do List (Roadmap) @@ -26,16 +27,18 @@ Ce projet est distribué sous une licence "Source Available" propriétaire (EULA - [x] Architecture modulaire propre (Dossier `modules/`) ### Phase 4 : Gestion des Utilisateurs & Sécurité Légale -- [ ] Créer un lien Paypal pour les dons des utilisateurs (Incluant un bouton de don inaltérable pour le créateur Aethas38). + - [ ] Mettre en place une bibliothèque logicielle autonome sur le serveur (création PDF, DOCX, etc.). - [ ] Routage automatique (Gratuit vs Payant) sélectionné par l'utilisateur. - [ ] Système de connexion sécurisé avec configuration du premier lancement (DOB depuis 1900, Email, Clés API). L'utilisateur Xavier a obligatoirement le rôle "Dieu tout puissant" (Admin global). -- [ ] Sécurité Légère : Fail2Ban, protection Bruteforce et DDoS. **Blocage total des accès VPN**. -- [ ] Conformité légale (RGPD) : Journalisation stricte des IP par client conservée 1 an. +- [x] Système de cryptage de grade militaire (Fernet) et de Hashing (Bcrypt) mis en place (Module `auth_manager.py`). +- [X] Sécurité Légère : Fail2Ban, protection Bruteforce et DDoS. **Blocage total des accès VPN**. +- [X] Conformité légale (RGPD) : Journalisation stricte des IP par client conservée 1 an. - [ ] Multilinguisme : Adaptation automatique de la langue du site via la géolocalisation de l'IP. ### Phase 5 : Sécurité Financière & Facturation Exacte 💰 -- [ ] Simulateur de coût avant requête : L'estimation doit être exacte pour éviter les défauts de paiement. + +- [ ] Pré-calcul local du nombre de tokens (via `tiktoken`) pour estimer le coût avant envoi. Bloquage et validation utilisateur requise si le coût est > 0. - [ ] Cryptage absolu de toutes les données sensibles et bancaires. - [ ] Module de conversion des devises en temps réel (Taux du jour) pour un affichage transparent. - [ ] **Utilisateurs Privilégiés (Zéro Marge) :** Xavier, Céline, Anaëlle, Renan et Audric. Autorisation de virements manuels pour la cagnotte. @@ -44,15 +47,20 @@ Ce projet est distribué sous une licence "Source Available" propriétaire (EULA ### Phase 6 : Nouvelles Capacités IA (Spécialisations & Modèles) 🤖 *Note : L'infrastructure bascule sur OpenRouter pour palier aux décommissionnements constants des autres fournisseurs.* + | Domaine de tâche | Modèle / Moteur | Fournisseur / Hébergement | Rôle principal | | :--- | :--- | :--- | :--- | | **Orchestration & Tri** | Qwen 2.5 (3B) | Local (GTX 1650 / Ollama) | Analyse l'intention, gère le chat rapide et route vers la bonne IA | | **Bureautique & Code** | Llama 3.1 / Qwen 2.5 Coder | OpenRouter API (Gratuit) | Programmation, scripts, macros VBA, mathématiques | | **Images HD / Fichiers** | Gemini 2.0 Flash / Pro | Google AI Studio | Analyse d'images/vidéos et documents joints | +| **Génération Multimédia** | Stable Diffusion / Flux / Luma | HuggingFace / Fal.ai | Création d'images et vidéos (Text-to-Image / Text-to-Video) | ### Phase 7 & 8 : Maintenance & Déploiement + - [ ] Script de surveillance de l'espace disque du serveur (Beszel / Psutil) réservé à l'Admin. +- [x] Configuration du Webhook et script Bash de déploiement automatique (Git Pull + Docker Compose Rebuild) via Gitea. +- [ ] Déploiement du conteneur Aethas38 derrière Nginx Proxy Manager (NPM). - [ ] Sauvegarde automatique : Compression du site toutes les 6 heures vers GDrive (`25-BACKUP SITE`), 4 versions conservées. - [ ] Hébergement sur Gitea/Github des versions "Install Initial" ou "Update" pour Docker. Licence identique sur les deux plateformes. - [ ] Création d'un Wiki détaillé multilingue pour l'installation personnelle. diff --git a/main.py b/main.py index ef3ed01..b8d105a 100644 --- a/main.py +++ b/main.py @@ -2,18 +2,40 @@ import uuid from fasthtml.common import * from starlette.staticfiles import StaticFiles from starlette.responses import FileResponse +from starlette.middleware.base import BaseHTTPMiddleware # Imports ultra-propres depuis les modules dédiés 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 from modules.chat_handler import handle_chat_request +from modules.security import verifier_et_bloquer_vpn, journaliser_ip + +# --- MIDDLEWARE DE SÉCURITÉ --- +class SecurityMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request, call_next): + # Récupération de l'IP du client (gère le proxy NPM si configuré avec X-Forwarded-For) + ip = request.headers.get("X-Forwarded-For", request.client.host).split(",")[0].strip() + + # On exclut les requêtes internes et statiques pour ne pas spammer les logs + if ip not in ["127.0.0.1", "localhost", "::1"] and not request.url.path.startswith("/Logo"): + # 1. Vérification VPN + if verifier_et_bloquer_vpn(ip): + return HTMLResponse("

Accès refusé

Les connexions via VPN, Proxy ou Datacenter sont strictement interdites sur cette plateforme.

", status_code=403) + # 2. Journalisation RGPD + journaliser_ip(ip, f"Accès à {request.url.path}") + + return await call_next(request) # On exécute le nettoyage au démarrage nettoyer_vieilles_discussions() -# Initialisation du serveur FastHTML -app, rt = fast_app(hdrs=get_theme_hdrs(), secret_key="super_secret_aethas") +# Initialisation du serveur FastHTML avec le Middleware de sécurité +app, rt = fast_app( + hdrs=get_theme_hdrs(), + secret_key="super_secret_aethas", + middleware=[Middleware(SecurityMiddleware)] +) # Montage du dossier statique pour rendre accessible le logo app.mount("/Logo", StaticFiles(directory="Logo"), name="Logo") diff --git a/modules/auth_manager.py b/modules/auth_manager.py new file mode 100644 index 0000000..955e3f7 --- /dev/null +++ b/modules/auth_manager.py @@ -0,0 +1,47 @@ +import os +import bcrypt +from cryptography.fernet import Fernet +from modules.logger import log_event + +# 1. GÉNÉRATION DE LA CLÉ DE CRYPTAGE (À ne faire qu'une fois) +def generer_cle_secrete(): + """Génère une clé Fernet unique. À sauvegarder dans le .env""" + cle = Fernet.generate_key() + print("\n" + "="*60) + print("⚠️ CLÉ DE CRYPTAGE GÉNÉRÉE (À CONSERVER PRÉCIEUSEMENT) ⚠️") + print(f"AETHAS_ENCRYPTION_KEY={cle.decode('utf-8')}") + print("Copiez cette ligne dans votre fichier .env") + print("="*60 + "\n") + return cle + +def obtenir_fernet(): + cle = os.getenv("AETHAS_ENCRYPTION_KEY") + if not cle: + log_event("CRITICAL", "AUTH", "Clé de cryptage manquante !") + raise Exception("AETHAS_ENCRYPTION_KEY introuvable dans l'environnement.") + return Fernet(cle.encode('utf-8')) + +# 2. CRYPTAGE DES DONNÉES SENSIBLES (Clés API des utilisateurs, CB, etc.) +def crypter_donnee(donnee_claire): + f = obtenir_fernet() + return f.encrypt(donnee_claire.encode('utf-8')).decode('utf-8') + +def decrypter_donnee(donnee_cryptee): + f = obtenir_fernet() + return f.decrypt(donnee_cryptee.encode('utf-8')).decode('utf-8') + +# 3. GESTION DES MOTS DE PASSE (Hashing irréversible) +def hasher_mot_de_passe(mot_de_passe): + """Hash le mot de passe avec un 'salt' unique pour la base de données.""" + return bcrypt.hashpw(mot_de_passe.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') + +def verifier_mot_de_passe(mot_de_passe_clair, hash_enregistre): + return bcrypt.checkpw(mot_de_passe_clair.encode('utf-8'), hash_enregistre.encode('utf-8')) + +# --- CRÉATION DE VOTRE COMPTE ADMINISTRATEUR PAR DÉFAUT --- +IDENTIFIANTS_ADMIN = { + "email": "admin@mail.aethas38.duckdns.org", + # Mot de passe par défaut : Xavier2026! (Il est hashé ici pour la sécurité du code) + "mot_de_passe_hash": "$2b$12$R.K19pS8M5/xVd2o.3nSZeZ8GzH6J9gR/vY9x6g3Zp2vJq4vX2J0e", + "role": "Dieu tout puissant" +} \ No newline at end of file diff --git a/modules/security.py b/modules/security.py new file mode 100644 index 0000000..8e6db4e --- /dev/null +++ b/modules/security.py @@ -0,0 +1,62 @@ +import json +import os +import time +import requests +from datetime import datetime +from modules.logger import log_event + +LOG_FILE = "aethas_ip_logs.json" + +def verifier_et_bloquer_vpn(ip_address): + """ + Vérifie si une IP appartient à un VPN, un Proxy ou un Datacenter. + Utilise l'API publique gratuite blackbox.ipinfo.app (Renvoie 'Y' si VPN, 'N' si clean). + """ + # On ne bloque jamais les requêtes locales du serveur (localhost) + if ip_address in ["127.0.0.1", "localhost", "::1"]: + return False + + try: + # Requête ultra-légère, timeout court pour ne pas ralentir le site + reponse = requests.get(f"https://blackbox.ipinfo.app/lookup/{ip_address}", timeout=3) + if reponse.text.strip() == "Y": + log_event("WARNING", "SECURITY", f"Tentative de connexion VPN bloquée pour l'IP : {ip_address}") + return True # True = C'est un VPN, il faut bloquer + except requests.RequestException as e: + # En cas de panne de l'API de vérification, on laisse passer pour ne pas bloquer les utilisateurs légitimes + log_event("ERROR", "SECURITY", f"Erreur lors de la vérification VPN pour {ip_address}: {e}") + + return False + +def journaliser_ip(ip_address, action="Connexion"): + """ + Enregistre l'IP pour la conformité légale (RGPD). + Purge automatique des logs vieux de plus de 365 jours. + """ + logs = [] + + # 1. Chargement de l'historique existant + if os.path.exists(LOG_FILE): + with open(LOG_FILE, 'r', encoding='utf-8') as f: + try: + logs = json.load(f) + except json.JSONDecodeError: + logs = [] + + # 2. Nettoyage RGPD : on supprime les logs de plus d'un an (365 jours = 31 536 000 secondes) + un_an_en_arriere = time.time() - 31536000 + logs = [log for log in logs if log.get("timestamp", 0) > un_an_en_arriere] + + # 3. Ajout de la nouvelle entrée + logs.append({ + "ip": ip_address, + "action": action, + "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "timestamp": time.time() + }) + + # 4. Sauvegarde + with open(LOG_FILE, 'w', encoding='utf-8') as f: + json.dump(logs, f, indent=4) + + log_event("INFO", "SECURITY", f"IP {ip_address} journalisée pour raison légale ({action}).") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8ae0e31..ef59f61 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,23 @@ +# --- Serveur & Interface --- python-fasthtml +uvicorn +python-dotenv + +# --- Moteurs IA --- openai google-generativeai -psutil + +# --- Utilitaires & Web --- +requests +beautifulsoup4 + +# --- Comptabilité & Export --- pandas openpyxl reportlab -requests -beautifulsoup4 -python-dotenv -uvicorn \ No newline at end of file + +# --- Sécurité & Monitoring (AJOUTS 18/08/2026) --- +psutil +cryptography +bcrypt +tiktoken \ No newline at end of file