YchKhan commited on
Commit
a413d29
·
verified ·
1 Parent(s): 37bb1ea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -75
app.py CHANGED
@@ -1,23 +1,22 @@
1
  import asyncio
2
- from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
3
- from fastapi.responses import HTMLResponse
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pydantic import BaseModel
7
- from typing import List, Dict
8
  import uvicorn
9
 
10
  # Initialiser l'application FastAPI
11
  app = FastAPI()
12
 
13
  # Configurer CORS pour autoriser toutes les origines
14
- # Permet à n'importe quel site web d'appeler votre API
15
  app.add_middleware(
16
  CORSMiddleware,
17
- allow_origins=["*"], # Autorise toutes les origines
18
  allow_credentials=True,
19
- allow_methods=["*"], # Autorise toutes les méthodes (GET, POST, etc.)
20
- allow_headers=["*"], # Autorise tous les en-têtes
21
  )
22
 
23
  # Monter un répertoire statique pour servir le fichier index.html
@@ -28,114 +27,103 @@ class MockRequest(BaseModel):
28
  parameter: str
29
 
30
  class ConnectionManager:
31
- """Gère les connexions WebSocket actives."""
32
  def __init__(self):
33
  self.active_connections: List[WebSocket] = []
34
- # Dictionnaire pour attendre les réponses des clients
35
  self.response_futures: Dict[str, asyncio.Future] = {}
36
 
37
  async def connect(self, websocket: WebSocket):
38
- """Accepte une nouvelle connexion WebSocket."""
39
  await websocket.accept()
40
  self.active_connections.append(websocket)
41
  print(f"Nouvelle connexion WebSocket. Total: {len(self.active_connections)}")
42
 
43
  def disconnect(self, websocket: WebSocket):
44
- """Ferme une connexion WebSocket."""
 
 
 
 
 
45
  self.active_connections.remove(websocket)
46
  print(f"Déconnexion WebSocket. Total: {len(self.active_connections)}")
47
 
48
- async def broadcast(self, message: str):
49
- """Envoie un message à tous les clients connectés."""
50
- # Pour ce cas simple, nous n'envoyons qu'au premier client connecté
51
- if self.active_connections:
52
- websocket = self.active_connections[0]
53
- await websocket.send_text(message)
54
- # Créer un Future pour attendre la réponse
55
- future = asyncio.get_event_loop().create_future()
56
- # Utilise l'identifiant du client comme clé, bien que simple, c'est plus robuste
57
- client_id = str(id(websocket))
58
- self.response_futures[client_id] = future
59
- return future
60
- return None
61
-
62
- manager = ConnectionManager()
63
 
 
 
64
 
65
- @app.get("/", response_class=HTMLResponse)
66
- async def root():
67
- """Serve the main HTML page."""
68
- try:
69
- with open("static/index.html", "r", encoding="utf-8") as f:
70
- return HTMLResponse(content=f.read())
71
- except FileNotFoundError:
72
- raise HTTPException(status_code=404, detail="index.html not found")
73
 
 
74
 
75
- @app.post("/v1/mock")
76
- async def mock_endpoint(payload: MockRequest):
77
- """
78
- Endpoint API qui prend un string, le transmet via WebSocket,
79
- attend une réponse et la retourne.
80
- """
81
  try:
82
- # Récupérer les données JSON du corps de la requête
83
- # data = await request.json()
84
  input_string = payload.parameter
85
-
86
- if input_string is None:
87
- return {"error": "Le paramètre 'parameter' est manquant."}
88
-
89
- print(f"Endpoint /v1/mock appelé avec: '{input_string}'")
90
 
91
  if not manager.active_connections:
92
  return {"error": "Aucun client WebSocket n'est connecté."}
93
 
94
- # Envoyer le message via WebSocket et obtenir un "future" pour la réponse
95
- print("Envoi du message au client WebSocket...")
96
- response_future = await manager.broadcast(input_string)
97
-
98
- if response_future is None:
99
- return {"error": "Échec de la diffusion du message."}
100
 
101
- try:
102
- # Attendre la réponse du client WebSocket avec un timeout de 10 secondes
103
- websocket_response = await asyncio.wait_for(response_future, timeout=10.0)
104
- print(f"Réponse reçue du WebSocket: '{websocket_response}'")
105
- return {"response_from_client": websocket_response}
106
-
107
- except asyncio.TimeoutError:
108
- print("Timeout: Aucune réponse du client WebSocket.")
109
- return {"error": "Timeout: Le client n'a pas répondu à temps."}
110
 
 
 
 
111
  except Exception as e:
112
- print(f"Erreur dans /v1/mock: {e}")
113
  return {"error": f"Une erreur interne est survenue: {str(e)}"}
114
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  @app.websocket("/ws")
117
  async def websocket_endpoint(websocket: WebSocket):
118
- """Gère la communication WebSocket avec le client."""
119
  await manager.connect(websocket)
120
  try:
121
  while True:
122
- # Attendre un message du client
123
- data = await websocket.receive_text()
124
- print(f"Message reçu du client: '{data}'")
125
-
126
- # Trouver le "future" correspondant et y mettre le résultat
127
- client_id = str(id(websocket))
128
- if client_id in manager.response_futures:
129
- manager.response_futures[client_id].set_result(data)
130
- del manager.response_futures[client_id] # Nettoyer après utilisation
 
 
131
 
132
  except WebSocketDisconnect:
133
  manager.disconnect(websocket)
134
- print("Client déconnecté.")
135
  except Exception as e:
136
  print(f"Erreur dans le WebSocket: {e}")
137
  manager.disconnect(websocket)
138
 
139
-
140
  if __name__ == "__main__":
141
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
  import asyncio
2
+ import uuid
3
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pydantic import BaseModel
7
+ from typing import List, Dict, Any
8
  import uvicorn
9
 
10
  # Initialiser l'application FastAPI
11
  app = FastAPI()
12
 
13
  # Configurer CORS pour autoriser toutes les origines
 
14
  app.add_middleware(
15
  CORSMiddleware,
16
+ allow_origins=["*"],
17
  allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
  )
21
 
22
  # Monter un répertoire statique pour servir le fichier index.html
 
27
  parameter: str
28
 
29
  class ConnectionManager:
30
+ """Gère les connexions WebSocket actives et les réponses en attente."""
31
  def __init__(self):
32
  self.active_connections: List[WebSocket] = []
33
+ # Utilise un ID de requête pour lier les requêtes aux réponses
34
  self.response_futures: Dict[str, asyncio.Future] = {}
35
 
36
  async def connect(self, websocket: WebSocket):
 
37
  await websocket.accept()
38
  self.active_connections.append(websocket)
39
  print(f"Nouvelle connexion WebSocket. Total: {len(self.active_connections)}")
40
 
41
  def disconnect(self, websocket: WebSocket):
42
+ # Annuler tous les futures en attente pour ce client déconnecté
43
+ keys_to_remove = [key for key, (ws, _) in self.response_futures.items() if ws == websocket]
44
+ for key in keys_to_remove:
45
+ self.response_futures[key][1].cancel()
46
+ del self.response_futures[key]
47
+
48
  self.active_connections.remove(websocket)
49
  print(f"Déconnexion WebSocket. Total: {len(self.active_connections)}")
50
 
51
+ async def send_action_and_wait(self, action: str, data: Any):
52
+ """Envoie une action JSON au client et attend une réponse."""
53
+ if not self.active_connections:
54
+ return None
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ request_id = str(uuid.uuid4())
57
+ websocket = self.active_connections[0] # Simplification : on envoie au premier client
58
 
59
+ future = asyncio.get_event_loop().create_future()
60
+ self.response_futures[request_id] = future
61
+
62
+ message_to_send = {"request_id": request_id, "action": action, "data": data}
63
+ await websocket.send_json(message_to_send)
64
+
65
+ return await future
 
66
 
67
+ manager = ConnectionManager()
68
 
69
+ async def handle_api_request(action: str, payload: MockRequest):
70
+ """Factorise la logique commune aux endpoints API."""
 
 
 
 
71
  try:
 
 
72
  input_string = payload.parameter
73
+ print(f"Endpoint pour l'action '{action}' appelé avec: '{input_string}'")
 
 
 
 
74
 
75
  if not manager.active_connections:
76
  return {"error": "Aucun client WebSocket n'est connecté."}
77
 
78
+ print(f"Envoi de l'action '{action}' au client WebSocket...")
79
+ websocket_response = await manager.send_action_and_wait(action, input_string)
80
+
81
+ if websocket_response is None:
82
+ return {"error": "Échec de la communication avec le client."}
 
83
 
84
+ print(f"Réponse reçue du WebSocket pour l'action '{action}': '{websocket_response}'")
85
+ return {"response_from_client": websocket_response}
 
 
 
 
 
 
 
86
 
87
+ except asyncio.CancelledError:
88
+ print("La tâche de réponse a été annulée (probablement déconnexion du client).")
89
+ return {"error": "La requête a été annulée car le client s'est déconnecté."}
90
  except Exception as e:
91
+ print(f"Erreur dans handle_api_request: {e}")
92
  return {"error": f"Une erreur interne est survenue: {str(e)}"}
93
 
94
+ @app.post("/v1/mock")
95
+ async def mock_endpoint(payload: MockRequest):
96
+ """Demande au client une phrase prédéfinie."""
97
+ return await handle_api_request("get_sentence", payload)
98
+
99
+ @app.post("/v1/reverse")
100
+ async def reverse_endpoint(payload: MockRequest):
101
+ """Demande au client d'inverser une chaîne de caractères."""
102
+ return await handle_api_request("reverse_string", payload)
103
+
104
 
105
  @app.websocket("/ws")
106
  async def websocket_endpoint(websocket: WebSocket):
 
107
  await manager.connect(websocket)
108
  try:
109
  while True:
110
+ # Attend une réponse JSON du client
111
+ response_data = await websocket.receive_json()
112
+ request_id = response_data.get("request_id")
113
+ response_payload = response_data.get("response")
114
+
115
+ if request_id and request_id in manager.response_futures:
116
+ # Marque le future comme terminé avec le résultat
117
+ manager.response_futures[request_id].set_result(response_payload)
118
+ del manager.response_futures[request_id]
119
+ else:
120
+ print(f"Réponse reçue avec un ID inconnu ou manquant: {request_id}")
121
 
122
  except WebSocketDisconnect:
123
  manager.disconnect(websocket)
 
124
  except Exception as e:
125
  print(f"Erreur dans le WebSocket: {e}")
126
  manager.disconnect(websocket)
127
 
 
128
  if __name__ == "__main__":
129
  uvicorn.run(app, host="0.0.0.0", port=7860)