diff --git a/.gitlab/docker-build.yml b/.gitlab/docker-build.yml index 268199fb..297c2b20 100644 --- a/.gitlab/docker-build.yml +++ b/.gitlab/docker-build.yml @@ -92,15 +92,15 @@ docker-build-prod: - docker context create tls-environment - docker buildx create --name zedBuilder --use tls-environment - docker buildx build - --cache-from type=registry,ref="$CI_REGISTRY_IMAGE" + --cache-from type=registry,ref="$CI_REGISTRY_IMAGE:latest" --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")" --build-arg "BUILD_REF=${CI_COMMIT_SHA}" --build-arg "CRAFTY_VER=${VERSION}" - --tag "$CI_REGISTRY_IMAGE" - --platform linux/arm64/v8,linux/amd64 . - - docker image tag "$CI_REGISTRY_IMAGE" "$CI_REGISTRY_IMAGE:${VERSION}" - - docker image push -a + --tag "$CI_REGISTRY_IMAGE:$VERSION" + --tag "$CI_REGISTRY_IMAGE:latest" + --platform linux/arm64/v8,linux/amd64 + --push . after_script: - | docker buildx rm zedBuilder && echo "Successfully Stopped builder instance" || echo "Failed to stop builder instance." diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3393d0..3f81a9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,32 @@ # Changelog -## --- [4.0.16] - 2022/10/09 +## --- [4.0.17] - 2022/TBD ### New features TBD ### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ +## --- [4.0.16] - 2022/10/23 +### New features +- Automatically set update url for (new) server creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/487)) +- Add filter to logs panel ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/484)) +### Bug fixes - Fix conditional issue with zip imports/uploads ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/476)) - Fix API Schedule updates ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/478)) - Add port constraint for all server creation & api ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/479)) - Clean up backup configs when deleting servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/480)) - Add timeout to socket for servers with incorrect port selection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/482)) +- Fix server_stats db file when deleting server ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/486)) +- Fix "cannot render after finish" from backup_now ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/489)) +- Fix Support Logs on windows by changing the way we declare projects working directory ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/491) | [Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/a6aa0f86797856a09c639317c5151c354f4024dc)) ### Tweaks - Fix sidebar to not move when scrolling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/481)) - Add the rest of CSS predefined colors to themes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/477)) -### Lang -TBD +- Only send realtime stats when clients connected ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/488))

## --- [4.0.15] - 2022/10/02 diff --git a/README.md b/README.md index babfc564..5ae83d5f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,5 @@ [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) - -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org) -[![Version](https://img.shields.io/badge/release-v4.0.16-blue)](https://gitlab.com/crafty-controller/crafty-4/-/releases) -[![Code Quality](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4) -[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) -[![Licence](https://img.shields.io/gitlab/license/20430749)](https://gitlab.com/crafty-controller/crafty-4/-/blob/master/LICENSE) -# Crafty Controller 4.0.16 +# Crafty Controller 4.0.17 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py index 29fdb856..ccb21879 100644 --- a/app/classes/models/server_stats.py +++ b/app/classes/models/server_stats.py @@ -120,6 +120,7 @@ class HelperServerStats: return None def get_all_servers_stats(self): + self.database.connect(reuse_if_open=True) servers = HelperServers.get_all_defined_servers() server_data = [] try: @@ -136,18 +137,23 @@ class HelperServerStats: logger.error( f"Stats collection failed with error: {ex}. Was a server just created?" ) + self.database.close() return server_data def get_history_stats(self, server_id, num_days): + self.database.connect(reuse_if_open=True) max_age = datetime.datetime.now() - timedelta(days=num_days) - return ( + server_stats = ( ServerStats.select() .where(ServerStats.created > max_age) .where(ServerStats.server_id == server_id) .execute(self.database) ) + self.database.connect(reuse_if_open=True) + return server_stats def insert_server_stats(self, server_stats): + self.database.connect(reuse_if_open=True) server_id = server_stats.get("id", 0) if server_id == 0: @@ -176,13 +182,18 @@ class HelperServerStats: } ).execute(self.database) + self.database.close() + def remove_old_stats(self, last_week): + self.database.connect(reuse_if_open=True) # self.select_database(self.server_id) ServerStats.delete().where(ServerStats.created < last_week).execute( self.database ) + self.database.close() def get_latest_server_stats(self): + self.database.connect(reuse_if_open=True) latest = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) @@ -190,12 +201,15 @@ class HelperServerStats: .limit(1) .get(self.database) ) + + self.database.close() try: return DatabaseShortcuts.get_data_obj(latest) except IndexError: return {} def get_server_stats(self): + self.database.connect(reuse_if_open=True) stats = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) @@ -203,63 +217,70 @@ class HelperServerStats: .limit(1) .first(self.database) ) + self.database.close() return DatabaseShortcuts.get_data_obj(stats) def server_id_exists(self): - # self.select_database(self.server_id) if not HelperServers.get_server_data_by_id(self.server_id): return False return True def sever_crashed(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) ServerStats.update(crashed=True).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def set_import(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) ServerStats.update(importing=True).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def finish_import(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) ServerStats.update(importing=False).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def get_import_status(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) import_status = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) + self.database.close() return import_status.importing def server_crash_reset(self): if self.server_id is None: return - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) ServerStats.update(crashed=False).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def is_crashed(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) svr: ServerStats = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) + self.database.close() return svr.crashed def set_update(self, value): if self.server_id is None: return - # self.select_database(self.server_id) + + self.database.connect(reuse_if_open=True) try: # Checks if server even exists ServerStats.select().where(ServerStats.server_id == self.server_id).execute( @@ -267,22 +288,26 @@ class HelperServerStats: ) except DoesNotExist as ex: logger.error(f"Database entry not found! {ex}") + self.database.close() return + ServerStats.update(updating=value).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def get_update_status(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) update_status = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) + self.database.close() return update_status.updating def set_first_run(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) # Sets first run to false try: # Checks if server even exists @@ -291,22 +316,25 @@ class HelperServerStats: ) except Exception as ex: logger.error(f"Database entry not found! {ex}") + self.database.close() return ServerStats.update(first_run=False).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def get_first_run(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) first_run = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) + self.database.close() return first_run.first_run def get_ttl_without_player(self): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) last_stat = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) @@ -320,6 +348,7 @@ class HelperServerStats: .order_by(ServerStats.created.desc()) .first(self.database) ) + self.database.close() return last_stat.created - last_stat_with_player.created def can_stop_no_players(self, time_limit): @@ -327,7 +356,7 @@ class HelperServerStats: return (time_limit == -1) or (ttl_no_players > time_limit) def set_waiting_start(self, value): - # self.select_database(self.server_id) + self.database.connect(reuse_if_open=True) try: # Checks if server even exists ServerStats.select().where(ServerStats.server_id == self.server_id).execute( @@ -335,15 +364,19 @@ class HelperServerStats: ) except DoesNotExist as ex: logger.error(f"Database entry not found! {ex}") + self.database.close() return ServerStats.update(waiting_start=value).where( ServerStats.server_id == self.server_id ).execute(self.database) + self.database.close() def get_waiting_start(self): + self.database.connect(reuse_if_open=True) waiting_start = ( ServerStats.select() .where(ServerStats.server_id == self.server_id) .get(self.database) ) + self.database.close() return waiting_start.waiting_start diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index de112f80..e09ff2ec 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -472,6 +472,15 @@ class Controller: data["create_type"] == "minecraft_java" and root_create_data["create_type"] == "download_jar" ): + # modded update urls from server jars will only update the installer + if create_data["category"] != "modded": + server_obj = self.servers.get_server_obj(new_server_id) + url = ( + f"https://serverjars.com/api/fetchJar/{create_data['category']}" + f"/{create_data['type']}/{create_data['version']}" + ) + server_obj.executable_update_url = url + self.servers.update_server(server_obj) self.server_jars.download_jar( create_data["category"], create_data["type"], @@ -555,7 +564,12 @@ class Controller: user_id, server_type="minecraft-java", ) - + # modded update urls from server jars will only update the installer + if jar != "modded": + server_obj = self.servers.get_server_obj(new_id) + url = f"https://serverjars.com/api/fetchJar/{jar}/{server}/{version}" + server_obj.executable_update_url = url + self.servers.update_server(server_obj) # download the jar self.server_jars.download_jar( jar, server, version, os.path.join(server_dir, server_file), new_id diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 9bc86017..257bf64f 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -1223,41 +1223,19 @@ class ServerInstance: # ********************************************************************************** def realtime_stats(self): - total_players = 0 - max_players = 0 - servers_ping = [] - raw_ping_result = [] - raw_ping_result = self.get_raw_server_stats(self.server_id) - - if f"{raw_ping_result.get('icon')}" == "b''": - raw_ping_result["icon"] = False - - servers_ping.append( - { - "id": raw_ping_result.get("id"), - "started": raw_ping_result.get("started"), - "running": raw_ping_result.get("running"), - "cpu": raw_ping_result.get("cpu"), - "mem": raw_ping_result.get("mem"), - "mem_percent": raw_ping_result.get("mem_percent"), - "world_name": raw_ping_result.get("world_name"), - "world_size": raw_ping_result.get("world_size"), - "server_port": raw_ping_result.get("server_port"), - "int_ping_results": raw_ping_result.get("int_ping_results"), - "online": raw_ping_result.get("online"), - "max": raw_ping_result.get("max"), - "players": raw_ping_result.get("players"), - "desc": raw_ping_result.get("desc"), - "version": raw_ping_result.get("version"), - "icon": raw_ping_result.get("icon"), - "crashed": self.is_crashed, - } - ) + # only get stats if clients are connected. + # no point in burning cpu if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_page_params( - "/panel/server_detail", - {"id": str(self.server_id)}, - "update_server_details", + total_players = 0 + max_players = 0 + servers_ping = [] + raw_ping_result = [] + raw_ping_result = self.get_raw_server_stats(self.server_id) + + if f"{raw_ping_result.get('icon')}" == "b''": + raw_ping_result["icon"] = False + + servers_ping.append( { "id": raw_ping_result.get("id"), "started": raw_ping_result.get("started"), @@ -1276,24 +1254,53 @@ 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")) - max_players += int(raw_ping_result.get("max")) - - self.record_server_stats() - - if (len(servers_ping) > 0) & (len(self.helper.websocket_helper.clients) > 0): - try: - self.helper.websocket_helper.broadcast_page( - "/panel/dashboard", "update_server_status", servers_ping + if len(self.helper.websocket_helper.clients) > 0: + self.helper.websocket_helper.broadcast_page_params( + "/panel/server_detail", + {"id": str(self.server_id)}, + "update_server_details", + { + "id": raw_ping_result.get("id"), + "started": raw_ping_result.get("started"), + "running": raw_ping_result.get("running"), + "cpu": raw_ping_result.get("cpu"), + "mem": raw_ping_result.get("mem"), + "mem_percent": raw_ping_result.get("mem_percent"), + "world_name": raw_ping_result.get("world_name"), + "world_size": raw_ping_result.get("world_size"), + "server_port": raw_ping_result.get("server_port"), + "int_ping_results": raw_ping_result.get("int_ping_results"), + "online": raw_ping_result.get("online"), + "max": raw_ping_result.get("max"), + "players": raw_ping_result.get("players"), + "desc": raw_ping_result.get("desc"), + "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" + ), + }, ) - self.helper.websocket_helper.broadcast_page( - "/status", "update_server_status", servers_ping - ) - except: - Console.critical("Can't broadcast server status to websocket") + total_players += int(raw_ping_result.get("online")) + max_players += int(raw_ping_result.get("max")) + + self.record_server_stats() + + if (len(servers_ping) > 0) & ( + len(self.helper.websocket_helper.clients) > 0 + ): + try: + self.helper.websocket_helper.broadcast_page( + "/panel/dashboard", "update_server_status", servers_ping + ) + self.helper.websocket_helper.broadcast_page( + "/status", "update_server_status", servers_ping + ) + except: + Console.critical("Can't broadcast server status to websocket") def get_servers_stats(self): diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index c0c49b9a..a90b4141 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -76,7 +76,7 @@ class AjaxHandler(BaseHandler): line = re.sub("(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)", "", line) line = re.sub("[A-z]{2}\b\b", "", line) line = self.helper.log_colors(html.escape(line)) - self.write(f"{line}
") + self.write(f"{line}
") # self.write(d.encode("utf-8")) except Exception as e: diff --git a/app/config/version.json b/app/config/version.json index 2dece081..f97d9f2b 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 0, - "sub": 16 + "sub": 17 } diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 72c37576..a50b55b5 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -294,9 +294,10 @@ headers: { 'X-XSRFToken': token }, url: '/ajax/backup_now?id=' + server_id, success: function (data) { - location.reload(); + return; }, }); + return; } function del_backup(filename, id) { diff --git a/app/frontend/templates/panel/server_logs.html b/app/frontend/templates/panel/server_logs.html index 8ab272b3..ccf40937 100644 --- a/app/frontend/templates/panel/server_logs.html +++ b/app/frontend/templates/panel/server_logs.html @@ -42,10 +42,17 @@
+ style="width: 100%; font-size: .8em; padding: 5px 10px; border: 1px solid var(--outline); background-color:var(--card-banner-bg);height:500px; overflow: scroll;">

+ + +
+
+

{{ translate('serverDetails', 'filterList', data['lang']) }}

+ +
@@ -55,15 +62,131 @@ - + + {% end %} {% block js %} - -{% end %} \ No newline at end of file +{% end %} diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 88a76115..4309b28a 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -360,7 +360,9 @@ "serverDetails": "Server Details", "terminal": "Terminal", "metrics": "Metrics", - "reset": "Reset Scroll" + "reset": "Reset Scroll", + "filter": "Filter Logs", + "filterList": "Filtered Words" }, "serverFiles": { "clickUpload": "Click here to select your files", diff --git a/main.py b/main.py index 60ad4409..344bf039 100644 --- a/main.py +++ b/main.py @@ -198,8 +198,22 @@ if __name__ == "__main__": if not controller.check_system_user(): controller.add_system_user() - project_root = os.path.dirname(__file__) - controller.set_project_root(project_root) + 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) + Console.debug(f"Execution Mode: {running_mode}") + Console.debug(f"Application path : '{application_path}'") + controller.clear_unexecuted_commands() controller.clear_support_status()