1+ import socketio
2+ from typing import Any
3+ from datetime import datetime
4+ from gamuLogger import Logger
5+
6+ from version import Version
7+
8+ from modular_server_manager import BaseInterface
9+
10+ Logger .set_module ("User Interface.WebSocket API" )
11+
12+ class WebSocketAPI (BaseInterface ):
13+ def __init__ (self , * args , ** kwargs ):
14+ Logger .trace ("Initializing WebSocketServer" )
15+ BaseInterface .__init__ (self ,
16+ * args ,
17+ ** kwargs
18+ )
19+ self .__sio = socketio .Server (cors_allowed_origins = '*' )
20+ self .__config_routes ()
21+
22+ def _get_sio (self ):
23+ """
24+ Get the SocketIO server instance.
25+
26+ :return: The SocketIO server instance.
27+ """
28+ return self .__sio
29+
30+ def __config_routes (self ):
31+ @self .__sio .event
32+ def connect (sid , environ ):
33+ Logger .info (f"Client connected: { sid } " )
34+
35+ @self .__sio .event
36+ def disconnect (sid ):
37+ Logger .info (f"Client disconnected: { sid } " )
38+
39+ self .__config_routes_user ()
40+ self .__config_routes_server ()
41+
42+ @staticmethod
43+ def normalize_types (data : dict [str , Any ]) -> dict [str , int | float | str | bool | None ]:
44+ normalized_data : dict [str , Any ] = {}
45+ for key , value in data .items ():
46+ if isinstance (value , (int , float , str , bool )) or value is None :
47+ normalized_data [key ] = value
48+ elif isinstance (value , datetime ):
49+ normalized_data [key ] = value .isoformat ()
50+ else :
51+ normalized_data [key ] = str (value )
52+ return normalized_data
53+
54+ def default_callback (self , event_name : str , ** kwargs ):
55+ Logger .debug (f"Transferring event '{ event_name } ' to WebSocket clients with data: { kwargs } " )
56+ self .__sio .emit (event_name , self .normalize_types (kwargs ))
57+
58+
59+ def __config_routes_user (self ):
60+ @self .__sio .event
61+ def user_login_request (sid , data : dict ):
62+ username = data .get ("username" )
63+ password = data .get ("password" )
64+ remember = data .get ("remember" , False )
65+
66+ try :
67+ if not isinstance (username , str ) or not isinstance (password , str ):
68+ Logger .warning (f"Invalid login attempt data from { sid } : { data } " )
69+ raise ValueError ("Invalid username or password format" )
70+
71+ if not isinstance (remember , bool ):
72+ remember = False
73+
74+ Logger .debug (f"User login attempt: { username } , remember: { remember } " )
75+ token = self .login (username , password , remember )
76+ except ValueError as e :
77+ Logger .info (f"User login failed for { username } : { str (e )} " )
78+ self .__sio .emit ("user_login_response" , {"success" : False , "error" : str (e )}, to = sid )
79+ return
80+ else :
81+ Logger .info (f"User login successful for { username } " )
82+ self .__sio .emit ("user_login_response" , {"success" : True , "token" : token .token }, to = sid )
83+
84+ @self .__sio .event
85+ def user_register_request (sid , data : dict ):
86+ username = data .get ("username" )
87+ password = data .get ("password" )
88+ remember = data .get ("remember" , False )
89+
90+ try :
91+ if not isinstance (username , str ) or not isinstance (password , str ):
92+ Logger .warning (f"Invalid registration attempt data from { sid } : { data } " )
93+ raise ValueError ("Invalid username or password format" )
94+
95+ if not isinstance (remember , bool ):
96+ remember = False
97+
98+ Logger .debug (f"User registration attempt: { username } , remember: { remember } " )
99+ token = self .register (username , password , remember )
100+ except ValueError as e :
101+ Logger .info (f"User registration failed for { username } : { str (e )} " )
102+ self .__sio .emit ("user_register_response" , {"success" : False , "error" : str (e )}, to = sid )
103+ return
104+ else :
105+ Logger .info (f"User registration successful for { username } " )
106+ self .__sio .emit ("user_register_response" , {"success" : True , "token" : token .token }, to = sid )
107+
108+ @self .__sio .event
109+ def user_logout_request (sid , data : dict ):
110+ token_str = data .get ("token" )
111+
112+ try :
113+ if not isinstance (token_str , str ):
114+ Logger .warning (f"Invalid logout attempt data from { sid } : { data } " )
115+ raise ValueError ("Invalid token format" )
116+
117+ Logger .debug (f"User logout attempt with token: { token_str } " )
118+ self .logout (token_str )
119+ except ValueError as e :
120+ Logger .info (f"User logout failed for token { token_str } : { str (e )} " )
121+ self .__sio .emit ("user_logout_response" , {"success" : False , "error" : str (e )}, to = sid )
122+ return
123+ else :
124+ Logger .info (f"User logout successful for token { token_str } " )
125+ self .__sio .emit ("user_logout_response" , {"success" : True }, to = sid )
126+
127+ @self .__sio .event
128+ def user_delete_request (sid , data : dict ):
129+ token_str = data .get ("token" )
130+
131+ try :
132+ if not isinstance (token_str , str ):
133+ Logger .warning (f"Invalid delete user attempt data from { sid } : { data } " )
134+ raise ValueError ("Invalid token format" )
135+
136+ Logger .debug (f"User delete attempt with token: { token_str } " )
137+ self .delete_user (token_str )
138+ except ValueError as e :
139+ Logger .info (f"User delete failed for token { token_str } : { str (e )} " )
140+ self .__sio .emit ("user_delete_response" , {"success" : False , "error" : str (e )}, to = sid )
141+ return
142+ else :
143+ Logger .info (f"User delete successful for token { token_str } " )
144+ self .__sio .emit ("user_delete_response" , {"success" : True }, to = sid )
145+
146+ @self .__sio .event
147+ def user_info_request (sid , data : dict ):
148+ token_str = data .get ("token" )
149+
150+ try :
151+ if not isinstance (token_str , str ):
152+ Logger .warning (f"Invalid user info request data from { sid } : { data } " )
153+ raise ValueError ("Invalid token format" )
154+
155+ Logger .debug (f"User info request with token: { token_str } " )
156+ user = self .get_user_info (token_str )
157+ except ValueError as e :
158+ Logger .info (f"User info request failed for token { token_str } : { str (e )} " )
159+ self .__sio .emit ("user_info_response" , {"success" : False , "error" : str (e )}, to = sid )
160+ return
161+ else :
162+ Logger .info (f"User info request successful for token { token_str } " )
163+ data = {
164+ "success" : True ,
165+ 'username' : user .username ,
166+ 'registered_at' : user .registered_at .isoformat (),
167+ 'last_login' : user .last_login .isoformat (),
168+ 'access_level' : user .access_level .value
169+ }
170+ self .__sio .emit ("user_info_response" , data , to = sid )
171+
172+ @self .__sio .event
173+ def user_update_password_request (sid , data : dict ):
174+ token_str = data .get ("token" )
175+ new_password = data .get ("password" )
176+
177+ try :
178+ if not isinstance (token_str , str ) or not isinstance (new_password , str ):
179+ Logger .warning (f"Invalid update password attempt data from { sid } : { data } " )
180+ raise ValueError ("Invalid token or password format" )
181+
182+ Logger .debug (f"User update password attempt with token: { token_str } " )
183+ self .update_password (token_str , new_password )
184+ except ValueError as e :
185+ Logger .info (f"User update password failed for token { token_str } : { str (e )} " )
186+ self .__sio .emit ("user_update_password_response" , {"success" : False , "error" : str (e )}, to = sid )
187+ return
188+ else :
189+ Logger .info (f"User update password successful for token { token_str } " )
190+ self .__sio .emit ("user_update_password_response" , {"success" : True }, to = sid )
191+
192+
193+ def __config_routes_server (self ):
194+ @self .__sio .event
195+ def get_versions_minecraft_request (sid , data : dict ):
196+ token_str = data .get ("token" )
197+
198+ try :
199+ if not isinstance (token_str , str ):
200+ Logger .warning (f"Invalid list MC versions request data from { sid } : { data } " )
201+ raise ValueError ("Invalid token format" )
202+
203+ Logger .debug (f"List MC versions request with token: { token_str } " )
204+ versions = self .list_mc_versions (token_str )
205+ except ValueError as e :
206+ Logger .info (f"List MC versions request failed for token { token_str } : { str (e )} " )
207+ self .__sio .emit ("get_versions_minecraft_response" , {"success" : False , "error" : str (e )}, to = sid )
208+ return
209+ else :
210+ Logger .info (f"List MC versions request successful for token { token_str } " )
211+ version_strs = [str (v ) for v in versions ]
212+ self .__sio .emit ("get_versions_minecraft_response" , {"success" : True , "versions" : version_strs }, to = sid )
213+
214+ @self .__sio .event
215+ def get_versions_forge_request (sid , data : dict ):
216+ token_str = data .get ("token" )
217+ mc_version_str = data .get ("mc_version" )
218+
219+ try :
220+ if not isinstance (token_str , str ) or not isinstance (mc_version_str , str ):
221+ Logger .warning (f"Invalid list Forge versions request data from { sid } : { data } " )
222+ raise ValueError ("Invalid token or MC version format" )
223+
224+ Logger .debug (f"List Forge versions request with token: { token_str } , MC version: { mc_version_str } " )
225+ versions = self .list_forge_versions (token_str , Version .from_string (mc_version_str ))
226+ except ValueError as e :
227+ Logger .info (f"List Forge versions request failed for token { token_str } : { str (e )} " )
228+ self .__sio .emit ("get_versions_forge_response" , {"success" : False , "error" : str (e )}, to = sid )
229+ return
230+ else :
231+ Logger .info (f"List Forge versions request successful for token { token_str } " )
232+ version_strs = [str (v ) for v in versions ]
233+ self .__sio .emit ("get_versions_forge_response" , {"success" : True , "versions" : version_strs }, to = sid )
234+
235+ @self .__sio .event
236+ def server_list_request (sid , data : dict ):
237+ token_str = data .get ("token" )
238+
239+ try :
240+ if not isinstance (token_str , str ):
241+ Logger .warning (f"Invalid list servers request data from { sid } : { data } " )
242+ raise ValueError ("Invalid token format" )
243+
244+ Logger .debug (f"List servers request with token: { token_str } " )
245+ servers = self .list_servers (token_str )
246+ except ValueError as e :
247+ Logger .info (f"List servers request failed for token { token_str } : { str (e )} " )
248+ self .__sio .emit ("servers_list_response" , {"success" : False , "error" : str (e )}, to = sid )
249+ return
250+ else :
251+ Logger .info (f"List servers request successful for token { token_str } " )
252+ server_data = [self .normalize_types (server ) for server in servers ]
253+ self .__sio .emit ("servers_list_response" , {"success" : True , "servers" : server_data }, to = sid )
254+
255+ @self .__sio .event
256+ def server_info_request (sid , data : dict ):
257+ token_str = data .get ("token" )
258+ server_name = data .get ("server_name" )
259+
260+ try :
261+ if not isinstance (token_str , str ) or not isinstance (server_name , str ):
262+ Logger .warning (f"Invalid get server info request data from { sid } : { data } " )
263+ raise ValueError ("Invalid token or server name format" )
264+
265+ Logger .debug (f"Get server info request with token: { token_str } , server name: { server_name } " )
266+ server_info = self .get_server_info (token_str , server_name )
267+ except ValueError as e :
268+ Logger .info (f"Get server info request failed for token { token_str } : { str (e )} " )
269+ self .__sio .emit ("server_info_response" , {"success" : False , "error" : str (e )}, to = sid )
270+ return
271+ else :
272+ Logger .info (f"Get server info request successful for token { token_str } " )
273+ self .__sio .emit ("server_info_response" , {"success" : True , "server_info" : self .normalize_types (server_info )}, to = sid )
274+
275+ @self .__sio .event
276+ def server_create_request (sid , data : dict ):
277+ token_str = data .get ("token" )
278+ name = data .get ("name" )
279+ _type = data .get ("type" )
280+ path = data .get ("path" )
281+ autostart = data .get ("autostart" , False )
282+ mc_version = data .get ("mc_version" )
283+ modloader_version = data .get ("modloader_version" , None )
284+ ram = data .get ("ram" )
285+
286+ try :
287+ if not isinstance (token_str , str ) or not isinstance (name , str ) or not isinstance (_type , str ) or not isinstance (path , str ) or not isinstance (mc_version , str ):
288+ Logger .warning (f"Invalid create server request data from { sid } : { data } " )
289+ raise ValueError ("Invalid format for one or more parameters" )
290+
291+ if not isinstance (autostart , bool ):
292+ autostart = False
293+
294+ if modloader_version is not None and not isinstance (modloader_version , str ):
295+ modloader_version = None
296+
297+ if not isinstance (ram , int ):
298+ raise ValueError ("Invalid RAM format" )
299+
300+ Logger .debug (f"Create server request with token: { token_str } , name: { name } , type: { _type } , path: { path } , autostart: { autostart } , mc_version: { mc_version } , modloader_version: { modloader_version } , ram: { ram } " )
301+ server_info = self .create_server (
302+ token_str ,
303+ name ,
304+ _type ,
305+ path ,
306+ autostart ,
307+ Version .from_string (mc_version ),
308+ Version .from_string (modloader_version ) if modloader_version else None ,
309+ ram
310+ )
311+ except ValueError as e :
312+ Logger .warning (f"Create server request failed for token { token_str } : { str (e )} " )
313+
314+
315+
316+
0 commit comments