Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, Request, HTTPException | |
| from fastmcp import FastMCP | |
| from authlib.integrations.starlette_client import OAuth | |
| from starlette.middleware.sessions import SessionMiddleware | |
| import os | |
| from typing import Dict, Any, Optional | |
| # Validation des variables d'environnement | |
| required_env_vars = ["CLIENT_ID", "CLIENT_SECRET"] | |
| missing_vars = [var for var in required_env_vars if not os.getenv(var)] | |
| if missing_vars: | |
| raise ValueError(f"Variables d'environnement manquantes: {', '.join(missing_vars)}") | |
| app = FastAPI() | |
| app.add_middleware( | |
| SessionMiddleware, | |
| secret_key="CHANGE_ME" | |
| ) | |
| # --- Config OAuth Twitter --- | |
| oauth = OAuth() | |
| oauth.register( | |
| name='twitter', | |
| client_id=os.getenv("CLIENT_ID"), | |
| client_secret=os.getenv("CLIENT_SECRET"), | |
| authorize_url="https://twitter.com/i/oauth2/authorize", | |
| access_token_url="https://api.twitter.com/2/oauth2/token", | |
| client_kwargs={ | |
| "scope": "tweet.read users.read offline.access", | |
| "token_endpoint_auth_method": "client_secret_post" | |
| } | |
| ) | |
| # --- Fonction d'authentification pour MCP --- | |
| async def mcp_auth(request: Request) -> Optional[Dict[str, Any]]: | |
| """ | |
| Fonction d'authentification pour FastMCP | |
| Retourne les données utilisateur si authentifié, None sinon | |
| """ | |
| user = request.session.get('user') | |
| if not user: | |
| return None | |
| return user | |
| # --- FastMCP Server avec auth intégrée --- | |
| mcp = FastMCP( | |
| name="TwitterMCP", | |
| stateless_http=True, | |
| auth=mcp_auth | |
| ) | |
| async def hello() -> str: | |
| """Fonction de test MCP""" | |
| return "Hello depuis FastMCP 🎉" | |
| async def get_current_user(request: Request) -> Dict[str, Any]: | |
| """Retourne les informations de l'utilisateur authentifié""" | |
| user = request.session.get('user', {}) | |
| return { | |
| "username": user.get('username'), | |
| "id": user.get('id'), | |
| "name": user.get('name') | |
| } | |
| async def tweet_something(request: Request, message: str) -> Dict[str, str]: | |
| """ | |
| Exemple d'outil qui pourrait tweeter (nécessiterait les bonnes permissions) | |
| """ | |
| user = request.session.get('user', {}) | |
| # Ici on pourrait utiliser le token stocké pour poster un tweet | |
| return { | |
| "status": "success", | |
| "message": f"Tweet simulé de @{user.get('username', 'unknown')}: {message}" | |
| } | |
| # --- Routes OAuth --- | |
| async def login(request: Request): | |
| """Initie le processus d'authentification Twitter""" | |
| try: | |
| redirect_uri = request.url_for("auth_callback") | |
| return await oauth.twitter.authorize_redirect(request, redirect_uri) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Erreur lors de l'initialisation OAuth: {str(e)}") | |
| async def auth_callback(request: Request) -> Dict[str, str]: | |
| """Gère le callback OAuth et stocke les infos utilisateur""" | |
| try: | |
| token = await oauth.twitter.authorize_access_token(request) | |
| # Récupère les infos de l'utilisateur | |
| user_req = await oauth.twitter.get( | |
| "https://api.twitter.com/2/users/me", | |
| token=token | |
| ) | |
| if user_req.status_code != 200: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Impossible de récupérer les informations utilisateur" | |
| ) | |
| userinfo = user_req.json().get('data', {}) | |
| if not userinfo: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Données utilisateur invalides" | |
| ) | |
| # Stocke les infos utilisateur et le token en session | |
| request.session['user'] = userinfo | |
| request.session['token'] = token | |
| return {"message": f"Connecté en tant que @{userinfo.get('username', 'utilisateur')}"} | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Erreur lors de l'authentification: {str(e)}") | |
| async def logout(request: Request): | |
| """Déconnecte l'utilisateur""" | |
| request.session.clear() | |
| return {"message": "Déconnecté avec succès"} | |
| async def get_profile(request: Request): | |
| """Retourne le profil de l'utilisateur connecté""" | |
| user = request.session.get('user') | |
| if not user: | |
| raise HTTPException(status_code=401, detail="Non authentifié") | |
| return {"user": user} | |
| # --- Route de santé --- | |
| async def health_check(): | |
| """Vérification de l'état de l'application""" | |
| return {"status": "ok", "message": "Service opérationnel"} | |
| # --- Intégration FastMCP dans FastAPI --- | |
| app.mount("/mcp", mcp.app) | |