From 87a242f1311e9d626977e1b4170a5d4be65fca76 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Thu, 11 Jul 2024 16:02:21 +0100 Subject: [PATCH 1/3] [+] Cooldown on server run, has to wait 30s to stop. Signed-off-by: Charles Le Maux --- generic_executor.py | 29 +++++++++++++++++------------ server_mc_manager.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/generic_executor.py b/generic_executor.py index 02ad1ee..8b79e26 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -111,7 +111,7 @@ def server_delete(name: str, user: UserRecord) -> tuple[HTTPStatus, str or None] try: firebase_manager.delete_server(user_id, name) file_manager.delete_non_empty_folder(server_path) - return HTTPStatus.OK, f"Successfully deleted server {name}." + return HTTPStatus.OK, f"Successfully deleted server '{name}'." except Exception as e: file_manager.log_error(type(e).__name__, str(e)) return HTTPStatus.INTERNAL_SERVER_ERROR, None @@ -132,7 +132,7 @@ def account_delete(user: UserRecord) -> tuple[HTTPStatus, str or None]: return HTTPStatus.EXPECTATION_FAILED, f"Database deletion failed | {e}" try: file_manager.delete_non_empty_folder("users/" + user_id) - return HTTPStatus.OK, f"Successfully deleted user {user_id}." + return HTTPStatus.OK, f"Successfully deleted user '{user_id}'." except Exception as e: file_manager.log_error(type(e).__name__, str(e)) return HTTPStatus.INTERNAL_SERVER_ERROR, f"Error deleting user '{user_id}' on the server." @@ -140,6 +140,7 @@ def account_delete(user: UserRecord) -> tuple[HTTPStatus, str or None]: def server_run(user: UserRecord, name: str) -> tuple[HTTPStatus, str or None]: user_id = user.uid + mc_manager.set_cooldown(user_id=user_id) try: port: int = firebase_manager.get_server_port(user_id) server_id = mc_manager.start_server(f"users/{user_id}/{name}", port) @@ -151,7 +152,7 @@ def server_run(user: UserRecord, name: str) -> tuple[HTTPStatus, str or None]: except Exception as e: return HTTPStatus.INTERNAL_SERVER_ERROR, (f"Error updating server {name}'s running state in database. " f"{type(e).__name__}{str(e)}.") - return HTTPStatus.ACCEPTED, f"Successfully started server {name}." + return HTTPStatus.ACCEPTED, f"Successfully started server '{name}'." except Exception as e: file_manager.log_error(type(e).__name__, str(e)) return HTTPStatus.INTERNAL_SERVER_ERROR, f"Error when running server: {e}" @@ -159,21 +160,25 @@ def server_run(user: UserRecord, name: str) -> tuple[HTTPStatus, str or None]: def server_stop(user: UserRecord, name: str) -> tuple[HTTPStatus, str or None]: port = None + user_id: str = user.uid + if mc_manager.has_cooldown(user_id=user_id): + return HTTPStatus.FORBIDDEN, f"Wait 30 seconds before stopping '{name}'." try: - port = firebase_manager.get_server_port(user.uid) + port = firebase_manager.get_server_port(user_id) if port is None: - return HTTPStatus.NOT_FOUND, f"Server {name} not found in firestore." + return HTTPStatus.NOT_FOUND, f"Server '{name}' not found in firestore." exists: bool = mc_manager.stop_server(port) if exists: - firebase_manager.update_server_running_state(user.uid, name, False) - return HTTPStatus.OK, f"Successfully stopped server {name}." - return HTTPStatus.OK, f"Server {name} already stopped." + firebase_manager.update_server_running_state(user_id, name, False) + return HTTPStatus.OK, f"Successfully stopped server '{name}'." + return HTTPStatus.OK, f"Server '{name}' already stopped." except Exception as e: file_manager.log_error(type(e).__name__, str(e)) if port: mc_manager.stop_server_forcefully(port) - return HTTPStatus.OK, f"Successfully stopped server {name}." - return HTTPStatus.INTERNAL_SERVER_ERROR, f"Error occurred when stopping server {name}." + firebase_manager.update_server_running_state(user_id, name, False) + return HTTPStatus.OK, f"Successfully stopped server '{name}'." + return HTTPStatus.INTERNAL_SERVER_ERROR, f"Error occurred when stopping server '{name}'." def update_property(uid: str, name: str, prop: str, value: str) -> tuple[HTTPStatus, str or None]: @@ -202,14 +207,14 @@ def update_properties(user: UserRecord, name: str, props: list[tuple[str, str]]) errors.append(message) if len(errors) > 0: return HTTPStatus.IM_A_TEAPOT, errors - return HTTPStatus.OK, f"Successfully updated server {name}." + return HTTPStatus.OK, f"Successfully updated server '{name}'." def run_command(user: UserRecord, command: str, name: str) -> tuple[HTTPStatus, str or None]: try: port = firebase_manager.get_server_port(user.uid) if port is None: - return HTTPStatus.NOT_FOUND, f"Server {name} not found in firestore." + return HTTPStatus.NOT_FOUND, f"Server '{name}' not found in firestore." mc_manager.execute_server_command(port, command) return HTTPStatus.OK, f"Command '{command}' executed successfully." except Exception as e: diff --git a/server_mc_manager.py b/server_mc_manager.py index e96984e..2408030 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -1,5 +1,6 @@ import subprocess import shlex +import time class MinecraftServerManager: @@ -9,6 +10,7 @@ class MinecraftServerManager: def __init__(self): self.servers: dict = {} self.servers_count: int = 0 + self.cooldowns = {} def start_server(self, server_directory: str, port: int, java_executable='java', jar_file='server.jar', memory_size='2048M') -> int or None: @@ -60,6 +62,21 @@ class MinecraftServerManager: return server_id return None + def set_cooldown(self, user_id): + expiry_timestamp = time.time() + 30 + self.cooldowns[user_id] = expiry_timestamp + + def has_cooldown(self, user_id): + expiry_timestamp = self.cooldowns.get(user_id) + if expiry_timestamp is None: + return False + current_time = time.time() + if current_time < expiry_timestamp: + return True + else: + del self.cooldowns[user_id] + return False + if __name__ == "__main__": pass From fa9ba6d3e0fb8088219604ba78e61204b5607f1c Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Thu, 11 Jul 2024 16:12:16 +0100 Subject: [PATCH 2/3] [+] Server deletion safety Errors are correctly logged in firebase. User can't break the website anymore. Signed-off-by: Charles Le Maux --- generic_executor.py | 5 +++++ server_mc_manager.py | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/generic_executor.py b/generic_executor.py index 8b79e26..030f30c 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -107,6 +107,11 @@ def server_create(user: UserRecord, name: str, version: str, framework: str = "p def server_delete(name: str, user: UserRecord) -> tuple[HTTPStatus, str or None]: user_id = user.uid + try: + port: int = firebase_manager.get_server_port(user_id) + mc_manager.stop_server_forcefully(port) + except Exception as e: + file_manager.log_error(type(e).__name__, str(e)+f" error when stopping server {name}") server_path: str = f"users/{user_id}/{name}" try: firebase_manager.delete_server(user_id, name) diff --git a/server_mc_manager.py b/server_mc_manager.py index 2408030..c217436 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -18,7 +18,7 @@ class MinecraftServerManager: if port in self.servers: return None - command = f"{java_executable} -Xmx{memory_size} -Xms{memory_size} -jar {jar_file} > /dev/null" + command = f"{java_executable} -Xmx{memory_size} -Xms{memory_size} -jar {jar_file} --nogui > /dev/null" process = subprocess.Popen(shlex.split(command), cwd=server_directory, stdin=subprocess.PIPE) self.servers_count = len(self.servers) + 1 @@ -45,12 +45,12 @@ class MinecraftServerManager: del self.servers[port] return True - def stop_server_forcefully(self, server_id) -> bool: - if server_id not in self.servers: + def stop_server_forcefully(self, port) -> bool: + if port not in self.servers: return False - process = self.servers[server_id]['process'] + process = self.servers[port]['process'] process.terminate() - del self.servers[server_id] + del self.servers[port] return True def get_servers(self): From cef726b3b6bc2ce0a5d7222ce05fcbfb3a78a622 Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Thu, 11 Jul 2024 16:12:54 +0100 Subject: [PATCH 3/3] [+] Server deletion safety Errors are correctly logged in firebase. User can't break the website anymore. Signed-off-by: Charles Le Maux --- generic_executor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/generic_executor.py b/generic_executor.py index 030f30c..284564c 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -168,6 +168,7 @@ def server_stop(user: UserRecord, name: str) -> tuple[HTTPStatus, str or None]: user_id: str = user.uid if mc_manager.has_cooldown(user_id=user_id): return HTTPStatus.FORBIDDEN, f"Wait 30 seconds before stopping '{name}'." + mc_manager.set_cooldown(user_id=user_id) try: port = firebase_manager.get_server_port(user_id) if port is None: