diff --git a/CHANGELOG.md b/CHANGELOG.md index e2697d8b..031e2b9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,12 @@ - Bump tornado to resolve #269 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/623)) - Bump crypto to resolve #267 & #268 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/622)) - Fix select installs failing to start, returning missing python package `packaging` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/629)) +- Fix public status page not updating #255 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/615)) ### Refactor - Consolidate remaining frontend functions into API V2, and remove ajax internal API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/585)) - Replace bleach with nh3 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/628)) +- Add API route for historical server stats ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/615)) +- Add API route for host stats ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/615)) ### Tweaks - Polish/Enhance display for InApp Documentation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/613)) - Add get_users command to Crafty's console ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/620)) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index ca6c8d22..c0bae7b0 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -105,9 +105,9 @@ class ServersController(metaclass=Singleton): return ret - def get_history_stats(self, server_id, days): + def get_history_stats(self, server_id, hours): srv = ServersController().get_server_instance_by_id(server_id) - return srv.stats_helper.get_history_stats(server_id, days) + return srv.stats_helper.get_history_stats(server_id, hours) @staticmethod def update_unloaded_server(server_obj): diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index faa12a7d..447cf80b 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -8,6 +8,7 @@ import requests from app.classes.controllers.servers_controller import ServersController from app.classes.models.server_permissions import PermissionsServers +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -179,9 +180,7 @@ class ServerJars: try: ServersController.set_import(server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) break except Exception as ex: @@ -206,11 +205,9 @@ class ServerJars: server_users = PermissionsServers.get_server_user_list(server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", "Executable download finished" ) time.sleep(3) - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) return success diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 51bf71a3..fc0a1eb6 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -17,6 +17,7 @@ from app.classes.models.users import HelperUsers from app.classes.models.servers import Servers from app.classes.models.server_permissions import PermissionsServers from app.classes.shared.main_models import DatabaseShortcuts +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -158,9 +159,7 @@ class HelpersManagement: server_users = PermissionsServers.get_server_user_list(server_id) for user in server_users: try: - self.helper.websocket_helper.broadcast_user( - user, "notification", audit_msg - ) + WebSocketManager().broadcast_user(user, "notification", audit_msg) except Exception as e: logger.error(f"Error broadcasting to user {user} - {e}") diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py index 14f85ad3..f2bcec62 100644 --- a/app/classes/models/server_stats.py +++ b/app/classes/models/server_stats.py @@ -50,6 +50,7 @@ class ServerStats(Model): max = IntegerField(default=0) players = CharField(default="") desc = CharField(default="Unable to Connect") + icon = CharField(default="") version = CharField(default="") updating = BooleanField(default=False) waiting_start = BooleanField(default=False) @@ -141,16 +142,20 @@ class HelperServerStats: self.database.close() return server_data - def get_history_stats(self, server_id, num_days): + def get_history_stats(self, server_id, num_hours): self.database.connect(reuse_if_open=True) - max_age = datetime.datetime.now() - timedelta(days=num_days) - server_stats = ( + max_age = datetime.datetime.now() - timedelta(hours=num_hours) + query_stats = ( ServerStats.select() .where(ServerStats.created > max_age) .where(ServerStats.server_id == server_id) + # .order_by(ServerStats.created.desc()) .execute(self.database) ) - self.database.connect(reuse_if_open=True) + server_stats = [] + for stat in query_stats: + server_stats.append(DatabaseShortcuts.get_data_obj(stat)) + self.database.close() return server_stats def insert_server_stats(self, server_stats): @@ -179,6 +184,7 @@ class HelperServerStats: ServerStats.max: server_stats.get("max", False), ServerStats.players: server_stats.get("players", False), ServerStats.desc: server_stats.get("desc", False), + ServerStats.icon: server_stats.get("icon", None), ServerStats.version: server_stats.get("version", False), } ).execute(self.database) diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index cebe76b7..155fe083 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -11,6 +11,7 @@ from app.classes.shared.helpers import Helpers from app.classes.shared.tasks import TasksManager from app.classes.shared.migration import MigrationManager from app.classes.shared.main_controller import Controller +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -118,7 +119,7 @@ class MainPrompt(cmd.Cmd): Console.info( "Stopping all server daemons / threads - This may take a few seconds" ) - self.helper.websocket_helper.disconnect_all() + WebSocketManager().disconnect_all() Console.info("Waiting for main thread to stop") while True: if self.tasks_manager.get_main_thread_run_status(): diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 27d68141..cc09dc4f 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -8,6 +8,7 @@ from zipfile import ZipFile, ZIP_DEFLATED from app.classes.shared.helpers import Helpers from app.classes.shared.console import Console +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -149,7 +150,7 @@ class FileHelpers: "percent": 0, "total_files": self.helper.human_readable_file_size(dir_bytes), } - self.helper.websocket_helper.broadcast_page_params( + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(server_id)}, "backup_status", @@ -194,7 +195,7 @@ class FileHelpers: "percent": percent, "total_files": self.helper.human_readable_file_size(dir_bytes), } - self.helper.websocket_helper.broadcast_page_params( + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(server_id)}, "backup_status", @@ -215,7 +216,7 @@ class FileHelpers: "percent": 0, "total_files": self.helper.human_readable_file_size(dir_bytes), } - self.helper.websocket_helper.broadcast_page_params( + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(server_id)}, "backup_status", @@ -274,7 +275,7 @@ class FileHelpers: "total_files": self.helper.human_readable_file_size(dir_bytes), } # send status results to page. - self.helper.websocket_helper.broadcast_page_params( + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(server_id)}, "backup_status", diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 10fee644..ba9c5a28 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -29,7 +29,6 @@ from app.classes.shared.null_writer import NullWriter from app.classes.shared.console import Console from app.classes.shared.installer import installer from app.classes.shared.translation import Translation -from app.classes.web.websocket_helper import WebSocketHelper with redirect_stderr(NullWriter()): import psutil @@ -78,7 +77,6 @@ class Helpers: self.passhasher = PasswordHasher() self.exiting = False - self.websocket_helper = WebSocketHelper(self) self.translation = Translation(self) self.update_available = False self.ignored_names = ["crafty_managed.txt", "db_stats"] diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py index e3762aad..1acf7a03 100644 --- a/app/classes/shared/import_helper.py +++ b/app/classes/shared/import_helper.py @@ -9,6 +9,7 @@ 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 +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -64,7 +65,7 @@ class ImportHelpers: 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", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) def import_java_zip_server(self, temp_dir, new_server_dir, port, new_id): import_thread = threading.Thread( @@ -108,7 +109,7 @@ class ImportHelpers: 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", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) # deletes temp dir FileHelpers.del_dirs(temp_dir) @@ -162,7 +163,7 @@ class ImportHelpers: 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", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) def import_bedrock_zip_server( self, temp_dir, new_server_dir, full_jar_path, port, new_id @@ -209,7 +210,7 @@ class ImportHelpers: 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", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) if os.name != "nt": if Helpers.check_file_exists(full_jar_path): os.chmod(full_jar_path, 0o2760) @@ -253,4 +254,4 @@ class ImportHelpers: 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", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index ccea5dac..4100b21e 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -33,6 +33,7 @@ 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 +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -113,7 +114,7 @@ class Controller: self.del_support_file(exec_user["support_logs"]) # pausing so on screen notifications can run for user time.sleep(7) - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( exec_user["user_id"], "notification", "Preparing your support logs" ) self.helper.ensure_dir_exists( @@ -209,17 +210,15 @@ class Controller: ) as f: f.write(sys_info_string) FileHelpers.make_compressed_archive(temp_zip_storage, temp_dir, sys_info_string) - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_user( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_user( exec_user["user_id"], "support_status_update", Helpers.calc_percent(temp_dir, temp_zip_storage + ".zip"), ) temp_zip_storage += ".zip" - self.helper.websocket_helper.broadcast_user( - exec_user["user_id"], "send_logs_bootbox", {} - ) + WebSocketManager().broadcast_user(exec_user["user_id"], "send_logs_bootbox", {}) self.users.set_support_path(exec_user["user_id"], temp_zip_storage) @@ -252,8 +251,8 @@ class Controller: results = Helpers.calc_percent(source_path, dest_path) self.log_stats = results - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_user( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_user( exec_user["user_id"], "support_status_update", results ) @@ -910,7 +909,7 @@ class Controller: def t_update_master_server_dir(self, new_server_path, user_id): new_server_path = self.helper.wtol_path(new_server_path) new_server_path = os.path.join(new_server_path, "servers") - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", "Checking dir" ) current_master = self.helper.wtol_path( @@ -920,7 +919,7 @@ class Controller: logger.info( "Admin tried to change server dir to current server dir. Canceling..." ) - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", "done", @@ -931,18 +930,18 @@ class Controller: "Admin tried to change server dir to be inside a sub directory of the" " current server dir. This will result in a copy loop." ) - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", "done", ) return - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", "Checking permissions" ) if not self.helper.ensure_dir_exists(new_server_path): - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -966,7 +965,7 @@ class Controller: new_server_path, server.get("server_uuid") ) if os.path.isdir(server_path): - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", f"Moving {server.get('server_name')}", @@ -1007,7 +1006,7 @@ class Controller: self.servers.update_unloaded_server(server_obj) self.servers.init_all_servers() self.helper.dir_migration = False - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/panel_config", "move_status", "done", diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index c1a11158..8205bd8a 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -32,6 +32,7 @@ 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.null_writer import NullWriter +from app.classes.shared.websocket_manager import WebSocketManager with redirect_stderr(NullWriter()): import psutil @@ -92,12 +93,13 @@ class ServerOutBuf: # TODO: Do not send data to clients who do not have permission to view # this server's console - self.helper.websocket_helper.broadcast_page_params( - "/panel/server_detail", - {"id": self.server_id}, - "vterm_new_line", - {"line": highlighted + "
"}, - ) + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_page_params( + "/panel/server_detail", + {"id": self.server_id}, + "vterm_new_line", + {"line": highlighted + "
"}, + ) # ********************************************************************************** @@ -251,6 +253,23 @@ class ServerInstance: seconds=5, id="stats_" + str(self.server_id), ) + logger.info(f"Saving server statistics {self.name} every {30} seconds") + Console.info(f"Saving server statistics {self.name} every {30} seconds") + try: + self.server_scheduler.add_job( + self.record_server_stats, + "interval", + seconds=30, + id="save_stats_" + str(self.server_id), + ) + except: + self.server_scheduler.remove_job("save_" + str(self.server_id)) + self.server_scheduler.add_job( + self.record_server_stats, + "interval", + seconds=30, + id="save_" + str(self.server_id), + ) def setup_server_run_command(self): # configure the server @@ -322,7 +341,7 @@ class ServerInstance: # Checks if user is currently attempting to move global server # dir if self.helper.dir_migration: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -337,7 +356,7 @@ class ServerInstance: if self.stats_helper.get_import_status() and not forge_install: if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -383,7 +402,7 @@ class ServerInstance: e_flag = True if not e_flag and self.settings["type"] == "minecraft-java": if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_eula_bootbox", {"id": self.server_id} ) else: @@ -416,7 +435,7 @@ class ServerInstance: except: if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -452,7 +471,7 @@ class ServerInstance: f"Server {self.name} failed to start with error code: {ex}" ) if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -479,7 +498,7 @@ class ServerInstance: # Checks for java on initial fail if not self.helper.detect_java(): if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -493,7 +512,7 @@ class ServerInstance: f"Server {self.name} failed to start with error code: {ex}" ) if user_id: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -540,7 +559,7 @@ class ServerInstance: self.stats_helper.set_first_run() loc_server_port = self.stats_helper.get_server_stats()["server_port"] # Sends port reminder message. - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -552,15 +571,11 @@ class ServerInstance: server_users = PermissionsServers.get_server_user_list(self.server_id) for user in server_users: if user != user_id: - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) else: server_users = PermissionsServers.get_server_user_list(self.server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) else: logger.warning( f"Server PID {self.process.pid} died right after starting " @@ -592,7 +607,7 @@ class ServerInstance: def check_internet_thread(self, user_id, user_lang): if user_id: if not Helpers.check_internet(): - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -719,9 +734,7 @@ class ServerInstance: server_users = PermissionsServers.get_server_user_list(self.server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) break def stop_crash_detection(self): @@ -834,7 +847,7 @@ class ServerInstance: self.record_server_stats() for user in server_users: - self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {}) + WebSocketManager().broadcast_user(user, "send_start_reload", {}) def restart_threaded_server(self, user_id): bu_conf = HelpersManagement.get_backup_config(self.server_id) @@ -1034,8 +1047,8 @@ class ServerInstance: logger.info(f"Backup Thread started for server {self.settings['server_name']}.") def a_backup_server(self): - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_page_params( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(self.server_id)}, "backup_reload", @@ -1045,7 +1058,7 @@ class ServerInstance: logger.info(f"Starting server {self.name} (ID {self.server_id}) backup") server_users = PermissionsServers.get_server_user_list(self.server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", self.helper.translation.translate( @@ -1120,8 +1133,8 @@ class ServerInstance: self.is_backingup = False logger.info(f"Backup of server: {self.name} completed") results = {"percent": 100, "total_files": 0, "current_file": 0} - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_page_params( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(self.server_id)}, "backup_status", @@ -1129,7 +1142,7 @@ class ServerInstance: ) server_users = PermissionsServers.get_server_user_list(self.server_id) for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", self.helper.translation.translate( @@ -1158,8 +1171,8 @@ class ServerInstance: f"Failed to create backup of server {self.name} (ID {self.server_id})" ) results = {"percent": 100, "total_files": 0, "current_file": 0} - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_page_params( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(self.server_id)}, "backup_status", @@ -1176,8 +1189,8 @@ class ServerInstance: def backup_status(self, source_path, dest_path): results = Helpers.calc_percent(source_path, dest_path) self.backup_stats = results - if len(self.helper.websocket_helper.clients) > 0: - self.helper.websocket_helper.broadcast_page_params( + if len(WebSocketManager().clients) > 0: + WebSocketManager().broadcast_page_params( "/panel/server_detail", {"id": str(self.server_id)}, "backup_status", @@ -1280,14 +1293,14 @@ class ServerInstance: self.stop_threaded_server() else: was_started = False - if len(self.helper.websocket_helper.clients) > 0: + if len(WebSocketManager().clients) > 0: # There are clients self.check_update() message = ( ' UPDATING...' ) for user in server_users: - self.helper.websocket_helper.broadcast_user_page( + WebSocketManager().broadcast_user_page( "/panel/server_detail", user, "update_button_status", @@ -1340,7 +1353,7 @@ class ServerInstance: # check if backup was successful if self.last_backup_failed: for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", "Backup failed for " + self.name + ". canceling update.", @@ -1386,11 +1399,11 @@ class ServerInstance: logger.info("Executable updated successfully. Starting Server") self.stats_helper.set_update(False) - if len(self.helper.websocket_helper.clients) > 0: + if len(WebSocketManager().clients) > 0: # There are clients self.check_update() for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", "Executable update finished for " + self.name, @@ -1398,7 +1411,7 @@ class ServerInstance: # sleep so first notif can completely run time.sleep(3) for user in server_users: - self.helper.websocket_helper.broadcast_user_page( + WebSocketManager().broadcast_user_page( "/panel/server_detail", user, "update_button_status", @@ -1408,10 +1421,10 @@ class ServerInstance: "wasRunning": was_started, }, ) - self.helper.websocket_helper.broadcast_user_page( + WebSocketManager().broadcast_user_page( user, "/panel/dashboard", "send_start_reload", {} ) - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", "Executable update finished for " + self.name, @@ -1428,7 +1441,7 @@ class ServerInstance: self.run_threaded_server(HelperUsers.get_user_id_by_name("system")) else: for user in server_users: - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user, "notification", "Executable update failed for " @@ -1438,7 +1451,7 @@ class ServerInstance: logger.error("Executable download failed.") self.stats_helper.set_update(False) for user in server_users: - self.helper.websocket_helper.broadcast_user(user, "remove_spinner", {}) + WebSocketManager().broadcast_user(user, "remove_spinner", {}) def start_dir_calc_task(self): server_dt = HelperServers.get_server_data_by_id(self.server_id) @@ -1467,7 +1480,7 @@ class ServerInstance: def realtime_stats(self): # only get stats if clients are connected. # no point in burning cpu - if len(self.helper.websocket_helper.clients) > 0: + if len(WebSocketManager().clients) > 0: total_players = 0 max_players = 0 servers_ping = [] @@ -1498,50 +1511,43 @@ class ServerInstance: "crashed": self.is_crashed, } ) - 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" - ), - "players_cache": self.player_cache, - }, - ) + + WebSocketManager().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"), + "players_cache": self.player_cache, + }, + ) total_players += int(raw_ping_result.get("online")) max_players += int(raw_ping_result.get("max")) - self.record_server_stats() + # self.record_server_stats() - if (len(servers_ping) > 0) & ( - len(self.helper.websocket_helper.clients) > 0 - ): + if len(servers_ping) > 0: try: - self.helper.websocket_helper.broadcast_page( + WebSocketManager().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") @@ -1606,6 +1612,7 @@ class ServerInstance: "players": ping_data.get("players", False), "desc": ping_data.get("server_description", False), "version": ping_data.get("server_version", False), + "icon": ping_data.get("server_icon"), } else: server_stats = { @@ -1624,6 +1631,7 @@ class ServerInstance: "players": False, "desc": False, "version": False, + "icon": None, } return server_stats @@ -1672,7 +1680,6 @@ class ServerInstance: } server_stats = {} - server = HelperServers.get_server_obj(server_id) if not server: return {} server_dt = HelperServers.get_server_data_by_id(server_id) @@ -1805,3 +1812,7 @@ class ServerInstance: minimum_to_exist = now - datetime.timedelta(days=max_age) self.stats_helper.remove_old_stats(minimum_to_exist) + + def get_server_history(self): + history = self.stats_helper.get_history_stats(self.server_id, 1) + return history diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index bb15b5e7..28e932be 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -20,6 +20,7 @@ from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.helpers import Helpers from app.classes.shared.main_controller import Controller from app.classes.web.tornado_handler import Webserver +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger("apscheduler") scheduler_intervals = { @@ -688,10 +689,10 @@ class TasksManager: # Stats are different host_stats = HelpersManagement.get_latest_hosts_stats() - if len(self.helper.websocket_helper.clients) > 0: + if len(WebSocketManager().clients) > 0: # There are clients try: - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/dashboard", "update_host_stats", { @@ -708,7 +709,7 @@ class TasksManager: }, ) except: - self.helper.websocket_helper.broadcast_page( + WebSocketManager().broadcast_page( "/panel/dashboard", "update_host_stats", { diff --git a/app/classes/web/websocket_helper.py b/app/classes/shared/websocket_manager.py similarity index 80% rename from app/classes/web/websocket_helper.py rename to app/classes/shared/websocket_manager.py index cd70df50..f48adef8 100644 --- a/app/classes/web/websocket_helper.py +++ b/app/classes/shared/websocket_manager.py @@ -1,26 +1,25 @@ import json import logging +from app.classes.shared.singleton import Singleton from app.classes.shared.console import Console +from app.classes.models.users import HelperUsers logger = logging.getLogger(__name__) -class WebSocketHelper: - def __init__(self, helper): - self.helper = helper +class WebSocketManager(metaclass=Singleton): + def __init__(self): self.clients = set() def add_client(self, client): self.clients.add(client) def remove_client(self, client): - self.clients.remove(client) - - def send_message(self, client, event_type: str, data): - if client.check_auth(): - message = str(json.dumps({"event": event_type, "data": data})) - client.write_message_helper(message) + if client in self.clients: + self.clients.remove(client) + else: + logger.exception("Error caught while removing unknown WebSocket client") def broadcast(self, event_type: str, data): logger.debug( @@ -29,13 +28,21 @@ class WebSocketHelper: ) for client in self.clients: try: - self.send_message(client, event_type, data) + client.send_message(event_type, data) except Exception as e: logger.exception( f"Error caught while sending WebSocket message to " f"{client.get_remote_ip()} {e}" ) + def broadcast_to_admins(self, event_type: str, data): + def filter_fn(client): + if client.get_user_id in 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 @@ -90,13 +97,14 @@ class WebSocketHelper: static_clients = self.clients clients = list(filter(filter_fn, static_clients)) logger.debug( - f"Sending to {len(clients)} out of {len(self.clients)} " + f"Sending to {len(clients)} \ + out of {len(self.clients)} " f"clients: {json.dumps({'event': event_type, 'data': data})}" ) for client in clients[:]: try: - self.send_message(client, event_type, data) + client.send_message(event_type, data) except Exception as e: logger.exception( f"Error catched while sending WebSocket message to " diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index f55da4ba..05250a6d 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -743,7 +743,7 @@ class PanelHandler(BaseHandler): 0, page_data["options"].pop(page_data["options"].index(days)) ) page_data["history_stats"] = self.controller.servers.get_history_stats( - server_id, days + server_id, hours=(days * 24) ) def get_banned_players_html(): diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 47977449..49dcb9de 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -12,6 +12,7 @@ from app.classes.web.routes.api.roles.index import ApiRolesIndexHandler from app.classes.web.routes.api.roles.role.index import ApiRolesRoleIndexHandler from app.classes.web.routes.api.roles.role.servers import ApiRolesRoleServersHandler from app.classes.web.routes.api.roles.role.users import ApiRolesRoleUsersHandler + from app.classes.web.routes.api.servers.index import ApiServersIndexHandler from app.classes.web.routes.api.servers.server.action import ( ApiServersServerActionHandler, @@ -21,7 +22,13 @@ from app.classes.web.routes.api.servers.server.logs import ApiServersServerLogsH from app.classes.web.routes.api.servers.server.public import ( ApiServersServerPublicHandler, ) +from app.classes.web.routes.api.servers.server.status import ( + ApiServersServerStatusHandler, +) from app.classes.web.routes.api.servers.server.stats import ApiServersServerStatsHandler +from app.classes.web.routes.api.servers.server.history import ( + ApiServersServerHistoryHandler, +) from app.classes.web.routes.api.servers.server.stdin import ApiServersServerStdinHandler from app.classes.web.routes.api.servers.server.tasks.index import ( ApiServersServerTasksIndexHandler, @@ -62,6 +69,7 @@ from app.classes.web.routes.api.crafty.config.index import ( from app.classes.web.routes.api.crafty.config.server_dir import ( ApiCraftyConfigServerDirHandler, ) +from app.classes.web.routes.api.crafty.stats.stats import ApiCraftyHostStatsHandler from app.classes.web.routes.api.crafty.clogs.index import ApiCraftyLogIndexHandler from app.classes.web.routes.api.crafty.imports.index import ApiImportFilesIndexHandler from app.classes.web.routes.api.crafty.exe_cache import ApiCraftyJarCacheIndexHandler @@ -100,11 +108,21 @@ def api_handlers(handler_args): ApiCraftyConfigServerDirHandler, handler_args, ), + ( + r"/api/v2/crafty/stats/?", + ApiCraftyHostStatsHandler, + handler_args, + ), ( r"/api/v2/crafty/logs/([a-z0-9_]+)/?", ApiCraftyLogIndexHandler, handler_args, ), + ( + r"/api/v2/crafty/JarCache/?", + ApiCraftyJarCacheIndexHandler, + handler_args, + ), ( r"/api/v2/import/file/unzip/?", ApiImportFilesIndexHandler, @@ -173,8 +191,8 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/crafty/JarCache/?", - ApiCraftyJarCacheIndexHandler, + r"/api/v2/servers/status/?", + ApiServersServerStatusHandler, handler_args, ), ( @@ -227,6 +245,11 @@ def api_handlers(handler_args): ApiServersServerStatsHandler, handler_args, ), + ( + r"/api/v2/servers/([0-9]+)/history/?", + ApiServersServerHistoryHandler, + handler_args, + ), ( r"/api/v2/servers/([0-9]+)/action/([a-z_]+)/?", ApiServersServerActionHandler, diff --git a/app/classes/web/routes/api/crafty/stats/stats.py b/app/classes/web/routes/api/crafty/stats/stats.py new file mode 100644 index 00000000..84569469 --- /dev/null +++ b/app/classes/web/routes/api/crafty/stats/stats.py @@ -0,0 +1,21 @@ +import logging +from app.classes.web.base_api_handler import BaseApiHandler + +logger = logging.getLogger(__name__) + + +class ApiCraftyHostStatsHandler(BaseApiHandler): + def get(self): + auth_data = self.authenticate_user() + if not auth_data: + return + + latest = self.controller.management.get_latest_hosts_stats() + + self.finish_json( + 200, + { + "status": "ok", + "data": latest, + }, + ) diff --git a/app/classes/web/routes/api/servers/server/history.py b/app/classes/web/routes/api/servers/server/history.py new file mode 100644 index 00000000..1a4aac24 --- /dev/null +++ b/app/classes/web/routes/api/servers/server/history.py @@ -0,0 +1,28 @@ +import logging +from app.classes.web.base_api_handler import BaseApiHandler +from app.classes.controllers.servers_controller import ServersController + + +logger = logging.getLogger(__name__) + + +class ApiServersServerHistoryHandler(BaseApiHandler): + def get(self, server_id: str): + auth_data = self.authenticate_user() + if not auth_data: + return + + if server_id not in [str(x["server_id"]) for x in auth_data[0]]: + # if the user doesn't have access to the server, return an error + return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) + + srv = ServersController().get_server_instance_by_id(server_id) + history = srv.get_server_history() + + self.finish_json( + 200, + { + "status": "ok", + "data": history, + }, + ) diff --git a/app/classes/web/routes/api/servers/server/status.py b/app/classes/web/routes/api/servers/server/status.py new file mode 100644 index 00000000..aab501d8 --- /dev/null +++ b/app/classes/web/routes/api/servers/server/status.py @@ -0,0 +1,32 @@ +import logging +from app.classes.web.base_api_handler import BaseApiHandler + +logger = logging.getLogger(__name__) + + +class ApiServersServerStatusHandler(BaseApiHandler): + def get(self): + servers_status = [] + servers_list = self.controller.servers.get_all_servers_stats() + for server in servers_list: + if server.get("server_data").get("show_status") is True: + servers_status.append( + { + "id": server.get("server_data").get("server_id"), + "world_name": server.get("stats").get("world_name"), + "running": server.get("stats").get("running"), + "online": server.get("stats").get("online"), + "max": server.get("stats").get("max"), + "version": server.get("stats").get("version"), + "desc": server.get("stats").get("desc"), + "icon": server.get("stats").get("icon"), + } + ) + + self.finish_json( + 200, + { + "status": "ok", + "data": servers_status, + }, + ) diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index c10184dc..69d1f0f5 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -14,6 +14,7 @@ import tornado.httpserver from app.classes.models.management import HelpersManagement 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.main_controller import Controller from app.classes.web.public_handler import PublicHandler from app.classes.web.panel_handler import PanelHandler @@ -32,7 +33,7 @@ from app.classes.web.api_handler import ( ListServers, SendCommand, ) -from app.classes.web.websocket_handler import SocketHandler +from app.classes.web.websocket_handler import WebSocketHandler from app.classes.web.static_handler import CustomStaticHandler from app.classes.web.upload_handler import UploadHandler from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage @@ -46,7 +47,13 @@ class Webserver: controller: Controller helper: Helpers - def __init__(self, helper, controller, tasks_manager, file_helper): + def __init__( + self, + helper: Helpers, + controller: Controller, + tasks_manager, + file_helper: FileHelpers, + ): self.ioloop = None self.http_server = None self.https_server = None @@ -151,7 +158,7 @@ class Webserver: (r"/", DefaultHandler, handler_args), (r"/panel/(.*)", PanelHandler, handler_args), (r"/server/(.*)", ServerHandler, handler_args), - (r"/ws", SocketHandler, handler_args), + (r"/ws", WebSocketHandler, handler_args), (r"/upload", UploadHandler, handler_args), (r"/status", StatusHandler, handler_args), # API Routes V1 diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 88510a80..2d7653f9 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -12,6 +12,7 @@ from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.main_controller import Controller from app.classes.web.base_handler import BaseHandler +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -115,7 +116,7 @@ class UploadHandler(BaseHandler): self.request.headers.get("X-FileName", None) ) if not str(filename).endswith(".zip"): - self.helper.websocket_helper.broadcast("close_upload_box", "error") + WebSocketManager().broadcast("close_upload_box", "error") self.finish("error") full_path = os.path.join(path, filename) @@ -315,13 +316,13 @@ class UploadHandler(BaseHandler): if self.do_upload: time.sleep(5) if files_left == 0: - self.helper.websocket_helper.broadcast("close_upload_box", "success") + WebSocketManager().broadcast("close_upload_box", "success") self.finish("success") # Nope, I'm sending "success" self.f.close() else: time.sleep(5) if files_left == 0: - self.helper.websocket_helper.broadcast("close_upload_box", "error") + WebSocketManager().broadcast("close_upload_box", "error") self.finish("error") def data_received(self, chunk): diff --git a/app/classes/web/websocket_handler.py b/app/classes/web/websocket_handler.py index a2e7f5a4..cde97584 100644 --- a/app/classes/web/websocket_handler.py +++ b/app/classes/web/websocket_handler.py @@ -4,15 +4,17 @@ import asyncio from urllib.parse import parse_qsl import tornado.websocket +from app.classes.shared.main_controller import Controller from app.classes.shared.helpers import Helpers +from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) -class SocketHandler(tornado.websocket.WebSocketHandler): +class WebSocketHandler(tornado.websocket.WebSocketHandler): page = None page_query_params = None - controller = None + controller: Controller = None tasks_manager = None translator = None io_loop = None @@ -40,23 +42,16 @@ class SocketHandler(tornado.websocket.WebSocketHandler): ) return remote_ip - def get_user_id(self): - _, _, user = self.controller.authentication.check(self.get_cookie("token")) - return user["user_id"] - - def check_auth(self): - return self.controller.authentication.check_bool(self.get_cookie("token")) - # pylint: disable=arguments-differ def open(self): logger.debug("Checking WebSocket authentication") if self.check_auth(): self.handle() else: - self.helper.websocket_helper.send_message( + WebSocketManager().broadcast_to_admins( self, "notification", "Not authenticated for WebSocket connection" ) - self.close() + self.close(1011, "Forbidden WS Access") self.controller.management.add_to_audit_log_raw( "unknown", 0, @@ -64,7 +59,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler): "Someone tried to connect via WebSocket without proper authentication", self.get_remote_ip(), ) - self.helper.websocket_helper.broadcast( + WebSocketManager().broadcast( "notification", "Someone tried to connect via WebSocket without proper authentication", ) @@ -79,24 +74,34 @@ class SocketHandler(tornado.websocket.WebSocketHandler): Helpers.remove_prefix(self.get_query_argument("page_query_params"), "?") ) ) - self.helper.websocket_helper.add_client(self) + WebSocketManager().add_client(self) logger.debug("Opened WebSocket connection") # pylint: disable=arguments-renamed - @staticmethod - def on_message(raw_message): + def on_message(self, raw_message): logger.debug(f"Got message from WebSocket connection {raw_message}") message = json.loads(raw_message) logger.debug(f"Event Type: {message['event']}, Data: {message['data']}") def on_close(self): - self.helper.websocket_helper.remove_client(self) + WebSocketManager().remove_client(self) logger.debug("Closed WebSocket connection") async def write_message_int(self, message): self.write_message(message) - def write_message_helper(self, message): + def write_message_async(self, message): asyncio.run_coroutine_threadsafe( self.write_message_int(message), self.io_loop.asyncio_loop ) + + def send_message(self, event_type: str, data): + message = str(json.dumps({"event": event_type, "data": data})) + self.write_message_async(message) + + def get_user_id(self): + _, _, user = self.controller.authentication.check(self.get_cookie("token")) + return user["user_id"] + + def check_auth(self): + return self.controller.authentication.check_bool(self.get_cookie("token")) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index eca34e75..ad298cf1 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -14,8 +14,7 @@ - + @@ -50,15 +49,9 @@ - - - + + + @@ -91,8 +84,7 @@ {% include notify.html %} - @@ -183,8 +175,7 @@ - + @@ -241,6 +232,7 @@ let usingWebSockets = false; let webSocket = null; + let websocketTimeoutId = null; // {% if request.protocol == 'https' %} usingWebSockets = true; @@ -291,18 +283,19 @@ wsOpen = false; console.log('Closed WebSocket', closeEvent); - if (typeof reconnectorId !== 'number') { - setTimeout(sendWssError, 7000); + if (!document.hidden) { + if (typeof reconnectorId !== 'number') { + setTimeout(sendWssError, 7000); + } + console.info("Reconnecting with a timeout of", (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000, "milliseconds"); + // Discard old websocket and create a new one in 5 seconds + wsInternal = null + reconnectorId = setTimeout(startWebSocket, (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000) + + failedConnectionCounter++; } - console.info("Reconnecting with a timeout of", (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000, "milliseconds"); - // Discard old websocket and create a new one in 5 seconds - wsInternal = null - reconnectorId = setTimeout(startWebSocket, (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000) - - failedConnectionCounter++; }; - webSocket = { on: function (event, callback) { console.log('registered ' + event + ' event'); @@ -315,6 +308,12 @@ } wsInternal.send(JSON.stringify(message)); + }, + close: function (code, reason) { + wsInternal.close(code, reason); + }, + getStatus: function () { + return wsInternal.readyState; } } } catch (error) { @@ -328,6 +327,21 @@ warn('WebSockets are not supported in Crafty if not using the https protocol') // {% end%} + // Managing Connexions for Multi Tab opened to reduce bandwith usage + document.addEventListener("visibilitychange", () => { + if (document.visibilityState == "hidden") { + websocketTimeoutId = setTimeout(() => { + webSocket.close(1000, "Closed due to Inactivity"); + console.log('%c[Crafty Controller] %cClose Websocket due to Tab not active after 5s', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;'); + }, 10000); + } else { + clearTimeout(websocketTimeoutId) + if (webSocket.getStatus() == WebSocket.CLOSED) { + startWebSocket(); + } + } + }); + if (webSocket) { webSocket.on('send_start_error', function (start_error) { var x = document.querySelector('.bootbox'); @@ -524,10 +538,10 @@ document.addEventListener('alpine:init', () => { console.log('%c[Crafty Controller] %cAlpine.js pre-initialization!', 'font-weight: 900; color: #800080;', 'color: #eee;'); - }) + }); document.addEventListener('alpine:initialized', () => { console.log('%c[Crafty Controller] %cAlpine.js initialized!', 'font-weight: 900; color: #800080;', 'color: #eee;'); - }) + }); $(document).ready(function () { console.log('%c[Crafty Controller] %cReady for JS!', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;'); diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 9aa207eb..377a109f 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -58,13 +58,11 @@
-

+

{{ translate('dashboard', 'cpuUsage', data['lang']) }}: {{ data.get('hosts_data').get('cpu_usage') }}

-

+

{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_percent') }}%

@@ -111,12 +109,9 @@ {% for item in data['hosts_data']['disk_json'] %} {% if item["mount"] in data["monitored"] %}
-

+

{{item["mount"]}}

-
+
{{item["used"]}} / + " role="progressbar" style="color: black; height: 100%; width: {{item['percent_used']}}%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">{{item["used"]}} / {{item["total"]}}
@@ -153,9 +147,7 @@ data['lang']) }} {% if len(data['servers']) > 0 %} {% if data['user_data']['hints'] %} - + {% end %} {% end %}
  {{ @@ -193,8 +185,7 @@ {% if server['alert'] %} - + {{ server['server_data']['server_name'] }}  {% else %} @@ -208,13 +199,11 @@ {% if server['user_command_permission'] %} {% if server['stats']['importing'] and server['stats']['running'] %} -  {{ translate('serverTerm', 'installing', +  {{ translate('serverTerm', 'installing', data['lang']) }} {% elif server['stats']['updating']%} -  {{ translate('serverTerm', 'updating', +  {{ translate('serverTerm', 'updating', data['lang']) }} {% elif server['stats']['waiting_start']%} @@ -226,31 +215,25 @@ {{ translate('serverTerm', 'importing', data['lang']) }} {% elif server['stats']['running'] %} - +   - +   - +   {% else %} - +   - +   - +   {% end %} @@ -258,8 +241,7 @@ -
+
+ " role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
{{server['stats']['cpu']}}% -
+
+ " role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
{{server['stats']['mem_percent']}}% - @@ -305,8 +284,7 @@ data['lang']) }}
{% if server['stats']['desc'] != 'False' %} -
{{ +
{{ server['stats']['desc'] }}

{% end %} @@ -334,16 +312,14 @@

- + {% end %}
{% for server in data['failed_servers'] %} -  {{server['server_name']}} +  {{server['server_name']}} @@ -368,28 +344,22 @@
- + {% if server['stats']['running'] %} {{ translate('dashboard', 'online', data['lang']) }} @@ -410,23 +380,17 @@ {% if server['stats']['running'] %}
@@ -452,31 +416,24 @@ {% elif server['stats']['importing']%} {% else %} @@ -488,15 +445,13 @@
-
+
{{ translate('dashboard', 'cpuUsage', data['lang']) }}
-
+
+ " role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
{{server['stats']['cpu']}}%
@@ -514,8 +468,7 @@
{{ translate('dashboard', 'memUsage', data['lang']) }}
-
+
+ " role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
{{server['stats']['mem_percent']}}% - @@ -554,8 +506,7 @@ data['lang']) }}
{% if server['stats']['desc'] != 'False' %} -
+
{{ server['stats']['desc'] }}

{% end %} @@ -796,6 +747,7 @@ /* Update Motd */ let motd = ""; if (server.desc) { + m_motd = `` + server.desc + ``; motd = `` + server.desc + ``; m_server_infos = server_infos + '
' + motd + '
' + "
"; server_infos = server_infos + '
' + motd + '
' + "
"; diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index d10fec51..5067e709 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -89,11 +89,11 @@ const cpu = [] {% for item in data['history_stats'] %} {% if 'minecraft-java' in data['server_stats']['server_type'] %} - players.push("{{ item.online }}"); + players.push("{{ item.get('online') }}"); {% end %} - dates.push("{{ item.created.strftime('%Y/%m/%d, %H:%M:%S') }}"); - ram.push("{{ item.mem_percent }}") - cpu.push("{{ item.cpu }}") + dates.push("{{ item.get('created').strftime('%Y/%m/%d, %H:%M:%S') }}"); + ram.push("{{ item.get('mem_percent') }}") + cpu.push("{{ item.get('cpu') }}") {% end %} var hist_chart = new Chart(ctxL, { type: 'line', diff --git a/app/frontend/templates/public/status.html b/app/frontend/templates/public/status.html index 6e1a2e4d..1190b9f8 100644 --- a/app/frontend/templates/public/status.html +++ b/app/frontend/templates/public/status.html @@ -30,9 +30,6 @@ - {% if data['running'] != 0 %} - - {% end %} {% for server in data['servers'] %} {% if server['server_data']['show_status'] %} @@ -47,10 +44,14 @@ {% if server['stats']['desc'] != 'False' %} - icon - {{ - server['stats']['desc'] }}
+
+
+ icon +
+
+ {{ server['stats']['desc'] }} +
+
{% end %} @@ -89,12 +90,9 @@
-