File size: 5,064 Bytes
e3fc855
a413d29
 
e3fc855
 
1c4883e
a413d29
e3fc855
 
 
 
 
 
 
 
a413d29
e3fc855
a413d29
 
e3fc855
 
 
 
 
be51c46
 
 
 
e3fc855
a413d29
e3fc855
 
a413d29
e3fc855
 
 
 
 
 
 
 
a413d29
 
 
 
 
 
e3fc855
 
 
a413d29
 
 
 
e3fc855
a413d29
 
330e345
a413d29
 
 
 
 
 
 
330e345
a413d29
330e345
a413d29
 
e3fc855
37bb1ea
a413d29
e3fc855
 
 
 
a413d29
 
 
 
 
e3fc855
a413d29
 
e3fc855
a413d29
 
 
e3fc855
a413d29
e3fc855
 
a413d29
 
 
 
 
 
 
 
 
 
e3fc855
 
 
 
 
 
a413d29
 
 
 
 
 
 
 
 
 
 
e3fc855
 
 
 
 
 
 
 
cc2d90e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import asyncio
import uuid
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Any
import uvicorn

# Initialiser l'application FastAPI
app = FastAPI()

# Configurer CORS pour autoriser toutes les origines
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Monter un répertoire statique pour servir le fichier index.html
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 et les réponses en attente."""
    def __init__(self):
        self.active_connections: List[WebSocket] = []
        # Utilise un ID de requête pour lier les requêtes aux réponses
        self.response_futures: Dict[str, asyncio.Future] = {}

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)
        print(f"Nouvelle connexion WebSocket. Total: {len(self.active_connections)}")

    def disconnect(self, websocket: WebSocket):
        # Annuler tous les futures en attente pour ce client déconnecté
        keys_to_remove = [key for key, (ws, _) in self.response_futures.items() if ws == websocket]
        for key in keys_to_remove:
            self.response_futures[key][1].cancel()
            del self.response_futures[key]
        
        self.active_connections.remove(websocket)
        print(f"Déconnexion WebSocket. Total: {len(self.active_connections)}")

    async def send_action_and_wait(self, action: str, data: Any):
        """Envoie une action JSON au client et attend une réponse."""
        if not self.active_connections:
            return None

        request_id = str(uuid.uuid4())
        websocket = self.active_connections[0] # Simplification : on envoie au premier client

        future = asyncio.get_event_loop().create_future()
        self.response_futures[request_id] = future
        
        message_to_send = {"request_id": request_id, "action": action, "data": data}
        await websocket.send_json(message_to_send)
        
        return await future

manager = ConnectionManager()

async def handle_api_request(action: str, payload: MockRequest):
    """Factorise la logique commune aux endpoints API."""
    try:
        input_string = payload.parameter
        print(f"Endpoint pour l'action '{action}' appelé avec: '{input_string}'")

        if not manager.active_connections:
            return {"error": "Aucun client WebSocket n'est connecté."}

        print(f"Envoi de l'action '{action}' au client WebSocket...")
        websocket_response = await manager.send_action_and_wait(action, input_string)
        
        if websocket_response is None:
             return {"error": "Échec de la communication avec le client."}

        print(f"Réponse reçue du WebSocket pour l'action '{action}': '{websocket_response}'")
        return {"response_from_client": websocket_response}

    except asyncio.CancelledError:
        print("La tâche de réponse a été annulée (probablement déconnexion du client).")
        return {"error": "La requête a été annulée car le client s'est déconnecté."}
    except Exception as e:
        print(f"Erreur dans handle_api_request: {e}")
        return {"error": f"Une erreur interne est survenue: {str(e)}"}

@app.post("/v1/mock")
async def mock_endpoint(payload: MockRequest):
    """Demande au client une phrase prédéfinie."""
    return await handle_api_request("get_sentence", payload)

@app.post("/v1/reverse")
async def reverse_endpoint(payload: MockRequest):
    """Demande au client d'inverser une chaîne de caractères."""
    return await handle_api_request("reverse_string", payload)


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            # Attend une réponse JSON du client
            response_data = await websocket.receive_json()
            request_id = response_data.get("request_id")
            response_payload = response_data.get("response")
            
            if request_id and request_id in manager.response_futures:
                # Marque le future comme terminé avec le résultat
                manager.response_futures[request_id].set_result(response_payload)
                del manager.response_futures[request_id]
            else:
                print(f"Réponse reçue avec un ID inconnu ou manquant: {request_id}")

    except WebSocketDisconnect:
        manager.disconnect(websocket)
    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)