From 65c8ca680b56c0ebce0f7eac2d473c08e17528f4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 17 Aug 2022 14:48:05 -0400 Subject: [PATCH 01/39] Add importing threading to jar servers. Refactor "downloading" status --- app/classes/controllers/servers_controller.py | 12 ++--- app/classes/minecraft/serverjars.py | 2 +- app/classes/models/server_stats.py | 17 +++--- app/classes/shared/main_controller.py | 54 ++++++++++++------- app/classes/shared/server.py | 2 +- app/classes/web/panel_handler.py | 8 +-- app/frontend/templates/panel/dashboard.html | 4 +- app/frontend/templates/panel/server_term.html | 4 +- .../20220817_schedule_rename_downloading.py | 17 ++++++ app/translations/en_EN.json | 4 +- 10 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 app/migrations/20220817_schedule_rename_downloading.py diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 650a16b0..b125590e 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -108,19 +108,19 @@ class ServersController(metaclass=Singleton): return ret @staticmethod - def set_download(server_id): + def set_import(server_id): srv = ServersController().get_server_instance_by_id(server_id) - return srv.stats_helper.set_download() + return srv.stats_helper.set_import() @staticmethod - def finish_download(server_id): + def finish_import(server_id): srv = ServersController().get_server_instance_by_id(server_id) - return srv.stats_helper.finish_download() + return srv.stats_helper.finish_import() @staticmethod - def get_download_status(server_id): + def get_import_status(server_id): server = ServersController().get_server_instance_by_id(server_id) - return server.stats_helper.get_download_status() + return server.stats_helper.get_import_status() def remove_server(self, server_id): roles_list = PermissionsServers.get_roles_from_server(server_id) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index a5eb11ba..90b49236 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -175,7 +175,7 @@ class ServerJars: # we submit a db update for it's stats. while True: try: - ServersController.set_download(server_id) + ServersController.set_import(server_id) for user in server_users: self.helper.websocket_helper.broadcast_user( user, "send_start_reload", {} diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py index e3c943e3..906eed8b 100644 --- a/app/classes/models/server_stats.py +++ b/app/classes/models/server_stats.py @@ -53,7 +53,7 @@ class ServerStats(Model): waiting_start = BooleanField(default=False) first_run = BooleanField(default=True) crashed = BooleanField(default=False) - downloading = BooleanField(default=False) + importing = BooleanField(default=False) class Meta: table_name = "server_stats" @@ -207,26 +207,26 @@ class HelperServerStats: ServerStats.server_id == self.server_id ).execute(self.database) - def set_download(self): + def set_import(self): # self.select_database(self.server_id) - ServerStats.update(downloading=True).where( + ServerStats.update(importing=True).where( ServerStats.server_id == self.server_id ).execute(self.database) - def finish_download(self): + def finish_import(self): # self.select_database(self.server_id) - ServerStats.update(downloading=False).where( + ServerStats.update(importing=False).where( ServerStats.server_id == self.server_id ).execute(self.database) - def get_download_status(self): + def get_import_status(self): # self.select_database(self.server_id) - download_status = ( + import_status = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) - return download_status.downloading + return import_status.importing def server_crash_reset(self): if self.server_id is None: @@ -249,7 +249,6 @@ class HelperServerStats: def set_update(self, value): if self.server_id is None: return - # self.select_database(self.server_id) try: # Checks if server even exists diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index bca24fe8..2332355c 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -5,6 +5,7 @@ import platform import shutil import time import logging +import threading from peewee import DoesNotExist # TZLocal is set as a hidden import on win pipeline @@ -16,6 +17,7 @@ from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.shared.main_models import DatabaseShortcuts from app.classes.models.users import HelperUsers from app.classes.models.roles import HelperRoles +from app.classes.models.server_permissions import PermissionsServers from app.classes.models.management import HelpersManagement from app.classes.models.servers import HelperServers from app.classes.controllers.crafty_perms_controller import CraftyPermsController @@ -537,25 +539,6 @@ class Controller: Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(backup_path) server_path = Helpers.get_os_understandable_path(server_path) - try: - FileHelpers.copy_dir(server_path, new_server_dir, True) - except shutil.Error as ex: - logger.error(f"Server import failed with error: {ex}") - - has_properties = False - for item in os.listdir(new_server_dir): - if str(item) == "server.properties": - has_properties = True - if not has_properties: - logger.info( - f"No server.properties found on zip file import. " - f"Creating one with port selection of {str(port)}" - ) - with open( - os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() full_jar_path = os.path.join(new_server_dir, server_jar) @@ -586,8 +569,41 @@ class Controller: port, server_type="minecraft-java", ) + import_thread = threading.Thread( + name=f"server_import-{server_id}", + target=self.import_threaded_jar_server, + daemon=True, + args=(server_path, new_server_dir, port, new_id), + ) + self.servers.set_import(new_id) + import_thread.start() return new_id + def import_threaded_jar_server(self, server_path, new_server_dir, port, new_id): + try: + FileHelpers.copy_dir(server_path, new_server_dir, True) + except shutil.Error as ex: + logger.error(f"Server import failed with error: {ex}") + + has_properties = False + for item in os.listdir(new_server_dir): + if str(item) == "server.properties": + has_properties = True + if not has_properties: + logger.info( + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as file: + file.write(f"server-port={port}") + file.close() + self.servers.finish_import(new_id) + server_users = PermissionsServers.get_server_user_list(new_id) + for user in server_users: + self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + def import_zip_server( self, server_name: str, diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 499a0080..e0e0552f 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -293,7 +293,7 @@ class ServerInstance: else: user_lang = HelperUsers.get_user_lang_by_id(user_id) - if self.stats_helper.get_download_status(): + if self.stats_helper.get_import_status(): if user_id: self.helper.websocket_helper.broadcast_user( user_id, diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a5e56029..5e2c495d 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -453,8 +453,8 @@ class PanelHandler(BaseHandler): for server in un_used_servers[:]: if flag == 0: server["stats"][ - "downloading" - ] = self.controller.servers.get_download_status( + "importing" + ] = self.controller.servers.get_import_status( str(server["stats"]["server_id"]["server_id"]) ) server["stats"]["crashed"] = self.controller.servers.is_crashed( @@ -571,11 +571,11 @@ class PanelHandler(BaseHandler): "started": "False", } if not self.failed_server: - page_data["downloading"] = self.controller.servers.get_download_status( + page_data["importing"] = self.controller.servers.get_import_status( server_id ) else: - page_data["downloading"] = False + page_data["importing"] = False page_data["server_id"] = server_id try: page_data["waiting_start"] = self.controller.servers.get_waiting_start( diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index ec5a21db..59805ddf 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -399,12 +399,12 @@ data['lang']) }} - {% elif server['stats']['downloading']%} + {% elif server['stats']['importing']%}
- {{ translate('serverTerm', 'downloading', data['lang']) }} + {{ translate('serverTerm', 'importing', data['lang']) }}
{% else %} diff --git a/app/frontend/templates/panel/server_term.html b/app/frontend/templates/panel/server_term.html index ffc5e51c..83f31519 100644 --- a/app/frontend/templates/panel/server_term.html +++ b/app/frontend/templates/panel/server_term.html @@ -65,9 +65,9 @@ - {% elif data['downloading'] %} + {% elif data['importing'] %}
- diff --git a/app/migrations/20220817_schedule_rename_downloading.py b/app/migrations/20220817_schedule_rename_downloading.py new file mode 100644 index 00000000..f74ce96d --- /dev/null +++ b/app/migrations/20220817_schedule_rename_downloading.py @@ -0,0 +1,17 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.rename_column("server_stats", "downloading", "importing") + + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.rename_column("server_stats", "importing", "downloading") + """ + Write your rollback migrations here. + """ diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index f398ccf5..c4ae1402 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -463,7 +463,7 @@ "serverTerm": { "commandInput": "Enter your command", "delay-explained": "The service/agent has recently started and is delaying the start of the minecraft server instance", - "downloading": "Downloading...", + "importing": "Importing...", "restart": "Restart", "sendCommand": "Send command", "start": "Start", @@ -558,4 +558,4 @@ "userSettings": "User Settings", "uses": "Number of uses allowed (-1==No Limit)" } -} +} \ No newline at end of file From eea5e329af87be22d620883e1912e69fafb97850 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 17 Aug 2022 17:22:03 -0400 Subject: [PATCH 02/39] Refactor and add import_helpers. Working jar imports --- app/classes/minecraft/serverjars.py | 4 +- app/classes/shared/import_helper.py | 67 +++++++++++++++++++ app/classes/shared/main_controller.py | 39 ++--------- app/frontend/templates/panel/dashboard.html | 4 +- .../20220817_schedule_rename_downloading.py | 0 main.py | 4 +- 6 files changed, 79 insertions(+), 39 deletions(-) create mode 100644 app/classes/shared/import_helper.py rename app/migrations/{ => stats}/20220817_schedule_rename_downloading.py (100%) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 90b49236..1ecfc0f1 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -190,7 +190,7 @@ class ServerJars: try: with open(path, "wb") as output: shutil.copyfileobj(r.raw, output) - ServersController.finish_download(server_id) + ServersController.finish_import(server_id) for user in server_users: self.helper.websocket_helper.broadcast_user( @@ -203,7 +203,7 @@ class ServerJars: return True except Exception as e: logger.error(f"Unable to save jar to {path} due to error:{e}") - ServersController.finish_download(server_id) + ServersController.finish_import(server_id) server_users = PermissionsServers.get_server_user_list(server_id) for user in server_users: self.helper.websocket_helper.broadcast_user( diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py new file mode 100644 index 00000000..61e1203d --- /dev/null +++ b/app/classes/shared/import_helper.py @@ -0,0 +1,67 @@ +from genericpath import isdir +import shutil +import os +import time +import logging +import threading + +from app.classes.controllers.server_perms_controller import PermissionsServers +from app.classes.controllers.servers_controller import ServersController +from app.classes.shared.helpers import Helpers +from app.classes.shared.file_helpers import FileHelpers + +logger = logging.getLogger(__name__) + + +class ImportHelpers: + allowed_quotes = ['"', "'", "`"] + + def __init__(self, helper, file_helper): + self.file_helper: FileHelpers = file_helper + self.helper: Helpers = helper + + def import_jar_server(self, server_path, new_server_dir, port, new_id): + import_thread = threading.Thread( + target=self.import_threaded_jar_server, + daemon=True, + args=(server_path, new_server_dir, port, new_id), + name=f"{new_id}_import", + ) + import_thread.start() + + def import_threaded_jar_server(self, server_path, new_server_dir, port, new_id): + for item in os.listdir(server_path): + if not item == "db_stats": + try: + if os.path.isdir(os.path.join(server_path, item)): + FileHelpers.copy_dir( + os.path.join(server_path, item), + os.path.join(new_server_dir, item), + ) + else: + FileHelpers.copy_file( + os.path.join(server_path, item), + os.path.join(new_server_dir, item), + ) + except shutil.Error as ex: + logger.error(f"Server import failed with error: {ex}") + + has_properties = False + for item in os.listdir(new_server_dir): + if str(item) == "server.properties": + has_properties = True + if not has_properties: + logger.info( + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as file: + file.write(f"server-port={port}") + file.close() + time.sleep(5) + ServersController.finish_import(new_id) + server_users = PermissionsServers.get_server_user_list(new_id) + for user in server_users: + self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 2332355c..ad0e46bc 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -30,15 +30,17 @@ from app.classes.shared.authentication import Authentication from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.file_helpers import FileHelpers +from app.classes.shared.import_helper import ImportHelpers from app.classes.minecraft.serverjars import ServerJars logger = logging.getLogger(__name__) class Controller: - def __init__(self, database, helper, file_helper): + def __init__(self, database, helper, file_helper, import_helper): self.helper: Helpers = helper self.file_helper: FileHelpers = file_helper + self.import_helper: ImportHelpers = import_helper self.server_jars: ServerJars = ServerJars(helper) self.users_helper: HelperUsers = HelperUsers(database, self.helper) self.roles_helper: HelperRoles = HelperRoles(database) @@ -569,41 +571,10 @@ class Controller: port, server_type="minecraft-java", ) - import_thread = threading.Thread( - name=f"server_import-{server_id}", - target=self.import_threaded_jar_server, - daemon=True, - args=(server_path, new_server_dir, port, new_id), - ) - self.servers.set_import(new_id) - import_thread.start() + ServersController.set_import(new_id) + self.import_helper.import_jar_server(server_path, new_server_dir, port, new_id) return new_id - def import_threaded_jar_server(self, server_path, new_server_dir, port, new_id): - try: - FileHelpers.copy_dir(server_path, new_server_dir, True) - except shutil.Error as ex: - logger.error(f"Server import failed with error: {ex}") - - has_properties = False - for item in os.listdir(new_server_dir): - if str(item) == "server.properties": - has_properties = True - if not has_properties: - logger.info( - f"No server.properties found on zip file import. " - f"Creating one with port selection of {str(port)}" - ) - with open( - os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() - self.servers.finish_import(new_id) - server_users = PermissionsServers.get_server_user_list(new_id) - for user in server_users: - self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) - def import_zip_server( self, server_name: str, diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 59805ddf..617c1ebe 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -193,9 +193,9 @@ {{ translate('dashboard', 'starting', data['lang']) }} - {% elif server['stats']['downloading']%} + {% elif server['stats']['importing']%} - {{ translate('serverTerm', 'downloading', + {{ translate('serverTerm', 'importing', data['lang']) }} {% else %} Date: Wed, 17 Aug 2022 17:41:44 -0400 Subject: [PATCH 03/39] Starting zip imports --- app/classes/shared/file_helpers.py | 5 +++- app/classes/shared/import_helper.py | 43 +++++++++++++++++++++++++++ app/classes/shared/main_controller.py | 29 ++---------------- 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 5cd38bbf..dc2eb282 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -27,7 +27,10 @@ class FileHelpers: FileHelpers.del_dirs(sub) else: # Delete file if it is a file: - sub.unlink() + try: + sub.unlink() + except: + logger.error(f"Unable to delete file {sub}") # This removes the top-level folder: path.rmdir() diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py index 61e1203d..4767a2c1 100644 --- a/app/classes/shared/import_helper.py +++ b/app/classes/shared/import_helper.py @@ -65,3 +65,46 @@ class ImportHelpers: server_users = PermissionsServers.get_server_user_list(new_id) for user in server_users: self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + + def import_java_zip_server(self, temp_dir, new_server_dir, port, new_id): + import_thread = threading.Thread( + target=self.import_threaded_java_zip_server, + daemon=True, + args=(temp_dir, new_server_dir, port, new_id), + name=f"{new_id}_import", + ) + import_thread.start() + + def import_threaded_java_zip_server(self, temp_dir, new_server_dir, port, new_id): + has_properties = False + # extracts archive to temp directory + for item in os.listdir(temp_dir): + if str(item) == "server.properties": + has_properties = True + try: + if not os.path.isdir(os.path.join(temp_dir, item)): + FileHelpers.move_file( + os.path.join(temp_dir, item), os.path.join(new_server_dir, item) + ) + else: + if item != "db_stats": + FileHelpers.move_dir( + os.path.join(temp_dir, item), + os.path.join(new_server_dir, item), + ) + except Exception as ex: + logger.error(f"ERROR IN ZIP IMPORT: {ex}") + if not has_properties: + logger.info( + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as file: + file.write(f"server-port={port}") + file.close() + + server_users = PermissionsServers.get_server_user_list(new_id) + for user in server_users: + self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index ad0e46bc..61438b81 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -596,32 +596,6 @@ class Controller: temp_dir = Helpers.get_os_understandable_path(zip_path) Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(backup_path) - has_properties = False - # extracts archive to temp directory - for item in os.listdir(temp_dir): - if str(item) == "server.properties": - has_properties = True - try: - if not os.path.isdir(os.path.join(temp_dir, item)): - FileHelpers.move_file( - os.path.join(temp_dir, item), os.path.join(new_server_dir, item) - ) - else: - FileHelpers.move_dir( - os.path.join(temp_dir, item), os.path.join(new_server_dir, item) - ) - except Exception as ex: - logger.error(f"ERROR IN ZIP IMPORT: {ex}") - if not has_properties: - logger.info( - f"No server.properties found on zip file import. " - f"Creating one with port selection of {str(port)}" - ) - with open( - os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() full_jar_path = os.path.join(new_server_dir, server_jar) @@ -653,6 +627,9 @@ class Controller: port, server_type="minecraft-java", ) + self.import_helper.import_java_zip_server( + temp_dir, new_server_dir, port, new_id + ) return new_id # ********************************************************************************** From 504ffd57f6b23923cefb650a741823e82dae8bb3 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 17 Aug 2022 18:14:16 -0400 Subject: [PATCH 04/39] Working zip imports --- app/classes/shared/file_helpers.py | 8 +++++--- app/classes/shared/import_helper.py | 1 + app/classes/shared/main_controller.py | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index dc2eb282..f9cde55a 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -31,9 +31,11 @@ class FileHelpers: sub.unlink() except: logger.error(f"Unable to delete file {sub}") - - # This removes the top-level folder: - path.rmdir() + try: + # This removes the top-level folder: + path.rmdir() + except: + logger.error("Unable to remove top level") return True @staticmethod diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py index 4767a2c1..60ca0eed 100644 --- a/app/classes/shared/import_helper.py +++ b/app/classes/shared/import_helper.py @@ -106,5 +106,6 @@ class ImportHelpers: file.close() server_users = PermissionsServers.get_server_user_list(new_id) + ServersController.finish_import(new_id) for user in server_users: self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 61438b81..0f3e3c97 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -627,6 +627,7 @@ class Controller: port, server_type="minecraft-java", ) + ServersController.set_import(new_id) self.import_helper.import_java_zip_server( temp_dir, new_server_dir, port, new_id ) From b10523ae0aa6eeb703f1256a32f50e3d8144bf47 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 17 Aug 2022 19:07:56 -0400 Subject: [PATCH 05/39] Add import status to working bedrock --- app/classes/shared/import_helper.py | 111 +++++++++++++++++++++++++- app/classes/shared/main_controller.py | 55 ++----------- app/classes/web/server_handler.py | 4 - 3 files changed, 115 insertions(+), 55 deletions(-) diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py index 60ca0eed..446683c1 100644 --- a/app/classes/shared/import_helper.py +++ b/app/classes/shared/import_helper.py @@ -1,7 +1,6 @@ -from genericpath import isdir -import shutil import os import time +import shutil import logging import threading @@ -71,7 +70,7 @@ class ImportHelpers: target=self.import_threaded_java_zip_server, daemon=True, args=(temp_dir, new_server_dir, port, new_id), - name=f"{new_id}_import", + name=f"{new_id}_java_zip_import", ) import_thread.start() @@ -109,3 +108,109 @@ class ImportHelpers: ServersController.finish_import(new_id) for user in server_users: self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + # deletes temp dir + FileHelpers.del_dirs(temp_dir) + + def import_bedrock_server( + self, server_path, new_server_dir, port, full_jar_path, new_id + ): + import_thread = threading.Thread( + target=self.import_threaded_java_zip_server, + daemon=True, + args=(server_path, new_server_dir, port, new_id), + name=f"{new_id}_bedrock_import", + ) + import_thread.start() + + def import_threaded_bedrock_server( + self, server_path, new_server_dir, port, full_jar_path, new_id + ): + for item in os.listdir(server_path): + if not item == "db_stats": + try: + if os.path.isdir(os.path.join(server_path, item)): + FileHelpers.copy_dir( + os.path.join(server_path, item), + os.path.join(new_server_dir, item), + ) + else: + FileHelpers.copy_file( + os.path.join(server_path, item), + os.path.join(new_server_dir, item), + ) + except shutil.Error as ex: + logger.error(f"Server import failed with error: {ex}") + + has_properties = False + for item in os.listdir(new_server_dir): + if str(item) == "server.properties": + has_properties = True + if not has_properties: + logger.info( + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as file: + file.write(f"server-port={port}") + file.close() + if os.name != "nt": + if Helpers.check_file_exists(full_jar_path): + os.chmod(full_jar_path, 0o2760) + ServersController.finish_import(new_id) + server_users = PermissionsServers.get_server_user_list(new_id) + for user in server_users: + self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + + def import_bedrock_zip_server( + self, temp_dir, new_server_dir, full_jar_path, port, new_id + ): + import_thread = threading.Thread( + target=self.import_threaded_bedrock_zip_server, + daemon=True, + args=(temp_dir, new_server_dir, full_jar_path, port, new_id), + name=f"{new_id}_bedrock_import", + ) + import_thread.start() + + def import_threaded_bedrock_zip_server( + self, temp_dir, new_server_dir, full_jar_path, port, new_id + ): + has_properties = False + # extracts archive to temp directory + for item in os.listdir(temp_dir): + if str(item) == "server.properties": + has_properties = True + try: + if not os.path.isdir(os.path.join(temp_dir, item)): + FileHelpers.move_file( + os.path.join(temp_dir, item), os.path.join(new_server_dir, item) + ) + else: + if item != "db_stats": + FileHelpers.move_dir( + os.path.join(temp_dir, item), + os.path.join(new_server_dir, item), + ) + except Exception as ex: + logger.error(f"ERROR IN ZIP IMPORT: {ex}") + if not has_properties: + logger.info( + f"No server.properties found on zip file import. " + f"Creating one with port selection of {str(port)}" + ) + with open( + os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" + ) as file: + file.write(f"server-port={port}") + file.close() + ServersController.finish_import(new_id) + server_users = PermissionsServers.get_server_user_list(new_id) + for user in server_users: + self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + if os.name != "nt": + if Helpers.check_file_exists(full_jar_path): + os.chmod(full_jar_path, 0o2760) + # deletes temp dir + FileHelpers.del_dirs(temp_dir) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 0f3e3c97..6aaf8cb1 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -652,25 +652,6 @@ class Controller: Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(backup_path) server_path = Helpers.get_os_understandable_path(server_path) - try: - FileHelpers.copy_dir(server_path, new_server_dir, True) - except shutil.Error as ex: - logger.error(f"Server import failed with error: {ex}") - - has_properties = False - for item in os.listdir(new_server_dir): - if str(item) == "server.properties": - has_properties = True - if not has_properties: - logger.info( - f"No server.properties found on zip file import. " - f"Creating one with port selection of {str(port)}" - ) - with open( - os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() full_jar_path = os.path.join(new_server_dir, server_exe) @@ -694,9 +675,10 @@ class Controller: port, server_type="minecraft-bedrock", ) - if os.name != "nt": - if Helpers.check_file_exists(full_jar_path): - os.chmod(full_jar_path, 0o2760) + ServersController.set_import(new_id) + self.import_helper.import_bedrock_server( + server_path, new_server_dir, port, full_jar_path, new_id + ) return new_id def import_bedrock_zip_server( @@ -714,32 +696,6 @@ class Controller: temp_dir = Helpers.get_os_understandable_path(zip_path) Helpers.ensure_dir_exists(new_server_dir) Helpers.ensure_dir_exists(backup_path) - has_properties = False - # extracts archive to temp directory - for item in os.listdir(temp_dir): - if str(item) == "server.properties": - has_properties = True - try: - if not os.path.isdir(os.path.join(temp_dir, item)): - FileHelpers.move_file( - os.path.join(temp_dir, item), os.path.join(new_server_dir, item) - ) - else: - FileHelpers.move_dir( - os.path.join(temp_dir, item), os.path.join(new_server_dir, item) - ) - except Exception as ex: - logger.error(f"ERROR IN ZIP IMPORT: {ex}") - if not has_properties: - logger.info( - f"No server.properties found on zip file import. " - f"Creating one with port selection of {str(port)}" - ) - with open( - os.path.join(new_server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() full_jar_path = os.path.join(new_server_dir, server_exe) @@ -763,6 +719,9 @@ class Controller: port, server_type="minecraft-bedrock", ) + self.import_helper.import_bedrock_zip_server( + temp_dir, new_server_dir, full_jar_path, port, new_id + ) if os.name != "nt": if Helpers.check_file_exists(full_jar_path): os.chmod(full_jar_path, 0o2760) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index df4ba684..7d605c2f 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -385,8 +385,6 @@ class ServerHandler(BaseHandler): new_server_id, self.get_remote_ip(), ) - # deletes temp dir - FileHelpers.del_dirs(zip_path) else: if len(server_parts) != 3: self.redirect("/panel/error?error=Invalid server data") @@ -516,8 +514,6 @@ class ServerHandler(BaseHandler): new_server_id, self.get_remote_ip(), ) - # deletes temp dir - FileHelpers.del_dirs(zip_path) else: if len(server_parts) != 2: self.redirect("/panel/error?error=Invalid server data") From c1d16522d0350c3bfd8bf202955fee18bc91ff7d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 19 Aug 2022 13:09:59 -0400 Subject: [PATCH 06/39] Add manager field to users --- app/classes/controllers/users_controller.py | 8 ++++ app/classes/models/users.py | 11 +++++ app/classes/web/api_handler.py | 3 +- app/classes/web/panel_handler.py | 47 +++++++++++++++++++ app/classes/web/routes/api/users/index.py | 2 + .../templates/panel/panel_config.html | 32 +++++++++++++ .../templates/panel/panel_edit_user.html | 26 +++++++++- app/migrations/20220819_user_manager.py | 16 +++++++ 8 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 app/migrations/20220819_user_manager.py diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index c3c90b2f..c62d209d 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -132,6 +132,10 @@ class UsersController: def set_support_path(user_id, support_path): HelperUsers.set_support_path(user_id, support_path) + @staticmethod + def get_managed_users(exec_user_id): + return HelperUsers.get_managed_users(exec_user_id) + def update_user(self, user_id: str, user_data=None, user_crafty_data=None): if user_crafty_data is None: user_crafty_data = {} @@ -206,6 +210,7 @@ class UsersController: def add_user( self, username, + manager, password, email="default@example.com", enabled: bool = True, @@ -213,6 +218,7 @@ class UsersController: ): return self.users_helper.add_user( username, + manager, password=password, email=email, enabled=enabled, @@ -236,6 +242,8 @@ class UsersController: ) def remove_user(self, user_id): + for user in self.get_managed_users(user_id): + self.update_user(user.user_id, {"manager": None}) return self.users_helper.remove_user(user_id) @staticmethod diff --git a/app/classes/models/users.py b/app/classes/models/users.py index ac204e3c..c3d2a3a8 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -6,6 +6,7 @@ from peewee import ( ForeignKeyField, CharField, AutoField, + IntegerField, DateTimeField, BooleanField, CompositeKey, @@ -40,6 +41,7 @@ class Users(BaseModel): server_order = CharField(default="") preparing = BooleanField(default=False) hints = BooleanField(default=True) + manager = IntegerField(default=None, null=True) class Meta: table_name = "users" @@ -138,6 +140,11 @@ class HelperUsers: user_query = Users.select().where(Users.user_id == user_id) return user_query + @staticmethod + def get_managed_users(exec_user_id): + user_query = Users.select().where(Users.manager == exec_user_id) + return user_query + @staticmethod def get_user(user_id): if user_id == 0: @@ -192,6 +199,7 @@ class HelperUsers: def add_user( self, username: str, + manager: str, password: str = None, email: t.Optional[str] = None, enabled: bool = True, @@ -209,6 +217,7 @@ class HelperUsers: Users.enabled: enabled, Users.superuser: superuser, Users.created: Helpers.get_time_as_string(), + Users.manager: manager, } ).execute() return user_id @@ -216,6 +225,7 @@ class HelperUsers: @staticmethod def add_rawpass_user( username: str, + manager: str, password: str = "", email: t.Optional[str] = "default@example.com", enabled: bool = True, @@ -229,6 +239,7 @@ class HelperUsers: Users.enabled: enabled, Users.superuser: superuser, Users.created: Helpers.get_time_as_string(), + Users.manager: manager, } ).execute() return user_id diff --git a/app/classes/web/api_handler.py b/app/classes/web/api_handler.py index 43af4ae8..34b09ee8 100644 --- a/app/classes/web/api_handler.py +++ b/app/classes/web/api_handler.py @@ -340,10 +340,11 @@ class CreateUser(ApiHandler): new_username = self.get_argument("username").lower() new_pass = self.get_argument("password") + manager = int(user_obj["user_id"]) if new_username: self.controller.users.add_user( - new_username, new_pass, "default@example.com", True, False + new_username, manager, new_pass, "default@example.com", True, False ) self.return_response( diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a5e56029..624450c5 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -860,6 +860,11 @@ class PanelHandler(BaseHandler): page_data["users"] = self.controller.users.get_all_users() page_data["roles"] = self.controller.roles.get_all_roles() page_data["auth-servers"][user.user_id] = super_auth_servers + page_data["managed_users"] = [] + else: + page_data["managed_users"] = self.controller.users.get_managed_users( + exec_user["user_id"] + ) template = "panel/panel_config.html" @@ -904,8 +909,14 @@ class PanelHandler(BaseHandler): ) if superuser: page_data["super-disabled"] = "" + page_data["users"] = self.controller.users.get_all_users() else: page_data["super-disabled"] = "disabled" + + page_data["manager"] = { + "user_id": -100, + "username": "None", + } for file in sorted( os.listdir(os.path.join(self.helper.root_dir, "app", "translations")) ): @@ -1077,6 +1088,17 @@ class PanelHandler(BaseHandler): page_data["roles_all"] = self.controller.roles.get_all_roles() page_data["servers_all"] = self.controller.servers.get_all_defined_servers() page_data["superuser"] = superuser + if page_data["user"]["manager"] is not None: + page_data["manager"] = self.controller.users.get_user_by_id( + page_data["user"]["manager"] + ) + else: + page_data["manager"] = { + "user_id": -100, + "username": "None", + } + if exec_user["superuser"]: + page_data["users"] = self.controller.users.get_all_users() page_data[ "permissions_all" ] = self.controller.crafty_perms.list_defined_crafty_permissions() @@ -1115,6 +1137,13 @@ class PanelHandler(BaseHandler): "/panel/error?error=Unauthorized access: not a user editor" ) return + if ( + self.controller.users.get_user_by_id(user_id)["manager"] + != exec_user["user_id"] + ) and not exec_user["superuser"]: + self.redirect( + "/panel/error?error=Unauthorized access: you cannot edit this user" + ) page_data["servers"] = [] page_data["role-servers"] = [] @@ -1962,6 +1991,13 @@ class PanelHandler(BaseHandler): else: superuser = 0 + if exec_user["superuser"]: + manager = self.get_argument("manager") + if manager == "": + manager = None + else: + manager = int(manager) + if not exec_user["superuser"]: if username is None or username == "": self.redirect("/panel/error?error=Invalid username") @@ -2013,6 +2049,7 @@ class PanelHandler(BaseHandler): user_data = { "username": username, + "manager": manager, "password": password0, "email": email, "enabled": enabled, @@ -2158,6 +2195,15 @@ class PanelHandler(BaseHandler): if username is None or username == "": self.redirect("/panel/error?error=Invalid username") return + + if exec_user["superuser"]: + manager = self.get_argument("manager") + if manager == "": + manager = None + else: + manager = int(manager) + else: + manager = int(exec_user["user_id"]) # does this user id exist? if self.controller.users.get_id_by_name(username) is not None: self.redirect("/panel/error?error=User exists") @@ -2172,6 +2218,7 @@ class PanelHandler(BaseHandler): user_id = self.controller.users.add_user( username, + manager=manager, password=password0, email=email, enabled=enabled, diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index 3e4cfdab..6f46740e 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -96,6 +96,7 @@ class ApiUsersIndexHandler(BaseApiHandler): username = data["username"] username = str(username).lower() + manager = int(user["user_id"]) password = data["password"] email = data.get("email", "default@example.com") enabled = data.get("enabled", True) @@ -149,6 +150,7 @@ class ApiUsersIndexHandler(BaseApiHandler): # TODO: do this in the most efficient way user_id = self.controller.users.add_user( username, + manager, password, email, enabled, diff --git a/app/frontend/templates/panel/panel_config.html b/app/frontend/templates/panel/panel_config.html index 94d5c0f1..0c89a596 100644 --- a/app/frontend/templates/panel/panel_config.html +++ b/app/frontend/templates/panel/panel_config.html @@ -87,6 +87,38 @@ {% end %} + {% for user in data['managed_users'] %} + + {{ user.username }} + + {% if user.enabled %} + + Yes + + {% else %} + + No + + + {% end %} + + +
    + {% for item in data['auth-servers'][user.user_id] %} +
  • {{item}}
  • + {% end %} +
+ + +
    + {% for item in data['user-roles'][user.user_id] %} +
  • {{item}}
  • + {% end %} +
+ + + + {% end %}
diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 7286f4a0..aa1939f0 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -121,6 +121,26 @@ data['lang']) }}{% end %} {% end %} + {% if data['superuser'] %} +
+ + +
+ {% end %} @@ -284,7 +304,11 @@ data['lang']) }}{% end %} {% else %} + +
+
+

Manager: {{data['manager']['username'] }}

diff --git a/app/migrations/20220819_user_manager.py b/app/migrations/20220819_user_manager.py new file mode 100644 index 00000000..07fa186f --- /dev/null +++ b/app/migrations/20220819_user_manager.py @@ -0,0 +1,16 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.add_columns("users", manager=peewee.IntegerField(null=True)) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.drop_columns("users", ["manager"]) + """ + Write your rollback migrations here. + """ From 7a98331b1ff7f7938dd94b4723b1dc322a826a68 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 19 Aug 2022 15:08:35 -0400 Subject: [PATCH 07/39] Add translations. Tweak frontend --- app/frontend/templates/panel/panel_edit_user.html | 9 ++++----- app/translations/en_EN.json | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index aa1939f0..e8c8a708 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -123,7 +123,8 @@ data['lang']) }}{% end %} {% if data['superuser'] %}
-
+ +
+ {% if data['superuser'] %} +
+ + +
+ {% end %} @@ -252,7 +275,7 @@
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
- Manager: {{ data['role_manager']['username'] }} + {{ translate('userConfig', 'manager', data['lang']) }}: {{ data['role_manager']['username'] }}

diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index f63a195b..fd1e8c26 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -245,7 +245,8 @@ "roleUsers": "Role Users: ", "serverAccess": "Access?", "serverName": "Server Name", - "serversDesc": "servers this role is allowed to access" + "serversDesc": "servers this role is allowed to access", + "selectManager": "Select a manager for this Role" }, "serverBackups": { "backupAtMidnight": "Auto-backup at midnight?", From 31b75b1c508c74e4b2541aee32efb7d70c7d5ee5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 10:24:43 -0400 Subject: [PATCH 13/39] Remove manager from role when user deleted --- app/classes/controllers/users_controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index c62d209d..bbaf9a05 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -2,6 +2,7 @@ import logging import typing as t from app.classes.models.users import HelperUsers +from app.classes.models.roles import HelperRoles from app.classes.models.crafty_permissions import ( PermissionsCrafty, EnumPermissionsCrafty, @@ -244,6 +245,8 @@ class UsersController: def remove_user(self, user_id): for user in self.get_managed_users(user_id): self.update_user(user.user_id, {"manager": None}) + for role in HelperRoles.get_managed_roles(user_id): + HelperRoles.update_role(role.role_id, {"manager": None}) return self.users_helper.remove_user(user_id) @staticmethod From 245d64ca455e798359984f9dc6a0e5f587e975d4 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 12:56:38 -0400 Subject: [PATCH 14/39] Refactor managed roles to users controller/model --- app/classes/controllers/roles_controller.py | 4 ---- app/classes/controllers/users_controller.py | 9 +++++++++ app/classes/models/roles.py | 4 ---- app/classes/models/users.py | 5 +++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py index 647902a3..7e20362c 100644 --- a/app/classes/controllers/roles_controller.py +++ b/app/classes/controllers/roles_controller.py @@ -67,10 +67,6 @@ class RolesController: def add_role(role_name, manager): return HelperRoles.add_role(role_name, manager) - @staticmethod - def get_managed_roles(exec_user_id): - return HelperRoles.get_managed_roles(exec_user_id) - class RoleServerJsonType(t.TypedDict): server_id: t.Union[str, int] permissions: str diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index bbaf9a05..b5c129ac 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -1,5 +1,6 @@ import logging import typing as t +from app.classes.models.servers import HelperServers from app.classes.models.users import HelperUsers from app.classes.models.roles import HelperRoles @@ -137,6 +138,14 @@ class UsersController: def get_managed_users(exec_user_id): return HelperUsers.get_managed_users(exec_user_id) + @staticmethod + def get_managed_roles(exec_user_id): + return HelperUsers.get_managed_roles(exec_user_id) + + @staticmethod + def get_created_servers(exec_user_id): + return HelperServers.get_total_owned_servers(exec_user_id) + def update_user(self, user_id: str, user_data=None, user_crafty_data=None): if user_crafty_data is None: user_crafty_data = {} diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py index 7c9b98b1..541f67e8 100644 --- a/app/classes/models/roles.py +++ b/app/classes/models/roles.py @@ -40,10 +40,6 @@ class HelperRoles: def get_all_roles(): return Roles.select() - @staticmethod - def get_managed_roles(exec_user_id): - return Roles.select().where(Roles.manager == exec_user_id) - @staticmethod def get_all_role_ids() -> t.List[int]: return [role.role_id for role in Roles.select(Roles.role_id).execute()] diff --git a/app/classes/models/users.py b/app/classes/models/users.py index c3d2a3a8..e625eec2 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -145,6 +145,11 @@ class HelperUsers: user_query = Users.select().where(Users.manager == exec_user_id) return user_query + @staticmethod + def get_managed_roles(exec_user_id): + roles_query = Roles.select().where(Roles.manager == exec_user_id) + return roles_query + @staticmethod def get_user(user_id): if user_id == 0: From cafa720bbab8311161de8e5f1b43e5ec0c79be30 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 13:00:20 -0400 Subject: [PATCH 15/39] Fix server quotas. Removed un-used perms columns --- app/classes/controllers/servers_controller.py | 2 + app/classes/models/crafty_permissions.py | 45 +++++-------------- app/classes/models/servers.py | 7 +++ app/classes/shared/main_controller.py | 31 +++++++++++-- app/classes/web/ajax_handler.py | 2 + app/classes/web/panel_handler.py | 4 +- app/classes/web/routes/api/servers/index.py | 7 ++- .../web/routes/api/servers/server/action.py | 1 + app/classes/web/server_handler.py | 41 +++++++++++------ app/migrations/20220820_quota.py | 16 +++++++ app/migrations/20220820_user_crafty.py | 20 +++++++++ 11 files changed, 117 insertions(+), 59 deletions(-) create mode 100644 app/migrations/20220820_quota.py create mode 100644 app/migrations/20220820_user_crafty.py diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 650a16b0..394e50fd 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -51,6 +51,7 @@ class ServersController(metaclass=Singleton): server_log_file: str, server_stop: str, server_type: str, + created_by: int, server_port: int = 25565, server_host: str = "127.0.0.1", ) -> int: @@ -85,6 +86,7 @@ class ServersController(metaclass=Singleton): server_log_file, server_stop, server_type, + created_by, server_port, server_host, ) diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 9b99bfb0..9f55715d 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -9,6 +9,7 @@ from peewee import ( ) from app.classes.models.base_model import BaseModel +from app.classes.models.servers import HelperServers from app.classes.models.users import Users, ApiKeys, HelperUsers from app.classes.shared.permission_helper import PermissionHelper @@ -23,9 +24,6 @@ class UserCrafty(BaseModel): limit_server_creation = IntegerField(default=-1) limit_user_creation = IntegerField(default=0) limit_role_creation = IntegerField(default=0) - created_server = IntegerField(default=0) - created_user = IntegerField(default=0) - created_role = IntegerField(default=0) class Meta: table_name = "user_crafty" @@ -107,9 +105,6 @@ class PermissionsCrafty: UserCrafty.limit_server_creation: 0, UserCrafty.limit_user_creation: 0, UserCrafty.limit_role_creation: 0, - UserCrafty.created_server: 0, - UserCrafty.created_user: 0, - UserCrafty.created_role: 0, } ).execute() user_crafty = PermissionsCrafty.get_user_crafty(user_id) @@ -159,11 +154,16 @@ class PermissionsCrafty: @staticmethod def get_created_quantity_list(user_id): - user_crafty = PermissionsCrafty.get_user_crafty(user_id) quantity_list = { - EnumPermissionsCrafty.SERVER_CREATION.name: user_crafty.created_server, - EnumPermissionsCrafty.USER_CONFIG.name: user_crafty.created_user, - EnumPermissionsCrafty.ROLES_CONFIG.name: user_crafty.created_role, + EnumPermissionsCrafty.SERVER_CREATION.name: HelperServers.get_total_owned_servers( + user_id + ), + EnumPermissionsCrafty.USER_CONFIG.name: HelperUsers.get_managed_users( + user_id + ).count(), + EnumPermissionsCrafty.ROLES_CONFIG.name: HelperUsers.get_managed_roles( + user_id + ).count(), } return quantity_list @@ -183,31 +183,6 @@ class PermissionsCrafty: or limit_list[permission.name] == -1 ) - @staticmethod - def add_server_creation(user_id: int): - """Increase the "Server Creation" counter for this user - - Args: - user_id (int): The modifiable user's ID - """ - UserCrafty.update(created_server=UserCrafty.created_server + 1).where( - UserCrafty.user_id == user_id - ).execute() - - @staticmethod - def add_user_creation(user_id): - user_crafty = PermissionsCrafty.get_user_crafty(user_id) - user_crafty.created_user += 1 - UserCrafty.save(user_crafty) - return user_crafty.created_user - - @staticmethod - def add_role_creation(user_id): - user_crafty = PermissionsCrafty.get_user_crafty(user_id) - user_crafty.created_role += 1 - UserCrafty.save(user_crafty) - return user_crafty.created_role - @staticmethod def get_api_key_permissions_list(key: ApiKeys): user = HelperUsers.get_user(key.user_id) diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 71ca4851..db61f2d7 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -38,6 +38,7 @@ class Servers(BaseModel): logs_delete_after = IntegerField(default=0) type = CharField(default="minecraft-java") show_status = BooleanField(default=1) + created_by = IntegerField(default=-100) class Meta: table_name = "servers" @@ -64,6 +65,7 @@ class HelperServers: server_log_file: str, server_stop: str, server_type: str, + created_by: int, server_port: int = 25565, server_host: str = "127.0.0.1", ) -> int: @@ -105,6 +107,7 @@ class HelperServers: Servers.stop_command: server_stop, Servers.backup_path: backup_path, Servers.type: server_type, + Servers.created_by: created_by, } ).execute() @@ -112,6 +115,10 @@ class HelperServers: def get_server_obj(server_id): return Servers.get_by_id(server_id) + @staticmethod + def get_total_owned_servers(user_id): + return Servers.select().where(Servers.created_by == user_id).count() + @staticmethod def get_server_type_by_id(server_id): server_type = Servers.select().where(Servers.server_id == server_id).get() diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index bca24fe8..3374ab27 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -244,7 +244,7 @@ class Controller: except: return {"percent": 0, "total_files": 0} - def create_api_server(self, data: dict): + def create_api_server(self, data: dict, user_id): server_fs_uuid = Helpers.create_uuid() new_server_path = os.path.join(self.helper.servers_dir, server_fs_uuid) backup_path = os.path.join(self.helper.backup_path, server_fs_uuid) @@ -307,7 +307,9 @@ class Controller: # TODO: Copy files from the zip file to the new server directory server_file = create_data["jarfile"] raise Exception("Not yet implemented") - _create_server_properties_if_needed(create_data["server_properties_port"]) + _create_server_properties_if_needed( + create_data["server_properties_port"], + ) min_mem = create_data["mem_min"] max_mem = create_data["mem_max"] @@ -403,6 +405,7 @@ class Controller: server_log_file=log_location, server_stop=stop_command, server_port=monitoring_port, + created_by=user_id, server_host=monitoring_host, server_type=monitoring_type, ) @@ -429,6 +432,7 @@ class Controller: min_mem: int, max_mem: int, port: int, + user_id: int, ): server_id = Helpers.create_uuid() server_dir = os.path.join(self.helper.servers_dir, server_id) @@ -489,6 +493,7 @@ class Controller: server_log_file, server_stop, port, + user_id, server_type="minecraft-java", ) @@ -524,6 +529,7 @@ class Controller: min_mem: int, max_mem: int, port: int, + user_id: int, ): server_id = Helpers.create_uuid() new_server_dir = os.path.join(self.helper.servers_dir, server_id) @@ -584,6 +590,7 @@ class Controller: server_log_file, server_stop, port, + user_id, server_type="minecraft-java", ) return new_id @@ -596,6 +603,7 @@ class Controller: min_mem: int, max_mem: int, port: int, + user_id: int, ): server_id = Helpers.create_uuid() new_server_dir = os.path.join(self.helper.servers_dir, server_id) @@ -664,6 +672,7 @@ class Controller: server_log_file, server_stop, port, + user_id, server_type="minecraft-java", ) return new_id @@ -673,7 +682,12 @@ class Controller: # ********************************************************************************** def import_bedrock_server( - self, server_name: str, server_path: str, server_exe: str, port: int + self, + server_name: str, + server_path: str, + server_exe: str, + port: int, + user_id: int, ): server_id = Helpers.create_uuid() new_server_dir = os.path.join(self.helper.servers_dir, server_id) @@ -727,6 +741,7 @@ class Controller: server_log_file, server_stop, port, + user_id, server_type="minecraft-bedrock", ) if os.name != "nt": @@ -735,7 +750,12 @@ class Controller: return new_id def import_bedrock_zip_server( - self, server_name: str, zip_path: str, server_exe: str, port: int + self, + server_name: str, + zip_path: str, + server_exe: str, + port: int, + user_id: int, ): server_id = Helpers.create_uuid() new_server_dir = os.path.join(self.helper.servers_dir, server_id) @@ -796,6 +816,7 @@ class Controller: server_log_file, server_stop, port, + user_id, server_type="minecraft-bedrock", ) if os.name != "nt": @@ -838,6 +859,7 @@ class Controller: server_log_file: str, server_stop: str, server_port: int, + created_by: int, server_type: str, server_host: str = "127.0.0.1", ): @@ -852,6 +874,7 @@ class Controller: server_log_file, server_stop, server_type, + created_by, server_port, server_host, ) diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 9aa41d28..bd8f9424 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -394,6 +394,7 @@ class AjaxHandler(BaseHandler): "1", "2", server_data["server_port"], + server_data["created_by"], ) new_server_id = new_server new_server = self.controller.servers.get_server_data(new_server) @@ -416,6 +417,7 @@ class AjaxHandler(BaseHandler): temp_dir, server_data["executable"], server_data["server_port"], + server_data["created_by"], ) new_server_id = new_server new_server = self.controller.servers.get_server_data(new_server) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 33bba144..a2681050 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -869,7 +869,7 @@ class PanelHandler(BaseHandler): for item in page_data["roles"]: page_data["assigned_roles"].append(item.role_id) - page_data["managed_roles"] = self.controller.roles.get_managed_roles( + page_data["managed_roles"] = self.controller.users.get_managed_roles( exec_user["user_id"] ) @@ -2282,7 +2282,6 @@ class PanelHandler(BaseHandler): server_id=0, source_ip=self.get_remote_ip(), ) - self.controller.crafty_perms.add_user_creation(exec_user["user_id"]) self.redirect("/panel/panel_config") elif page == "edit_role": @@ -2359,7 +2358,6 @@ class PanelHandler(BaseHandler): server_id=0, source_ip=self.get_remote_ip(), ) - self.controller.crafty_perms.add_role_creation(exec_user["user_id"]) self.redirect("/panel/panel_config") else: diff --git a/app/classes/web/routes/api/servers/index.py b/app/classes/web/routes/api/servers/index.py index 7db12f45..b94b4c01 100644 --- a/app/classes/web/routes/api/servers/index.py +++ b/app/classes/web/routes/api/servers/index.py @@ -665,10 +665,9 @@ class ApiServersIndexHandler(BaseApiHandler): }, ) - new_server_id, new_server_uuid = self.controller.create_api_server(data) - - # Increase the server creation counter - self.controller.crafty_perms.add_server_creation(user["user_id"]) + new_server_id, new_server_uuid = self.controller.create_api_server( + data, user["user_id"] + ) self.controller.servers.stats.record_stats() diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index cf9163b9..e5b3ae23 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -84,6 +84,7 @@ class ApiServersServerActionHandler(BaseApiHandler): new_server_log_file, server_data.get("stop_command"), server_data.get("type"), + user_id, server_data.get("server_port"), ) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index b49b6410..e83da7cd 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -265,6 +265,7 @@ class ServerHandler(BaseHandler): backup_path = os.path.join(self.helper.backup_path, new_server_uuid) server_port = server_data.get("server_port") server_type = server_data.get("type") + created_by = exec_user["user_id"] new_server_id = self.controller.servers.create_server( new_server_name, @@ -276,6 +277,7 @@ class ServerHandler(BaseHandler): new_server_log_file, stop_command, server_type, + created_by, server_port, ) if not exec_user["superuser"]: @@ -292,9 +294,6 @@ class ServerHandler(BaseHandler): self.controller.users.add_role_to_user( exec_user["user_id"], role_id ) - self.controller.crafty_perms.add_server_creation( - exec_user["user_id"] - ) self.controller.servers.init_all_servers() @@ -354,6 +353,7 @@ class ServerHandler(BaseHandler): min_mem, max_mem, port, + exec_user["user_id"], ) self.controller.management.add_to_audit_log( exec_user["user_id"], @@ -370,7 +370,13 @@ class ServerHandler(BaseHandler): return new_server_id = self.controller.import_zip_server( - server_name, zip_path, import_server_jar, min_mem, max_mem, port + server_name, + zip_path, + import_server_jar, + min_mem, + max_mem, + port, + exec_user["user_id"], ) if new_server_id == "false": self.redirect( @@ -403,6 +409,7 @@ class ServerHandler(BaseHandler): min_mem, max_mem, port, + exec_user["user_id"], ) self.controller.management.add_to_audit_log( exec_user["user_id"], @@ -430,9 +437,6 @@ class ServerHandler(BaseHandler): self.controller.users.add_role_to_user( exec_user["user_id"], role_id ) - self.controller.crafty_perms.add_server_creation( - exec_user["user_id"] - ) else: for role in captured_roles: @@ -485,7 +489,11 @@ class ServerHandler(BaseHandler): return new_server_id = self.controller.import_bedrock_server( - server_name, import_server_path, import_server_exe, port + server_name, + import_server_path, + import_server_exe, + port, + exec_user["user_id"], ) self.controller.management.add_to_audit_log( exec_user["user_id"], @@ -502,7 +510,11 @@ class ServerHandler(BaseHandler): return new_server_id = self.controller.import_bedrock_zip_server( - server_name, zip_path, import_server_exe, port + server_name, + zip_path, + import_server_exe, + port, + exec_user["user_id"], ) if new_server_id == "false": self.redirect( @@ -528,7 +540,13 @@ class ServerHandler(BaseHandler): # TODO: add server type check here and call the correct server # add functions if not a jar new_server_id = self.controller.create_jar_server( - server_type, server_version, server_name, min_mem, max_mem, port + server_type, + server_version, + server_name, + min_mem, + max_mem, + port, + exec_user["user_id"], ) self.controller.management.add_to_audit_log( exec_user["user_id"], @@ -556,9 +574,6 @@ class ServerHandler(BaseHandler): self.controller.users.add_role_to_user( exec_user["user_id"], role_id ) - self.controller.crafty_perms.add_server_creation( - exec_user["user_id"] - ) else: for role in captured_roles: diff --git a/app/migrations/20220820_quota.py b/app/migrations/20220820_quota.py new file mode 100644 index 00000000..a1d93e49 --- /dev/null +++ b/app/migrations/20220820_quota.py @@ -0,0 +1,16 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.add_columns("servers", created_by=peewee.IntegerField(default=-100)) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.drop_columns("servers", ["created_by"]) + """ + Write your rollback migrations here. + """ diff --git a/app/migrations/20220820_user_crafty.py b/app/migrations/20220820_user_crafty.py new file mode 100644 index 00000000..6d1557d7 --- /dev/null +++ b/app/migrations/20220820_user_crafty.py @@ -0,0 +1,20 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.drop_columns("user_crafty", ["created_server"]) + migrator.drop_columns("user_crafty", ["created_user"]) + migrator.drop_columns("user_crafty", ["created_role"]) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.add_columns("user_crafty", created_server=peewee.IntegerField(default=0)) + migrator.add_columns("user_crafty", created_user=peewee.IntegerField(default=0)) + migrator.add_columns("user_crafty", created_role=peewee.IntegerField(default=0)) + """ + Write your rollback migrations here. + """ From 0f439170833dad52ed07a3985c465a2849ee7534 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 13:15:44 -0400 Subject: [PATCH 16/39] Remove unused methods --- .../controllers/crafty_perms_controller.py | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py index 9c79c33a..111e3971 100644 --- a/app/classes/controllers/crafty_perms_controller.py +++ b/app/classes/controllers/crafty_perms_controller.py @@ -60,26 +60,6 @@ class CraftyPermsController: permissions_list = PermissionsCrafty.get_permissions(permissions_mask) return permissions_list - @staticmethod - def add_server_creation(user_id): - """Increase the "Server Creation" counter for this user - - Args: - user_id (int): The modifiable user's ID - - Returns: - int: The new count of servers created by this user - """ - return PermissionsCrafty.add_server_creation(user_id) - - @staticmethod - def add_user_creation(user_id): - return PermissionsCrafty.add_user_creation(user_id) - - @staticmethod - def add_role_creation(user_id): - return PermissionsCrafty.add_role_creation(user_id) - @staticmethod def get_api_key_permissions_list(key: ApiKeys): return PermissionsCrafty.get_api_key_permissions_list(key) From cbbb9f9fd2dc2eef65f52cd672f046c747a4e740 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 13:45:10 -0400 Subject: [PATCH 17/39] Fix clone server not taking into account quotas --- app/classes/web/server_handler.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index e83da7cd..d61d42b2 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -224,6 +224,17 @@ class ServerHandler(BaseHandler): if server_id is not None: if command == "clone_server": + if ( + not superuser + and not self.controller.crafty_perms.can_create_server( + exec_user["user_id"] + ) + ): + self.redirect( + "/panel/error?error=Unauthorized access: " + "not a server creator or server limit reached" + ) + return def is_name_used(name): for server in self.controller.servers.get_all_defined_servers(): @@ -231,6 +242,7 @@ class ServerHandler(BaseHandler): return True return + template = "/panel/dashboard" server_data = self.controller.servers.get_server_data_by_id( server_id ) From 0672b9378a6272303bf55f8a9adb706f39358418 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 20 Aug 2022 18:51:35 -0400 Subject: [PATCH 18/39] Add more secure logic to add/edit users --- app/classes/controllers/roles_controller.py | 2 + app/classes/web/panel_handler.py | 42 ++++++++++++++----- app/classes/web/server_handler.py | 13 ++++-- app/frontend/templates/panel/dashboard.html | 3 -- .../templates/panel/panel_edit_user.html | 10 ++++- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/app/classes/controllers/roles_controller.py b/app/classes/controllers/roles_controller.py index 7e20362c..ab5dcd5a 100644 --- a/app/classes/controllers/roles_controller.py +++ b/app/classes/controllers/roles_controller.py @@ -115,6 +115,7 @@ class RolesController: role_id: t.Union[str, int], role_name: t.Optional[str], servers: t.Optional[t.Iterable[RoleServerJsonType]], + manager: int, ) -> None: """Update a role with a name and a list of servers @@ -153,6 +154,7 @@ class RolesController: up_data = { "role_name": role_name, "last_update": Helpers.get_time_as_string(), + "manager": manager, } # TODO: do the last_update on the db side HelperRoles.update_role(role_id, up_data) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a2681050..396df7c2 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -897,7 +897,7 @@ class PanelHandler(BaseHandler): ) return - page_data["roles_all"] = self.controller.roles.get_all_roles() + page_data["roles"] = self.controller.roles.get_all_roles() page_data["servers"] = [] page_data["servers_all"] = self.controller.servers.get_all_defined_servers() page_data["role-servers"] = [] @@ -920,6 +920,8 @@ class PanelHandler(BaseHandler): else: page_data["super-disabled"] = "disabled" + page_data["exec_user"] = exec_user["user_id"] + page_data["manager"] = { "user_id": -100, "username": "None", @@ -1092,7 +1094,8 @@ class PanelHandler(BaseHandler): page_data["user"] = self.controller.users.get_user_by_id(user_id) page_data["servers"] = set() page_data["role-servers"] = page_role_servers - page_data["roles_all"] = self.controller.roles.get_all_roles() + page_data["roles"] = self.controller.roles.get_all_roles() + page_data["exec_user"] = exec_user["user_id"] page_data["servers_all"] = self.controller.servers.get_all_defined_servers() page_data["superuser"] = superuser if page_data["user"]["manager"] is not None: @@ -1274,17 +1277,14 @@ class PanelHandler(BaseHandler): user_roles = self.get_user_roles() page_data["new_role"] = False role_id = self.get_argument("id", None) + role = self.controller.roles.get_role(role_id) page_data["role"] = self.controller.roles.get_role_with_servers(role_id) if exec_user["superuser"]: defined_servers = self.controller.servers.list_defined_servers() - manager = self.get_argument("manager", None) - if manager == "": - manager = None else: defined_servers = self.controller.servers.get_authorized_servers( exec_user["user_id"] ) - manager = exec_user["user_id"] page_servers = [] for server in defined_servers: if server not in page_servers: @@ -1311,7 +1311,11 @@ class PanelHandler(BaseHandler): "username": "None", } - if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions: + if ( + EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions + and exec_user["user_id"] != role["manager"] + and not exec_user["superuser"] + ): self.redirect( "/panel/error?error=Unauthorized access: not a role editor" ) @@ -1996,6 +2000,7 @@ class PanelHandler(BaseHandler): "system user is not editable" ) user_id = bleach.clean(self.get_argument("id", None)) + user = self.controller.users.get_user_by_id(user_id) username = bleach.clean(self.get_argument("username", None).lower()) if ( username != self.controller.users.get_user_by_id(user_id)["username"] @@ -2034,8 +2039,10 @@ class PanelHandler(BaseHandler): manager = None else: manager = int(manager) + else: + manager = user["manager"] - if not exec_user["superuser"]: + if not exec_user["superuser"] and exec_user["user_id"] != user["user_id"]: if username is None or username == "": self.redirect("/panel/error?error=Invalid username") return @@ -2288,7 +2295,13 @@ class PanelHandler(BaseHandler): role_id = bleach.clean(self.get_argument("id", None)) role_name = bleach.clean(self.get_argument("role_name", None)) - if EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions: + role = self.controller.roles.get_role(role_id) + + if ( + EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions + and exec_user["user_id"] != role["manager"] + and not exec_user["superuser"] + ): self.redirect( "/panel/error?error=Unauthorized access: not a role editor" ) @@ -2304,9 +2317,18 @@ class PanelHandler(BaseHandler): self.redirect("/panel/error?error=Invalid Role ID") return + if exec_user["superuser"]: + manager = self.get_argument("manager", None) + if manager == "": + manager = None + else: + manager = role["manager"] + servers = self.get_role_servers() - self.controller.roles.update_role_advanced(role_id, role_name, servers) + self.controller.roles.update_role_advanced( + role_id, role_name, servers, manager + ) self.controller.management.add_to_audit_log( exec_user["user_id"], diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index d61d42b2..a8dce089 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -1,6 +1,7 @@ import json import logging import os +import time import tornado.web import tornado.escape import bleach @@ -230,9 +231,15 @@ class ServerHandler(BaseHandler): exec_user["user_id"] ) ): - self.redirect( - "/panel/error?error=Unauthorized access: " - "not a server creator or server limit reached" + time.sleep(3) + self.helper.websocket_helper.broadcast_user( + exec_user["user_id"], + "send_start_error", + { + "error": "" + " Not a server creator or server limit reached." + }, ) return diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index ec5a21db..f692018c 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -893,9 +893,6 @@ message: '
  {% raw translate("dashboard", "bePatientClone", data["lang"]) %}
', closeButton: false, }); - setTimeout(function () { - location.reload(); - }, 5000) } diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index e8c8a708..6f165f52 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -162,21 +162,29 @@ data['lang']) }}{% end %} - {% for role in data['roles_all'] %} + {% for role in data['roles'] %} + {% if data['superuser'] or role.role_id in data['user']['roles'] or role.manager == data['exec_user'] %} {{ role.role_name }} {% if role.role_id in data['user']['roles'] %} + {% if role.manager == data['exec_user'] or data['superuser'] %} {% else %} + + {% end %} + {% elif data['superuser'] or role.manager == data['exec_user'] %} {% end %} + {% end %} {% end %} From 6b82170d6ff9e178c7114aa6d088b9f3b3feca5a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Aug 2022 20:47:15 -0400 Subject: [PATCH 19/39] Appease the linter --- app/classes/models/crafty_permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 9f55715d..22383408 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -155,7 +155,7 @@ class PermissionsCrafty: @staticmethod def get_created_quantity_list(user_id): quantity_list = { - EnumPermissionsCrafty.SERVER_CREATION.name: HelperServers.get_total_owned_servers( + EnumPermissionsCrafty.SERVER_CREATION.name: HelperServers.get_total_owned_servers( # pylint: disable=line-too-long user_id ), EnumPermissionsCrafty.USER_CONFIG.name: HelperUsers.get_managed_users( From 02b6c3b545277fe939167998dde9254a3cead0ff Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Aug 2022 20:58:46 -0400 Subject: [PATCH 20/39] Fix logical issues with panel handler --- app/classes/controllers/users_controller.py | 2 +- app/classes/web/panel_handler.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index b5c129ac..13b8fb4f 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -254,7 +254,7 @@ class UsersController: def remove_user(self, user_id): for user in self.get_managed_users(user_id): self.update_user(user.user_id, {"manager": None}) - for role in HelperRoles.get_managed_roles(user_id): + for role in HelperUsers.get_managed_roles(user_id): HelperRoles.update_role(role.role_id, {"manager": None}) return self.users_helper.remove_user(user_id) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 396df7c2..11b6b03b 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1313,7 +1313,7 @@ class PanelHandler(BaseHandler): if ( EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_crafty_permissions - and exec_user["user_id"] != role["manager"] + or exec_user["user_id"] != role["manager"] and not exec_user["superuser"] ): self.redirect( @@ -2042,7 +2042,10 @@ class PanelHandler(BaseHandler): else: manager = user["manager"] - if not exec_user["superuser"] and exec_user["user_id"] != user["user_id"]: + if ( + not exec_user["superuser"] + and int(exec_user["user_id"]) != user["manager"] + ): if username is None or username == "": self.redirect("/panel/error?error=Invalid username") return @@ -2110,13 +2113,13 @@ class PanelHandler(BaseHandler): user_id, user_data=user_data, user_crafty_data=user_crafty_data ) - self.controller.management.add_to_audit_log( - exec_user["user_id"], - f"Edited user {username} (UID:{user_id}) with roles {roles} " - f"and permissions {permissions_mask}", - server_id=0, - source_ip=self.get_remote_ip(), - ) + self.controller.management.add_to_audit_log( + exec_user["user_id"], + f"Edited user {username} (UID:{user_id}) with roles {roles} " + f"and permissions {permissions_mask}", + server_id=0, + source_ip=self.get_remote_ip(), + ) self.redirect("/panel/panel_config") elif page == "edit_user_apikeys": From 44afc72d3a7badfa46e89f8de6ea016a71db3724 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 21 Aug 2022 12:41:28 -0400 Subject: [PATCH 21/39] Add variable shutdown timeouts --- app/classes/models/servers.py | 1 + app/classes/shared/server.py | 7 +++++-- app/classes/web/panel_handler.py | 2 ++ app/frontend/templates/panel/server_config.html | 11 +++++++++++ app/migrations/20220821_shutdown_timeout.py | 16 ++++++++++++++++ app/translations/en_EN.json | 7 +++++-- 6 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 app/migrations/20220821_shutdown_timeout.py diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 71ca4851..e22a428c 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -38,6 +38,7 @@ class Servers(BaseModel): logs_delete_after = IntegerField(default=0) type = CharField(default="minecraft-java") show_status = BooleanField(default=1) + shutdown_timeout = IntegerField(default=60) class Meta: table_name = "servers" diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 499a0080..97192323 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -612,12 +612,15 @@ class ServerInstance: # caching the name and pid number server_name = self.name server_pid = self.process.pid + self.shutdown_timeout = self.settings["shutdown_timeout"] while running: i += 1 + ttk = int(self.shutdown_timeout - (i * 2)) logstr = ( f"Server {server_name} is still running " - f"- waiting 2s to see if it stops ({int(60-(i*2))} " + "- waiting 2s to see if it stops" + f"({ttk} " f"seconds until force close)" ) logger.info(logstr) @@ -626,7 +629,7 @@ class ServerInstance: time.sleep(2) # if we haven't closed in 60 seconds, let's just slam down on the PID - if i >= 30: + if i >= round(self.shutdown_timeout / 2, 0): logger.info( f"Server {server_name} is still running - Forcing the process down" ) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a5e56029..4f2ce71c 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1418,6 +1418,7 @@ class PanelHandler(BaseHandler): return server_name = self.get_argument("server_name", None) server_obj = self.controller.servers.get_server_obj(server_id) + shutdown_timeout = self.get_argument("shutdown_timeout", 60) if superuser: server_path = self.get_argument("server_path", None) if Helpers.is_os_windows(): @@ -1498,6 +1499,7 @@ class PanelHandler(BaseHandler): ) server_obj.server_name = server_name + server_obj.shutdown_timeout = shutdown_timeout if superuser: if Helpers.validate_traversal( self.helper.get_servers_root_dir(), server_path diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index 6c9a8b36..cc3f7cf7 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -163,6 +163,17 @@ value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1" required> +
+ + +
{% end %}
diff --git a/app/migrations/20220821_shutdown_timeout.py b/app/migrations/20220821_shutdown_timeout.py new file mode 100644 index 00000000..68130360 --- /dev/null +++ b/app/migrations/20220821_shutdown_timeout.py @@ -0,0 +1,16 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.add_columns("servers", shutdown_timeout=peewee.IntegerField(default=60)) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.drop_columns("servers", ["shutdown_timeout"]) + """ + Write your rollback migrations here. + """ diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index f398ccf5..4c73f2b0 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -324,7 +324,10 @@ "stopBeforeDeleting": "Please stop the server before deleting it", "update": "Update Executable", "yesDelete": "Yes, delete", - "yesDeleteFiles": "Yes, delete files" + "yesDeleteFiles": "Yes, delete files", + "shutdownTimeout": "Shutdown Timeout", + "timeoutExplain1": "How long Crafty will wait for your server to shutdown after executing the", + "timeoutExplain2": "command before it forces the process down." }, "serverConfigHelp": { "desc": "Here is where you can change the configuration of your server", @@ -558,4 +561,4 @@ "userSettings": "User Settings", "uses": "Number of uses allowed (-1==No Limit)" } -} +} \ No newline at end of file From d06db6e028f0429f373ab12532e11e222e98ee48 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 21 Aug 2022 12:46:07 -0400 Subject: [PATCH 22/39] Fix formatting for general users --- app/frontend/templates/panel/server_config.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index cc3f7cf7..e1c38e1e 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -163,6 +163,8 @@ value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1" required>
+ {% end %} +
- {% end %}
\ No newline at end of file diff --git a/app/frontend/templates/panel/parts/server_controls_list.html b/app/frontend/templates/panel/parts/server_controls_list.html index 8e40e7ec..c0398d41 100644 --- a/app/frontend/templates/panel/parts/server_controls_list.html +++ b/app/frontend/templates/panel/parts/server_controls_list.html @@ -49,4 +49,8 @@ {{ translate('serverDetails', 'playerControls', data['lang']) }} {% end %} + \ No newline at end of file diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html new file mode 100644 index 00000000..08939772 --- /dev/null +++ b/app/frontend/templates/panel/server_metrics.html @@ -0,0 +1,179 @@ +{% extends ../base.html %} + +{% block meta %} +{% end %} + +{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %} + +{% block content %} + +
+ + +
+
+ +
+ +
+ + + {% include "parts/details_stats.html %} + +
+ +
+
+
+ + {% include "parts/server_controls_list.html %} + + + {% include "parts/m_server_controls_list.html %} + + + + +
+   +   +   +
+
+
+
+
+ + + +
+ + +{% end %} \ No newline at end of file From 7e5d6fddda2fa1fe3941238eb22eb68f3711b72d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 21 Aug 2022 21:47:05 -0400 Subject: [PATCH 25/39] Add zoom to chart --- app/frontend/templates/base.html | 1 + .../templates/panel/parts/m_server_controls_list.html | 2 +- .../templates/panel/parts/server_controls_list.html | 2 +- app/frontend/templates/panel/server_metrics.html | 8 +++++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 1d69675f..37e15a3e 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -41,6 +41,7 @@ + diff --git a/app/frontend/templates/panel/parts/m_server_controls_list.html b/app/frontend/templates/panel/parts/m_server_controls_list.html index 371dca05..dba3802a 100644 --- a/app/frontend/templates/panel/parts/m_server_controls_list.html +++ b/app/frontend/templates/panel/parts/m_server_controls_list.html @@ -30,7 +30,7 @@ {% if data['permissions']['Players'] in data['user_permissions'] and data['server_data']['type'] != 'minecraft-bedrock' %} {{ translate('serverDetails', 'playerControls', data['lang']) }} {% end %} - Metrics + Metrics \ No newline at end of file diff --git a/app/frontend/templates/panel/parts/server_controls_list.html b/app/frontend/templates/panel/parts/server_controls_list.html index c0398d41..17fa1b74 100644 --- a/app/frontend/templates/panel/parts/server_controls_list.html +++ b/app/frontend/templates/panel/parts/server_controls_list.html @@ -51,6 +51,6 @@ {% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 08939772..19ed4ba3 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -62,7 +62,9 @@ const ram = [] const cpu = [] {% for item in data['history_stats'] %} + {% if 'minecraft-java' in data['server_stats']['server_type'] %} players.push("{{ item.online }}"); + {% end %} dates.push("{{ item.created }}"); ram.push("{{ item.mem_percent }}") cpu.push("{{ item.cpu }}") @@ -109,7 +111,11 @@ max: Math.max.apply(this, max_nums) + 5 } }] - } + }, + zoom: { + enabled: true, + mode: 'y' + }, } }); From eec9f0fc95041fc5237b4d1352abec3cbd21a83c Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 21 Aug 2022 21:53:24 -0400 Subject: [PATCH 26/39] Remove zooming --- app/frontend/templates/panel/server_metrics.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 19ed4ba3..812a81de 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -111,11 +111,7 @@ max: Math.max.apply(this, max_nums) + 5 } }] - }, - zoom: { - enabled: true, - mode: 'y' - }, + } } }); From 6ad9863cbf36c68c603eaf10e3249c4d742a4ac2 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 22 Aug 2022 01:15:18 -0400 Subject: [PATCH 27/39] Still kinda broken, but this is cool --- app/frontend/templates/base.html | 11 +- .../templates/panel/server_metrics.html | 119 +++++++----------- 2 files changed, 52 insertions(+), 78 deletions(-) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 37e15a3e..543d3c71 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -40,8 +40,15 @@ - - + + + diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 812a81de..3b65e9d7 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -41,11 +41,7 @@ -
-   -   -   -
+ @@ -80,7 +76,8 @@ borderColor: [ 'rgba(136, 98, 224, .5)', ], - borderWidth: 2 + borderWidth: 2, + tension: 0 }, { label: "MEM", @@ -88,7 +85,8 @@ borderColor: [ 'rgba(33, 150, 243, .5)', ], - borderWidth: 2 + borderWidth: 2, + tension: 0 }, { label: "CPU", @@ -96,86 +94,55 @@ borderColor: [ 'rgba(255, 175, 0, .5)', ], - borderWidth: 2 + borderWidth: 2, + tension: 0 }, ] }, options: { + plugins: { + zoom: { + zoom: { + wheel: { + enabled: true, + }, + drag: { + enabled: true, + modifierKey: "shift" + }, + pinch: { + enabled: true + }, + mode: 'x', + modifierKey: 'shift', + }, + }, + pan: { + enabled: true, + mode: "xy", + threshhold: 1, + pan: { + enabled: true, + mode: "xy", + threshhold: 1, + } + + } + }, fill: false, lineTension: 5, responsive: true, scales: { - yAxes: [{ - ticks: { - min: 0, - max: Math.max.apply(this, max_nums) + 5 - } - }] + y: { + min: 0, + }, + x: { + position: 'right', + min: -500, + } } } }); - - var mem_hidden = false; - var cpu_hidden = false; - var players_hidden = false; - - function toggle_mem() { - if (!mem_hidden) { - hist_chart.data.datasets = hist_chart.data.datasets.filter(function (obj) { return obj.label != "MEM" }); - mem_hidden = true; - // Repaint - } else { - hist_chart.data.datasets.splice(1, 0, { - label: "MEM", - data: ram, - borderColor: [ - 'rgba(33, 150, 243, .5)', - ], - borderWidth: 2 - }); - mem_hidden = false; - } - hist_chart.update(); - - } - function toggle_cpu() { - if (!cpu_hidden) { - hist_chart.data.datasets = hist_chart.data.datasets.filter(function (obj) { return obj.label != "CPU" }); - cpu_hidden = true; - // Repaint - } else { - hist_chart.data.datasets.push({ - label: "CPU", - data: cpu, - borderColor: [ - 'rgba(255, 175, 0, .5)', - ], - borderWidth: 2 - }); - cpu_hidden = false; - } - hist_chart.update(); - - } - function toggle_players() { - if (!players_hidden) { - hist_chart.data.datasets = hist_chart.data.datasets.filter(function (obj) { return obj.label != "Players" }); - players_hidden = true; - // Repaint - } else { - hist_chart.data.datasets.splice(0, 0, { - label: "Players", - data: players, - borderColor: [ - 'rgba(136, 98, 224, .5)', - ], - borderWidth: 2 - }); - players_hidden = false; - } - hist_chart.update(); - - } {% end %} \ No newline at end of file From fc0d2891dc7cfbb361e854a293b5ca65394db3a7 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Mon, 22 Aug 2022 22:05:48 +0100 Subject: [PATCH 28/39] Add libcurl.so.4 This is a dependancy for bedrock Ref: #145 --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index c1bae3a6..4bd83bba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ RUN apt-get update \ && apt-get -y --no-install-recommends install \ sudo \ gcc \ + libcurl4 \ python3 \ python3-dev \ python3-pip \ From 037758accbae8253d3df7178b41f5107af9d1fb1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Aug 2022 20:04:40 -0400 Subject: [PATCH 29/39] Working live graph --- app/classes/shared/server.py | 1 + app/frontend/templates/base.html | 6 +- .../panel/parts/m_server_controls_list.html | 2 +- .../panel/parts/server_controls_list.html | 2 +- .../templates/panel/server_metrics.html | 104 +++++++++++++++--- app/translations/en_EN.json | 8 +- 6 files changed, 99 insertions(+), 24 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 499a0080..74e93ba7 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -1223,6 +1223,7 @@ class ServerInstance: "version": raw_ping_result.get("version"), "icon": raw_ping_result.get("icon"), "crashed": self.is_crashed, + "created": datetime.datetime.now().strftime("%Y/%m/%d, %H:%M:%S"), }, ) total_players += int(raw_ping_result.get("online")) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 543d3c71..1ab50da8 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -40,12 +40,12 @@ - + diff --git a/app/frontend/templates/panel/parts/m_server_controls_list.html b/app/frontend/templates/panel/parts/m_server_controls_list.html index dba3802a..52f3d04d 100644 --- a/app/frontend/templates/panel/parts/m_server_controls_list.html +++ b/app/frontend/templates/panel/parts/m_server_controls_list.html @@ -30,7 +30,7 @@ {% if data['permissions']['Players'] in data['user_permissions'] and data['server_data']['type'] != 'minecraft-bedrock' %} {{ translate('serverDetails', 'playerControls', data['lang']) }} {% end %} - Metrics + {{ translate('serverDetails', 'metrics', data['lang']) }} \ No newline at end of file diff --git a/app/frontend/templates/panel/parts/server_controls_list.html b/app/frontend/templates/panel/parts/server_controls_list.html index 17fa1b74..4212bde6 100644 --- a/app/frontend/templates/panel/parts/server_controls_list.html +++ b/app/frontend/templates/panel/parts/server_controls_list.html @@ -51,6 +51,6 @@ {% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 3b65e9d7..01456c61 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -38,8 +38,14 @@ {% include "parts/m_server_controls_list.html %} - - + + {% if data['user_data']['hints'] %} + + {% end %} @@ -50,7 +56,14 @@ + {% end %} \ No newline at end of file diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index f398ccf5..cd3620d0 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -351,7 +351,8 @@ "playerControls": "Player Management", "schedule": "Schedule", "serverDetails": "Server Details", - "terminal": "Terminal" + "terminal": "Terminal", + "metrics": "Metrics" }, "serverFiles": { "clickUpload": "Click here to select your files", @@ -472,6 +473,9 @@ "stopScroll": "Stop Auto Scrolling", "updating": "Updating..." }, + "serverMetrics": { + "resetZoom": "Reset Zoom" + }, "serverWizard": { "absoluteServerPath": "Absolute path to your server", "absoluteZipPath": "Absolute path to your server", @@ -558,4 +562,4 @@ "userSettings": "User Settings", "uses": "Number of uses allowed (-1==No Limit)" } -} +} \ No newline at end of file From 1e569300fce6d2f27aa695d0514832816f51fbb5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Aug 2022 20:23:09 -0400 Subject: [PATCH 30/39] Add translations --- app/frontend/templates/panel/server_metrics.html | 4 ++-- app/translations/en_EN.json | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 01456c61..61eaa659 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -42,8 +42,8 @@ id="reset-button"> {{ translate('serverMetrics', 'resetZoom', data['lang']) }} {% if data['user_data']['hints'] %} - {% end %} diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index cd3620d0..1c86e364 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -474,7 +474,9 @@ "updating": "Updating..." }, "serverMetrics": { - "resetZoom": "Reset Zoom" + "resetZoom": "Reset Zoom", + "zoomHint1": "To zoom on the graph hold your shift key then use your scroll wheel.", + "zoomHint2": "Alternatively hold the shift key then click and drag the area you'd like to zoom in on." }, "serverWizard": { "absoluteServerPath": "Absolute path to your server", From 9831705677466b3a0ccc30bbcc59b430ae3bf0b2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Aug 2022 22:39:39 -0400 Subject: [PATCH 31/39] Fix height on different viewports --- app/frontend/templates/panel/server_metrics.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 61eaa659..9bf46941 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -42,11 +42,13 @@ id="reset-button"> {{ translate('serverMetrics', 'resetZoom', data['lang']) }} {% if data['user_data']['hints'] %} - + {% end %} - +
+ +
@@ -57,6 +59,10 @@