"""Application FastAPI principale.""" import json from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from datetime import datetime # #region agent log def write_debug_log(hypothesisId: str, location: str, message: str, data: dict = None): """Écrit un log NDJSON pour le debug.""" log_entry = { "sessionId": "debug-session", "runId": "run1", "hypothesisId": hypothesisId, "location": location, "message": message, "data": data or {}, "timestamp": datetime.now().timestamp() * 1000 } with open(r"d:\\dev_new_pc\\chartbastan\\.cursor\\debug.log", "a") as f: f.write(json.dumps(log_entry) + "\n") # #endregion app = FastAPI( title="Chartbastan API", description=""" API publique pour les prédictions de matchs basées sur l'énergie collective. ## Endpoints Publics - **Prédictions**: Accès aux prédictions de matchs avec filtres - **Matchs**: Liste des matchs avec filtres par ligue et statut ## Authentification Les endpoints publics ne nécessitent pas d'authentification. Pour un usage intensif, générez une clé API via le dashboard développeur. """, version="1.0.0", docs_url="/docs", redoc_url="/redoc", openapi_url="/openapi.json" ) # #region agent log write_debug_log("A", "main.py:1", "FastAPI app initialized", {"app_title": "Chartbastan API"}) # #endregion # Rate limiting middleware DÉSACTIVÉ PERMANENTMENT # app.add_middleware(RateLimitMiddleware, public_limit=10, authenticated_limit=100) # #region agent log write_debug_log("E", "main.py:27", "RateLimitMiddleware DISABLED permanently", {"status": "disabled"}) # #endregion # CORS configuration app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allow all origins for public API allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # #region agent log write_debug_log("E", "main.py:35", "CORSMiddleware configured", {"allow_origins": ["*"], "allow_methods": ["*"], "allow_headers": ["*"]}) # #endregion @app.middleware("http") async def log_requests(request: Request, call_next): """Log toutes les requêtes entrantes pour diagnostic.""" start_time = datetime.now() write_debug_log("A", "main.py:44", "Incoming request", { "method": request.method, "url": str(request.url), "path": request.url.path, "headers": dict(request.headers) }) response = await call_next(request) process_time = (datetime.now() - start_time).total_seconds() write_debug_log("A", "main.py:44", "Request completed", { "method": request.method, "url": str(request.url), "path": request.url.path, "status_code": response.status_code, "process_time": process_time }) return response @app.get("/") def read_root(): """Endpoint racine de l'API.""" write_debug_log("A", "main.py:58", "Root endpoint called", {"path": "/"}) return {"message": "Chartbastan API"} @app.get("/health") def health_check(): """Endpoint de vérification de santé.""" return {"status": "healthy"} # ============================================================ # AUTH ENDPOINTS - Définis directement ici pour garantir l'enregistrement # ============================================================ from fastapi import Depends, HTTPException, status from sqlalchemy.orm import Session from pydantic import BaseModel, EmailStr from typing import Optional from passlib.context import CryptContext from app.database import get_db from app.models.user import User pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") class LoginRequest(BaseModel): email: EmailStr password: str class RegisterRequest(BaseModel): email: EmailStr password: str name: Optional[str] = None referral_code: Optional[str] = None def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: return pwd_context.hash(password) @app.post("/api/v1/auth/login") def login_user(request: LoginRequest, db: Session = Depends(get_db)): """Connecter un utilisateur.""" user = db.query(User).filter(User.email == request.email).first() if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Email ou mot de passe incorrect" ) if not user.password_hash: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Email ou mot de passe incorrect" ) if not verify_password(request.password, user.password_hash): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Email ou mot de passe incorrect" ) return { "data": { "id": user.id, "email": user.email, "name": user.name, "is_premium": user.is_premium, "referral_code": user.referral_code, "created_at": str(user.created_at), }, "meta": { "timestamp": datetime.utcnow().isoformat(), "version": "v1" } } @app.post("/api/v1/auth/register", status_code=status.HTTP_201_CREATED) def register_user(request: RegisterRequest, db: Session = Depends(get_db)): """Inscrire un nouvel utilisateur.""" import secrets import string existing_user = db.query(User).filter(User.email == request.email).first() if existing_user: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Cet email est déjà utilisé" ) # Générer code de parrainage alphabet = string.ascii_uppercase + string.digits referral_code = ''.join(secrets.choice(alphabet) for _ in range(8)) password_hash = get_password_hash(request.password) new_user = User( email=request.email, password_hash=password_hash, name=request.name, is_premium=False, referral_code=referral_code, created_at=datetime.utcnow(), updated_at=datetime.utcnow() ) db.add(new_user) db.commit() db.refresh(new_user) return { "data": { "id": new_user.id, "email": new_user.email, "name": new_user.name, "is_premium": new_user.is_premium, "referral_code": new_user.referral_code, "created_at": str(new_user.created_at), }, "meta": { "timestamp": datetime.utcnow().isoformat(), "version": "v1" } } @app.post("/api/v1/auth/logout") def logout_user(): """Déconnecter l'utilisateur.""" return { "data": {"message": "Déconnexion réussie"}, "meta": { "timestamp": datetime.utcnow().isoformat(), "version": "v1" } } # ============================================================ # FIN AUTH ENDPOINTS # ============================================================ # Include API routers from app.api.v1 import users from app.api.v1 import auth from app.api.v1 import predictions from app.api.v1 import backtesting from app.api.v1 import leaderboard from app.api.v1 import badges from app.api.public.v1 import predictions as public_predictions from app.api.public.v1 import matches as public_matches # #region agent log write_debug_log("C", "main.py:101", "Including routers", { "routers": ["users", "auth", "predictions", "backtesting", "leaderboard", "badges", "public_predictions", "public_matches"] }) # #endregion app.include_router(users.router, prefix="/api/v1/users", tags=["users"]) app.include_router(auth.router, prefix="/api/v1/auth", tags=["auth"]) app.include_router(predictions.router) app.include_router(backtesting.router) app.include_router(leaderboard.router) app.include_router(badges.router, prefix="/api/v1/badges", tags=["badges"]) # Public API routers app.include_router(public_predictions.router) app.include_router(public_matches.router) # Trigger reload