From 28b3a4f8f3dd434b43d0a67dafb2d60455a7a47f Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 3 Nov 2023 16:41:23 -0400 Subject: [PATCH 01/20] Add loading screen while servers init --- app/classes/controllers/servers_controller.py | 5 ++ app/classes/shared/helpers.py | 1 + app/classes/web/panel_handler.py | 3 +- app/frontend/templates/panel/loading.html | 64 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/frontend/templates/panel/loading.html diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index c0bae7b0..95c54bdb 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -22,6 +22,7 @@ from app.classes.models.server_permissions import ( PermissionsServers, EnumPermissionsServer, ) +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -36,6 +37,7 @@ class ServersController(metaclass=Singleton): self.management_helper = management_helper self.servers_list = [] self.stats = Stats(self.helper, self) + self.ws = WebSocketManager() # ********************************************************************************** # Generic Servers Methods @@ -171,6 +173,9 @@ class ServersController(metaclass=Singleton): self.failed_servers = [] for server in servers: + self.ws.broadcast( + "update", {"message": f"Initializing {server['server_name']}."} + ) server_id = server.get("server_id") # if we have already initialized this server, let's skip it. diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 62ce8819..7524cf90 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -80,6 +80,7 @@ class Helpers: self.translation = Translation(self) self.update_available = False self.ignored_names = ["crafty_managed.txt", "db_stats"] + self.crafty_starting = False @staticmethod def auto_installer_fix(ex): diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index e1d21f03..f12505eb 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1609,7 +1609,8 @@ class PanelHandler(BaseHandler): logs_thread.start() self.redirect("/panel/dashboard") return - + if self.helper.crafty_starting: + template = "panel/loading.html" self.render( template, data=page_data, diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html new file mode 100644 index 00000000..1caeca5f --- /dev/null +++ b/app/frontend/templates/panel/loading.html @@ -0,0 +1,64 @@ +{% extends ../base.html %} + +{% block meta %} +{% end %} + +{% block title %}Crafty Controller Starting{% end %} + +{% block content %} +
+
+
+ +
+
+
+
+

Crafty Is Starting

+
+ +
+
+ + +{% end %} \ No newline at end of file From 61c1621cbb3eb956db90b41ef695f112bc248db4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 3 Nov 2023 16:41:37 -0400 Subject: [PATCH 02/20] Add messages for loading screen --- main.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index 143dfb4f..35608ca0 100644 --- a/main.py +++ b/main.py @@ -109,11 +109,12 @@ if __name__ == "__main__": ) args = parser.parse_args() - helper.ensure_logging_setup() - + helper.crafty_starting = True + # Init WebSocket Manager Here + ws = WebSocketManager() + ws.broadcast("update", {"message": "Setting Up Logging"}) setup_logging(debug=args.verbose) - if args.verbose: Console.level = "debug" @@ -126,15 +127,18 @@ if __name__ == "__main__": # print our pretty start message do_intro() + ws.broadcast("update", {"message": "Securing Session For Process"}) # our session file, helps prevent multiple controller agents on the same machine. helper.create_session_file(ignore=args.ignore) + ws.broadcast("update", {"message": "Initializing Database"}) # start the database database = peewee.SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) database_proxy.initialize(database) + ws.broadcast("update", {"message": "Checking For Database Migrations"}) migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations @@ -165,8 +169,6 @@ if __name__ == "__main__": Console.info("No flag found. Secrets are staying") file_helper = FileHelpers(helper) import_helper = ImportHelpers(helper, file_helper) - # Init WebSocket Manager Here - WebSocketManager() # now the tables are created, we can load the tasks_manager and server controller controller = Controller(database, helper, file_helper, import_helper) Console.info("Checking for remote changes to config.json") @@ -174,6 +176,7 @@ if __name__ == "__main__": Console.info("Remote change complete.") import3 = Import3(helper, controller) + ws.broadcast("update", {"message": "Starting Task Scheduler"}) tasks_manager = TasksManager(helper, controller, file_helper) tasks_manager.start_webserver() @@ -225,6 +228,7 @@ if __name__ == "__main__": "the server may be limited." ) + ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") def controller_setup(): @@ -272,7 +276,7 @@ if __name__ == "__main__": time.sleep(0.01) # Wait for the daemon info message Console.info("Setting up Crafty's internal components...") - + ws.broadcast("update", {"message": "Final checks and launching"}) # Start the setup threads tasks_starter_thread.start() internet_check_thread.start() @@ -282,8 +286,9 @@ if __name__ == "__main__": tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() - + ws.broadcast("send_start_reload", "") Console.info("Crafty has fully started and is now ready for use!") + helper.crafty_starting = False # Check if new version available remote_ver = helper.check_remote_version() From 4152e2be695fedf494b05b03a0444afdb5fa7670 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 3 Nov 2023 16:55:41 -0400 Subject: [PATCH 03/20] Only show server inits to admins --- app/classes/controllers/servers_controller.py | 2 +- app/classes/shared/websocket_manager.py | 2 +- main.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 95c54bdb..92a68d81 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -173,7 +173,7 @@ class ServersController(metaclass=Singleton): self.failed_servers = [] for server in servers: - self.ws.broadcast( + self.ws.broadcast_to_admins( "update", {"message": f"Initializing {server['server_name']}."} ) server_id = server.get("server_id") diff --git a/app/classes/shared/websocket_manager.py b/app/classes/shared/websocket_manager.py index f48adef8..b840ba36 100644 --- a/app/classes/shared/websocket_manager.py +++ b/app/classes/shared/websocket_manager.py @@ -37,7 +37,7 @@ class WebSocketManager(metaclass=Singleton): def broadcast_to_admins(self, event_type: str, data): def filter_fn(client): - if client.get_user_id in HelperUsers.get_super_user_list(): + if str(client.get_user_id()) in str(HelperUsers.get_super_user_list()): return True return False diff --git a/main.py b/main.py index 35608ca0..452e89f0 100644 --- a/main.py +++ b/main.py @@ -195,6 +195,7 @@ if __name__ == "__main__": # init servers logger.info("Initializing all servers defined") Console.info("Initializing all servers defined") + ws.broadcast("update", {"message": "Initializing Servers"}) controller.servers.init_all_servers() def tasks_starter(): From 547872eda6f4a4c02b4ec2e2fcf792e6c5ca2b55 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 11:23:54 -0500 Subject: [PATCH 04/20] Set starting to false before sending refresh --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 452e89f0..c5ec1297 100644 --- a/main.py +++ b/main.py @@ -287,9 +287,9 @@ if __name__ == "__main__": tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() + helper.crafty_starting = False ws.broadcast("send_start_reload", "") Console.info("Crafty has fully started and is now ready for use!") - helper.crafty_starting = False # Check if new version available remote_ver = helper.check_remote_version() From 16f8df20ac3653824e5620e10ed38e5d8e38046c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 21:20:39 +0000 Subject: [PATCH 05/20] Update lang pl_PL with minor fixes Thanks Terrariadlc --- app/translations/pl_PL.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 828ec0b1..2c60c6df 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -326,8 +326,8 @@ "bePatientDeleteFiles": "Poczekaj, aż usuniemy twój serwer i jego pliki. Strona za chwilę się zamknie.", "bePatientUpdate": "Poczekaj kiedy my aktualizujemy twój serwer. Pobieranie zależy od prędkości twojego internetu.
Strona się odświeży za chwile.", "cancel": "Anuluj", - "crashTime": "Crash wyszedł poza limit czasu", - "crashTimeDesc": "How long should we wait before we consider your server as crashed?", + "crashTime": "Crash serwera wyszedł poza limit czasu", + "crashTimeDesc": "Jak długo powinniśmy poczekać zanim uznać serwer za zcrashowany?", "deleteFilesQuestion": "Usuń pliki serwera z maszyny?", "deleteFilesQuestionMessage": "Czy chcesz aby Crafty usunął wszystkie pliki tego serwera?

To zawiera backupy.", "deleteServer": "Usuń serwer", @@ -403,7 +403,7 @@ "filterList": "Filtrowane słowa", "logs": "Logi", "metrics": "Statystyki", - "playerControls": "Player Management", + "playerControls": "Zarządzanie użytkownikami", "reset": "Resetuj Scrolla", "schedule": "Harmonogram", "serverDetails": "Detale serwera", @@ -421,7 +421,7 @@ "deleteItemQuestion": "Czy jesteś pewien że chcesz usunąć \" + name + \"?", "deleteItemQuestionMessage": "Usuwasz \\\"\" + path + \"\\\"!

Ta akcja jest nieodwracalna i zostanie usunięta na zawsze!", "download": "Pobierz", - "editingFile": "Edytuję plik", + "editingFile": "Edytuj plik", "error": "Error while getting files", "fileReadError": "Error odczytu pliku", "files": "Pliki", @@ -432,7 +432,7 @@ "rename": "Zmień nazwę", "renameItemQuestion": "Jaka ma być nowa nazwa?", "save": "Zapisz", - "size": "Włącz zmienianie rozmiaru edytora", + "size": "Włącz rozszerzanie i zmniejszanie edytora", "stayHere": "NIE WYCHODŹ Z TEJ STRONY!", "unsupportedLanguage": "Uwaga: To nie jest wspierany typ pliku", "unzip": "Rozpakuj", @@ -545,7 +545,7 @@ "buildServer": "Zbuduj serwer!", "clickRoot": "Kilknij tutaj aby zaznaczyć główną ścieżkę", "close": "Zamknij", - "defaultPort": "25565 podstawowy", + "defaultPort": "Domyślnie 25565", "downloading": "Pobieranie serwera...", "explainRoot": "Proszę, kliknij przycisk poniżej aby zaznaczyć główną ścieżkę w tym archiwum", "importServer": "Importuj egzystujący serwer", @@ -640,12 +640,12 @@ "edit": "Edytuj", "enabled": "Włączony", "jar_update": "Plik startowy zaktualizowany", - "kill": "Serwer zatrzymany", + "kill": "Serwer został zabity", "name": "Nazwa", "new": "Nowy Webhook", "newWebhook": "Nowy Webhook", "no-webhook": "Nie posiadasz aktualnie żadnych Webhooków dla tego serwera. Aby dodać webhook kliknij na", - "run": "Włącz Webhook", + "run": "Przetestuj Webhook", "send_command": "Komenda serwera otrzymana!", "start_server": "Serwer włączony", "stop_server": "Serwer wyłączony", From 7db259eb982acb9e53a77de2e1671ac3affd64c8 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 21:26:34 +0000 Subject: [PATCH 06/20] Update changelog !675 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd883a7..4784608b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ TBD - Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) - Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) ### Lang -TBD +- pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

## --- [4.2.1] - 2023/11/01 From 151f77589baff531dec5619855148c97c48875e5 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 18:15:11 -0500 Subject: [PATCH 07/20] Restructure main.py to be more comprehensive --- main.py | 318 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 195 insertions(+), 123 deletions(-) diff --git a/main.py b/main.py index c5ec1297..151c6364 100644 --- a/main.py +++ b/main.py @@ -51,7 +51,179 @@ except ModuleNotFoundError as err: helper.auto_installer_fix(err) +def internet_check(): + """ + This checks to see if the Crafty host is connected to the + internet. This will show a warning in the console if no interwebs. + """ + print() + logger.info("Checking Internet. This may take a minute.") + Console.info("Checking Internet. This may take a minute.") + + if not helper.check_internet(): + logger.warning( + "We have detected the machine running Crafty has no " + "connection to the internet. Client connections to " + "the server may be limited." + ) + Console.warning( + "We have detected the machine running Crafty has no " + "connection to the internet. Client connections to " + "the server may be limited." + ) + + +def controller_setup(): + """ + Method sets up the software controllers. + This also sets the application path as well as the + master server dir (if not set). + + This also clears the support logs status. + """ + if not controller.check_system_user(): + controller.add_system_user() + + if getattr(sys, "frozen", False): + application_path = os.path.dirname(sys.executable) + running_mode = "Frozen/executable" + else: + try: + app_full_path = os.path.realpath(__file__) + application_path = os.path.dirname(app_full_path) + running_mode = "Non-interactive (e.g. 'python main.py')" + except NameError: + application_path = os.getcwd() + running_mode = "Interactive" + + controller.set_project_root(application_path) + master_server_dir = controller.management.get_master_server_dir() + if master_server_dir == "": + logger.debug("Could not find master server path. Setting default") + controller.set_master_server_dir( + os.path.join(controller.project_root, "servers") + ) + else: + helper.servers_dir = master_server_dir + + Console.debug(f"Execution Mode: {running_mode}") + Console.debug(f"Application path : '{application_path}'") + + controller.clear_support_status() + + +def tasks_starter(): + """ + Method starts stats recording, app scheduler, and + serverjars/steamCMD cache refreshers + """ + # start stats logging + tasks_manager.start_stats_recording() + + # once the controller is up and stats are logging, we can kick off + # the scheduler officially + tasks_manager.start_scheduler() + + # refresh our cache and schedule for every 12 hoursour cache refresh + # for serverjars.com + tasks_manager.serverjar_cache_refresher() + + +def signal_handler(signum, _frame): + """ + Method handles sigterm and shuts the app down. + """ + if not args.daemon: + print() # for newline after prompt + signame = signal.Signals(signum).name + logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") + Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") + tasks_manager._main_graceful_exit() + crafty_prompt.universal_exit() + + +def do_cleanup(): + """ + Checks Crafty's temporary directory and clears it out on boot. + """ + try: + logger.info("Removing old temp dirs") + FileHelpers.del_dirs(os.path.join(controller.project_root, "temp")) + except: + logger.info("Did not find old temp dir.") + os.mkdir(os.path.join(controller.project_root, "temp")) + + +def do_version_check(): + """ + Checks for remote version differences. + + Prints in terminal with differences if true. + + Also sets helper variable to update available when pages + are served. + """ + + # Check if new version available + remote_ver = helper.check_remote_version() + if remote_ver: + notice = f""" + A new version of Crafty is available! + {'/' * 37} + New version available: {remote_ver} + Current version: {pkg_version.parse(helper.get_version_string())} + {'/' * 37} + """ + Console.yellow(notice) + + crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > " + + +def setup_starter(): + """ + This method starts our setup threads. + (tasks scheduler, internet checks, controller setups) + + Once our threads complete we will set our startup + variable to false and send a reload to any clients waiting. + + + """ + if not args.daemon: + time.sleep(0.01) # Wait for the prompt to start + print() # Make a newline after the prompt so logs are on an empty line + else: + time.sleep(0.01) # Wait for the daemon info message + + Console.info("Setting up Crafty's internal components...") + ws.broadcast("update", {"message": "Final checks and launching"}) + # Start the setup threads + tasks_starter_thread.start() + internet_check_thread.start() + controller_setup_thread.start() + + # Wait for the setup threads to finish + tasks_starter_thread.join() + internet_check_thread.join() + controller_setup_thread.join() + helper.crafty_starting = False + ws.broadcast("send_start_reload", "") + do_version_check() + Console.info("Crafty has fully started and is now ready for use!") + + do_cleanup() + + if not args.daemon: + # Put the prompt under the cursor + crafty_prompt.print_prompt() + + def do_intro(): + """ + Runs the Crafty Controller Terminal Intro with information about the software + This method checks for a "settings file" or config.json. If it does not find + one it will create one. + """ logger.info("***** Crafty Controller Started *****") version = helper.get_version_string() @@ -72,6 +244,11 @@ def do_intro(): def setup_logging(debug=True): + """ + This method sets up our logging for Crafty. It takes + one optional (defaulted to True) parameter which + determines whether or not the logging level is "debug" or verbose. + """ logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") if os.path.exists(logging_config_file): @@ -142,10 +319,16 @@ if __name__ == "__main__": migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations - # do our installer stuff + # init classes + # now the tables are created, we can load the tasks_manager and server controller user_helper = HelperUsers(database, helper) management_helper = HelpersManagement(database, helper) installer = DatabaseBuilder(database, helper, user_helper, management_helper) + file_helper = FileHelpers(helper) + import_helper = ImportHelpers(helper, file_helper) + controller = Controller(database, helper, file_helper, import_helper) + tasks_manager = TasksManager(helper, controller, file_helper) + import3 = Import3(helper, controller) FRESH_INSTALL = installer.is_fresh_install() if FRESH_INSTALL: @@ -167,28 +350,17 @@ if __name__ == "__main__": helper.set_setting("reset_secrets_on_next_boot", False) else: Console.info("No flag found. Secrets are staying") - file_helper = FileHelpers(helper) - import_helper = ImportHelpers(helper, file_helper) - # now the tables are created, we can load the tasks_manager and server controller - controller = Controller(database, helper, file_helper, import_helper) + + # Check to see if client config.json version is different than the + # Master config.json in helpers.py Console.info("Checking for remote changes to config.json") controller.get_config_diff() Console.info("Remote change complete.") - import3 = Import3(helper, controller) - ws.broadcast("update", {"message": "Starting Task Scheduler"}) - tasks_manager = TasksManager(helper, controller, file_helper) + # startup the web server + ws.broadcast("update", {"message": "Starting Tornado Webserver"}) tasks_manager.start_webserver() - def signal_handler(signum, _frame): - if not args.daemon: - print() # for newline after prompt - signame = signal.Signals(signum).name - logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") - Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") - tasks_manager._main_graceful_exit() - crafty_prompt.universal_exit() - signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGINT, signal_handler) @@ -198,124 +370,24 @@ if __name__ == "__main__": ws.broadcast("update", {"message": "Initializing Servers"}) controller.servers.init_all_servers() - def tasks_starter(): - # start stats logging - tasks_manager.start_stats_recording() - - # once the controller is up and stats are logging, we can kick off - # the scheduler officially - tasks_manager.start_scheduler() - - # refresh our cache and schedule for every 12 hoursour cache refresh - # for serverjars.com - tasks_manager.serverjar_cache_refresher() - + # start up our tasks handler in tasks.py tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter") - def internet_check(): - print() - logger.info("Checking Internet. This may take a minute.") - Console.info("Checking Internet. This may take a minute.") - - if not helper.check_internet(): - logger.warning( - "We have detected the machine running Crafty has no " - "connection to the internet. Client connections to " - "the server may be limited." - ) - Console.warning( - "We have detected the machine running Crafty has no " - "connection to the internet. Client connections to " - "the server may be limited." - ) - + # check to see if instance has internet ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") - def controller_setup(): - if not controller.check_system_user(): - controller.add_system_user() - - if getattr(sys, "frozen", False): - application_path = os.path.dirname(sys.executable) - running_mode = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - application_path = os.path.dirname(app_full_path) - running_mode = "Non-interactive (e.g. 'python main.py')" - except NameError: - application_path = os.getcwd() - running_mode = "Interactive" - - controller.set_project_root(application_path) - master_server_dir = controller.management.get_master_server_dir() - if master_server_dir == "": - logger.debug("Could not find master server path. Setting default") - controller.set_master_server_dir( - os.path.join(controller.project_root, "servers") - ) - else: - helper.servers_dir = master_server_dir - - Console.debug(f"Execution Mode: {running_mode}") - Console.debug(f"Application path : '{application_path}'") - - controller.clear_support_status() - + # start the Crafty console. crafty_prompt = MainPrompt( helper, tasks_manager, migration_manager, controller, import3 ) + # set up all controllers controller_setup_thread = Thread(target=controller_setup, name="controller_setup") - def setup_starter(): - if not args.daemon: - time.sleep(0.01) # Wait for the prompt to start - print() # Make a newline after the prompt so logs are on an empty line - else: - time.sleep(0.01) # Wait for the daemon info message + setup_starter_thread = Thread(target=setup_starter, name="setup_starter") - Console.info("Setting up Crafty's internal components...") - ws.broadcast("update", {"message": "Final checks and launching"}) - # Start the setup threads - tasks_starter_thread.start() - internet_check_thread.start() - controller_setup_thread.start() - - # Wait for the setup threads to finish - tasks_starter_thread.join() - internet_check_thread.join() - controller_setup_thread.join() - helper.crafty_starting = False - ws.broadcast("send_start_reload", "") - Console.info("Crafty has fully started and is now ready for use!") - - # Check if new version available - remote_ver = helper.check_remote_version() - if remote_ver: - notice = f""" - A new version of Crafty is available! - {'/' * 37} - New version available: {remote_ver} - Current version: {pkg_version.parse(helper.get_version_string())} - {'/' * 37} - """ - Console.yellow(notice) - - crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > " - try: - logger.info("Removing old temp dirs") - FileHelpers.del_dirs(os.path.join(controller.project_root, "temp")) - except: - logger.info("Did not find old temp dir.") - os.mkdir(os.path.join(controller.project_root, "temp")) - - if not args.daemon: - # Put the prompt under the cursor - crafty_prompt.print_prompt() - - Thread(target=setup_starter, name="setup_starter").start() + setup_starter_thread.start() if not args.daemon: # Start the Crafty prompt From c13ebeaf51f306b08d8788c38f4b6dfdd655f801 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 21:04:02 -0500 Subject: [PATCH 08/20] Use constant variables for run mode and app path --- main.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index 151c6364..a1dee302 100644 --- a/main.py +++ b/main.py @@ -20,6 +20,18 @@ from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() +# Get the path our application is running on. +if getattr(sys, "frozen", False): + APPLICATION_PATH = os.path.dirname(sys.executable) + RUNNING_MODE = "Frozen/executable" +else: + try: + app_full_path = os.path.realpath(__file__) + APPLICATION_PATH = os.path.dirname(app_full_path) + RUNNING_MODE = "Non-interactive (e.g. 'python main.py')" + except NameError: + APPLICATION_PATH = os.getcwd() + RUNNING_MODE = "Interactive" if helper.check_root(): Console.critical( "Root detected. Root/Admin access denied. " @@ -84,19 +96,7 @@ def controller_setup(): if not controller.check_system_user(): controller.add_system_user() - if getattr(sys, "frozen", False): - application_path = os.path.dirname(sys.executable) - running_mode = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - application_path = os.path.dirname(app_full_path) - running_mode = "Non-interactive (e.g. 'python main.py')" - except NameError: - application_path = os.getcwd() - running_mode = "Interactive" - - controller.set_project_root(application_path) + controller.set_project_root(APPLICATION_PATH) master_server_dir = controller.management.get_master_server_dir() if master_server_dir == "": logger.debug("Could not find master server path. Setting default") @@ -106,8 +106,8 @@ def controller_setup(): else: helper.servers_dir = master_server_dir - Console.debug(f"Execution Mode: {running_mode}") - Console.debug(f"Application path : '{application_path}'") + Console.debug(f"Execution Mode: {RUNNING_MODE}") + Console.debug(f"Application path : '{APPLICATION_PATH}'") controller.clear_support_status() From aa0eee68e3117abebce46166e1ecb030f2b85a1a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 21:21:28 -0500 Subject: [PATCH 09/20] Remove border line on loadup --- app/frontend/templates/panel/loading.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html index 1caeca5f..7445f254 100644 --- a/app/frontend/templates/panel/loading.html +++ b/app/frontend/templates/panel/loading.html @@ -7,7 +7,7 @@ {% block content %}
-
+
From 999460f95e36c317b142824b846ed92829b2fd5b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 00:55:19 -0500 Subject: [PATCH 10/20] Del redundant statements (webserver not started) appease the linter --- main.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/main.py b/main.py index b997bba8..8851c75f 100644 --- a/main.py +++ b/main.py @@ -16,10 +16,13 @@ from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers from app.classes.models.management import HelpersManagement from app.classes.shared.import_helper import ImportHelpers +from app.classes.shared.translation import Translation from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() +translation = Translation(helper) +HOST_LANG = helper.get_setting("language") # Get the path our application is running on. if getattr(sys, "frozen", False): APPLICATION_PATH = os.path.dirname(sys.executable) @@ -96,7 +99,6 @@ def controller_setup(): if not controller.check_system_user(): controller.add_system_user() - controller.set_project_root(APPLICATION_PATH) master_server_dir = controller.management.get_master_server_dir() if master_server_dir == "": logger.debug("Could not find master server path. Setting default") @@ -196,18 +198,34 @@ def setup_starter(): time.sleep(0.01) # Wait for the daemon info message Console.info("Setting up Crafty's internal components...") - ws.broadcast("update", {"message": "Final checks and launching"}) # Start the setup threads + web_sock.broadcast( + "update", {"message": translation.translate("startup", "tasks", HOST_LANG)} + ) + time.sleep(2) tasks_starter_thread.start() + web_sock.broadcast( + "update", {"message": translation.translate("startup", "internet", HOST_LANG)} + ) + time.sleep(2) internet_check_thread.start() + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "internals", HOST_LANG)}, + ) + time.sleep(2) controller_setup_thread.start() # Wait for the setup threads to finish + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "almost", HOST_LANG)}, + ) tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() helper.crafty_starting = False - ws.broadcast("send_start_reload", "") + web_sock.broadcast("send_start_reload", "") do_version_check() Console.info("Crafty has fully started and is now ready for use!") @@ -297,8 +315,7 @@ if __name__ == "__main__": helper.ensure_logging_setup() helper.crafty_starting = True # Init WebSocket Manager Here - ws = WebSocketManager() - ws.broadcast("update", {"message": "Setting Up Logging"}) + web_sock = WebSocketManager() setup_logging(debug=args.verbose) if args.verbose: Console.level = "debug" @@ -311,19 +328,13 @@ if __name__ == "__main__": # print our pretty start message do_intro() - - ws.broadcast("update", {"message": "Securing Session For Process"}) # our session file, helps prevent multiple controller agents on the same machine. helper.create_session_file(ignore=args.ignore) - - ws.broadcast("update", {"message": "Initializing Database"}) # start the database database = peewee.SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) database_proxy.initialize(database) - - ws.broadcast("update", {"message": "Checking For Database Migrations"}) migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations @@ -335,6 +346,7 @@ if __name__ == "__main__": file_helper = FileHelpers(helper) import_helper = ImportHelpers(helper, file_helper) controller = Controller(database, helper, file_helper, import_helper) + controller.set_project_root(APPLICATION_PATH) tasks_manager = TasksManager(helper, controller, file_helper) import3 = Import3(helper, controller) FRESH_INSTALL = installer.is_fresh_install() @@ -366,7 +378,6 @@ if __name__ == "__main__": Console.info("Remote change complete.") # startup the web server - ws.broadcast("update", {"message": "Starting Tornado Webserver"}) tasks_manager.start_webserver() signal.signal(signal.SIGTERM, signal_handler) @@ -375,14 +386,16 @@ if __name__ == "__main__": # init servers logger.info("Initializing all servers defined") Console.info("Initializing all servers defined") - ws.broadcast("update", {"message": "Initializing Servers"}) + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "serverInit", HOST_LANG)}, + ) controller.servers.init_all_servers() # start up our tasks handler in tasks.py tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter") # check to see if instance has internet - ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") # start the Crafty console. From 90ed32738f77a28b5b7adf071c62d87c093b5735 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 00:55:26 -0500 Subject: [PATCH 11/20] Add translations --- app/classes/controllers/servers_controller.py | 9 +++++++-- app/translations/en_EN.json | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 5c4df789..57b3348d 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -13,6 +13,7 @@ from app.classes.shared.server import ServerInstance from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.main_models import DatabaseShortcuts +from app.classes.shared.translation import Translation from app.classes.minecraft.stats import Stats @@ -39,6 +40,7 @@ class ServersController(metaclass=Singleton): self.stats = Stats(self.helper, self) self.ws = WebSocketManager() self.server_subpage = {} + self.translation = Translation(self.helper) # ********************************************************************************** # Generic Servers Methods @@ -172,10 +174,13 @@ class ServersController(metaclass=Singleton): def init_all_servers(self): servers = self.get_all_defined_servers() self.failed_servers = [] - + init_trans = self.translation.translate( + "startup", "server", self.helper.get_setting("language") + ) for server in servers: self.ws.broadcast_to_admins( - "update", {"message": f"Initializing {server['server_name']}."} + "update", + {"message": f"{init_trans}{server['server_name']}."}, ) server_id = server.get("server_id") diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 6db96dfc..c1168973 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -590,6 +590,14 @@ "newServer": "Create New Server", "servers": "Servers" }, + "startup": { + "serverInit": "Initializing Servers", + "server": "Initializing ", + "internet": "Checking for internet connection", + "tasks": "Starting Tasks Scheduler", + "internals": "Configuring and starting Crafty's internal componenets", + "almost": "Finishing up. Hang on tight..." + }, "userConfig": { "apiKey": "API Keys", "auth": "Authorized? ", @@ -655,4 +663,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From fb41bc244388ba75cefa2d81d356e5ee25b1b45a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 01:05:00 -0500 Subject: [PATCH 12/20] Prevent stack for general user on boot --- app/classes/web/panel_handler.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index e285d4e5..8ac827c3 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -210,6 +210,8 @@ class PanelHandler(BaseHandler): error = self.get_argument("error", "WTF Error!") template = "panel/denied.html" + if self.helper.crafty_starting: + page = "loading" now = time.time() formatted_time = str( @@ -243,9 +245,13 @@ class PanelHandler(BaseHandler): for r in exec_user["roles"]: role = self.controller.roles.get_role(r) exec_user_role.add(role["role_name"]) - defined_servers = self.controller.servers.get_authorized_servers( - exec_user["user_id"] - ) + # get_auth_servers will throw an exception if run while Crafty is starting + if not self.helper.crafty_starting: + defined_servers = self.controller.servers.get_authorized_servers( + exec_user["user_id"] + ) + else: + defined_servers = [] user_order = self.controller.users.get_user_by_id(exec_user["user_id"]) user_order = user_order["server_order"].split(",") From b60e6933dd69cd2cb19fe3c2f9a6f06f58c55c3e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:12:16 -0500 Subject: [PATCH 13/20] Add broadcast to non-admin function --- app/classes/shared/websocket_manager.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/classes/shared/websocket_manager.py b/app/classes/shared/websocket_manager.py index b840ba36..7cda296d 100644 --- a/app/classes/shared/websocket_manager.py +++ b/app/classes/shared/websocket_manager.py @@ -43,6 +43,14 @@ class WebSocketManager(metaclass=Singleton): self.broadcast_with_fn(filter_fn, event_type, data) + def broadcast_to_non_admins(self, event_type: str, data): + def filter_fn(client): + if str(client.get_user_id()) not in str(HelperUsers.get_super_user_list()): + return True + return False + + self.broadcast_with_fn(filter_fn, event_type, data) + def broadcast_page(self, page: str, event_type: str, data): def filter_fn(client): return client.page == page From ca02c9d57590a44f3e460baf9af1080e39546a13 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:14:09 -0500 Subject: [PATCH 14/20] Add user-based translation to startup --- app/classes/controllers/servers_controller.py | 11 +++++------ app/frontend/templates/panel/loading.html | 15 ++++++++++++--- app/translations/en_EN.json | 1 + main.py | 17 +++++------------ 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 57b3348d..10ac0568 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -13,7 +13,6 @@ from app.classes.shared.server import ServerInstance from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.main_models import DatabaseShortcuts -from app.classes.shared.translation import Translation from app.classes.minecraft.stats import Stats @@ -40,7 +39,6 @@ class ServersController(metaclass=Singleton): self.stats = Stats(self.helper, self) self.ws = WebSocketManager() self.server_subpage = {} - self.translation = Translation(self.helper) # ********************************************************************************** # Generic Servers Methods @@ -174,13 +172,14 @@ class ServersController(metaclass=Singleton): def init_all_servers(self): servers = self.get_all_defined_servers() self.failed_servers = [] - init_trans = self.translation.translate( - "startup", "server", self.helper.get_setting("language") - ) for server in servers: self.ws.broadcast_to_admins( "update", - {"message": f"{init_trans}{server['server_name']}."}, + {"section": "server", "server": server["server_name"]}, + ) + self.ws.broadcast_to_non_admins( + "update", + {"section": "init"}, ) server_id = server.get("server_id") diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html index 7445f254..b4848beb 100644 --- a/app/frontend/templates/panel/loading.html +++ b/app/frontend/templates/panel/loading.html @@ -15,7 +15,13 @@

-

Crafty Is Starting

+

+ {{ translate('startup', 'starting', data['lang']) }}

@@ -48,8 +54,11 @@ }, 2000); if (webSocket) { webSocket.on('update', function (data) { - console.log("got status") - $("#status").html(data.message); + if ("server" in data) { + $("#status").html(`${$("#status").data(data.section)} ${data.server}...`); + } else { + $("#status").html($("#status").data(data.section)); + } }); webSocket.on('send_start_reload', function () { setTimeout(function () { diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index c1168973..ab4c54ce 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -591,6 +591,7 @@ "servers": "Servers" }, "startup": { + "starting": "Crafty Is Starting...", "serverInit": "Initializing Servers", "server": "Initializing ", "internet": "Checking for internet connection", diff --git a/main.py b/main.py index 8851c75f..cac130c1 100644 --- a/main.py +++ b/main.py @@ -16,13 +16,10 @@ from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers from app.classes.models.management import HelpersManagement from app.classes.shared.import_helper import ImportHelpers -from app.classes.shared.translation import Translation from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() -translation = Translation(helper) -HOST_LANG = helper.get_setting("language") # Get the path our application is running on. if getattr(sys, "frozen", False): APPLICATION_PATH = os.path.dirname(sys.executable) @@ -199,19 +196,15 @@ def setup_starter(): Console.info("Setting up Crafty's internal components...") # Start the setup threads - web_sock.broadcast( - "update", {"message": translation.translate("startup", "tasks", HOST_LANG)} - ) + web_sock.broadcast("update", {"section": "tasks"}) time.sleep(2) tasks_starter_thread.start() - web_sock.broadcast( - "update", {"message": translation.translate("startup", "internet", HOST_LANG)} - ) + web_sock.broadcast("update", {"section": "internet"}) time.sleep(2) internet_check_thread.start() web_sock.broadcast( "update", - {"message": translation.translate("startup", "internals", HOST_LANG)}, + {"section": "internals"}, ) time.sleep(2) controller_setup_thread.start() @@ -219,7 +212,7 @@ def setup_starter(): # Wait for the setup threads to finish web_sock.broadcast( "update", - {"message": translation.translate("startup", "almost", HOST_LANG)}, + {"section": "almost"}, ) tasks_starter_thread.join() internet_check_thread.join() @@ -388,7 +381,7 @@ if __name__ == "__main__": Console.info("Initializing all servers defined") web_sock.broadcast( "update", - {"message": translation.translate("startup", "serverInit", HOST_LANG)}, + {"section": "serverInit"}, ) controller.servers.init_all_servers() From 09241bdc54460d3238df4664d4789933e54702a6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:22:40 -0500 Subject: [PATCH 15/20] Appease the linter --- app/classes/controllers/servers_controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 10ac0568..88923194 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -37,7 +37,7 @@ class ServersController(metaclass=Singleton): self.management_helper = management_helper self.servers_list = [] self.stats = Stats(self.helper, self) - self.ws = WebSocketManager() + self.web_sock = WebSocketManager() self.server_subpage = {} # ********************************************************************************** @@ -173,11 +173,11 @@ class ServersController(metaclass=Singleton): servers = self.get_all_defined_servers() self.failed_servers = [] for server in servers: - self.ws.broadcast_to_admins( + self.web_sock.broadcast_to_admins( "update", {"section": "server", "server": server["server_name"]}, ) - self.ws.broadcast_to_non_admins( + self.web_sock.broadcast_to_non_admins( "update", {"section": "init"}, ) From a91c3257f76968ffb625fd89383e3d27138e99c0 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 13:04:18 -0500 Subject: [PATCH 16/20] Bump cryptography for CVE-2023-49083 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1e9feb0f..e64e270f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ argon2-cffi==23.1.0 cached_property==1.5.2 colorama==0.4.6 croniter==1.4.1 -cryptography==41.0.4 +cryptography==41.0.7 libgravatar==1.0.4 nh3==0.2.14 packaging==23.2 From 0d6cd5c42e0010de669e276396b65ce91df5f052 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 13:11:37 -0500 Subject: [PATCH 17/20] Bump openSSL --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e64e270f..7b1adcfc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ nh3==0.2.14 packaging==23.2 peewee==3.13 psutil==5.9.5 -pyOpenSSL==23.2.0 +pyOpenSSL==23.3.0 pyjwt==2.8.0 PyYAML==6.0.1 requests==2.31.0 From 9e54eafd508ed92e16a7896fc06c3f434185c35d Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 11:16:50 +0000 Subject: [PATCH 18/20] Update changelog !668 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd883a7..1c8189a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog ## --- [4.2.2] - 2023/TBD ### New features -TBD +- Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Refactor - Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670)) +- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) ### Tweaks From b2035eef60c43246e4e7b45e47feec62823d246e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 11:29:18 +0000 Subject: [PATCH 19/20] Fix sonar findings --- app/classes/shared/main_controller.py | 2 +- app/frontend/templates/panel/loading.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 27104b62..c8ac3b3c 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -85,7 +85,7 @@ class Controller: encoding="utf-8", ) as f: self.auth_tracker = json.load(f) - except: + except (FileNotFoundError, json.JSONDecodeError): self.auth_tracker = {} def log_attempt(self, remote_ip, username): diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html index b4848beb..a56a75ee 100644 --- a/app/frontend/templates/panel/loading.html +++ b/app/frontend/templates/panel/loading.html @@ -10,7 +10,7 @@
+ alt="Crafty Logo, Crafty is loading" width="20%" style="clear: both;">


@@ -70,4 +70,4 @@ }); -{% end %} \ No newline at end of file +{% end %} From e37bd47806d6841d8cf6f715274677d7c0b9945e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 11:57:56 +0000 Subject: [PATCH 20/20] Update changelog !680 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c8189a5..927b03ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) +- Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667))