Sebastiankay commited on
Commit
4989987
·
verified ·
1 Parent(s): f194199

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +75 -39
server.py CHANGED
@@ -1,30 +1,40 @@
1
- from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import HTMLResponse
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.templating import Jinja2Templates
6
  import json
 
7
  import uvicorn
 
 
8
 
9
- app = FastAPI()
 
 
10
 
11
- # Configure CORS
 
 
12
  app.add_middleware(
13
  CORSMiddleware,
14
- allow_origins=["*"],
15
  allow_credentials=True,
16
  allow_methods=["*"],
17
  allow_headers=["*"],
18
  )
19
 
20
- # Serve static files
21
  app.mount("/static", StaticFiles(directory="static"), name="static")
22
-
23
- # Set up Jinja2 templates
24
  templates = Jinja2Templates(directory="templates")
25
 
26
- with open("data.json", "r", encoding="utf8") as file:
27
- maps_data = json.load(file)
 
 
 
 
 
 
28
 
29
 
30
  @app.get("/", response_class=HTMLResponse)
@@ -35,53 +45,66 @@ async def read_index(request: Request):
35
  @app.get("/map/{normalized_name}", response_class=HTMLResponse)
36
  async def get_map_by_normalized_name(request: Request, normalized_name: str):
37
  map_entry = maps_data.get(normalized_name)
38
- if map_entry:
39
- filtered_maps = [map_entry]
40
- else:
41
- filtered_maps = []
42
  return templates.TemplateResponse("map.html", {"request": request, "maps": filtered_maps})
43
 
44
 
45
  class ConnectionManager:
46
  def __init__(self):
47
- self.groups: dict[str, set[WebSocket]] = {}
48
- self.connections: dict[WebSocket, str] = {}
 
49
 
50
  async def connect(self, websocket: WebSocket):
51
  await websocket.accept()
 
52
 
53
  def disconnect(self, websocket: WebSocket):
54
  if websocket in self.connections:
55
  group_name = self.connections[websocket]
56
  self.remove_from_group(websocket, group_name)
 
57
 
58
  async def join_group(self, websocket: WebSocket, group_name: str):
59
  # Remove from previous group if exists
60
  if websocket in self.connections:
61
  old_group = self.connections[websocket]
62
  self.remove_from_group(websocket, old_group)
63
-
64
  # Add to new group
65
  if group_name not in self.groups:
66
  self.groups[group_name] = set()
67
  self.groups[group_name].add(websocket)
68
  self.connections[websocket] = group_name
69
- print(f"Client joined group '{group_name}'")
70
 
71
  def remove_from_group(self, websocket: WebSocket, group_name: str):
72
  if group_name in self.groups and websocket in self.groups[group_name]:
73
  self.groups[group_name].remove(websocket)
74
- if len(self.groups[group_name]) == 0:
75
  del self.groups[group_name]
76
- print(f"Group '{group_name}' deleted (no members)")
 
77
  if websocket in self.connections:
78
  del self.connections[websocket]
79
- print(f"Client removed from group '{group_name}'")
80
 
81
  async def broadcast_to_group(self, message: str, group_name: str):
82
  if group_name in self.groups:
83
- for connection in self.groups[group_name]:
84
- await connection.send_text(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
 
87
  manager = ConnectionManager()
@@ -93,41 +116,54 @@ async def websocket_endpoint(websocket: WebSocket):
93
  try:
94
  while True:
95
  data = await websocket.receive_text()
 
 
96
  try:
97
  message = json.loads(data)
 
98
  # Handle join messages
99
- if message.get('type') == 'join':
100
- group_name = message.get('group')
101
  if group_name:
102
  await manager.join_group(websocket, group_name)
 
 
103
  else:
104
- error = json.dumps({
105
- "type": "error",
106
- "message": "Missing group name in join request"
107
- })
108
- await websocket.send_text(error)
109
-
 
110
  # Broadcast all other messages to the group
111
  else:
112
  if websocket in manager.connections:
113
  group_name = manager.connections[websocket]
114
  await manager.broadcast_to_group(data, group_name)
115
  else:
116
- error = json.dumps({
117
- "type": "error",
118
- "message": "Join a group before sending messages"
119
- })
120
- await websocket.send_text(error)
121
-
122
  except json.JSONDecodeError:
123
- # Handle non-JSON messages by broadcasting directly
124
  if websocket in manager.connections:
125
  group_name = manager.connections[websocket]
126
  await manager.broadcast_to_group(data, group_name)
127
-
128
  except WebSocketDisconnect:
 
129
  manager.disconnect(websocket)
 
 
 
 
 
 
 
 
 
130
 
131
 
132
  if __name__ == "__main__":
133
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
+ from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect, status
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import HTMLResponse
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.templating import Jinja2Templates
6
  import json
7
+ import asyncio
8
  import uvicorn
9
+ from typing import Dict, Set
10
+ import logging
11
 
12
+ # Logging konfigurieren
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
 
16
+ app = FastAPI(title="EFT Group Map API")
17
+
18
+ # CORS optimiert
19
  app.add_middleware(
20
  CORSMiddleware,
21
+ allow_origins=["*"], # In Produktion spezifische Origins verwenden
22
  allow_credentials=True,
23
  allow_methods=["*"],
24
  allow_headers=["*"],
25
  )
26
 
 
27
  app.mount("/static", StaticFiles(directory="static"), name="static")
 
 
28
  templates = Jinja2Templates(directory="templates")
29
 
30
+ # Caching für Maps-Daten
31
+ maps_data = {}
32
+ try:
33
+ with open("data.json", "r", encoding="utf8") as file:
34
+ maps_data = json.load(file)
35
+ except FileNotFoundError:
36
+ logger.error("data.json nicht gefunden")
37
+ maps_data = {}
38
 
39
 
40
  @app.get("/", response_class=HTMLResponse)
 
45
  @app.get("/map/{normalized_name}", response_class=HTMLResponse)
46
  async def get_map_by_normalized_name(request: Request, normalized_name: str):
47
  map_entry = maps_data.get(normalized_name)
48
+ filtered_maps = [map_entry] if map_entry else []
 
 
 
49
  return templates.TemplateResponse("map.html", {"request": request, "maps": filtered_maps})
50
 
51
 
52
  class ConnectionManager:
53
  def __init__(self):
54
+ self.groups: Dict[str, Set[WebSocket]] = {}
55
+ self.connections: Dict[WebSocket, str] = {}
56
+ self.message_stats = {"total_sent": 0, "total_received": 0}
57
 
58
  async def connect(self, websocket: WebSocket):
59
  await websocket.accept()
60
+ logger.info(f"Neue WebSocket-Verbindung: {websocket.client}")
61
 
62
  def disconnect(self, websocket: WebSocket):
63
  if websocket in self.connections:
64
  group_name = self.connections[websocket]
65
  self.remove_from_group(websocket, group_name)
66
+ logger.info(f"Client disconnected from group '{group_name}'")
67
 
68
  async def join_group(self, websocket: WebSocket, group_name: str):
69
  # Remove from previous group if exists
70
  if websocket in self.connections:
71
  old_group = self.connections[websocket]
72
  self.remove_from_group(websocket, old_group)
73
+
74
  # Add to new group
75
  if group_name not in self.groups:
76
  self.groups[group_name] = set()
77
  self.groups[group_name].add(websocket)
78
  self.connections[websocket] = group_name
79
+ logger.info(f"Client joined group '{group_name}' (Total groups: {len(self.groups)})")
80
 
81
  def remove_from_group(self, websocket: WebSocket, group_name: str):
82
  if group_name in self.groups and websocket in self.groups[group_name]:
83
  self.groups[group_name].remove(websocket)
84
+ if not self.groups[group_name]: # Leere Gruppen entfernen
85
  del self.groups[group_name]
86
+ logger.info(f"Group '{group_name}' deleted (no members)")
87
+
88
  if websocket in self.connections:
89
  del self.connections[websocket]
 
90
 
91
  async def broadcast_to_group(self, message: str, group_name: str):
92
  if group_name in self.groups:
93
+ disconnected = set()
94
+ for connection in self.groups[group_name].copy(): # Kopie für sichere Iteration
95
+ try:
96
+ await connection.send_text(message)
97
+ self.message_stats["total_sent"] += 1
98
+ except Exception as e:
99
+ logger.error(f"Fehler beim Senden an Client: {e}")
100
+ disconnected.add(connection)
101
+
102
+ # Entferne defekte Verbindungen
103
+ for conn in disconnected:
104
+ self.disconnect(conn)
105
+
106
+ def get_stats(self):
107
+ return {"active_groups": len(self.groups), "total_connections": len(self.connections), **self.message_stats}
108
 
109
 
110
  manager = ConnectionManager()
 
116
  try:
117
  while True:
118
  data = await websocket.receive_text()
119
+ manager.message_stats["total_received"] += 1
120
+
121
  try:
122
  message = json.loads(data)
123
+
124
  # Handle join messages
125
+ if message.get("type") == "join":
126
+ group_name = message.get("group")
127
  if group_name:
128
  await manager.join_group(websocket, group_name)
129
+ # Bestätigungsmessage
130
+ await websocket.send_text(json.dumps({"type": "joined", "group": group_name, "message": f"Erfolgreich Gruppe '{group_name}' beigetreten"}))
131
  else:
132
+ error_msg = {"type": "error", "message": "Missing group name in join request"}
133
+ await websocket.send_text(json.dumps(error_msg))
134
+
135
+ # Ping/Pong für Verbindungsprüfung
136
+ elif message.get("type") == "ping":
137
+ await websocket.send_text(json.dumps({"type": "pong"}))
138
+
139
  # Broadcast all other messages to the group
140
  else:
141
  if websocket in manager.connections:
142
  group_name = manager.connections[websocket]
143
  await manager.broadcast_to_group(data, group_name)
144
  else:
145
+ error_msg = {"type": "error", "message": "Join a group before sending messages"}
146
+ await websocket.send_text(json.dumps(error_msg))
147
+
 
 
 
148
  except json.JSONDecodeError:
149
+ logger.warning(f"Invalid JSON received: {data}")
150
  if websocket in manager.connections:
151
  group_name = manager.connections[websocket]
152
  await manager.broadcast_to_group(data, group_name)
153
+
154
  except WebSocketDisconnect:
155
+ logger.info("WebSocket disconnected")
156
  manager.disconnect(websocket)
157
+ except Exception as e:
158
+ logger.error(f"Unerwarteter Fehler: {e}")
159
+ manager.disconnect(websocket)
160
+
161
+
162
+ # Health Check Endpoint
163
+ @app.get("/health")
164
+ async def health_check():
165
+ return {"status": "healthy", "websocket_stats": manager.get_stats()}
166
 
167
 
168
  if __name__ == "__main__":
169
+ uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")