733 lines
26 KiB
Python
733 lines
26 KiB
Python
from fastapi import FastAPI, HTTPException, Header, Depends, Query
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional, List, Dict
|
|
from datetime import datetime, date
|
|
from enum import Enum
|
|
import uvicorn
|
|
import logging
|
|
import win32com.client
|
|
from config import settings, validate_settings
|
|
from sage_connector import SageConnector
|
|
|
|
# =====================================================
|
|
# LOGGING
|
|
# =====================================================
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
handlers=[logging.FileHandler("sage_gateway.log"), logging.StreamHandler()],
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# =====================================================
|
|
# ENUMS
|
|
# =====================================================
|
|
class TypeDocument(int, Enum):
|
|
DEVIS = 0
|
|
BON_LIVRAISON = 1
|
|
BON_RETOUR = 2
|
|
COMMANDE = 3
|
|
PREPARATION = 4
|
|
FACTURE = 5
|
|
|
|
|
|
# =====================================================
|
|
# MODÈLES
|
|
# =====================================================
|
|
class FiltreRequest(BaseModel):
|
|
filtre: Optional[str] = ""
|
|
|
|
|
|
class CodeRequest(BaseModel):
|
|
code: str
|
|
|
|
|
|
class ChampLibreRequest(BaseModel):
|
|
doc_id: str
|
|
type_doc: int
|
|
nom_champ: str
|
|
valeur: str
|
|
|
|
|
|
class DevisRequest(BaseModel):
|
|
client_id: str
|
|
date_devis: Optional[date] = None
|
|
lignes: List[
|
|
Dict
|
|
] # Format: {article_code, quantite, prix_unitaire_ht, remise_pourcentage}
|
|
|
|
|
|
class TransformationRequest(BaseModel):
|
|
numero_source: str
|
|
type_source: int
|
|
type_cible: int
|
|
|
|
|
|
class StatutRequest(BaseModel):
|
|
nouveau_statut: int
|
|
|
|
|
|
# =====================================================
|
|
# SÉCURITÉ
|
|
# =====================================================
|
|
def verify_token(x_sage_token: str = Header(...)):
|
|
"""Vérification du token d'authentification"""
|
|
if x_sage_token != settings.sage_gateway_token:
|
|
logger.warning(f"❌ Token invalide reçu: {x_sage_token[:20]}...")
|
|
raise HTTPException(401, "Token invalide")
|
|
return True
|
|
|
|
|
|
# =====================================================
|
|
# APPLICATION
|
|
# =====================================================
|
|
app = FastAPI(
|
|
title="Sage Gateway - Windows Server",
|
|
version="1.0.0",
|
|
description="Passerelle d'accès à Sage 100c pour VPS Linux",
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.cors_origins,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
allow_credentials=True,
|
|
)
|
|
|
|
sage: Optional[SageConnector] = None
|
|
|
|
|
|
# =====================================================
|
|
# LIFECYCLE
|
|
# =====================================================
|
|
@app.on_event("startup")
|
|
def startup():
|
|
global sage
|
|
|
|
logger.info("🚀 Démarrage Sage Gateway Windows...")
|
|
|
|
# Validation config
|
|
try:
|
|
validate_settings()
|
|
logger.info("✅ Configuration validée")
|
|
except ValueError as e:
|
|
logger.error(f"❌ Configuration invalide: {e}")
|
|
raise
|
|
|
|
# Connexion Sage
|
|
sage = SageConnector(
|
|
settings.chemin_base, settings.utilisateur, settings.mot_de_passe
|
|
)
|
|
|
|
if not sage.connecter():
|
|
raise RuntimeError("❌ Impossible de se connecter à Sage 100c")
|
|
|
|
logger.info("✅ Sage Gateway démarré et connecté")
|
|
|
|
|
|
@app.on_event("shutdown")
|
|
def shutdown():
|
|
if sage:
|
|
sage.deconnecter()
|
|
logger.info("👋 Sage Gateway arrêté")
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - SYSTÈME
|
|
# =====================================================
|
|
@app.get("/health")
|
|
def health():
|
|
"""Health check"""
|
|
return {
|
|
"status": "ok",
|
|
"sage_connected": sage is not None and sage.cial is not None,
|
|
"cache_info": sage.get_cache_info() if sage else None,
|
|
"timestamp": datetime.now().isoformat(),
|
|
}
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - CLIENTS
|
|
# =====================================================
|
|
@app.post("/sage/clients/list", dependencies=[Depends(verify_token)])
|
|
def clients_list(req: FiltreRequest):
|
|
"""Liste des clients avec filtre optionnel"""
|
|
try:
|
|
clients = sage.lister_tous_clients(req.filtre)
|
|
return {"success": True, "data": clients}
|
|
except Exception as e:
|
|
logger.error(f"Erreur liste clients: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/clients/get", dependencies=[Depends(verify_token)])
|
|
def client_get(req: CodeRequest):
|
|
"""Lecture d'un client par code"""
|
|
try:
|
|
client = sage.lire_client(req.code)
|
|
if not client:
|
|
raise HTTPException(404, f"Client {req.code} non trouvé")
|
|
return {"success": True, "data": client}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture client: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - ARTICLES
|
|
# =====================================================
|
|
@app.post("/sage/articles/list", dependencies=[Depends(verify_token)])
|
|
def articles_list(req: FiltreRequest):
|
|
"""Liste des articles avec filtre optionnel"""
|
|
try:
|
|
articles = sage.lister_tous_articles(req.filtre)
|
|
return {"success": True, "data": articles}
|
|
except Exception as e:
|
|
logger.error(f"Erreur liste articles: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/articles/get", dependencies=[Depends(verify_token)])
|
|
def article_get(req: CodeRequest):
|
|
"""Lecture d'un article par référence"""
|
|
try:
|
|
article = sage.lire_article(req.code)
|
|
if not article:
|
|
raise HTTPException(404, f"Article {req.code} non trouvé")
|
|
return {"success": True, "data": article}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture article: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - DEVIS
|
|
# =====================================================
|
|
@app.post("/sage/devis/create", dependencies=[Depends(verify_token)])
|
|
def creer_devis(req: DevisRequest):
|
|
"""Création d'un devis"""
|
|
try:
|
|
# Transformer en format attendu par sage_connector
|
|
devis_data = {
|
|
"client": {"code": req.client_id, "intitule": ""},
|
|
"date_devis": req.date_devis or date.today(),
|
|
"lignes": req.lignes,
|
|
}
|
|
|
|
resultat = sage.creer_devis_enrichi(devis_data)
|
|
return {"success": True, "data": resultat}
|
|
except Exception as e:
|
|
logger.error(f"Erreur création devis: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/devis/get", dependencies=[Depends(verify_token)])
|
|
def lire_devis(req: CodeRequest):
|
|
"""Lecture d'un devis"""
|
|
try:
|
|
devis = sage.lire_devis(req.code)
|
|
if not devis:
|
|
raise HTTPException(404, f"Devis {req.code} non trouvé")
|
|
return {"success": True, "data": devis}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture devis: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/devis/{id}/statut", dependencies=[Depends(verify_token)])
|
|
def changer_statut_devis(doc_id: str, req: StatutRequest):
|
|
"""Changement de statut d'un devis"""
|
|
try:
|
|
devis_status = sage.changer_statut_devis(doc_id, req.nouveau_statut)
|
|
return {"success": True, "message": "Statut mis à jour"}
|
|
except Exception as e:
|
|
logger.error(f"Erreur MAJ statut: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/devis/list", dependencies=[Depends(verify_token)])
|
|
def devis_list(
|
|
limit: int = 100, statut: Optional[int] = None, inclure_lignes: bool = Query(True)
|
|
):
|
|
"""
|
|
📋 Liste tous les devis avec filtres optionnels
|
|
|
|
Args:
|
|
limit: Nombre max de devis à retourner
|
|
statut: Filtre par statut (optionnel)
|
|
inclure_lignes: Si True, charge les lignes de chaque devis (par défaut: True)
|
|
|
|
✅ AMÉLIORATION: Charge maintenant les lignes de chaque devis
|
|
"""
|
|
try:
|
|
if not sage or not sage.cial:
|
|
raise HTTPException(503, "Service Sage indisponible")
|
|
|
|
with sage._com_context(), sage._lock_com:
|
|
factory = sage.cial.FactoryDocumentVente
|
|
devis_list = []
|
|
index = 1
|
|
max_iterations = limit * 3
|
|
erreurs_consecutives = 0
|
|
max_erreurs = 50
|
|
|
|
logger.info(
|
|
f"🔍 Recherche devis (limit={limit}, statut={statut}, inclure_lignes={inclure_lignes})"
|
|
)
|
|
|
|
while (
|
|
len(devis_list) < limit
|
|
and index < max_iterations
|
|
and erreurs_consecutives < max_erreurs
|
|
):
|
|
try:
|
|
persist = factory.List(index)
|
|
if persist is None:
|
|
logger.debug(f"Fin de liste à l'index {index}")
|
|
break
|
|
|
|
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
|
doc.Read()
|
|
|
|
# Filtrer uniquement devis (type 0)
|
|
doc_type = getattr(doc, "DO_Type", -1)
|
|
if doc_type != 0:
|
|
index += 1
|
|
continue
|
|
|
|
doc_statut = getattr(doc, "DO_Statut", 0)
|
|
|
|
# Filtre statut
|
|
if statut is not None and doc_statut != statut:
|
|
index += 1
|
|
continue
|
|
|
|
# ✅ Charger client via .Client
|
|
client_code = ""
|
|
client_intitule = ""
|
|
|
|
try:
|
|
client_obj = getattr(doc, "Client", None)
|
|
if client_obj:
|
|
client_obj.Read()
|
|
client_code = getattr(client_obj, "CT_Num", "").strip()
|
|
client_intitule = getattr(
|
|
client_obj, "CT_Intitule", ""
|
|
).strip()
|
|
logger.debug(
|
|
f"✅ Client: {client_code} - {client_intitule}"
|
|
)
|
|
except Exception as e:
|
|
logger.debug(f"Erreur chargement client: {e}")
|
|
# Fallback sur cache si code disponible
|
|
if client_code:
|
|
client_cache = sage.lire_client(client_code)
|
|
if client_cache:
|
|
client_intitule = client_cache.get("intitule", "")
|
|
|
|
# ✅✅ NOUVEAU: Charger les lignes si demandé
|
|
lignes = []
|
|
if inclure_lignes:
|
|
try:
|
|
factory_lignes = getattr(doc, "FactoryDocumentLigne", None)
|
|
if not factory_lignes:
|
|
factory_lignes = getattr(
|
|
doc, "FactoryDocumentVenteLigne", None
|
|
)
|
|
|
|
if factory_lignes:
|
|
ligne_index = 1
|
|
while ligne_index <= 100: # Max 100 lignes par devis
|
|
try:
|
|
ligne_persist = factory_lignes.List(ligne_index)
|
|
if ligne_persist is None:
|
|
break
|
|
|
|
ligne = win32com.client.CastTo(
|
|
ligne_persist, "IBODocumentLigne3"
|
|
)
|
|
ligne.Read()
|
|
|
|
# Charger référence article
|
|
article_ref = ""
|
|
try:
|
|
article_ref = getattr(
|
|
ligne, "AR_Ref", ""
|
|
).strip()
|
|
if not article_ref:
|
|
article_obj = getattr(
|
|
ligne, "Article", None
|
|
)
|
|
if article_obj:
|
|
article_obj.Read()
|
|
article_ref = getattr(
|
|
article_obj, "AR_Ref", ""
|
|
).strip()
|
|
except:
|
|
pass
|
|
|
|
lignes.append(
|
|
{
|
|
"article": article_ref,
|
|
"designation": getattr(
|
|
ligne, "DL_Design", ""
|
|
),
|
|
"quantite": float(
|
|
getattr(ligne, "DL_Qte", 0.0)
|
|
),
|
|
"prix_unitaire": float(
|
|
getattr(
|
|
ligne, "DL_PrixUnitaire", 0.0
|
|
)
|
|
),
|
|
"montant_ht": float(
|
|
getattr(ligne, "DL_MontantHT", 0.0)
|
|
),
|
|
}
|
|
)
|
|
|
|
ligne_index += 1
|
|
except Exception as e:
|
|
logger.debug(f"Erreur ligne {ligne_index}: {e}")
|
|
break
|
|
except Exception as e:
|
|
logger.debug(f"Erreur chargement lignes: {e}")
|
|
|
|
devis_list.append(
|
|
{
|
|
"numero": getattr(doc, "DO_Piece", ""),
|
|
"date": str(getattr(doc, "DO_Date", "")),
|
|
"client_code": client_code,
|
|
"client_intitule": client_intitule,
|
|
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
|
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
|
"statut": doc_statut,
|
|
"lignes": lignes, # ✅ Lignes incluses
|
|
}
|
|
)
|
|
|
|
erreurs_consecutives = 0
|
|
index += 1
|
|
|
|
except Exception as e:
|
|
erreurs_consecutives += 1
|
|
logger.debug(f"⚠️ Erreur index {index}: {e}")
|
|
index += 1
|
|
|
|
if erreurs_consecutives >= max_erreurs:
|
|
logger.warning(
|
|
f"⚠️ Arrêt après {max_erreurs} erreurs consécutives"
|
|
)
|
|
break
|
|
|
|
nb_avec_client = sum(1 for d in devis_list if d["client_intitule"])
|
|
nb_avec_lignes = sum(1 for d in devis_list if d.get("lignes"))
|
|
logger.info(
|
|
f"✅ {len(devis_list)} devis retournés "
|
|
f"({nb_avec_client} avec client, {nb_avec_lignes} avec lignes)"
|
|
)
|
|
|
|
return {"success": True, "data": devis_list}
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"❌ Erreur liste devis: {e}", exc_info=True)
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - DOCUMENTS
|
|
# =====================================================
|
|
@app.post("/sage/documents/get", dependencies=[Depends(verify_token)])
|
|
def lire_document(numero: str, type_doc: int):
|
|
"""Lecture d'un document (commande, facture, etc.)"""
|
|
try:
|
|
doc = sage.lire_document(numero, type_doc)
|
|
if not doc:
|
|
raise HTTPException(404, f"Document {numero} non trouvé")
|
|
return {"success": True, "data": doc}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture document: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/documents/transform", dependencies=[Depends(verify_token)])
|
|
def transformer_document(req: TransformationRequest):
|
|
"""Transformation de document (devis → commande, etc.)"""
|
|
try:
|
|
resultat = sage.transformer_document(
|
|
req.numero_source, req.type_source, req.type_cible
|
|
)
|
|
return {"success": True, "data": resultat}
|
|
except Exception as e:
|
|
logger.error(f"Erreur transformation: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/documents/champ-libre", dependencies=[Depends(verify_token)])
|
|
def maj_champ_libre(req: ChampLibreRequest):
|
|
"""Mise à jour d'un champ libre"""
|
|
try:
|
|
success = sage.mettre_a_jour_champ_libre(
|
|
req.doc_id, req.type_doc, req.nom_champ, req.valeur
|
|
)
|
|
return {"success": success}
|
|
except Exception as e:
|
|
logger.error(f"Erreur MAJ champ libre: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/documents/derniere-relance", dependencies=[Depends(verify_token)])
|
|
def maj_derniere_relance(doc_id: str, type_doc: int):
|
|
"""📅 Met à jour le champ 'Dernière relance' d'un document"""
|
|
try:
|
|
success = sage.mettre_a_jour_derniere_relance(doc_id, type_doc)
|
|
return {"success": success}
|
|
except Exception as e:
|
|
logger.error(f"Erreur MAJ dernière relance: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - CONTACTS
|
|
# =====================================================
|
|
@app.post("/sage/contact/read", dependencies=[Depends(verify_token)])
|
|
def contact_read(req: CodeRequest):
|
|
"""Lecture du contact principal d'un client"""
|
|
try:
|
|
contact = sage.lire_contact_principal_client(req.code)
|
|
if not contact:
|
|
raise HTTPException(404, f"Contact non trouvé pour client {req.code}")
|
|
return {"success": True, "data": contact}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture contact: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/commandes/list", dependencies=[Depends(verify_token)])
|
|
def commandes_list(limit: int = 100, statut: Optional[int] = None):
|
|
"""Liste toutes les commandes"""
|
|
try:
|
|
with sage._com_context(), sage._lock_com:
|
|
factory = sage.cial.FactoryDocumentVente
|
|
commandes = []
|
|
index = 1
|
|
max_iterations = limit * 3
|
|
|
|
while len(commandes) < limit and index < max_iterations:
|
|
try:
|
|
persist = factory.List(index)
|
|
if persist is None:
|
|
break
|
|
|
|
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
|
doc.Read()
|
|
|
|
# Filtrer commandes (type 3)
|
|
if getattr(doc, "DO_Type", -1) != 3:
|
|
index += 1
|
|
continue
|
|
|
|
doc_statut = getattr(doc, "DO_Statut", 0)
|
|
|
|
if statut is None or doc_statut == statut:
|
|
# Charger client
|
|
client_code = ""
|
|
client_intitule = ""
|
|
|
|
try:
|
|
client_obj = getattr(doc, "Client", None)
|
|
if client_obj:
|
|
client_obj.Read()
|
|
client_code = getattr(client_obj, "CT_Num", "").strip()
|
|
client_intitule = getattr(
|
|
client_obj, "CT_Intitule", ""
|
|
).strip()
|
|
except:
|
|
pass
|
|
|
|
commandes.append(
|
|
{
|
|
"numero": getattr(doc, "DO_Piece", ""),
|
|
"date": str(getattr(doc, "DO_Date", "")),
|
|
"client_code": client_code,
|
|
"client_intitule": client_intitule,
|
|
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
|
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
|
"statut": doc_statut,
|
|
}
|
|
)
|
|
|
|
index += 1
|
|
except:
|
|
index += 1
|
|
continue
|
|
|
|
logger.info(f"✅ {len(commandes)} commandes retournées")
|
|
return {"success": True, "data": commandes}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erreur liste commandes: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/factures/list", dependencies=[Depends(verify_token)])
|
|
def factures_list(limit: int = 100, statut: Optional[int] = None):
|
|
"""Liste toutes les factures"""
|
|
try:
|
|
with sage._com_context(), sage._lock_com:
|
|
factory = sage.cial.FactoryDocumentVente
|
|
factures = []
|
|
index = 1
|
|
max_iterations = limit * 3
|
|
|
|
while len(factures) < limit and index < max_iterations:
|
|
try:
|
|
persist = factory.List(index)
|
|
if persist is None:
|
|
break
|
|
|
|
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
|
doc.Read()
|
|
|
|
# Filtrer factures (type 5)
|
|
if getattr(doc, "DO_Type", -1) != 5:
|
|
index += 1
|
|
continue
|
|
|
|
doc_statut = getattr(doc, "DO_Statut", 0)
|
|
|
|
if statut is None or doc_statut == statut:
|
|
# Charger client
|
|
client_code = ""
|
|
client_intitule = ""
|
|
|
|
try:
|
|
client_obj = getattr(doc, "Client", None)
|
|
if client_obj:
|
|
client_obj.Read()
|
|
client_code = getattr(client_obj, "CT_Num", "").strip()
|
|
client_intitule = getattr(
|
|
client_obj, "CT_Intitule", ""
|
|
).strip()
|
|
except:
|
|
pass
|
|
|
|
# Champ libre dernière relance
|
|
derniere_relance = None
|
|
try:
|
|
derniere_relance = getattr(doc, "DO_DerniereRelance", None)
|
|
except:
|
|
pass
|
|
|
|
factures.append(
|
|
{
|
|
"numero": getattr(doc, "DO_Piece", ""),
|
|
"date": str(getattr(doc, "DO_Date", "")),
|
|
"client_code": client_code,
|
|
"client_intitule": client_intitule,
|
|
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
|
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
|
"statut": doc_statut,
|
|
"derniere_relance": (
|
|
str(derniere_relance) if derniere_relance else None
|
|
),
|
|
}
|
|
)
|
|
|
|
index += 1
|
|
except:
|
|
index += 1
|
|
continue
|
|
|
|
logger.info(f"✅ {len(factures)} factures retournées")
|
|
return {"success": True, "data": factures}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erreur liste factures: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.post("/sage/client/remise-max", dependencies=[Depends(verify_token)])
|
|
def lire_remise_max_client(code: str):
|
|
"""Récupère la remise max autorisée pour un client"""
|
|
try:
|
|
client_obj = sage._lire_client_obj(code)
|
|
|
|
if not client_obj:
|
|
raise HTTPException(404, f"Client {code} introuvable")
|
|
|
|
remise_max = 10.0 # Défaut
|
|
|
|
try:
|
|
remise_max = float(getattr(client_obj, "CT_RemiseMax", 10.0))
|
|
except:
|
|
pass
|
|
|
|
logger.info(f"✅ Remise max client {code}: {remise_max}%")
|
|
|
|
return {
|
|
"success": True,
|
|
"data": {"client_code": code, "remise_max": remise_max},
|
|
}
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Erreur lecture remise: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# ENDPOINTS - ADMIN
|
|
# =====================================================
|
|
@app.post("/sage/cache/refresh", dependencies=[Depends(verify_token)])
|
|
def refresh_cache():
|
|
"""Force le rafraîchissement du cache"""
|
|
try:
|
|
sage.forcer_actualisation_cache()
|
|
return {
|
|
"success": True,
|
|
"message": "Cache actualisé",
|
|
"info": sage.get_cache_info(),
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Erreur refresh cache: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
@app.get("/sage/cache/info", dependencies=[Depends(verify_token)])
|
|
def cache_info_get():
|
|
"""Informations sur le cache (endpoint GET)"""
|
|
try:
|
|
return {"success": True, "data": sage.get_cache_info()}
|
|
except Exception as e:
|
|
logger.error(f"Erreur info cache: {e}")
|
|
raise HTTPException(500, str(e))
|
|
|
|
|
|
# =====================================================
|
|
# LANCEMENT
|
|
# =====================================================
|
|
if __name__ == "__main__":
|
|
uvicorn.run(
|
|
"main:app",
|
|
host=settings.api_host,
|
|
port=settings.api_port,
|
|
reload=False, # Pas de reload en production
|
|
log_level="info",
|
|
)
|