GamerC0der commited on
Commit
3c55128
·
verified ·
1 Parent(s): 8fd826a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -0
app.py ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ from http.server import HTTPServer, BaseHTTPRequestHandler, ThreadingHTTPServer
4
+ import json
5
+ import sys
6
+ import os
7
+ import random
8
+ import threading
9
+ import time
10
+ from datetime import datetime, timedelta
11
+
12
+ sessions = {}
13
+ clients = {}
14
+
15
+ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
16
+ def _add_cors_headers(self):
17
+ self.send_header('Access-Control-Allow-Origin', '*')
18
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
19
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type')
20
+
21
+ def do_OPTIONS(self):
22
+ self.send_response(200)
23
+ self._add_cors_headers()
24
+ self.end_headers()
25
+
26
+ def do_GET(self):
27
+ if self.path == '/':
28
+ self.send_response(200)
29
+ self.send_header('Content-type', 'application/json')
30
+ self._add_cors_headers()
31
+ self.end_headers()
32
+ response = {'message': 'API Server is running!', 'status': 'ok'}
33
+ self.wfile.write(json.dumps(response).encode())
34
+ elif self.path == '/health':
35
+ self.send_response(200)
36
+ self.send_header('Content-type', 'application/json')
37
+ self._add_cors_headers()
38
+ self.end_headers()
39
+ response = {'status': 'healthy', 'active_sessions': len(sessions)}
40
+ self.wfile.write(json.dumps(response).encode())
41
+ elif self.path.startswith('/api/session'):
42
+ self.handle_session_get()
43
+ elif self.path.startswith('/api/events/'):
44
+ self.handle_sse_connection()
45
+ else:
46
+ self.send_response(404)
47
+ self.send_header('Content-type', 'application/json')
48
+ self._add_cors_headers()
49
+ self.end_headers()
50
+ response = {'error': 'Endpoint not found'}
51
+ self.wfile.write(json.dumps(response).encode())
52
+
53
+ def do_POST(self):
54
+ if self.path == '/api/session':
55
+ self.handle_session_post()
56
+ elif self.path == '/api/message':
57
+ self.handle_message_post()
58
+ elif self.path.startswith('/api/session/'):
59
+ pass
60
+ else:
61
+ content_length = int(self.headers['Content-Length']) if self.headers.get('Content-Length') else 0
62
+ post_data = self.rfile.read(content_length) if content_length > 0 else b''
63
+
64
+ try:
65
+ data = json.loads(post_data.decode()) if post_data else {}
66
+ except json.JSONDecodeError:
67
+ self.send_response(400)
68
+ self.send_header('Content-type', 'application/json')
69
+ self.end_headers()
70
+ response = {'error': 'Invalid JSON'}
71
+ self.wfile.write(json.dumps(response).encode())
72
+ return
73
+
74
+ self.send_response(200)
75
+ self.send_header('Content-type', 'application/json')
76
+ self._add_cors_headers()
77
+ self.end_headers()
78
+ response = {'message': 'Data received', 'data': data}
79
+ self.wfile.write(json.dumps(response).encode())
80
+
81
+ def handle_session_get(self):
82
+ path_parts = self.path.split('/')
83
+ if len(path_parts) >= 4 and path_parts[3]:
84
+ session_code = path_parts[3].upper()
85
+ if session_code in sessions:
86
+ self.send_response(200)
87
+ self.send_header('Content-type', 'application/json')
88
+ self._add_cors_headers()
89
+ self.end_headers()
90
+ response = {'exists': True, 'session_code': session_code}
91
+ self.wfile.write(json.dumps(response).encode())
92
+ else:
93
+ self.send_response(404)
94
+ self.send_header('Content-type', 'application/json')
95
+ self._add_cors_headers()
96
+ self.end_headers()
97
+ response = {'exists': False, 'session_code': session_code}
98
+ self.wfile.write(json.dumps(response).encode())
99
+ else:
100
+ self.send_response(400)
101
+ self.send_header('Content-type', 'application/json')
102
+ self._add_cors_headers()
103
+ self.end_headers()
104
+ response = {'error': 'Missing session code'}
105
+ self.wfile.write(json.dumps(response).encode())
106
+
107
+ def handle_session_post(self):
108
+ content_length = int(self.headers['Content-Length']) if self.headers.get('Content-Length') else 0
109
+ post_data = self.rfile.read(content_length) if content_length > 0 else b''
110
+
111
+ try:
112
+ data = json.loads(post_data.decode()) if post_data else {}
113
+ except json.JSONDecodeError:
114
+ self.send_response(400)
115
+ self.send_header('Content-type', 'application/json')
116
+ self.end_headers()
117
+ response = {'error': 'Invalid JSON'}
118
+ self.wfile.write(json.dumps(response).encode())
119
+ return
120
+ session_code = None
121
+ attempts = 0
122
+ while session_code is None and attempts < 100:
123
+ code = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', k=4))
124
+ if code not in sessions:
125
+ session_code = code
126
+ attempts += 1
127
+
128
+ if session_code is None:
129
+ self.send_response(500)
130
+ self.send_header('Content-type', 'application/json')
131
+ self._add_cors_headers()
132
+ self.end_headers()
133
+ response = {'error': 'Could not generate unique session code'}
134
+ self.wfile.write(json.dumps(response).encode())
135
+ return
136
+ sessions[session_code] = {
137
+ 'clients': [],
138
+ 'created_at': datetime.now(),
139
+ 'host': data.get('client_id', 'unknown')
140
+ }
141
+
142
+ self.send_response(201)
143
+ self.send_header('Content-type', 'application/json')
144
+ self._add_cors_headers()
145
+ self.end_headers()
146
+ response = {'session_code': session_code, 'created': True}
147
+ self.wfile.write(json.dumps(response).encode())
148
+
149
+ def handle_sse_connection(self):
150
+ path_parts = self.path.split('/')
151
+ if len(path_parts) >= 5 and path_parts[3] and path_parts[4]:
152
+ session_code = path_parts[3].upper()
153
+ client_id = path_parts[4]
154
+
155
+ if session_code in sessions and client_id:
156
+ if client_id not in sessions[session_code]['clients']:
157
+ sessions[session_code]['clients'].append(client_id)
158
+
159
+ clients[client_id] = {
160
+ 'session': session_code,
161
+ 'connection': self
162
+ }
163
+
164
+ self.send_response(200)
165
+ self.send_header('Content-Type', 'text/event-stream')
166
+ self.send_header('Cache-Control', 'no-cache')
167
+ self.send_header('Connection', 'keep-alive')
168
+ self._add_cors_headers()
169
+ self.end_headers()
170
+
171
+ try:
172
+ while True:
173
+ time.sleep(30)
174
+ self.wfile.write(b': keepalive\n\n')
175
+ self.wfile.flush()
176
+ except:
177
+ if client_id in clients:
178
+ if client_id in sessions[session_code]['clients']:
179
+ sessions[session_code]['clients'].remove(client_id)
180
+ del clients[client_id]
181
+ else:
182
+ self.send_response(404)
183
+ self.send_header('Content-type', 'application/json')
184
+ self._add_cors_headers()
185
+ self.end_headers()
186
+ response = {'error': 'Session not found or missing client ID'}
187
+ self.wfile.write(json.dumps(response).encode())
188
+ else:
189
+ self.send_response(400)
190
+ self.send_header('Content-type', 'application/json')
191
+ self._add_cors_headers()
192
+ self.end_headers()
193
+ response = {'error': 'Missing session code or client ID'}
194
+ self.wfile.write(json.dumps(response).encode())
195
+
196
+ def handle_message_post(self):
197
+ content_length = int(self.headers['Content-Length']) if self.headers.get('Content-Length') else 0
198
+ post_data = self.rfile.read(content_length) if content_length > 0 else b''
199
+
200
+ try:
201
+ data = json.loads(post_data.decode())
202
+ except json.JSONDecodeError:
203
+ self.send_response(400)
204
+ self.send_header('Content-type', 'application/json')
205
+ self._add_cors_headers()
206
+ self.end_headers()
207
+ response = {'error': 'Invalid JSON'}
208
+ self.wfile.write(json.dumps(response).encode())
209
+ return
210
+
211
+ client_id = data.get('client_id')
212
+ session_code = data.get('session_code')
213
+
214
+ if session_code and session_code in sessions:
215
+ broadcast_message = {
216
+ 'type': 'message',
217
+ 'client_id': client_id,
218
+ 'username': data.get('username', f'User-{client_id[:8]}'),
219
+ 'content': data['content'],
220
+ 'timestamp': datetime.now().isoformat(),
221
+ 'session_code': session_code
222
+ }
223
+
224
+ for cid in sessions[session_code]['clients']:
225
+ if cid in clients and cid != client_id:
226
+ try:
227
+ conn = clients[cid]['connection']
228
+ event_data = f"data: {json.dumps(broadcast_message)}\n\n"
229
+ conn.wfile.write(event_data.encode())
230
+ conn.wfile.flush()
231
+ except:
232
+ pass
233
+
234
+ self.send_response(200)
235
+ self.send_header('Content-type', 'application/json')
236
+ self._add_cors_headers()
237
+ self.end_headers()
238
+ response = {'sent': True}
239
+ self.wfile.write(json.dumps(response).encode())
240
+ else:
241
+ self.send_response(404)
242
+ self.send_header('Content-type', 'application/json')
243
+ self._add_cors_headers()
244
+ self.end_headers()
245
+ response = {'error': 'Session not found'}
246
+ self.wfile.write(json.dumps(response).encode())
247
+
248
+ def log_message(self, format, *args):
249
+ pass
250
+
251
+
252
+ def cleanup_sessions():
253
+ current_time = datetime.now()
254
+ expired_sessions = []
255
+
256
+ for session_code, session_data in sessions.items():
257
+ if current_time - session_data['created_at'] > timedelta(minutes=15):
258
+ expired_sessions.append(session_code)
259
+
260
+ for session_code in expired_sessions:
261
+ if session_code in sessions:
262
+ for client_id in sessions[session_code]['clients']:
263
+ if client_id in clients:
264
+ try:
265
+ conn = clients[client_id]['connection']
266
+ event_data = f"data: {json.dumps({'type': 'session_expired', 'session_code': session_code})}\n\n"
267
+ conn.wfile.write(event_data.encode())
268
+ conn.wfile.flush()
269
+ except:
270
+ pass
271
+ del sessions[session_code]
272
+
273
+ if expired_sessions:
274
+ print(f"Cleaned up {len(expired_sessions)} expired sessions")
275
+
276
+ def run_server(port=8000, host='localhost'):
277
+ server_address = (host, port)
278
+ httpd = ThreadingHTTPServer(server_address, SimpleHTTPRequestHandler)
279
+ print(f"Server running on http://{host}:{port}")
280
+ print("Press Ctrl+C to stop")
281
+
282
+ def cleanup_timer():
283
+ while True:
284
+ time.sleep(300)
285
+ cleanup_sessions()
286
+
287
+ cleanup_thread = threading.Thread(target=cleanup_timer, daemon=True)
288
+ cleanup_thread.start()
289
+
290
+ try:
291
+ httpd.serve_forever()
292
+ except KeyboardInterrupt:
293
+ print("\nServer stopped")
294
+ httpd.server_close()
295
+
296
+ if __name__ == '__main__':
297
+ port = int(sys.argv[1]) if len(sys.argv) > 1 else 7860
298
+ run_server(port)