from fastapi import FastAPI, Query from fastapi.middleware.cors import CORSMiddleware from typing import List, Optional import os import typesense from utils.config import TYPESENSE_HOST, TYPESENSE_PORT, TYPESENSE_API_KEY from db.postgres_connector import SessionLocal from db.models import Document from functools import lru_cache app = FastAPI(title="Scrap-Dji Turbo API") # Configuration CORS pour permettre les requêtes depuis le frontend app.add_middleware( CORSMiddleware, allow_origins=["*"], # En production, spécifier les domaines autorisés allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Cache LRU pour les requêtes ultra-fréquentes (Zéro latence) @lru_cache(maxsize=100) def get_cached_search(q: str, pays: Optional[str]): # Cette fonction est appelée par le endpoint async return None # Placeholder pour la logique interne ts_client = typesense.Client({ 'nodes': [{'host': TYPESENSE_HOST, 'port': TYPESENSE_PORT, 'protocol': 'http'}], 'api_key': TYPESENSE_API_KEY, 'connection_timeout_seconds': 1 }) SEARCH_CACHE = {} @app.get("/search") async def search(q: str = Query(...), pays: Optional[str] = None): # Check simple cache manuel (plus rapide pour l'async) cache_key = f"{q}_{pays}" if cache_key in SEARCH_CACHE: return SEARCH_CACHE[cache_key] try: res = ts_client.collections['documents'].documents.search({ 'q': q, 'query_by': 'titre,texte', 'filter_by': f'pays:={pays}' if pays else '' }) hits = res['hits'] SEARCH_CACHE[cache_key] = hits # Mise en cache return hits except: # Fallback final : Recherche dans le fichier JSON local (Mode Test) local_file = "data/search_index.json" if os.path.exists(local_file): import json with open(local_file, "r", encoding="utf-8") as f: data = json.load(f) results = [d for d in data if q.lower() in d['titre'].lower() or q.lower() in d['texte'].lower()] if pays: results = [d for d in results if d.get('pays') == pays] return results[:10] # Fallback SQL Optimisé (Indexé) - essayera quand même si Postgres est là try: session = SessionLocal() results = session.query(Document).filter(Document.titre.ilike(f"%{q}%")).limit(5).all() session.close() return results except: return [] @app.get("/health") def health(): return {"status": "turbo-charged"}