Public Access
feat: Préparation au déploiement Docker, sécurisation du README et ajout du module de facturation
This commit is contained in:
1 parent
317ecddc56
commit
37ee5c6234
5 files changed
+163
-12
No files matched your search
@@ -1,12 +1,13 @@
|
|||||||
# Aethas38 - Multi-IA Orchestrateur (Minecraft-Tech Edition)
|
# Aethas38 - Multi-IA Orchestrateur (Minecraft-Tech Edition)
|
||||||
|
|
||||||
Plateforme SaaS d'IA modulaire : Orchestrateur local, routeur intelligent, sécurisé et facturable.
|
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)
|
## 📜 Licence & Droits d'Utilisation (EULA)
|
||||||
Ce projet est distribué sous une licence "Source Available" propriétaire (EULA) rédigée par Xavier Fernandez (Aethas38).
|
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é.
|
* **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.
|
* **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)
|
## 📋 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/`)
|
- [x] Architecture modulaire propre (Dossier `modules/`)
|
||||||
|
|
||||||
### Phase 4 : Gestion des Utilisateurs & Sécurité Légale
|
### 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).
|
<!-- MAJ 18/08/2026 14h30 : Mise à jour de l'état d'avancement des modules de cryptage et sécurité -->
|
||||||
- [ ] Mettre en place une bibliothèque logicielle autonome sur le serveur (création PDF, DOCX, etc.).
|
- [ ] 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.
|
- [ ] 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).
|
- [ ] 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**.
|
- [x] Système de cryptage de grade militaire (Fernet) et de Hashing (Bcrypt) mis en place (Module `auth_manager.py`). <!-- NOUVEAU -->
|
||||||
- [ ] Conformité légale (RGPD) : Journalisation stricte des IP par client conservée 1 an.
|
- [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.
|
- [ ] Multilinguisme : Adaptation automatique de la langue du site via la géolocalisation de l'IP.
|
||||||
|
|
||||||
### Phase 5 : Sécurité Financière & Facturation Exacte 💰
|
### 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.
|
<!-- MAJ 18/08/2026 14h30 : Ajout de la contrainte de pré-calcul local des tokens (tiktoken) et validation UI -->
|
||||||
|
- [ ] 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. <!-- NOUVEAU -->
|
||||||
- [ ] Cryptage absolu de toutes les données sensibles et bancaires.
|
- [ ] 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.
|
- [ ] 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.
|
- [ ] **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) 🤖
|
### 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.*
|
*Note : L'infrastructure bascule sur OpenRouter pour palier aux décommissionnements constants des autres fournisseurs.*
|
||||||
|
<!-- MAJ 18/08/2026 14h30 : Ajout de la ligne pour la gestion Multimédia (Image/Video) -->
|
||||||
|
|
||||||
| Domaine de tâche | Modèle / Moteur | Fournisseur / Hébergement | Rôle principal |
|
| 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 |
|
| **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 |
|
| **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 |
|
| **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) | <!-- NOUVEAU -->
|
||||||
|
|
||||||
### Phase 7 & 8 : Maintenance & Déploiement
|
### Phase 7 & 8 : Maintenance & Déploiement
|
||||||
|
<!-- MAJ 18/08/2026 14h30 : Précision sur les Webhooks Gitea et le déploiement NPM -->
|
||||||
- [ ] Script de surveillance de l'espace disque du serveur (Beszel / Psutil) réservé à l'Admin.
|
- [ ] 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. <!-- NOUVEAU -->
|
||||||
|
- [ ] Déploiement du conteneur Aethas38 derrière Nginx Proxy Manager (NPM). <!-- NOUVEAU -->
|
||||||
- [ ] Sauvegarde automatique : Compression du site toutes les 6 heures vers GDrive (`25-BACKUP SITE`), 4 versions conservées.
|
- [ ] 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.
|
- [ ] 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.
|
- [ ] Création d'un Wiki détaillé multilingue pour l'installation personnelle.
|
||||||
|
|||||||
@@ -2,18 +2,40 @@ import uuid
|
|||||||
from fasthtml.common import *
|
from fasthtml.common import *
|
||||||
from starlette.staticfiles import StaticFiles
|
from starlette.staticfiles import StaticFiles
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
# Imports ultra-propres depuis les modules dédiés
|
# Imports ultra-propres depuis les modules dédiés
|
||||||
from modules.theme import get_theme_hdrs
|
from modules.theme import get_theme_hdrs
|
||||||
from modules.database import nettoyer_vieilles_discussions, HISTORY_FILE, load_history, save_history
|
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.ui_components import render_main_page, render_sidebar
|
||||||
from modules.chat_handler import handle_chat_request
|
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("<h1 style='color:red; text-align:center;'>Accès refusé</h1><p style='text-align:center;'>Les connexions via VPN, Proxy ou Datacenter sont strictement interdites sur cette plateforme.</p>", 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
|
# On exécute le nettoyage au démarrage
|
||||||
nettoyer_vieilles_discussions()
|
nettoyer_vieilles_discussions()
|
||||||
|
|
||||||
# Initialisation du serveur FastHTML
|
# Initialisation du serveur FastHTML avec le Middleware de sécurité
|
||||||
app, rt = fast_app(hdrs=get_theme_hdrs(), secret_key="super_secret_aethas")
|
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
|
# Montage du dossier statique pour rendre accessible le logo
|
||||||
app.mount("/Logo", StaticFiles(directory="Logo"), name="Logo")
|
app.mount("/Logo", StaticFiles(directory="Logo"), name="Logo")
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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}).")
|
||||||
+17
-5
@@ -1,11 +1,23 @@
|
|||||||
|
# --- Serveur & Interface ---
|
||||||
python-fasthtml
|
python-fasthtml
|
||||||
|
uvicorn
|
||||||
|
python-dotenv
|
||||||
|
|
||||||
|
# --- Moteurs IA ---
|
||||||
openai
|
openai
|
||||||
google-generativeai
|
google-generativeai
|
||||||
psutil
|
|
||||||
|
# --- Utilitaires & Web ---
|
||||||
|
requests
|
||||||
|
beautifulsoup4
|
||||||
|
|
||||||
|
# --- Comptabilité & Export ---
|
||||||
pandas
|
pandas
|
||||||
openpyxl
|
openpyxl
|
||||||
reportlab
|
reportlab
|
||||||
requests
|
|
||||||
beautifulsoup4
|
# --- Sécurité & Monitoring (AJOUTS 18/08/2026) ---
|
||||||
python-dotenv
|
psutil
|
||||||
uvicorn
|
cryptography
|
||||||
|
bcrypt
|
||||||
|
tiktoken
|
||||||
Reference in new issue
Block a user