YchKhan commited on
Commit
0c8a459
·
verified ·
1 Parent(s): 588855b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -71
app.py CHANGED
@@ -1,23 +1,23 @@
1
  import asyncio
2
- import uuid
3
- from fastapi import FastAPI, WebSocket, WebSocketDisconnect
4
- from fastapi.staticfiles import StaticFiles
5
  from fastapi.responses import HTMLResponse
 
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from pydantic import BaseModel
8
- from typing import List, Dict, Any
9
  import uvicorn
10
 
11
  # Initialiser l'application FastAPI
12
  app = FastAPI()
13
 
14
  # Configurer CORS pour autoriser toutes les origines
 
15
  app.add_middleware(
16
  CORSMiddleware,
17
- allow_origins=["*"],
18
  allow_credentials=True,
19
- allow_methods=["*"],
20
- allow_headers=["*"],
21
  )
22
 
23
  # Monter un répertoire statique pour servir le fichier index.html
@@ -28,69 +28,39 @@ class MockRequest(BaseModel):
28
  parameter: str
29
 
30
  class ConnectionManager:
31
- """Gère les connexions WebSocket actives et les réponses en attente."""
32
  def __init__(self):
33
  self.active_connections: List[WebSocket] = []
34
- # Utilise un ID de requête pour lier les requêtes aux réponses
35
  self.response_futures: Dict[str, asyncio.Future] = {}
36
 
37
  async def connect(self, websocket: WebSocket):
 
38
  await websocket.accept()
39
  self.active_connections.append(websocket)
40
  print(f"Nouvelle connexion WebSocket. Total: {len(self.active_connections)}")
41
 
42
  def disconnect(self, websocket: WebSocket):
43
- # Annuler tous les futures en attente pour ce client déconnecté
44
- keys_to_remove = [key for key, (ws, _) in self.response_futures.items() if ws == websocket]
45
- for key in keys_to_remove:
46
- self.response_futures[key][1].cancel()
47
- del self.response_futures[key]
48
-
49
  self.active_connections.remove(websocket)
50
  print(f"Déconnexion WebSocket. Total: {len(self.active_connections)}")
51
 
52
- async def send_action_and_wait(self, action: str, data: Any):
53
- """Envoie une action JSON au client et attend une réponse."""
54
- if not self.active_connections:
55
- return None
56
-
57
- request_id = str(uuid.uuid4())
58
- websocket = self.active_connections[0] # Simplification : on envoie au premier client
59
-
60
- future = asyncio.get_event_loop().create_future()
61
- self.response_futures[request_id] = future
62
-
63
- message_to_send = {"request_id": request_id, "action": action, "data": data}
64
- await websocket.send_json(message_to_send)
65
-
66
- return await future
67
 
68
  manager = ConnectionManager()
69
 
70
- async def handle_api_request(action: str, payload: MockRequest):
71
- """Factorise la logique commune aux endpoints API."""
72
- try:
73
- input_string = payload.parameter
74
- print(f"Endpoint pour l'action '{action}' appelé avec: '{input_string}'")
75
-
76
- if not manager.active_connections:
77
- return {"error": "Aucun client WebSocket n'est connecté."}
78
-
79
- print(f"Envoi de l'action '{action}' au client WebSocket...")
80
- websocket_response = await manager.send_action_and_wait(action, input_string)
81
-
82
- if websocket_response is None:
83
- return {"error": "Échec de la communication avec le client."}
84
-
85
- print(f"Réponse reçue du WebSocket pour l'action '{action}': '{websocket_response}'")
86
- return {"response_from_client": websocket_response}
87
-
88
- except asyncio.CancelledError:
89
- print("La tâche de réponse a été annulée (probablement déconnexion du client).")
90
- return {"error": "La requête a été annulée car le client s'est déconnecté."}
91
- except Exception as e:
92
- print(f"Erreur dans handle_api_request: {e}")
93
- return {"error": f"Une erreur interne est survenue: {str(e)}"}
94
 
95
  @app.get("/", response_class=HTMLResponse)
96
  async def root():
@@ -101,39 +71,71 @@ async def root():
101
  except FileNotFoundError:
102
  raise HTTPException(status_code=404, detail="index.html not found")
103
 
 
104
  @app.post("/v1/mock")
105
  async def mock_endpoint(payload: MockRequest):
106
- """Demande au client une phrase prédéfinie."""
107
- return await handle_api_request("get_sentence", payload)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- @app.post("/v1/reverse")
110
- async def reverse_endpoint(payload: MockRequest):
111
- """Demande au client d'inverser une chaîne de caractères."""
112
- return await handle_api_request("reverse_string", payload)
 
 
 
 
 
 
 
 
 
113
 
114
 
115
  @app.websocket("/ws")
116
  async def websocket_endpoint(websocket: WebSocket):
 
117
  await manager.connect(websocket)
118
  try:
119
  while True:
120
- # Attend une réponse JSON du client
121
- response_data = await websocket.receive_json()
122
- request_id = response_data.get("request_id")
123
- response_payload = response_data.get("response")
124
-
125
- if request_id and request_id in manager.response_futures:
126
- # Marque le future comme terminé avec le résultat
127
- manager.response_futures[request_id].set_result(response_payload)
128
- del manager.response_futures[request_id]
129
- else:
130
- print(f"Réponse reçue avec un ID inconnu ou manquant: {request_id}")
131
 
132
  except WebSocketDisconnect:
133
  manager.disconnect(websocket)
 
134
  except Exception as e:
135
  print(f"Erreur dans le WebSocket: {e}")
136
  manager.disconnect(websocket)
137
 
 
138
  if __name__ == "__main__":
139
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
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
  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():
 
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)