refactor(auth): reorganize imports and remove unused dependencies - Added some missing auth
This commit is contained in:
parent
18d72b3bf9
commit
18603ded6e
4 changed files with 35 additions and 432 deletions
293
api.py
293
api.py
|
|
@ -16,9 +16,10 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
import os
|
import os
|
||||||
from pathlib import Path as FilePath
|
from pathlib import Path as FilePath
|
||||||
from data.data import TAGS_METADATA, templates_signature_email
|
from data.data import TAGS_METADATA
|
||||||
from config.config import settings
|
from config.config import settings
|
||||||
from database import (
|
from database import (
|
||||||
|
User,
|
||||||
init_db,
|
init_db,
|
||||||
async_session_factory,
|
async_session_factory,
|
||||||
get_session,
|
get_session,
|
||||||
|
|
@ -58,7 +59,6 @@ from schemas import (
|
||||||
FactureUpdate,
|
FactureUpdate,
|
||||||
LivraisonCreate,
|
LivraisonCreate,
|
||||||
LivraisonUpdate,
|
LivraisonUpdate,
|
||||||
StatutSignature,
|
|
||||||
ArticleCreate,
|
ArticleCreate,
|
||||||
Article,
|
Article,
|
||||||
ArticleUpdate,
|
ArticleUpdate,
|
||||||
|
|
@ -93,9 +93,10 @@ from core.sage_context import (
|
||||||
from utils.generic_functions import (
|
from utils.generic_functions import (
|
||||||
_preparer_lignes_document,
|
_preparer_lignes_document,
|
||||||
universign_envoyer,
|
universign_envoyer,
|
||||||
universign_statut,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from core.dependencies import get_current_user
|
||||||
|
|
||||||
if os.path.exists("/app"):
|
if os.path.exists("/app"):
|
||||||
LOGS_DIR = FilePath("/app/logs")
|
LOGS_DIR = FilePath("/app/logs")
|
||||||
else:
|
else:
|
||||||
|
|
@ -976,266 +977,6 @@ async def commande_vers_facture(
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/signatures/relances-auto", tags=["Admin"])
|
|
||||||
async def relancer_signatures_automatique(session: AsyncSession = Depends(get_session)):
|
|
||||||
try:
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
date_limite = datetime.now() - timedelta(days=7)
|
|
||||||
|
|
||||||
query = select(SignatureLog).where(
|
|
||||||
SignatureLog.statut.in_(
|
|
||||||
[StatutSignatureDB.EN_ATTENTE, StatutSignatureDB.ENVOYE]
|
|
||||||
),
|
|
||||||
SignatureLog.date_envoi < date_limite,
|
|
||||||
SignatureLog.nb_relances < 3, # Max 3 relances
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await session.execute(query)
|
|
||||||
signatures_a_relancer = result.scalars().all()
|
|
||||||
|
|
||||||
nb_relances = 0
|
|
||||||
|
|
||||||
for signature in signatures_a_relancer:
|
|
||||||
try:
|
|
||||||
nb_jours = (datetime.now() - signature.date_envoi).days
|
|
||||||
jours_restants = 30 - nb_jours # Lien expire après 30 jours
|
|
||||||
|
|
||||||
if jours_restants <= 0:
|
|
||||||
signature.statut = StatutSignatureDB.EXPIRE
|
|
||||||
continue
|
|
||||||
|
|
||||||
template = templates_signature_email["relance_signature"]
|
|
||||||
|
|
||||||
type_labels = {
|
|
||||||
0: "Devis",
|
|
||||||
10: "Commande",
|
|
||||||
30: "Bon de Livraison",
|
|
||||||
60: "Facture",
|
|
||||||
50: "Avoir",
|
|
||||||
}
|
|
||||||
|
|
||||||
variables = {
|
|
||||||
"NOM_SIGNATAIRE": signature.nom_signataire,
|
|
||||||
"TYPE_DOC": type_labels.get(signature.type_document, "Document"),
|
|
||||||
"NUMERO": signature.document_id,
|
|
||||||
"NB_JOURS": str(nb_jours),
|
|
||||||
"JOURS_RESTANTS": str(jours_restants),
|
|
||||||
"SIGNER_URL": signature.signer_url,
|
|
||||||
"CONTACT_EMAIL": settings.smtp_from,
|
|
||||||
}
|
|
||||||
|
|
||||||
sujet = template["sujet"]
|
|
||||||
corps = template["corps_html"]
|
|
||||||
|
|
||||||
for var, valeur in variables.items():
|
|
||||||
sujet = sujet.replace(f"{{{{{var}}}}}", str(valeur))
|
|
||||||
corps = corps.replace(f"{{{{{var}}}}}", str(valeur))
|
|
||||||
|
|
||||||
email_log = EmailLog(
|
|
||||||
id=str(uuid.uuid4()),
|
|
||||||
destinataire=signature.email_signataire,
|
|
||||||
sujet=sujet,
|
|
||||||
corps_html=corps,
|
|
||||||
document_ids=signature.document_id,
|
|
||||||
type_document=signature.type_document,
|
|
||||||
statut=StatutEmailDB.EN_ATTENTE,
|
|
||||||
date_creation=datetime.now(),
|
|
||||||
nb_tentatives=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
session.add(email_log)
|
|
||||||
email_queue.enqueue(email_log.id)
|
|
||||||
|
|
||||||
signature.est_relance = True
|
|
||||||
signature.nb_relances = (signature.nb_relances or 0) + 1
|
|
||||||
|
|
||||||
nb_relances += 1
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f" Relance envoyée: {signature.document_id} ({signature.nb_relances}/3)"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur relance signature {signature.id}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"signatures_verifiees": len(signatures_a_relancer),
|
|
||||||
"relances_envoyees": nb_relances,
|
|
||||||
"message": f"{nb_relances} email(s) de relance envoyé(s)",
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur relances automatiques: {e}")
|
|
||||||
raise HTTPException(500, str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/signature/universign/status", tags=["Signatures"])
|
|
||||||
async def statut_signature(docId: str = Query(...)):
|
|
||||||
try:
|
|
||||||
async with async_session_factory() as session:
|
|
||||||
query = select(SignatureLog).where(SignatureLog.document_id == docId)
|
|
||||||
result = await session.execute(query)
|
|
||||||
signature_log = result.scalar_one_or_none()
|
|
||||||
|
|
||||||
if not signature_log:
|
|
||||||
raise HTTPException(404, "Signature introuvable")
|
|
||||||
|
|
||||||
statut = await universign_statut(signature_log.transaction_id)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"doc_id": docId,
|
|
||||||
"statut": statut["statut"],
|
|
||||||
"date_signature": statut.get("date_signature"),
|
|
||||||
}
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur statut signature: {e}")
|
|
||||||
raise HTTPException(500, str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/signatures", tags=["Signatures"])
|
|
||||||
async def lister_signatures(
|
|
||||||
statut: Optional[StatutSignature] = Query(None),
|
|
||||||
limit: int = Query(100, le=1000),
|
|
||||||
session: AsyncSession = Depends(get_session),
|
|
||||||
):
|
|
||||||
query = select(SignatureLog).order_by(SignatureLog.date_envoi.desc())
|
|
||||||
|
|
||||||
if statut:
|
|
||||||
statut_db = StatutSignatureDB[statut.value]
|
|
||||||
query = query.where(SignatureLog.statut == statut_db)
|
|
||||||
|
|
||||||
query = query.limit(limit)
|
|
||||||
result = await session.execute(query)
|
|
||||||
signatures = result.scalars().all()
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
"id": sig.id,
|
|
||||||
"document_id": sig.document_id,
|
|
||||||
"type_document": sig.type_document.value,
|
|
||||||
"transaction_id": sig.transaction_id,
|
|
||||||
"signer_url": sig.signer_url,
|
|
||||||
"email_signataire": sig.email_signataire,
|
|
||||||
"nom_signataire": sig.nom_signataire,
|
|
||||||
"statut": sig.statut.value,
|
|
||||||
"date_envoi": sig.date_envoi.isoformat() if sig.date_envoi else None,
|
|
||||||
"date_signature": (
|
|
||||||
sig.date_signature.isoformat() if sig.date_signature else None
|
|
||||||
),
|
|
||||||
"est_relance": sig.est_relance,
|
|
||||||
"nb_relances": sig.nb_relances or 0,
|
|
||||||
}
|
|
||||||
for sig in signatures
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/signatures/{transaction_id}/status", tags=["Signatures"])
|
|
||||||
async def statut_signature_detail(
|
|
||||||
transaction_id: str, session: AsyncSession = Depends(get_session)
|
|
||||||
):
|
|
||||||
query = select(SignatureLog).where(SignatureLog.transaction_id == transaction_id)
|
|
||||||
result = await session.execute(query)
|
|
||||||
signature_log = result.scalar_one_or_none()
|
|
||||||
|
|
||||||
if not signature_log:
|
|
||||||
raise HTTPException(404, f"Transaction {transaction_id} introuvable")
|
|
||||||
|
|
||||||
statut_universign = await universign_statut(transaction_id)
|
|
||||||
|
|
||||||
if statut_universign.get("statut") != "ERREUR":
|
|
||||||
statut_map = {
|
|
||||||
"EN_ATTENTE": StatutSignatureDB.EN_ATTENTE,
|
|
||||||
"ENVOYE": StatutSignatureDB.ENVOYE,
|
|
||||||
"SIGNE": StatutSignatureDB.SIGNE,
|
|
||||||
"REFUSE": StatutSignatureDB.REFUSE,
|
|
||||||
"EXPIRE": StatutSignatureDB.EXPIRE,
|
|
||||||
}
|
|
||||||
|
|
||||||
nouveau_statut = statut_map.get(
|
|
||||||
statut_universign["statut"], StatutSignatureDB.EN_ATTENTE
|
|
||||||
)
|
|
||||||
|
|
||||||
signature_log.statut = nouveau_statut
|
|
||||||
|
|
||||||
if statut_universign.get("date_signature"):
|
|
||||||
signature_log.date_signature = datetime.fromisoformat(
|
|
||||||
statut_universign["date_signature"].replace("Z", "+00:00")
|
|
||||||
)
|
|
||||||
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"transaction_id": transaction_id,
|
|
||||||
"document_id": signature_log.document_id,
|
|
||||||
"statut": signature_log.statut.value,
|
|
||||||
"email_signataire": signature_log.email_signataire,
|
|
||||||
"date_envoi": (
|
|
||||||
signature_log.date_envoi.isoformat() if signature_log.date_envoi else None
|
|
||||||
),
|
|
||||||
"date_signature": (
|
|
||||||
signature_log.date_signature.isoformat()
|
|
||||||
if signature_log.date_signature
|
|
||||||
else None
|
|
||||||
),
|
|
||||||
"signer_url": signature_log.signer_url,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/signatures/refresh-all", tags=["Signatures"])
|
|
||||||
async def rafraichir_statuts_signatures(session: AsyncSession = Depends(get_session)):
|
|
||||||
query = select(SignatureLog).where(
|
|
||||||
SignatureLog.statut.in_(
|
|
||||||
[StatutSignatureDB.EN_ATTENTE, StatutSignatureDB.ENVOYE]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await session.execute(query)
|
|
||||||
signatures = result.scalars().all()
|
|
||||||
nb_mises_a_jour = 0
|
|
||||||
|
|
||||||
for sig in signatures:
|
|
||||||
try:
|
|
||||||
statut_universign = await universign_statut(sig.transaction_id)
|
|
||||||
|
|
||||||
if statut_universign.get("statut") != "ERREUR":
|
|
||||||
statut_map = {
|
|
||||||
"SIGNE": StatutSignatureDB.SIGNE,
|
|
||||||
"REFUSE": StatutSignatureDB.REFUSE,
|
|
||||||
"EXPIRE": StatutSignatureDB.EXPIRE,
|
|
||||||
}
|
|
||||||
|
|
||||||
nouveau = statut_map.get(statut_universign["statut"])
|
|
||||||
|
|
||||||
if nouveau and nouveau != sig.statut:
|
|
||||||
sig.statut = nouveau
|
|
||||||
|
|
||||||
if statut_universign.get("date_signature"):
|
|
||||||
sig.date_signature = datetime.fromisoformat(
|
|
||||||
statut_universign["date_signature"].replace("Z", "+00:00")
|
|
||||||
)
|
|
||||||
|
|
||||||
nb_mises_a_jour += 1
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur refresh signature {sig.transaction_id}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"nb_signatures_verifiees": len(signatures),
|
|
||||||
"nb_mises_a_jour": nb_mises_a_jour,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class EmailBatch(BaseModel):
|
class EmailBatch(BaseModel):
|
||||||
destinataires: List[EmailStr] = Field(..., min_length=1, max_length=100)
|
destinataires: List[EmailStr] = Field(..., min_length=1, max_length=100)
|
||||||
sujet: str = Field(..., min_length=1, max_length=500)
|
sujet: str = Field(..., min_length=1, max_length=500)
|
||||||
|
|
@ -1756,14 +1497,19 @@ class TemplatePreview(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
@app.get("/templates/emails", response_model=List[TemplateEmail], tags=["Emails"])
|
@app.get("/templates/emails", response_model=List[TemplateEmail], tags=["Emails"])
|
||||||
async def lister_templates():
|
async def lister_templates(
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
return [TemplateEmail(**template) for template in templates_email_db.values()]
|
return [TemplateEmail(**template) for template in templates_email_db.values()]
|
||||||
|
|
||||||
|
|
||||||
@app.get(
|
@app.get(
|
||||||
"/templates/emails/{template_id}", response_model=TemplateEmail, tags=["Emails"]
|
"/templates/emails/{template_id}", response_model=TemplateEmail, tags=["Emails"]
|
||||||
)
|
)
|
||||||
async def lire_template(template_id: str):
|
async def lire_template(
|
||||||
|
template_id: str,
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
if template_id not in templates_email_db:
|
if template_id not in templates_email_db:
|
||||||
raise HTTPException(404, f"Template {template_id} introuvable")
|
raise HTTPException(404, f"Template {template_id} introuvable")
|
||||||
|
|
||||||
|
|
@ -1771,7 +1517,10 @@ async def lire_template(template_id: str):
|
||||||
|
|
||||||
|
|
||||||
@app.post("/templates/emails", response_model=TemplateEmail, tags=["Emails"])
|
@app.post("/templates/emails", response_model=TemplateEmail, tags=["Emails"])
|
||||||
async def creer_template(template: TemplateEmail):
|
async def creer_template(
|
||||||
|
template: TemplateEmail,
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
template_id = str(uuid.uuid4())
|
template_id = str(uuid.uuid4())
|
||||||
|
|
||||||
templates_email_db[template_id] = {
|
templates_email_db[template_id] = {
|
||||||
|
|
@ -1790,7 +1539,11 @@ async def creer_template(template: TemplateEmail):
|
||||||
@app.put(
|
@app.put(
|
||||||
"/templates/emails/{template_id}", response_model=TemplateEmail, tags=["Emails"]
|
"/templates/emails/{template_id}", response_model=TemplateEmail, tags=["Emails"]
|
||||||
)
|
)
|
||||||
async def modifier_template(template_id: str, template: TemplateEmail):
|
async def modifier_template(
|
||||||
|
template_id: str,
|
||||||
|
template: TemplateEmail,
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
if template_id not in templates_email_db:
|
if template_id not in templates_email_db:
|
||||||
raise HTTPException(404, f"Template {template_id} introuvable")
|
raise HTTPException(404, f"Template {template_id} introuvable")
|
||||||
|
|
||||||
|
|
@ -1811,7 +1564,10 @@ async def modifier_template(template_id: str, template: TemplateEmail):
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/templates/emails/{template_id}", tags=["Emails"])
|
@app.delete("/templates/emails/{template_id}", tags=["Emails"])
|
||||||
async def supprimer_template(template_id: str):
|
async def supprimer_template(
|
||||||
|
template_id: str,
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
if template_id not in templates_email_db:
|
if template_id not in templates_email_db:
|
||||||
raise HTTPException(404, f"Template {template_id} introuvable")
|
raise HTTPException(404, f"Template {template_id} introuvable")
|
||||||
|
|
||||||
|
|
@ -2641,6 +2397,7 @@ async def lister_utilisateurs_debug(
|
||||||
limit: int = Query(100, le=1000),
|
limit: int = Query(100, le=1000),
|
||||||
role: Optional[str] = Query(None),
|
role: Optional[str] = Query(None),
|
||||||
verified_only: bool = Query(False),
|
verified_only: bool = Query(False),
|
||||||
|
user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
from database import User
|
from database import User
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from database import get_session, User, RefreshToken, LoginAttempt
|
from database import get_session, User, RefreshToken, LoginAttempt
|
||||||
|
from core.dependencies import get_current_user
|
||||||
from security.auth import (
|
from security.auth import (
|
||||||
hash_password,
|
hash_password,
|
||||||
verify_password,
|
verify_password,
|
||||||
|
|
@ -19,7 +20,6 @@ from security.auth import (
|
||||||
hash_token,
|
hash_token,
|
||||||
)
|
)
|
||||||
from services.email_service import AuthEmailService
|
from services.email_service import AuthEmailService
|
||||||
from core.dependencies import get_current_user
|
|
||||||
from config.config import settings
|
from config.config import settings
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||||
from fastapi.responses import FileResponse
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select, func, and_
|
from sqlalchemy import select, func
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
|
from core.dependencies import get_current_user
|
||||||
from data.data import templates_signature_email
|
from data.data import templates_signature_email
|
||||||
from email_queue import email_queue
|
from email_queue import email_queue
|
||||||
from database import UniversignSignerStatus, UniversignTransactionStatus, get_session
|
from database import UniversignSignerStatus, UniversignTransactionStatus, get_session
|
||||||
|
|
@ -32,7 +32,9 @@ from schemas import (
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter(prefix="/universign", tags=["Universign"])
|
router = APIRouter(
|
||||||
|
prefix="/universign", tags=["Universign"], dependencies=[Depends(get_current_user)]
|
||||||
|
)
|
||||||
|
|
||||||
sync_service = UniversignSyncService(
|
sync_service = UniversignSyncService(
|
||||||
api_url=settings.universign_api_url, api_key=settings.universign_api_key
|
api_url=settings.universign_api_url, api_key=settings.universign_api_key
|
||||||
|
|
@ -494,14 +496,11 @@ async def sync_all_transactions(
|
||||||
return {"success": True, "stats": stats, "timestamp": datetime.now().isoformat()}
|
return {"success": True, "stats": stats, "timestamp": datetime.now().isoformat()}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/webhook")
|
@router.post("/webhook", dependencies=[])
|
||||||
@router.post("/webhook/")
|
@router.post("/webhook/", dependencies=[])
|
||||||
async def webhook_universign(
|
async def webhook_universign(
|
||||||
request: Request, session: AsyncSession = Depends(get_session)
|
request: Request, session: AsyncSession = Depends(get_session)
|
||||||
):
|
):
|
||||||
"""
|
|
||||||
CORRECTION : Extraction correcte du transaction_id selon la structure réelle d'Universign
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
payload = await request.json()
|
payload = await request.json()
|
||||||
|
|
||||||
|
|
@ -1082,159 +1081,6 @@ async def trouver_transactions_inconsistantes(
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/admin/nettoyer-transactions-erreur", tags=["Admin"])
|
|
||||||
async def nettoyer_transactions_erreur(
|
|
||||||
age_jours: int = Query(
|
|
||||||
7, description="Supprimer les transactions en erreur de plus de X jours"
|
|
||||||
),
|
|
||||||
session: AsyncSession = Depends(get_session),
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
date_limite = datetime.now() - timedelta(days=age_jours)
|
|
||||||
|
|
||||||
query = select(UniversignTransaction).where(
|
|
||||||
and_(
|
|
||||||
UniversignTransaction.local_status == LocalDocumentStatus.ERROR,
|
|
||||||
UniversignTransaction.created_at < date_limite,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await session.execute(query)
|
|
||||||
transactions = result.scalars().all()
|
|
||||||
|
|
||||||
supprimees = []
|
|
||||||
for tx in transactions:
|
|
||||||
supprimees.append(
|
|
||||||
{
|
|
||||||
"transaction_id": tx.transaction_id,
|
|
||||||
"document_id": tx.sage_document_id,
|
|
||||||
"date_creation": tx.created_at.isoformat(),
|
|
||||||
"erreur": tx.sync_error,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await session.delete(tx)
|
|
||||||
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"transactions_supprimees": len(supprimees),
|
|
||||||
"age_limite_jours": age_jours,
|
|
||||||
"details": supprimees,
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur nettoyage: {e}")
|
|
||||||
raise HTTPException(500, str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/debug/webhook-payload/{transaction_id}", tags=["Debug"])
|
|
||||||
async def voir_dernier_webhook(
|
|
||||||
transaction_id: str, session: AsyncSession = Depends(get_session)
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
query = select(UniversignTransaction).where(
|
|
||||||
UniversignTransaction.transaction_id == transaction_id
|
|
||||||
)
|
|
||||||
result = await session.execute(query)
|
|
||||||
tx = result.scalar_one_or_none()
|
|
||||||
|
|
||||||
if not tx:
|
|
||||||
raise HTTPException(404, "Transaction introuvable")
|
|
||||||
|
|
||||||
logs_query = (
|
|
||||||
select(UniversignSyncLog)
|
|
||||||
.where(
|
|
||||||
and_(
|
|
||||||
UniversignSyncLog.transaction_id == tx.id,
|
|
||||||
UniversignSyncLog.sync_type.like("webhook:%"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.order_by(UniversignSyncLog.sync_timestamp.desc())
|
|
||||||
.limit(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
logs_result = await session.execute(logs_query)
|
|
||||||
last_webhook_log = logs_result.scalar_one_or_none()
|
|
||||||
|
|
||||||
if not last_webhook_log:
|
|
||||||
return {
|
|
||||||
"transaction_id": transaction_id,
|
|
||||||
"webhook_recu": tx.webhook_received,
|
|
||||||
"dernier_payload": None,
|
|
||||||
"message": "Aucun webhook reçu pour cette transaction",
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"transaction_id": transaction_id,
|
|
||||||
"webhook_recu": tx.webhook_received,
|
|
||||||
"dernier_webhook": {
|
|
||||||
"timestamp": last_webhook_log.sync_timestamp.isoformat(),
|
|
||||||
"type": last_webhook_log.sync_type,
|
|
||||||
"success": last_webhook_log.success,
|
|
||||||
"payload": json.loads(last_webhook_log.changes_detected)
|
|
||||||
if last_webhook_log.changes_detected
|
|
||||||
else None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur debug webhook: {e}")
|
|
||||||
raise HTTPException(500, str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/transactions/{transaction_id}/document/download", tags=["Documents Signés"]
|
|
||||||
)
|
|
||||||
async def telecharger_document_signe(
|
|
||||||
transaction_id: str, session: AsyncSession = Depends(get_session)
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
query = select(UniversignTransaction).where(
|
|
||||||
UniversignTransaction.transaction_id == transaction_id
|
|
||||||
)
|
|
||||||
result = await session.execute(query)
|
|
||||||
transaction = result.scalar_one_or_none()
|
|
||||||
|
|
||||||
if not transaction:
|
|
||||||
raise HTTPException(404, f"Transaction {transaction_id} introuvable")
|
|
||||||
|
|
||||||
if not transaction.signed_document_path:
|
|
||||||
raise HTTPException(
|
|
||||||
404,
|
|
||||||
"Document signé non disponible localement. "
|
|
||||||
"Utilisez POST /admin/download-missing-documents pour le récupérer.",
|
|
||||||
)
|
|
||||||
|
|
||||||
file_path = Path(transaction.signed_document_path)
|
|
||||||
|
|
||||||
if not file_path.exists():
|
|
||||||
logger.warning(f"Fichier perdu : {file_path}")
|
|
||||||
raise HTTPException(
|
|
||||||
404,
|
|
||||||
"Fichier introuvable sur le serveur. "
|
|
||||||
"Utilisez POST /admin/download-missing-documents pour le récupérer.",
|
|
||||||
)
|
|
||||||
|
|
||||||
download_name = (
|
|
||||||
f"{transaction.sage_document_id}_"
|
|
||||||
f"{transaction.sage_document_type.name}_"
|
|
||||||
f"signe.pdf"
|
|
||||||
)
|
|
||||||
|
|
||||||
return FileResponse(
|
|
||||||
path=str(file_path), media_type="application/pdf", filename=download_name
|
|
||||||
)
|
|
||||||
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur téléchargement document : {e}", exc_info=True)
|
|
||||||
raise HTTPException(500, str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/transactions/{transaction_id}/document/info", tags=["Documents Signés"])
|
@router.get("/transactions/{transaction_id}/document/info", tags=["Documents Signés"])
|
||||||
async def info_document_signe(
|
async def info_document_signe(
|
||||||
transaction_id: str, session: AsyncSession = Depends(get_session)
|
transaction_id: str, session: AsyncSession = Depends(get_session)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import httpx
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, Tuple, List
|
from typing import Optional, Tuple, List
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import false, select, true, update, and_
|
from sqlalchemy import false, select, update, and_
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from config.config import settings
|
from config.config import settings
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue