186 lines
5.2 KiB
Python
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)
|