fix: configuration de StaticFiles sur FastAPI et mise à jour des liens du logo en local
Build and Push Docker Image / build-and-push (push) Successful in 39s

This commit is contained in:
xavier committed 2026-10-07 02:04:48 +02:00
1 parent 554f00f2b1
commit 02ba8bfff3
3 files changed
+10 -29

No files matched your search

+6 -1
View File
@@ -1,5 +1,6 @@
from fastapi import FastAPI, Depends, HTTPException, status, Request, Response from fastapi import FastAPI, Depends, HTTPException, status, Request, Response
from fastapi.responses import RedirectResponse, FileResponse from fastapi.responses import RedirectResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
import os import os
@@ -14,6 +15,11 @@ Base.metadata.create_all(bind=engine)
app = FastAPI(title="AETHAS38 - Orchestrateur Multi-IA") app = FastAPI(title="AETHAS38 - Orchestrateur Multi-IA")
# Configuration des fichiers statiques (images, css locaux, etc.)
assets_path = os.path.join(os.getcwd(), "frontend", "assets")
os.makedirs(assets_path, exist_ok=True) # Crée le dossier s'il n'existe pas
app.mount("/static", StaticFiles(directory=assets_path), name="static")
def is_setup_required(db: Session) -> bool: def is_setup_required(db: Session) -> bool:
"""Vérifie si la base de données contient au moins un administrateur.""" """Vérifie si la base de données contient au moins un administrateur."""
admin = db.query(User).filter(User.is_admin == True).first() admin = db.query(User).filter(User.is_admin == True).first()
@@ -31,7 +37,6 @@ def setup_page(db: Session = Depends(get_db)):
if not is_setup_required(db): if not is_setup_required(db):
return RedirectResponse(url="/login") return RedirectResponse(url="/login")
# Chemin vers le fichier HTML depuis la racine de l'application Docker (/app)
frontend_path = os.path.join(os.getcwd(), "frontend", "index.html") frontend_path = os.path.join(os.getcwd(), "frontend", "index.html")
if not os.path.exists(frontend_path): if not os.path.exists(frontend_path):
raise HTTPException(status_code=404, detail="Interface introuvable.") raise HTTPException(status_code=404, detail="Interface introuvable.")
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+4 -28
View File
@@ -35,19 +35,13 @@
<div v-if="isSidebarOpen" @click="toggleSidebar" class="md:hidden fixed inset-0 bg-black/60 z-40 transition-opacity"></div> <div v-if="isSidebarOpen" @click="toggleSidebar" class="md:hidden fixed inset-0 bg-black/60 z-40 transition-opacity"></div>
<!-- 1. BARRE LATÉRALE (Rétractable) --> <!-- 1. BARRE LATÉRALE (Rétractable) -->
<!-- Conteneur qui gère l'espace (w-72 ou w-0) pour pousser le contenu principal sans l'écraser -->
<div :class="['transition-all duration-300 flex-shrink-0 relative z-50', isSidebarOpen ? 'w-72' : 'w-0']"> <div :class="['transition-all duration-300 flex-shrink-0 relative z-50', isSidebarOpen ? 'w-72' : 'w-0']">
<!-- Contenu réel de la sidebar, taille fixe à 72 -->
<aside :class="['absolute top-0 left-0 h-full w-72 bg-aethas-panel border-r border-gray-700 flex flex-col transition-transform duration-300', isSidebarOpen ? 'translate-x-0' : '-translate-x-full']"> <aside :class="['absolute top-0 left-0 h-full w-72 bg-aethas-panel border-r border-gray-700 flex flex-col transition-transform duration-300', isSidebarOpen ? 'translate-x-0' : '-translate-x-full']">
<!-- En-tête Sidebar avec Logo -->
<div class="p-4 border-b border-gray-700 flex items-center justify-between"> <div class="p-4 border-b border-gray-700 flex items-center justify-between">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<!-- LOGO LATÉRAL : Remplacez l'URL par le lien direct de votre Google Drive --> <img src="/static/logo.png" alt="Logo" class="w-10 h-10 rounded-full border border-aethas-cyan object-cover bg-gray-800 flex-shrink-0">
<img src="https://drive.google.com/file/d/1RqkxQzLKzrS4m2vGedt1sQEPRDed8FnP/view?usp=drive_link" alt="Logo" class="w-10 h-10 rounded-full border border-aethas-cyan object-cover bg-gray-800 flex-shrink-0">
<span class="font-bold text-lg tracking-wider neon-text">AETHAS38</span> <span class="font-bold text-lg tracking-wider neon-text">AETHAS38</span>
</div> </div>
<!-- Bouton fermer (surtout utile sur mobile) -->
<button @click="toggleSidebar" class="md:hidden text-gray-400 hover:text-white"> <button @click="toggleSidebar" class="md:hidden text-gray-400 hover:text-white">
<i data-lucide="x" class="w-5 h-5"></i> <i data-lucide="x" class="w-5 h-5"></i>
</button> </button>
@@ -89,40 +83,29 @@
<!-- ZONE PRINCIPALE --> <!-- ZONE PRINCIPALE -->
<main class="flex-1 flex flex-col min-w-0 z-10 transition-all duration-300"> <main class="flex-1 flex flex-col min-w-0 z-10 transition-all duration-300">
<!-- Header (Affichage du menu et du logo quand la barre est réduite) -->
<header class="flex items-center justify-between p-4 border-b border-gray-700 bg-aethas-panel h-[73px]"> <header class="flex items-center justify-between p-4 border-b border-gray-700 bg-aethas-panel h-[73px]">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<!-- Bouton Menu (Burger) -->
<button @click="toggleSidebar" class="text-gray-400 hover:text-white transition-colors p-1.5 rounded hover:bg-gray-700" title="Basculer le menu"> <button @click="toggleSidebar" class="text-gray-400 hover:text-white transition-colors p-1.5 rounded hover:bg-gray-700" title="Basculer le menu">
<i data-lucide="menu" class="w-6 h-6"></i> <i data-lucide="menu" class="w-6 h-6"></i>
</button> </button>
<!-- Logo du Header (Visible uniquement quand la sidebar est cachée) -->
<div v-show="!isSidebarOpen" class="flex items-center gap-2 transition-opacity duration-300"> <div v-show="!isSidebarOpen" class="flex items-center gap-2 transition-opacity duration-300">
<!-- LOGO HEADER : Remplacez l'URL ici aussi --> <img src="/static/logo.png" alt="Logo" class="w-8 h-8 rounded-full border border-aethas-cyan object-cover bg-gray-800 flex-shrink-0">
<img src="https://drive.google.com/file/d/1RqkxQzLKzrS4m2vGedt1sQEPRDed8FnP/view?usp=drive_link" alt="Logo" class="w-8 h-8 rounded-full border border-aethas-cyan object-cover bg-gray-800 flex-shrink-0">
<span class="font-bold text-lg neon-text">AETHAS38</span> <span class="font-bold text-lg neon-text">AETHAS38</span>
</div> </div>
</div> </div>
</header> </header>
<!-- Zone de conversation Centrale -->
<div class="flex-1 overflow-y-auto p-4 md:p-8 flex flex-col items-center justify-center"> <div class="flex-1 overflow-y-auto p-4 md:p-8 flex flex-col items-center justify-center">
<!-- LOGO CENTRAL : Et ici --> <img src="/static/logo.png" alt="AETHAS38 Logo" class="w-24 h-24 mb-6 rounded-full border-2 border-aethas-cyan shadow-[0_0_15px_rgba(6,182,212,0.5)] object-cover bg-gray-800 flex-shrink-0">
<img src="https://drive.google.com/file/d/1RqkxQzLKzrS4m2vGedt1sQEPRDed8FnP/view?usp=drive_link" alt="AETHAS38 Logo" class="w-24 h-24 mb-6 rounded-full border-2 border-aethas-cyan shadow-[0_0_15px_rgba(6,182,212,0.5)] object-cover bg-gray-800 flex-shrink-0">
<h2 class="text-2xl font-bold text-gray-200 mb-2 text-center">Comment puis-je vous aider aujourd'hui ?</h2> <h2 class="text-2xl font-bold text-gray-200 mb-2 text-center">Comment puis-je vous aider aujourd'hui ?</h2>
<p class="text-gray-500 text-sm text-center">L'orchestrateur Multi-IA est prêt.</p> <p class="text-gray-500 text-sm text-center">L'orchestrateur Multi-IA est prêt.</p>
</div> </div>
<!-- 2. CHAMP DE TEXTE (Auto-extensible avec ascenseur) -->
<div class="p-4 md:p-6 w-full max-w-4xl mx-auto"> <div class="p-4 md:p-6 w-full max-w-4xl mx-auto">
<div class="relative flex items-end bg-gray-800 border border-gray-600 rounded-2xl p-2 shadow-lg focus-within:border-aethas-cyan focus-within:ring-1 focus-within:ring-aethas-cyan transition-all"> <div class="relative flex items-end bg-gray-800 border border-gray-600 rounded-2xl p-2 shadow-lg focus-within:border-aethas-cyan focus-within:ring-1 focus-within:ring-aethas-cyan transition-all">
<button class="p-3 text-gray-400 hover:text-aethas-cyan transition-colors flex-shrink-0" title="Joindre un fichier"> <button class="p-3 text-gray-400 hover:text-aethas-cyan transition-colors flex-shrink-0" title="Joindre un fichier">
<i data-lucide="paperclip" class="w-5 h-5"></i> <i data-lucide="paperclip" class="w-5 h-5"></i>
</button> </button>
<!-- Textarea connecté à Vue.js pour la hauteur dynamique -->
<textarea <textarea
v-model="prompt" v-model="prompt"
@input="autoResize" @input="autoResize"
@@ -130,7 +113,6 @@
rows="1" rows="1"
class="w-full bg-transparent text-gray-200 p-3 max-h-[200px] overflow-y-auto focus:outline-none resize-none leading-relaxed" class="w-full bg-transparent text-gray-200 p-3 max-h-[200px] overflow-y-auto focus:outline-none resize-none leading-relaxed"
placeholder="Posez votre question à l'Orchestrateur..."></textarea> placeholder="Posez votre question à l'Orchestrateur..."></textarea>
<button class="p-3 text-aethas-cyan hover:text-white transition-colors flex-shrink-0" title="Envoyer"> <button class="p-3 text-aethas-cyan hover:text-white transition-colors flex-shrink-0" title="Envoyer">
<i data-lucide="send" class="w-5 h-5"></i> <i data-lucide="send" class="w-5 h-5"></i>
</button> </button>
@@ -146,28 +128,22 @@
createApp({ createApp({
setup() { setup() {
const username = ref("Admin"); const username = ref("Admin");
const isSidebarOpen = ref(true); // La barre latérale est ouverte par défaut const isSidebarOpen = ref(true);
const prompt = ref(""); const prompt = ref("");
const promptTextarea = ref(null); const promptTextarea = ref(null);
// Gère l'ouverture/fermeture de la barre latérale
const toggleSidebar = () => { const toggleSidebar = () => {
isSidebarOpen.value = !isSidebarOpen.value; isSidebarOpen.value = !isSidebarOpen.value;
}; };
// Ajuste la hauteur de la zone de texte dynamiquement
const autoResize = () => { const autoResize = () => {
const el = promptTextarea.value; const el = promptTextarea.value;
if (!el) return; if (!el) return;
// On réinitialise temporairement la hauteur pour recalculer la vraie taille du contenu
el.style.height = 'auto'; el.style.height = 'auto';
// On ajuste à la hauteur du texte. Le CSS "max-h-[200px]" bloquera l'agrandissement et fera apparaître l'ascenseur
el.style.height = el.scrollHeight + 'px'; el.style.height = el.scrollHeight + 'px';
}; };
onMounted(() => { onMounted(() => {
// Initialise les icônes à l'ouverture de la page
lucide.createIcons(); lucide.createIcons();
}); });