🚀 Déploiement automatique RAG CHU 2025-06-30 17:06:09
Browse files- Dockerfile +31 -36
- backend/src/main.py +54 -41
- pyproject.toml +3 -1
Dockerfile
CHANGED
|
@@ -1,58 +1,53 @@
|
|
| 1 |
# Dockerfile pour le backend RAG CHU
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
# Variables d'environnement
|
| 5 |
-
ENV PYTHONUNBUFFERED=1
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
UV_PROJECT_ENVIRONMENT=/app/.venv
|
| 11 |
-
|
| 12 |
-
# Installer les dépendances système
|
| 13 |
RUN apt-get update && apt-get install -y \
|
| 14 |
curl \
|
| 15 |
build-essential \
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
| 18 |
&& rm -rf /var/lib/apt/lists/*
|
| 19 |
|
| 20 |
-
#
|
| 21 |
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 22 |
-
ENV PATH="/root/.
|
| 23 |
|
| 24 |
-
#
|
| 25 |
WORKDIR /app
|
| 26 |
|
| 27 |
-
# Copier
|
| 28 |
-
COPY
|
| 29 |
-
|
| 30 |
-
# Installer les dépendances Python
|
| 31 |
-
RUN uv sync --frozen --no-dev && \
|
| 32 |
-
chmod -R 777 .venv
|
| 33 |
-
|
| 34 |
-
# Copier le frontend et le builder
|
| 35 |
-
COPY frontend/ ./frontend/
|
| 36 |
|
| 37 |
-
#
|
| 38 |
-
|
| 39 |
-
RUN npm install && npm run build
|
| 40 |
|
| 41 |
-
#
|
| 42 |
-
|
|
|
|
| 43 |
|
| 44 |
-
# Créer les répertoires nécessaires
|
| 45 |
-
RUN mkdir -p uploads static
|
| 46 |
-
chmod 777 /tmp && \
|
| 47 |
-
chmod -R 777 /app/.venv && \
|
| 48 |
-
chmod -R 777 /app
|
| 49 |
|
| 50 |
# Exposer le port
|
| 51 |
-
EXPOSE
|
| 52 |
|
| 53 |
# Healthcheck
|
| 54 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
| 55 |
-
CMD curl -f http://localhost:
|
| 56 |
|
| 57 |
-
#
|
| 58 |
-
CMD ["uv", "run", "--
|
|
|
|
| 1 |
# Dockerfile pour le backend RAG CHU
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
| 4 |
+
# Métadonnées
|
| 5 |
+
LABEL maintainer="CHU Developer <dev@chu.com>"
|
| 6 |
+
LABEL description="Backend RAG pour documents médicaux CHU"
|
| 7 |
+
|
| 8 |
# Variables d'environnement
|
| 9 |
+
ENV PYTHONUNBUFFERED=1
|
| 10 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 11 |
+
ENV UV_CACHE_DIR=/tmp/uv-cache
|
| 12 |
+
|
| 13 |
+
# Installation des dépendances système
|
|
|
|
|
|
|
|
|
|
| 14 |
RUN apt-get update && apt-get install -y \
|
| 15 |
curl \
|
| 16 |
build-essential \
|
| 17 |
+
libmagic1 \
|
| 18 |
+
poppler-utils \
|
| 19 |
+
tesseract-ocr \
|
| 20 |
+
tesseract-ocr-fra \
|
| 21 |
+
libreoffice \
|
| 22 |
&& rm -rf /var/lib/apt/lists/*
|
| 23 |
|
| 24 |
+
# Installation d'uv
|
| 25 |
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 26 |
+
ENV PATH="/root/.cargo/bin:$PATH"
|
| 27 |
|
| 28 |
+
# Répertoire de travail
|
| 29 |
WORKDIR /app
|
| 30 |
|
| 31 |
+
# Copier les fichiers de configuration
|
| 32 |
+
COPY pyproject.toml uv.lock* ./
|
| 33 |
+
COPY backend/pyproject.toml backend/uv.lock* ./backend/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
+
# Installation des dépendances
|
| 36 |
+
RUN uv sync --frozen
|
|
|
|
| 37 |
|
| 38 |
+
# Copier le code source
|
| 39 |
+
COPY backend/src ./backend/src
|
| 40 |
+
COPY backend/.env.example ./backend/.env.example
|
| 41 |
|
| 42 |
+
# Créer les répertoires nécessaires
|
| 43 |
+
RUN mkdir -p uploads backend/static
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
# Exposer le port
|
| 46 |
+
EXPOSE 8000
|
| 47 |
|
| 48 |
# Healthcheck
|
| 49 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
| 50 |
+
CMD curl -f http://localhost:8000/api/health || exit 1
|
| 51 |
|
| 52 |
+
# Point d'entrée
|
| 53 |
+
CMD ["uv", "run", "--directory", "backend", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
backend/src/main.py
CHANGED
|
@@ -79,6 +79,17 @@ class ConnectionManager:
|
|
| 79 |
|
| 80 |
manager = ConnectionManager()
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
# Modèles Pydantic
|
| 83 |
class ChatRequest(BaseModel):
|
| 84 |
question: str
|
|
@@ -438,48 +449,50 @@ async def delete_document(document_id: str):
|
|
| 438 |
logger.error(f"Erreur suppression document {document_id}: {e}")
|
| 439 |
raise HTTPException(status_code=500, detail="Erreur lors de la suppression")
|
| 440 |
|
| 441 |
-
#
|
| 442 |
@app.get("/")
|
| 443 |
-
async def
|
| 444 |
-
"""
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
<
|
| 461 |
-
<
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
<
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
|
|
|
|
|
|
| 483 |
|
| 484 |
if __name__ == "__main__":
|
| 485 |
import uvicorn
|
|
|
|
| 79 |
|
| 80 |
manager = ConnectionManager()
|
| 81 |
|
| 82 |
+
# Configuration des fichiers statiques pour servir le frontend React
|
| 83 |
+
# Chemin vers le build du frontend (depuis le répertoire backend)
|
| 84 |
+
frontend_build_path = Path(__file__).parent.parent.parent / "frontend" / "build"
|
| 85 |
+
|
| 86 |
+
if frontend_build_path.exists():
|
| 87 |
+
# Servir les fichiers statiques du frontend
|
| 88 |
+
app.mount("/static", StaticFiles(directory=frontend_build_path / "static"), name="static")
|
| 89 |
+
logger.info(f"Frontend statique configuré: {frontend_build_path}")
|
| 90 |
+
else:
|
| 91 |
+
logger.warning(f"Répertoire frontend introuvable: {frontend_build_path}")
|
| 92 |
+
|
| 93 |
# Modèles Pydantic
|
| 94 |
class ChatRequest(BaseModel):
|
| 95 |
question: str
|
|
|
|
| 449 |
logger.error(f"Erreur suppression document {document_id}: {e}")
|
| 450 |
raise HTTPException(status_code=500, detail="Erreur lors de la suppression")
|
| 451 |
|
| 452 |
+
# Routes pour servir le frontend React
|
| 453 |
@app.get("/")
|
| 454 |
+
async def serve_react_app():
|
| 455 |
+
"""Servir l'application React"""
|
| 456 |
+
index_path = frontend_build_path / "index.html"
|
| 457 |
+
if index_path.exists():
|
| 458 |
+
return HTMLResponse(content=index_path.read_text(), status_code=200)
|
| 459 |
+
else:
|
| 460 |
+
# Fallback si pas de frontend
|
| 461 |
+
return HTMLResponse("""
|
| 462 |
+
<!DOCTYPE html>
|
| 463 |
+
<html>
|
| 464 |
+
<head>
|
| 465 |
+
<title>RAG CHU - Erreur Frontend</title>
|
| 466 |
+
<style>
|
| 467 |
+
body { font-family: Arial, sans-serif; margin: 40px; background: #f8d7da; color: #721c24; }
|
| 468 |
+
.error { background: white; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; }
|
| 469 |
+
</style>
|
| 470 |
+
</head>
|
| 471 |
+
<body>
|
| 472 |
+
<div class="error">
|
| 473 |
+
<h1>❌ Frontend React non trouvé</h1>
|
| 474 |
+
<p>Le frontend n'a pas été correctement buildé.</p>
|
| 475 |
+
<p><strong>Chemin recherché:</strong> <code>{frontend_build_path}</code></p>
|
| 476 |
+
<p><a href="/docs">📖 Documentation API</a></p>
|
| 477 |
+
</div>
|
| 478 |
+
</body>
|
| 479 |
+
</html>
|
| 480 |
+
""")
|
| 481 |
+
|
| 482 |
+
# Catch-all pour les routes React (SPA routing)
|
| 483 |
+
@app.get("/{path:path}")
|
| 484 |
+
async def serve_spa(path: str):
|
| 485 |
+
"""Servir l'application React pour toutes les routes non-API"""
|
| 486 |
+
# Exclure les routes API
|
| 487 |
+
if path.startswith("api/") or path.startswith("docs") or path.startswith("ws"):
|
| 488 |
+
raise HTTPException(status_code=404, detail="Route API non trouvée")
|
| 489 |
+
|
| 490 |
+
# Servir index.html pour toutes les autres routes (SPA routing)
|
| 491 |
+
index_path = frontend_build_path / "index.html"
|
| 492 |
+
if index_path.exists():
|
| 493 |
+
return HTMLResponse(content=index_path.read_text(), status_code=200)
|
| 494 |
+
else:
|
| 495 |
+
raise HTTPException(status_code=404, detail="Frontend non disponible")
|
| 496 |
|
| 497 |
if __name__ == "__main__":
|
| 498 |
import uvicorn
|
pyproject.toml
CHANGED
|
@@ -2,7 +2,9 @@
|
|
| 2 |
name = "backend"
|
| 3 |
version = "0.1.0"
|
| 4 |
description = "Backend RAG médical avec Qdrant et LangChain"
|
| 5 |
-
|
|
|
|
|
|
|
| 6 |
requires-python = ">=3.9"
|
| 7 |
dependencies = [
|
| 8 |
# API Framework
|
|
|
|
| 2 |
name = "backend"
|
| 3 |
version = "0.1.0"
|
| 4 |
description = "Backend RAG médical avec Qdrant et LangChain"
|
| 5 |
+
authors = [
|
| 6 |
+
{ name = "CHU Developer", email = "dev@chu.com" }
|
| 7 |
+
]
|
| 8 |
requires-python = ">=3.9"
|
| 9 |
dependencies = [
|
| 10 |
# API Framework
|