Public Access
Test : Intégration bouton PayPal Sandbox et activation du Serveur Mail
Build and Push Docker Image / build-and-push (push) Successful in 1m45s
Build and Push Docker Image / build-and-push (push) Successful in 1m45s
This commit is contained in:
1 parent
632c23069a
commit
2ea18701b5
12 files changed
+486
-187
No files matched your search
+129
-30
@@ -1,10 +1,32 @@
|
||||
import os
|
||||
import requests
|
||||
from modules.paypal_checkout import render_paypal_recharge_widget
|
||||
from fasthtml.common import *
|
||||
from modules.database import load_history
|
||||
from modules.paypal import render_paypal_donation_widget
|
||||
from modules.billing_manager import obtenir_solde
|
||||
|
||||
def get_openrouter_stats():
|
||||
api_key = os.getenv("OPENROUTER_API_KEY")
|
||||
if not api_key:
|
||||
return {}
|
||||
try:
|
||||
resp = requests.get(
|
||||
"https://openrouter.ai/api/v1/auth/key",
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
timeout=3
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
return resp.json().get("data", {})
|
||||
except:
|
||||
pass
|
||||
return {}
|
||||
|
||||
def render_user_msg(text):
|
||||
return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble")
|
||||
return Div(
|
||||
P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"),
|
||||
cls="user-bubble"
|
||||
)
|
||||
|
||||
def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, execution_time=None):
|
||||
time_badge = Span(f"⏱️ {execution_time}s", style="margin-right: 10px; color:#aaa; font-size:0.8em;") if execution_time else ""
|
||||
@@ -16,6 +38,7 @@ def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, executi
|
||||
time_badge,
|
||||
style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 15px; display: flex; flex-wrap: wrap; gap: 5px;"
|
||||
)
|
||||
|
||||
actions = Div(
|
||||
Button("📄 PDF", onclick="downloadResponsePDF(this)", cls="msg-action-btn", title="Télécharger en PDF"),
|
||||
Button("📘 DOC", onclick="downloadResponseDOC(this)", cls="msg-action-btn", title="Télécharger en Word"),
|
||||
@@ -25,12 +48,14 @@ def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, executi
|
||||
Button("📧 Email", onclick="shareEmail(this)", cls="msg-action-btn", title="Envoyer par courriel"),
|
||||
cls="msg-actions"
|
||||
)
|
||||
|
||||
return Div(
|
||||
encart_info,
|
||||
Div(text, cls="markdown-raw", style="display:none;"),
|
||||
Div(cls="markdown-rendered", style="line-height: 1.6;"),
|
||||
actions,
|
||||
cls="ai-bubble", style=f"border-left: 4px solid {couleur};"
|
||||
cls="ai-bubble",
|
||||
style=f"border-left: 4px solid {couleur};"
|
||||
)
|
||||
|
||||
def render_sidebar():
|
||||
@@ -45,7 +70,8 @@ def render_sidebar():
|
||||
Button(pin_icon, cls="history-btn", hx_post=f"/pin/{chat_id}", hx_target="#sidebar-content", hx_swap="outerHTML"),
|
||||
Button("✏️", cls="history-btn", hx_post=f"/rename/{chat_id}", hx_prompt="Nouveau nom :", hx_target="#sidebar-content", hx_swap="outerHTML"),
|
||||
Button("🗑️", cls="history-btn", hx_post=f"/delete/{chat_id}", hx_target="#sidebar-content", hx_swap="outerHTML"),
|
||||
cls="history-item", id=f"chat-item-{chat_id}"
|
||||
cls="history-item",
|
||||
id=f"chat-item-{chat_id}"
|
||||
)
|
||||
)
|
||||
return Div(*items, id="sidebar-content", cls="sidebar-history", hx_swap_oob="true")
|
||||
@@ -53,15 +79,21 @@ def render_sidebar():
|
||||
def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
||||
history = load_history()
|
||||
messages_ui = []
|
||||
|
||||
if chat_id in history:
|
||||
for msg in history[chat_id].get('messages', []):
|
||||
if msg['role'] == 'user':
|
||||
messages_ui.append(render_user_msg(msg['content']))
|
||||
else:
|
||||
messages_ui.append(render_ai_msg(
|
||||
msg['content'], msg.get('intention', ''), msg.get('fournisseur', ''),
|
||||
msg.get('modele', ''), msg.get('couleur', '#333'), msg.get('execution_time', '')
|
||||
msg['content'],
|
||||
msg.get('intention', ''),
|
||||
msg.get('fournisseur', ''),
|
||||
msg.get('modele', ''),
|
||||
msg.get('couleur', '#333'),
|
||||
msg.get('execution_time', '')
|
||||
))
|
||||
|
||||
if messages_ui:
|
||||
messages_ui.append(Script("setTimeout(renderMarkdown, 150);"))
|
||||
|
||||
@@ -82,10 +114,22 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
||||
solde_perso = obtenir_solde(utilisateur_nom)
|
||||
|
||||
if role.lower() in ["super admin", "admin"]:
|
||||
stats_or = get_openrouter_stats()
|
||||
limit_rem = stats_or.get('limit_remaining')
|
||||
limit_rem = float(limit_rem) if limit_rem is not None else 0.0
|
||||
usage_daily = stats_or.get('usage_daily')
|
||||
usage_daily = float(usage_daily) if usage_daily is not None else 0.0
|
||||
usage_monthly = stats_or.get('usage_monthly')
|
||||
usage_monthly = float(usage_monthly) if usage_monthly is not None else 0.0
|
||||
|
||||
badge_finance = Div(
|
||||
P(f"👤 {utilisateur_nom.capitalize()} ({role})", style="color: #00e5ff; margin: 0; font-weight: bold; font-size: 0.9em;"),
|
||||
P(f"💳 Solde personnel : {solde_perso:.2f} $", style="color: #aaa; margin: 2px 0; font-size: 0.8em;"),
|
||||
P(f"🏦 Cagnotte globale : {obtenir_solde('xavier'):.2f} $", style="color: #4caf50; margin: 0; font-size: 0.8em; font-weight: bold;"),
|
||||
P(f"🌐 OR Restant : {limit_rem:.4f} $", style="color: #ff9800; margin: 2px 0 0; font-size: 0.8em; font-weight: bold;"),
|
||||
P(f"📊 OR Conso (Jour/Mois) : {usage_daily:.4f}$ / {usage_monthly:.4f}$", style="color: #8bc34a; margin: 0; font-size: 0.75em;"),
|
||||
# AJOUT DU BOUTON PAYPAL POUR TESTER L'ACHAT DE 10$
|
||||
render_paypal_recharge_widget(10.00),
|
||||
style="padding: 10px; background: #181818; border-radius: 6px; border: 1px solid #333;"
|
||||
)
|
||||
else:
|
||||
@@ -97,13 +141,15 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
||||
|
||||
return Title("Aethas38 - Core"), Body(
|
||||
auto_refresh_script,
|
||||
|
||||
# --- SIDEBAR FIXE (BUREAU) & MASQUABLE (MOBILE) ---
|
||||
Div(
|
||||
Div(
|
||||
Img(src="/Logo/logo_aethas.svg", style="max-width: 80%; height: auto; display: block; margin: 0 auto 20px;"),
|
||||
Div(
|
||||
A("+ Nouvelle discussion", href="/new_chat", cls="new-chat-btn"),
|
||||
A("💾 Exporter JSON", href="/export_json", target="_blank", cls="export-btn"),
|
||||
A("🚪 Déconnexion", href="/logout", cls="export-btn", style="background-color: #8b0000; color: white; border-color: #8b0000; margin-top: 5px;"), # NOUVEAU BOUTON ICI
|
||||
A("🚪 Déconnexion", href="/logout", cls="export-btn", style="background-color: #8b0000; color: white; border-color: #8b0000; margin-top: 5px;"),
|
||||
|
||||
Div(
|
||||
render_paypal_donation_widget(),
|
||||
@@ -119,38 +165,69 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
||||
Div(badge_finance, cls="sidebar-footer"),
|
||||
cls="sidebar"
|
||||
),
|
||||
|
||||
# --- CONTENU CENTRAL ---
|
||||
Div(
|
||||
# HEADER MOBILE (VISIBLE SEULEMENT SUR SMARTPHONE)
|
||||
Div(
|
||||
Button("☰", onclick="toggleSidebar()", cls="hamburger-btn"),
|
||||
Img(src="/Logo/logo_aethas.svg", style="height: 30px;"),
|
||||
Div(style="width: 30px;"), # Espaceur pour centrer le logo
|
||||
cls="mobile-header"
|
||||
),
|
||||
|
||||
Div(*messages_ui, id="chat-history", cls="chat-container"),
|
||||
Div(Div(Div(cls="tech-spinner"), Span("Analyse en cours... (~5s à 10s)"), cls="tech-loader-container"), id="global-loader", cls="htmx-indicator", style="max-width: 1000px; margin: 0 auto; width: 100%; padding: 0 20px; box-sizing: border-box; padding-bottom: 120px;"),
|
||||
|
||||
Div(
|
||||
Div(Div(cls="tech-spinner"), Span("Analyse en cours... (~5s à 10s)"), cls="tech-loader-container"),
|
||||
id="global-loader",
|
||||
cls="htmx-indicator"
|
||||
),
|
||||
|
||||
# --- LAYOUT ZONE DE SAISIE RESTRUCTURÉ ---
|
||||
Div(
|
||||
Form(
|
||||
Input(type="hidden", name="chat_id", value=chat_id),
|
||||
Div(
|
||||
Div(id="file-preview-list"),
|
||||
|
||||
# RANGÉE 1 : BOUTON +, TEXTAREA ET BOUTON ENVOI
|
||||
Div(
|
||||
Label("+", _for="file-upload", cls="file-btn"),
|
||||
Input(type="file", name="files", id="file-upload", multiple=True, style="display:none;", onchange="handleFileSelection(this)"),
|
||||
|
||||
Textarea(name="prompt", id="prompt-input", placeholder="Requête, lien ou glissez vos fichiers ici...", cls="input-field", rows="1", oninput="autoResize(this)"),
|
||||
Button("↑", type="submit", cls="btn-submit"),
|
||||
cls="textarea-wrapper"
|
||||
),
|
||||
|
||||
# RANGÉE 2 : LES OPTIONS (SOUS LA TEXTAREA)
|
||||
Div(
|
||||
Label(
|
||||
Input(type="checkbox", name="mode_education", id="mode-education-toggle", style="margin-right: 6px; accent-color: #8bc34a; cursor: pointer;"),
|
||||
"🎓 Éducation",
|
||||
style="color: #aaa; font-size: 0.85em; display: flex; align-items: center; cursor: pointer; white-space: nowrap;"
|
||||
),
|
||||
Label(
|
||||
Input(type="checkbox", name="mode_payant", id="mode-payant-toggle", style="margin-right: 6px; accent-color: #00e5ff; cursor: pointer;"),
|
||||
"👑 Payant",
|
||||
style="color: #aaa; font-size: 0.85em; display: flex; align-items: center; margin-right: 10px; cursor: pointer; white-space: nowrap;"
|
||||
style="color: #aaa; font-size: 0.85em; display: flex; align-items: center; cursor: pointer; white-space: nowrap;"
|
||||
),
|
||||
Label(
|
||||
Input(type="checkbox", name="mode_multi_ia", id="mode-multi-toggle", style="margin-right: 6px; accent-color: #d97757; cursor: pointer;"),
|
||||
"⚡ Multi-IA",
|
||||
style="color: #aaa; font-size: 0.85em; display: flex; align-items: center; margin-right: 15px; cursor: pointer; white-space: nowrap;"
|
||||
style="color: #aaa; font-size: 0.85em; display: flex; align-items: center; cursor: pointer; white-space: nowrap;"
|
||||
),
|
||||
|
||||
Textarea(name="prompt", id="prompt-input", placeholder="Requête, lien ou glissez vos fichiers ici...", cls="input-field", rows="1", oninput="autoResize(this)"),
|
||||
Button("↑", type="submit", cls="btn-submit"),
|
||||
cls="input-row"
|
||||
cls="options-row"
|
||||
),
|
||||
cls="input-container-box"
|
||||
),
|
||||
hx_post="/chat", hx_target="#chat-history", hx_swap="beforeend", hx_indicator="#global-loader",
|
||||
hx_post="/chat",
|
||||
hx_target="#chat-history",
|
||||
hx_swap="beforeend",
|
||||
hx_indicator="#global-loader",
|
||||
hx_on__after_request="this.reset(); document.getElementById('file-preview-list').innerHTML=''; document.getElementById('prompt-input').style.height='36px';",
|
||||
enctype="multipart/form-data", style="width: 100%; display: flex; justify-content: center;"
|
||||
enctype="multipart/form-data",
|
||||
style="width: 100%; display: flex; justify-content: center;"
|
||||
),
|
||||
cls="input-area"
|
||||
),
|
||||
@@ -169,20 +246,18 @@ def render_admin_moderation_panel(utilisateur_cible):
|
||||
Select(
|
||||
Option("Avertissement simple", value="Avertissement"),
|
||||
Option("Bannissement 1 jour", value="Ban 1J"),
|
||||
Option("Bannissement 1 semaine", value="Ban 1Sem"),
|
||||
Option("Bannissement 1 mois", value="Ban 1M"),
|
||||
Option("Bannissement définitif et signalement PHAROS", value="Ban Definitif + Pharos"),
|
||||
Option("Faux positif (Levée de sanction)", value="Annulation"),
|
||||
name="decision", cls="input-field"
|
||||
name="decision",
|
||||
cls="input-field"
|
||||
),
|
||||
style="margin-bottom: 15px;"
|
||||
),
|
||||
Div(
|
||||
Label("Commentaire libre de l'administrateur (Preuve d'audit) :", style="font-weight: bold;"),
|
||||
Textarea(name="commentaire", rows="4", cls="input-field", placeholder="Raison de la décision, contexte juridique..."),
|
||||
Label("Commentaire (Preuve d'audit) :", style="font-weight: bold;"),
|
||||
Textarea(name="commentaire", rows="4", cls="input-field", placeholder="Raison..."),
|
||||
style="margin-bottom: 15px;"
|
||||
),
|
||||
Button("Valider la décision et archiver", type="submit", cls="btn-submit", style="background-color: #ff4444; color: white;"),
|
||||
Button("Valider la décision et archiver", type="submit", cls="btn-submit", style="background-color: #ff4444; color: white; width: 100%;"),
|
||||
hx_post="/admin_decision",
|
||||
hx_target="#admin-result",
|
||||
style="background: #1a1a1a; padding: 20px; border-radius: 8px; border: 1px solid #333;"
|
||||
@@ -217,22 +292,46 @@ def render_login_page(error=None):
|
||||
style="background-color: #0d0d0d; color: #fff; font-family: monospace; height: 100vh; display: flex; align-items: center; justify-content: center;"
|
||||
)
|
||||
|
||||
def render_popup_paiement(cout_estime, prompt_sauvegarde, chat_id, multi_ia_state=False):
|
||||
def render_popup_paiement(cout_estime, prompt_sauvegarde, chat_id, multi_ia_state=False, edu_state=False):
|
||||
multi_ia_val = "on" if multi_ia_state else "off"
|
||||
edu_val = "on" if edu_state else "off"
|
||||
return Div(
|
||||
Div(
|
||||
H3("⚠️ Validation Financière Requise", style="color: #ff9800; margin-top: 0;"),
|
||||
P("Cette requête nécessite l'utilisation d'un modèle d'IA payant.", style="color: #ccc;"),
|
||||
P(f"Coût estimé (TTC) : {cout_estime} $", style="font-size: 1.2em; font-weight: bold; color: #fff; background: #333; padding: 10px; border-radius: 5px; text-align: center;"),
|
||||
P(f"Coût estimé API (TTC) : {cout_estime} $", style="font-size: 1.2em; font-weight: bold; color: #fff; background: #333; padding: 10px; border-radius: 5px; text-align: center;"),
|
||||
|
||||
Div(
|
||||
Button("Annuler la requête", type="button", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #555; width: 30%; font-size: 0.8em;"),
|
||||
Button("Annuler la requête", type="button", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #555; width: 30%; font-size: 0.8em; border-radius: 8px;"),
|
||||
|
||||
Form(Input(type="hidden", name="prompt", value=prompt_sauvegarde), Input(type="hidden", name="chat_id", value=chat_id), Input(type="hidden", name="mode_payant", value="off"), Input(type="hidden", name="mode_multi_ia", value=multi_ia_val), Input(type="hidden", name="validation_acquise", value="true"), Input(type="hidden", name="choix_utilisateur", value="gratuit_force"), Button("Tenter en Gratuit ⚠️", type="submit", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #00e5ff; width: 100%; color: black; font-size: 0.8em;"), hx_post="/chat", hx_target="#chat-history", hx_swap="beforeend", hx_indicator="#global-loader", style="width: 30%;"),
|
||||
Form(
|
||||
Input(type="hidden", name="prompt", value=prompt_sauvegarde),
|
||||
Input(type="hidden", name="chat_id", value=chat_id),
|
||||
Input(type="hidden", name="mode_payant", value="off"),
|
||||
Input(type="hidden", name="mode_multi_ia", value=multi_ia_val),
|
||||
Input(type="hidden", name="mode_education", value=edu_val),
|
||||
Input(type="hidden", name="validation_acquise", value="true"),
|
||||
Input(type="hidden", name="choix_utilisateur", value="gratuit_force"),
|
||||
Button("Tenter en Gratuit ⚠️", type="submit", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #00e5ff; width: 100%; color: black; font-size: 0.8em; border-radius: 8px;"),
|
||||
hx_post="/chat", hx_target="#chat-history", hx_swap="beforeend", hx_indicator="#global-loader", style="width: 30%;"
|
||||
),
|
||||
|
||||
Form(Input(type="hidden", name="prompt", value=prompt_sauvegarde), Input(type="hidden", name="chat_id", value=chat_id), Input(type="hidden", name="mode_payant", value="on"), Input(type="hidden", name="mode_multi_ia", value=multi_ia_val), Input(type="hidden", name="validation_acquise", value="true"), Input(type="hidden", name="choix_utilisateur", value="paiement_accepte"), Button("Payer l'API", type="submit", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #4caf50; width: 100%; font-size: 0.8em;"), hx_post="/chat", hx_target="#chat-history", hx_swap="beforeend", hx_indicator="#global-loader", style="width: 30%;"),
|
||||
Form(
|
||||
Input(type="hidden", name="prompt", value=prompt_sauvegarde),
|
||||
Input(type="hidden", name="chat_id", value=chat_id),
|
||||
Input(type="hidden", name="mode_payant", value="on"),
|
||||
Input(type="hidden", name="mode_multi_ia", value=multi_ia_val),
|
||||
Input(type="hidden", name="mode_education", value=edu_val),
|
||||
Input(type="hidden", name="validation_acquise", value="true"),
|
||||
Input(type="hidden", name="choix_utilisateur", value="paiement_accepte"),
|
||||
Button("Payer l'API", type="submit", onclick="document.getElementById('popup-paiement').remove();", cls="btn-submit", style="background-color: #4caf50; width: 100%; font-size: 0.8em; border-radius: 8px;"),
|
||||
hx_post="/chat", hx_target="#chat-history", hx_swap="beforeend", hx_indicator="#global-loader", style="width: 30%;"
|
||||
),
|
||||
|
||||
style="display: flex; justify-content: space-between; margin-top: 20px; gap: 10px;"
|
||||
), style="background: #1a1a1a; padding: 25px; border-radius: 12px; border: 2px solid #ff9800; width: 500px; box-shadow: 0 10px 30px rgba(0,0,0,0.8);"
|
||||
), id="popup-paiement", style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; z-index: 9999;"
|
||||
),
|
||||
style="background: #1a1a1a; padding: 25px; border-radius: 12px; border: 2px solid #ff9800; width: 500px; max-width: 90vw; box-shadow: 0 10px 30px rgba(0,0,0,0.8);"
|
||||
),
|
||||
id="popup-paiement",
|
||||
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; z-index: 9999;"
|
||||
)
|
||||
Reference in new issue
Block a user