|
|
import asyncio |
|
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request |
|
|
from fastapi.responses import HTMLResponse |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from pydantic import BaseModel |
|
|
from typing import List, Dict |
|
|
import uvicorn |
|
|
|
|
|
|
|
|
app = FastAPI() |
|
|
|
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
|
|
|
class MockRequest(BaseModel): |
|
|
"""Définit la structure attendue pour le corps de la requête POST.""" |
|
|
parameter: str |
|
|
|
|
|
class ConnectionManager: |
|
|
"""Gère les connexions WebSocket actives.""" |
|
|
def __init__(self): |
|
|
self.active_connections: List[WebSocket] = [] |
|
|
|
|
|
self.response_futures: Dict[str, asyncio.Future] = {} |
|
|
|
|
|
async def connect(self, websocket: WebSocket): |
|
|
"""Accepte une nouvelle connexion WebSocket.""" |
|
|
await websocket.accept() |
|
|
self.active_connections.append(websocket) |
|
|
print(f"Nouvelle connexion WebSocket. Total: {len(self.active_connections)}") |
|
|
|
|
|
def disconnect(self, websocket: WebSocket): |
|
|
"""Ferme une connexion WebSocket.""" |
|
|
self.active_connections.remove(websocket) |
|
|
print(f"Déconnexion WebSocket. Total: {len(self.active_connections)}") |
|
|
|
|
|
async def broadcast(self, message: str): |
|
|
"""Envoie un message à tous les clients connectés.""" |
|
|
|
|
|
if self.active_connections: |
|
|
websocket = self.active_connections[0] |
|
|
await websocket.send_text(message) |
|
|
|
|
|
future = asyncio.get_event_loop().create_future() |
|
|
|
|
|
client_id = str(id(websocket)) |
|
|
self.response_futures[client_id] = future |
|
|
return future |
|
|
return None |
|
|
|
|
|
manager = ConnectionManager() |
|
|
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
|
async def root(): |
|
|
"""Serve the main HTML page.""" |
|
|
try: |
|
|
with open("static/index.html", "r", encoding="utf-8") as f: |
|
|
return HTMLResponse(content=f.read()) |
|
|
except FileNotFoundError: |
|
|
raise HTTPException(status_code=404, detail="index.html not found") |
|
|
|
|
|
|
|
|
@app.post("/v1/mock") |
|
|
async def mock_endpoint(payload: MockRequest): |
|
|
""" |
|
|
Endpoint API qui prend un string, le transmet via WebSocket, |
|
|
attend une réponse et la retourne. |
|
|
""" |
|
|
try: |
|
|
|
|
|
|
|
|
input_string = payload.parameter |
|
|
|
|
|
if input_string is None: |
|
|
return {"error": "Le paramètre 'parameter' est manquant."} |
|
|
|
|
|
print(f"Endpoint /v1/mock appelé avec: '{input_string}'") |
|
|
|
|
|
if not manager.active_connections: |
|
|
return {"error": "Aucun client WebSocket n'est connecté."} |
|
|
|
|
|
|
|
|
print("Envoi du message au client WebSocket...") |
|
|
response_future = await manager.broadcast(input_string) |
|
|
|
|
|
if response_future is None: |
|
|
return {"error": "Échec de la diffusion du message."} |
|
|
|
|
|
try: |
|
|
|
|
|
websocket_response = await asyncio.wait_for(response_future, timeout=10.0) |
|
|
print(f"Réponse reçue du WebSocket: '{websocket_response}'") |
|
|
return {"response_from_client": websocket_response} |
|
|
|
|
|
except asyncio.TimeoutError: |
|
|
print("Timeout: Aucune réponse du client WebSocket.") |
|
|
return {"error": "Timeout: Le client n'a pas répondu à temps."} |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Erreur dans /v1/mock: {e}") |
|
|
return {"error": f"Une erreur interne est survenue: {str(e)}"} |
|
|
|
|
|
|
|
|
@app.websocket("/ws") |
|
|
async def websocket_endpoint(websocket: WebSocket): |
|
|
"""Gère la communication WebSocket avec le client.""" |
|
|
await manager.connect(websocket) |
|
|
try: |
|
|
while True: |
|
|
|
|
|
data = await websocket.receive_text() |
|
|
print(f"Message reçu du client: '{data}'") |
|
|
|
|
|
|
|
|
client_id = str(id(websocket)) |
|
|
if client_id in manager.response_futures: |
|
|
manager.response_futures[client_id].set_result(data) |
|
|
del manager.response_futures[client_id] |
|
|
|
|
|
except WebSocketDisconnect: |
|
|
manager.disconnect(websocket) |
|
|
print("Client déconnecté.") |
|
|
except Exception as e: |
|
|
print(f"Erreur dans le WebSocket: {e}") |
|
|
manager.disconnect(websocket) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
uvicorn.run(app, host="0.0.0.0", port=7860) |