feat: Ajout version simplifiée sans IA et correction des ports
- Ajout docker-compose.simple.yml avec ports modifiés (15432, 16379, 19000, 19001, 18000) - Création app_simple.py sans dépendances IA - Ajout Dockerfile.simple et requirements.simple.txt - Correction attribut metadata réservé dans database.py - Ajout scripts de démarrage et test simplifiés - Configuration .env.simple pour version sans IA
This commit is contained in:
parent
64582646ea
commit
6f63821728
67
Makefile.simple
Normal file
67
Makefile.simple
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
ENV ?= infra/.env
|
||||||
|
|
||||||
|
# Charger les variables d'environnement
|
||||||
|
include $(ENV)
|
||||||
|
export
|
||||||
|
|
||||||
|
.PHONY: help up down start-simple logs ps clean restart
|
||||||
|
|
||||||
|
help: ## Afficher l'aide
|
||||||
|
@echo "Commandes disponibles :"
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
up: ## Démarrer tous les services (version complète)
|
||||||
|
cd infra && docker compose up -d
|
||||||
|
|
||||||
|
up-simple: ## Démarrer les services simplifiés (sans IA)
|
||||||
|
cd infra && docker compose -f docker-compose.simple.yml up -d
|
||||||
|
|
||||||
|
down: ## Arrêter tous les services
|
||||||
|
cd infra && docker compose down
|
||||||
|
|
||||||
|
down-simple: ## Arrêter les services simplifiés
|
||||||
|
cd infra && docker compose -f docker-compose.simple.yml down
|
||||||
|
|
||||||
|
start-simple: ## Initialiser l'infrastructure simplifiée
|
||||||
|
bash ops/start-simple.sh
|
||||||
|
|
||||||
|
logs: ## Afficher les logs
|
||||||
|
cd infra && docker compose logs -f --tail=200
|
||||||
|
|
||||||
|
logs-simple: ## Afficher les logs (version simplifiée)
|
||||||
|
cd infra && docker compose -f docker-compose.simple.yml logs -f --tail=200
|
||||||
|
|
||||||
|
ps: ## Afficher le statut des services
|
||||||
|
cd infra && docker compose ps
|
||||||
|
|
||||||
|
ps-simple: ## Afficher le statut des services (version simplifiée)
|
||||||
|
cd infra && docker compose -f docker-compose.simple.yml ps
|
||||||
|
|
||||||
|
clean: ## Nettoyer les volumes et images
|
||||||
|
cd infra && docker compose down -v
|
||||||
|
docker system prune -f
|
||||||
|
|
||||||
|
restart: ## Redémarrer tous les services
|
||||||
|
cd infra && docker compose restart
|
||||||
|
|
||||||
|
build: ## Reconstruire les images
|
||||||
|
cd infra && docker compose build --no-cache
|
||||||
|
|
||||||
|
build-simple: ## Reconstruire les images (version simplifiée)
|
||||||
|
cd infra && docker compose -f docker-compose.simple.yml build --no-cache
|
||||||
|
|
||||||
|
test-api: ## Tester l'API
|
||||||
|
curl -F "file=@tests/data/sample.pdf" \
|
||||||
|
-F "id_dossier=D-2025-001" \
|
||||||
|
-F "source=upload" \
|
||||||
|
-F "etude_id=E-001" \
|
||||||
|
-F "utilisateur_id=U-123" \
|
||||||
|
http://localhost:8000/api/import
|
||||||
|
|
||||||
|
status: ## Vérifier le statut de tous les services
|
||||||
|
@echo "=== Statut des services ==="
|
||||||
|
@make ps-simple
|
||||||
|
@echo ""
|
||||||
|
@echo "=== Test de connectivité ==="
|
||||||
|
@curl -s http://localhost:8000/api/health || echo "API non accessible"
|
12
docker/host-api/Dockerfile.simple
Normal file
12
docker/host-api/Dockerfile.simple
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y libmagic1 && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY docker/host-api/requirements.simple.txt requirements.txt
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY services/host_api /app
|
||||||
|
|
||||||
|
CMD ["uvicorn", "app_simple:app", "--host", "0.0.0.0", "--port", "8000"]
|
9
docker/host-api/requirements.simple.txt
Normal file
9
docker/host-api/requirements.simple.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fastapi==0.115.0
|
||||||
|
uvicorn[standard]==0.30.6
|
||||||
|
pydantic==2.8.2
|
||||||
|
sqlalchemy==2.0.35
|
||||||
|
psycopg[binary]==3.2.1
|
||||||
|
minio==7.2.7
|
||||||
|
redis==5.0.7
|
||||||
|
python-multipart==0.0.9
|
||||||
|
requests==2.32.3
|
21
infra/.env.simple
Normal file
21
infra/.env.simple
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Configuration simplifiée sans IA
|
||||||
|
PROJECT_NAME=notariat
|
||||||
|
DOMAIN=localhost
|
||||||
|
TZ=Europe/Paris
|
||||||
|
|
||||||
|
# Base de données PostgreSQL
|
||||||
|
POSTGRES_USER=notariat
|
||||||
|
POSTGRES_PASSWORD=notariat_pwd
|
||||||
|
POSTGRES_DB=notariat
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
# MinIO (stockage objet)
|
||||||
|
MINIO_ROOT_USER=minio
|
||||||
|
MINIO_ROOT_PASSWORD=minio_pwd
|
||||||
|
MINIO_BUCKET=ingest
|
||||||
|
|
||||||
|
# Configuration de développement
|
||||||
|
DEBUG=true
|
||||||
|
LOG_LEVEL=debug
|
83
infra/docker-compose.simple.yml
Normal file
83
infra/docker-compose.simple.yml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
x-env: &default-env
|
||||||
|
TZ: ${TZ}
|
||||||
|
PUID: "1000"
|
||||||
|
PGID: "1000"
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
volumes:
|
||||||
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "15432:5432"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7
|
||||||
|
command: ["redis-server", "--appendonly", "yes"]
|
||||||
|
volumes:
|
||||||
|
- redis:/data
|
||||||
|
ports:
|
||||||
|
- "16379:6379"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
minio:
|
||||||
|
image: minio/minio:latest
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- minio:/data
|
||||||
|
ports:
|
||||||
|
- "19000:9000"
|
||||||
|
- "19001:9001"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
host-api:
|
||||||
|
image: notariat-api-simple
|
||||||
|
env_file: ./.env
|
||||||
|
environment:
|
||||||
|
<<: *default-env
|
||||||
|
DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
MINIO_ENDPOINT: http://minio:9000
|
||||||
|
MINIO_BUCKET: ${MINIO_BUCKET}
|
||||||
|
volumes:
|
||||||
|
- ../services/host_api:/app
|
||||||
|
- ../ops/seed:/seed:ro
|
||||||
|
- ../ops/seed/schemas:/schemas:ro
|
||||||
|
ports:
|
||||||
|
- "18000:8000"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
- minio
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Worker désactivé pour la version simplifiée
|
||||||
|
# worker:
|
||||||
|
# build:
|
||||||
|
# context: ../docker/worker
|
||||||
|
# env_file: ./.env
|
||||||
|
# environment:
|
||||||
|
# <<: *default-env
|
||||||
|
# DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
|
||||||
|
# REDIS_URL: redis://redis:6379/0
|
||||||
|
# MINIO_ENDPOINT: http://minio:9000
|
||||||
|
# MINIO_BUCKET: ${MINIO_BUCKET}
|
||||||
|
# volumes:
|
||||||
|
# - ../services/worker:/app
|
||||||
|
# - ../ops/seed:/seed:ro
|
||||||
|
# depends_on:
|
||||||
|
# - host-api
|
||||||
|
# restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
|
redis:
|
||||||
|
minio:
|
@ -1,5 +1,3 @@
|
|||||||
version: "3.9"
|
|
||||||
|
|
||||||
x-env: &default-env
|
x-env: &default-env
|
||||||
TZ: ${TZ}
|
TZ: ${TZ}
|
||||||
PUID: "1000"
|
PUID: "1000"
|
||||||
@ -25,7 +23,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio:RELEASE.2025-01-13T00-00-00Z
|
image: minio/minio:latest
|
||||||
command: server /data --console-address ":9001"
|
command: server /data --console-address ":9001"
|
||||||
environment:
|
environment:
|
||||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
41
ops/start-simple.sh
Executable file
41
ops/start-simple.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Démarrage du pipeline notarial simplifié (sans IA)..."
|
||||||
|
|
||||||
|
# Aller dans le répertoire infra
|
||||||
|
cd "$(dirname "$0")/../infra"
|
||||||
|
|
||||||
|
# Copier le fichier d'environnement simplifié
|
||||||
|
cp -n .env.simple .env || true
|
||||||
|
echo "Fichier .env créé à partir de .env.simple"
|
||||||
|
|
||||||
|
# Télécharger les images Docker
|
||||||
|
echo "Téléchargement des images Docker..."
|
||||||
|
docker compose -f docker-compose.simple.yml pull
|
||||||
|
|
||||||
|
# Démarrer les services de base
|
||||||
|
echo "Démarrage des services de base..."
|
||||||
|
docker compose -f docker-compose.simple.yml up -d postgres redis minio
|
||||||
|
|
||||||
|
# Attendre que les services soient prêts
|
||||||
|
echo "Attente du démarrage des services..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Configuration MinIO
|
||||||
|
echo "Configuration de MinIO..."
|
||||||
|
# Créer l'alias MinIO
|
||||||
|
mc alias set local http://127.0.0.1:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD || true
|
||||||
|
# Créer le bucket
|
||||||
|
mc mb -p local/$MINIO_BUCKET || true
|
||||||
|
|
||||||
|
# Démarrer les services applicatifs
|
||||||
|
echo "Démarrage des services applicatifs..."
|
||||||
|
docker compose -f docker-compose.simple.yml up -d host-api worker
|
||||||
|
|
||||||
|
echo "Démarrage terminé !"
|
||||||
|
echo "Services disponibles :"
|
||||||
|
echo "- API: http://localhost:8000/api"
|
||||||
|
echo "- MinIO Console: http://localhost:9001"
|
||||||
|
echo "- PostgreSQL: localhost:5432"
|
||||||
|
echo "- Redis: localhost:6379"
|
202
services/host_api/app_simple.py
Normal file
202
services/host_api/app_simple.py
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
"""
|
||||||
|
API d'ingestion simplifiée pour le pipeline notarial (sans IA)
|
||||||
|
"""
|
||||||
|
from fastapi import FastAPI, UploadFile, File, Form, HTTPException, Depends
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
import uuid
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from domain.models import ImportMeta, DocumentStatus
|
||||||
|
from domain.database import get_db, init_db
|
||||||
|
from routes import health
|
||||||
|
from utils.storage import store_document
|
||||||
|
|
||||||
|
# Configuration du logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="Notariat Pipeline API (Simplifié)",
|
||||||
|
description="API d'ingestion simplifiée pour le traitement de documents notariaux (sans IA)",
|
||||||
|
version="1.0.0-simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configuration CORS
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # À restreindre en production
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inclusion des routes
|
||||||
|
app.include_router(health.router, prefix="/api", tags=["health"])
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def startup_event():
|
||||||
|
"""Initialisation au démarrage de l'application"""
|
||||||
|
logger.info("Démarrage de l'API Notariat Pipeline (Simplifié)")
|
||||||
|
await init_db()
|
||||||
|
|
||||||
|
@app.on_event("shutdown")
|
||||||
|
async def shutdown_event():
|
||||||
|
"""Nettoyage à l'arrêt de l'application"""
|
||||||
|
logger.info("Arrêt de l'API Notariat Pipeline (Simplifié)")
|
||||||
|
|
||||||
|
@app.exception_handler(Exception)
|
||||||
|
async def global_exception_handler(request, exc):
|
||||||
|
"""Gestionnaire d'exceptions global"""
|
||||||
|
logger.error(f"Erreur non gérée: {exc}", exc_info=True)
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"detail": "Erreur interne du serveur"}
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def root():
|
||||||
|
"""Point d'entrée principal"""
|
||||||
|
return {
|
||||||
|
"message": "API Notariat Pipeline (Simplifié)",
|
||||||
|
"version": "1.0.0-simple",
|
||||||
|
"status": "running",
|
||||||
|
"features": {
|
||||||
|
"ai_disabled": True,
|
||||||
|
"ocr_enabled": False,
|
||||||
|
"classification_enabled": False,
|
||||||
|
"extraction_enabled": False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.post("/api/import")
|
||||||
|
async def import_document(
|
||||||
|
file: UploadFile = File(...),
|
||||||
|
id_dossier: str = Form(...),
|
||||||
|
source: str = Form("upload"),
|
||||||
|
etude_id: str = Form(...),
|
||||||
|
utilisateur_id: str = Form(...),
|
||||||
|
db = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Import d'un nouveau document dans le pipeline (version simplifiée)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Vérification du type de fichier
|
||||||
|
allowed_types = ["application/pdf", "image/jpeg", "image/png", "image/tiff"]
|
||||||
|
if file.content_type not in allowed_types:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=415,
|
||||||
|
detail=f"Type de fichier non supporté: {file.content_type}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Génération d'un ID unique
|
||||||
|
doc_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Lecture du contenu du fichier
|
||||||
|
content = await file.read()
|
||||||
|
file_size = len(content)
|
||||||
|
|
||||||
|
# Stockage du document
|
||||||
|
storage_path = await store_document(doc_id, content, file.filename)
|
||||||
|
|
||||||
|
# Création de l'enregistrement en base
|
||||||
|
from domain.database import Document
|
||||||
|
document = Document(
|
||||||
|
id=doc_id,
|
||||||
|
filename=file.filename or "unknown",
|
||||||
|
mime_type=file.content_type,
|
||||||
|
size=file_size,
|
||||||
|
status=DocumentStatus.PENDING.value,
|
||||||
|
id_dossier=id_dossier,
|
||||||
|
etude_id=etude_id,
|
||||||
|
utilisateur_id=utilisateur_id,
|
||||||
|
source=source
|
||||||
|
)
|
||||||
|
|
||||||
|
db.add(document)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(document)
|
||||||
|
|
||||||
|
logger.info(f"Document {doc_id} importé avec succès (version simplifiée)")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "stored",
|
||||||
|
"id_document": doc_id,
|
||||||
|
"message": "Document stocké (traitement IA désactivé)",
|
||||||
|
"storage_path": storage_path
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur lors de l'import du document: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.get("/api/documents/{document_id}")
|
||||||
|
async def get_document(
|
||||||
|
document_id: str,
|
||||||
|
db = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Récupération des informations d'un document
|
||||||
|
"""
|
||||||
|
from domain.database import Document
|
||||||
|
document = db.query(Document).filter(Document.id == document_id).first()
|
||||||
|
|
||||||
|
if not document:
|
||||||
|
raise HTTPException(status_code=404, detail="Document non trouvé")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": document.id,
|
||||||
|
"filename": document.filename,
|
||||||
|
"mime_type": document.mime_type,
|
||||||
|
"size": document.size,
|
||||||
|
"status": document.status,
|
||||||
|
"id_dossier": document.id_dossier,
|
||||||
|
"etude_id": document.etude_id,
|
||||||
|
"utilisateur_id": document.utilisateur_id,
|
||||||
|
"created_at": document.created_at,
|
||||||
|
"updated_at": document.updated_at,
|
||||||
|
"processing_steps": document.processing_steps,
|
||||||
|
"extracted_data": document.extracted_data,
|
||||||
|
"errors": document.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.get("/api/documents")
|
||||||
|
async def list_documents(
|
||||||
|
etude_id: str = None,
|
||||||
|
id_dossier: str = None,
|
||||||
|
limit: int = 50,
|
||||||
|
offset: int = 0,
|
||||||
|
db = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Liste des documents avec filtres
|
||||||
|
"""
|
||||||
|
from domain.database import Document
|
||||||
|
query = db.query(Document)
|
||||||
|
|
||||||
|
if etude_id:
|
||||||
|
query = query.filter(Document.etude_id == etude_id)
|
||||||
|
|
||||||
|
if id_dossier:
|
||||||
|
query = query.filter(Document.id_dossier == id_dossier)
|
||||||
|
|
||||||
|
documents = query.offset(offset).limit(limit).all()
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": doc.id,
|
||||||
|
"filename": doc.filename,
|
||||||
|
"mime_type": doc.mime_type,
|
||||||
|
"size": doc.size,
|
||||||
|
"status": doc.status,
|
||||||
|
"id_dossier": doc.id_dossier,
|
||||||
|
"etude_id": doc.etude_id,
|
||||||
|
"utilisateur_id": doc.utilisateur_id,
|
||||||
|
"created_at": doc.created_at,
|
||||||
|
"updated_at": doc.updated_at
|
||||||
|
}
|
||||||
|
for doc in documents
|
||||||
|
]
|
@ -52,7 +52,7 @@ class ProcessingLog(Base):
|
|||||||
completed_at = Column(DateTime(timezone=True))
|
completed_at = Column(DateTime(timezone=True))
|
||||||
duration = Column(Integer) # en millisecondes
|
duration = Column(Integer) # en millisecondes
|
||||||
error_message = Column(Text)
|
error_message = Column(Text)
|
||||||
metadata = Column(JSON, default={})
|
step_metadata = Column(JSON, default={})
|
||||||
|
|
||||||
def get_db() -> Generator[Session, None, None]:
|
def get_db() -> Generator[Session, None, None]:
|
||||||
"""Dépendance pour obtenir une session de base de données"""
|
"""Dépendance pour obtenir une session de base de données"""
|
||||||
|
18
test-simple.sh
Executable file
18
test-simple.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=== Test du pipeline notarial simplifié ==="
|
||||||
|
|
||||||
|
# Test de santé de l'API
|
||||||
|
echo "1. Test de santé de l'API..."
|
||||||
|
curl -s http://localhost:8000/api/health | jq . || echo "API non accessible"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "2. Test de l'endpoint racine..."
|
||||||
|
curl -s http://localhost:8000/ | jq . || echo "Endpoint racine non accessible"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "3. Test de liste des documents..."
|
||||||
|
curl -s http://localhost:8000/api/documents | jq . || echo "Liste des documents non accessible"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Tests terminés ==="
|
Loading…
x
Reference in New Issue
Block a user