2026-02-01 09:31:38 +01:00

186 lines
5.2 KiB
Python

"""
Application FastAPI avec authentification intégrée.
Ce fichier remplace main.py pour résoudre le problème de 404.
"""
import json
import secrets
import string
from datetime import datetime
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException, status, Request
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
from pydantic import BaseModel, EmailStr
from passlib.context import CryptContext
from app.database import get_db
from app.models.user import User
# App FastAPI
app = FastAPI(
title="Chartbastan API",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Password hashing - using pbkdf2_sha256 instead of bcrypt for Windows compatibility
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
# ==================== SCHEMAS ====================
class LoginRequest(BaseModel):
email: EmailStr
password: str
class RegisterRequest(BaseModel):
email: EmailStr
password: str
name: Optional[str] = None
referral_code: Optional[str] = None
# ==================== HELPER FUNCTIONS ====================
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)
def generate_referral_code() -> str:
alphabet = string.ascii_uppercase + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(8))
# ==================== ENDPOINTS ====================
@app.get("/")
def read_root():
return {"message": "Chartbastan API"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
@app.post("/api/v1/auth/login")
def login(request: LoginRequest, db: Session = Depends(get_db)):
"""Login endpoint."""
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) if user.created_at else None,
},
"meta": {
"timestamp": datetime.utcnow().isoformat(),
"version": "v1"
}
}
@app.post("/api/v1/auth/register", status_code=status.HTTP_201_CREATED)
def register(request: RegisterRequest, db: Session = Depends(get_db)):
"""Register endpoint."""
existing = db.query(User).filter(User.email == request.email).first()
if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Cet email est déjà utilisé"
)
referral_code = generate_referral_code()
while db.query(User).filter(User.referral_code == referral_code).first():
referral_code = generate_referral_code()
new_user = User(
email=request.email,
password_hash=get_password_hash(request.password),
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():
"""Logout endpoint."""
return {
"data": {"message": "Déconnexion réussie"},
"meta": {
"timestamp": datetime.utcnow().isoformat(),
"version": "v1"
}
}
# ==================== AUTRES ROUTERS ====================
from app.api.v1 import users, predictions, backtesting, leaderboard, badges
from app.api.public.v1 import predictions as public_predictions
from app.api.public.v1 import matches as public_matches
app.include_router(users.router, prefix="/api/v1/users", tags=["users"])
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"])
app.include_router(public_predictions.router)
app.include_router(public_matches.router)