diff --git a/backend/main.py b/backend/main.py index fd722e0..b40f2ba 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,16 +1,43 @@ -from fastapi import FastAPI +from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, Response +from pydantic import BaseModel +import requests +import asyncio import os +import uuid +import json +import subprocess +import urllib3 +import socket import config +# Forçage IPv4 +old_getaddrinfo = socket.getaddrinfo +def force_ipv4_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): + return old_getaddrinfo(host, port, socket.AF_INET, type, proto, flags) +socket.getaddrinfo = force_ipv4_getaddrinfo + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + app = FastAPI(title="AETHAS38 API") BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FRONTEND_DIR = os.path.join(BASE_DIR, "frontend") +SESSIONS_DIR = os.path.join(BASE_DIR, "sessions") +MEDIA_DIR = os.path.join(SESSIONS_DIR, "media") +TEMP_DIR = os.path.join(SESSIONS_DIR, "temp") + +os.makedirs(MEDIA_DIR, exist_ok=True) +os.makedirs(TEMP_DIR, exist_ok=True) -# Montage dynamique app.mount("/static", StaticFiles(directory=FRONTEND_DIR), name="static") +app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") + +class ExportRequest(BaseModel): + content: str + format: str # pdf, txt, md, json + type: str # prompt ou response @app.get("/", response_class=HTMLResponse) async def serve_admin(): @@ -25,4 +52,89 @@ async def get_config(): @app.post("/api/config") async def update_config(data: dict): config.save_config(data) - return {"status": "success", "message": "Configuration sauvegardée avec succès."} \ No newline at end of file + return {"status": "success", "message": "Configuration sauvegardée avec succès."} + +@app.post("/api/export") +async def export_content(req: ExportRequest): + filename_base = f"aethas38_{req.type}_{uuid.uuid4().hex[:8]}" + + if req.format == "txt": + return Response( + content=req.content, + media_type="text/plain", + headers={"Content-Disposition": f"attachment; filename={filename_base}.txt"} + ) + + elif req.format == "md": + return Response( + content=f"# AETHAS38 Export ({req.type.upper()})\n\n{req.content}", + media_type="text/markdown", + headers={"Content-Disposition": f"attachment; filename={filename_base}.md"} + ) + + elif req.format == "json": + json_data = json.dumps({"type": req.type, "content": req.content}, indent=4, ensure_ascii=False) + return Response( + content=json_data, + media_type="application/json", + headers={"Content-Disposition": f"attachment; filename={filename_base}.json"} + ) + + elif req.format == "pdf": + # Utilisation de LibreOffice en mode headless + html_filename = os.path.join(TEMP_DIR, f"{filename_base}.html") + pdf_filename = os.path.join(TEMP_DIR, f"{filename_base}.pdf") + + # Création d'un HTML propre pour la conversion + html_content = f""" + + +
+ + + + +{req.content}
+ + + """ + + with open(html_filename, "w", encoding="utf-8") as f: + f.write(html_content) + + try: + # Commande LibreOffice headless pour convertir le HTML en PDF + # Note: selon l'OS (Windows vs Linux Docker), la commande peut s'appeler 'soffice' ou 'libreoffice' + cmd = ["soffice", "--headless", "--convert-to", "pdf", "--outdir", TEMP_DIR, html_filename] + + # Exécution synchrone dans un thread pour ne pas bloquer l'API + def run_libreoffice(): + subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + await asyncio.to_thread(run_libreoffice) + + if not os.path.exists(pdf_filename): + raise Exception("La génération du PDF par LibreOffice a échoué.") + + with open(pdf_filename, "rb") as pdf_file: + pdf_bytes = pdf_file.read() + + # Nettoyage des fichiers temporaires + os.remove(html_filename) + os.remove(pdf_filename) + + return Response( + content=pdf_bytes, + media_type="application/pdf", + headers={"Content-Disposition": f"attachment; filename={filename_base}.pdf"} + ) + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erreur de génération PDF via LibreOffice : {str(e)}") + + raise HTTPException(status_code=400, detail="Format d'export non supporté.") \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index 47abfe6..c3d5f58 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -8,4 +8,5 @@ openpyxl fastapi uvicorn pydantic -python-dotenv \ No newline at end of file +python-dotenv +httpx \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index d23c345..766228e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,7 +3,7 @@ -Aucun fournisseur supplémentaire configuré.
-