diff --git a/CHANGELOG.md b/CHANGELOG.md index 1245e3ff..4af8ce1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,15 @@ # Changelog -## --- [4.1.4] - 2023/TBD +## --- [4.2.0] - 2023/TBD ### New features -TBD +- Finish and Activate Arcadia notification backend ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/621)) ### Bug fixes - PWA: Removed the custom offline page in favour of browser default ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/607)) - Fix hidden servers appearing visible on public mobile status page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/612)) - Correctly handle if a server returns a string instead of json data on socket ping ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/614)) +- 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)) ### Refactor -- Refractor/Replace bleach with nh3 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/616)) +- Consolidate remaining frontend functions into API V2, and remove ajax internal API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/585)) ### 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/README.md b/README.md index eb3cd642..5d2379c8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) -# Crafty Controller 4.1.4 +# Crafty Controller 4.2.0 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 7c423da9..cda74fea 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -79,8 +79,8 @@ class ManagementController: # Audit_Log Methods # ********************************************************************************** @staticmethod - def get_actity_log(): - return HelpersManagement.get_actity_log() + def get_activity_log(): + return HelpersManagement.get_activity_log() def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None): return self.management_helper.add_to_audit_log( diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 667e01b4..99147a63 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -31,7 +31,7 @@ class UsersController: for permission in PermissionsCrafty.get_permissions_list() ], }, - "quantity": {"type": "number", "minimum": 0}, + "quantity": {"type": "number", "minimum": -1}, "enabled": {"type": "boolean"}, } self.user_jsonschema_props: t.Final = { @@ -46,7 +46,7 @@ class UsersController: "password": { "type": "string", "maxLength": 20, - "minLength": 4, + "minLength": 6, "examples": ["crafty"], "title": "Password", }, @@ -73,6 +73,8 @@ class UsersController: "examples": [False], "title": "Superuser", }, + "manager": {"type": ["integer", "null"]}, + "theme": {"type": "string"}, "permissions": { "type": "array", "items": { @@ -84,7 +86,7 @@ class UsersController: "roles": { "type": "array", "items": { - "type": "string", + "type": "integer", "minLength": 1, }, }, diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 1213bda0..fc0a1eb6 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -146,7 +146,7 @@ class HelpersManagement: # Audit_Log Methods # ********************************************************************************** @staticmethod - def get_actity_log(): + def get_activity_log(): query = AuditLog.select() return DatabaseShortcuts.return_db_rows(query) diff --git a/app/classes/models/users.py b/app/classes/models/users.py index b0612017..ccd8f1b0 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -45,6 +45,7 @@ class Users(BaseModel): manager = IntegerField(default=None, null=True) pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png") theme = CharField(default="default") + cleared_notifs = CharField(default="default") class Meta: table_name = "users" @@ -171,6 +172,7 @@ class HelperUsers: "roles": [], "servers": [], "support_logs": "", + "cleared_notifs": "", } user = model_to_dict(Users.get(Users.user_id == user_id)) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 53a5a56a..cc09dc4f 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -327,20 +327,11 @@ class FileHelpers: return "false" return - # TODO Look if not redundant with the precendent function - # TODO Prefixed ajax_ to differentiate and not broke things - - def ajax_unzip_server(self, zip_path, user_id): + def unzip_server(self, zip_path, user_id): if Helpers.check_file_perms(zip_path): temp_dir = tempfile.mkdtemp() with zipfile.ZipFile(zip_path, "r") as zip_ref: # extracts archive to temp directory zip_ref.extractall(temp_dir) if user_id: - WebSocketManager().broadcast_user( - user_id, "send_temp_path", {"path": temp_dir} - ) - - def ajax_backup_select(self, path, user_id): - if user_id: - WebSocketManager().broadcast_user(user_id, "send_temp_path", {"path": path}) + return temp_dir diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 30f70d3a..ba9c5a28 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -577,20 +577,16 @@ class Helpers: return version_data - @staticmethod - def get_announcements(): - data = ( - '[{"id":"1","date":"Unknown",' - '"title":"Error getting Announcements",' - '"desc":"Error getting Announcements","link":""}]' - ) - + def get_announcements(self): + data = [] try: - response = requests.get("https://craftycontrol.com/notify.json", timeout=2) + response = requests.get("https://craftycontrol.com/notify", timeout=2) data = json.loads(response.content) except Exception as e: logger.error(f"Failed to fetch notifications with error: {e}") + if self.update_available: + data.append(self.update_available) return data def get_version_string(self): @@ -1090,87 +1086,6 @@ class Helpers: return data - def generate_tree(self, folder, output=""): - dir_list = [] - unsorted_files = [] - file_list = os.listdir(folder) - for item in file_list: - if os.path.isdir(os.path.join(folder, item)): - dir_list.append(item) - elif str(item) != self.ignored_names: - unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted( - unsorted_files, key=str.casefold - ) - for raw_filename in file_list: - filename = html.escape(raw_filename) - rel = os.path.join(folder, raw_filename) - dpath = os.path.join(folder, filename) - if os.path.isdir(rel): - if filename not in self.ignored_names: - output += f"""
  • - \n
    - - - - {filename} - -
  • - \n""" - else: - if filename not in self.ignored_names: - output += f"""
  • - {filename}
  • """ - return output - - def generate_dir(self, folder, output=""): - dir_list = [] - unsorted_files = [] - file_list = os.listdir(folder) - for item in file_list: - if os.path.isdir(os.path.join(folder, item)): - dir_list.append(item) - elif str(item) != self.ignored_names: - unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted( - unsorted_files, key=str.casefold - ) - output += f"""\n" - return output - @staticmethod def generate_zip_tree(folder, output=""): file_list = os.listdir(folder) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 2a299934..4100b21e 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -5,6 +5,7 @@ from datetime import datetime import platform import shutil import time +import json import logging import threading from peewee import DoesNotExist @@ -85,6 +86,17 @@ class Controller: def set_project_root(self, root_dir): self.project_root = root_dir + def set_config_json(self, data): + current_config = self.helper.get_all_settings() + for key in current_config: + if key in data: + current_config[key] = data[key] + keys = list(current_config.keys()) + keys.sort() + sorted_data = {i: current_config[i] for i in keys} + with open(self.helper.settings_file, "w", encoding="utf-8") as f: + json.dump(sorted_data, f, indent=4) + def package_support_logs(self, exec_user): if exec_user["preparing"]: return @@ -299,15 +311,6 @@ class Controller: Helpers.ensure_dir_exists(new_server_path) Helpers.ensure_dir_exists(backup_path) - def _copy_import_dir_files(existing_server_path): - existing_server_path = Helpers.get_os_understandable_path( - existing_server_path - ) - try: - FileHelpers.copy_dir(existing_server_path, new_server_path, True) - except shutil.Error as ex: - logger.error(f"Server import failed with error: {ex}") - def _create_server_properties_if_needed(port, empty=False): properties_file = os.path.join(new_server_path, "server.properties") has_properties = os.path.exists(properties_file) @@ -335,22 +338,25 @@ class Controller: server_file = f"{create_data['type']}-{create_data['version']}.jar" # Create an EULA file - with open( - os.path.join(new_server_path, "eula.txt"), "w", encoding="utf-8" - ) as file: - file.write( - "eula=" + ("true" if create_data["agree_to_eula"] else "false") - ) + if "agree_to_eula" in create_data: + with open( + os.path.join(new_server_path, "eula.txt"), "w", encoding="utf-8" + ) as file: + file.write( + "eula=" + + ("true" if create_data["agree_to_eula"] else "false") + ) elif root_create_data["create_type"] == "import_server": - _copy_import_dir_files(create_data["existing_server_path"]) server_file = create_data["jarfile"] elif root_create_data["create_type"] == "import_zip": # TODO: Copy files from the zip file to the new server directory server_file = create_data["jarfile"] raise NotImplementedError("Not yet implemented") - _create_server_properties_if_needed( - create_data["server_properties_port"], - ) + # self.import_helper.import_java_zip_server() + if data["create_type"] == "minecraft_java": + _create_server_properties_if_needed( + create_data["server_properties_port"], + ) min_mem = create_data["mem_min"] max_mem = create_data["mem_max"] @@ -363,30 +369,72 @@ class Controller: def _wrap_jar_if_windows(): return f'"{server_file}"' if Helpers.is_os_windows() else server_file - server_command = ( - f"java -Xms{_gibs_to_mibs(min_mem)}M " - f"-Xmx{_gibs_to_mibs(max_mem)}M " - f"-jar {_wrap_jar_if_windows()} nogui" - ) + if root_create_data["create_type"] == "download_jar": + if Helpers.is_os_windows(): + # Let's check for and setup for install server commands + if create_data["type"] == "forge": + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f'-jar "{server_file}" --installServer' + ) + else: + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f'-jar "{server_file}" nogui' + ) + else: + if create_data["type"] == "forge": + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f"-jar {server_file} --installServer" + ) + else: + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f"-jar {server_file} nogui" + ) + else: + server_command = ( + f"java -Xms{_gibs_to_mibs(min_mem)}M " + f"-Xmx{_gibs_to_mibs(max_mem)}M " + f"-jar {_wrap_jar_if_windows()} nogui" + ) + elif data["create_type"] == "minecraft_bedrock": if root_create_data["create_type"] == "import_server": existing_server_path = Helpers.get_os_understandable_path( create_data["existing_server_path"] ) - try: - FileHelpers.copy_dir(existing_server_path, new_server_path, True) - except shutil.Error as ex: - logger.error(f"Server import failed with error: {ex}") + if Helpers.is_os_windows(): + server_command = ( + f'"{os.path.join(new_server_path, create_data["executable"])}"' + ) + else: + server_command = f"./{create_data['executable']}" + logger.debug("command: " + server_command) + server_file = create_data["executable"] elif root_create_data["create_type"] == "import_zip": # TODO: Copy files from the zip file to the new server directory raise NotImplementedError("Not yet implemented") + else: + server_file = "bedrock_server" + if Helpers.is_os_windows(): + # if this is windows we will override the linux bedrock server name. + server_file = "bedrock_server.exe" + full_jar_path = os.path.join(new_server_path, server_file) + + if self.helper.is_os_windows(): + server_command = f'"{full_jar_path}"' + else: + server_command = f"./{server_file}" _create_server_properties_if_needed(0, True) - server_command = create_data["command"] - server_file = ( - "./bedrock_server" # HACK: This is a hack to make the server start - ) + server_command = create_data.get("command", server_command) elif data["create_type"] == "custom": # TODO: working_directory, executable_update if root_create_data["create_type"] == "raw_exec": @@ -450,131 +498,85 @@ class Controller: server_host=monitoring_host, server_type=monitoring_type, ) - - if ( - 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']}" + if data["create_type"] == "minecraft_java": + if 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"], + create_data["version"], + full_jar_path, + new_server_id, ) - server_obj.executable_update_url = url - self.servers.update_server(server_obj) - self.server_jars.download_jar( - create_data["category"], - create_data["type"], - create_data["version"], - full_jar_path, - new_server_id, - ) + elif root_create_data["create_type"] == "import_server": + ServersController.set_import(new_server_id) + self.import_helper.import_jar_server( + create_data["existing_server_path"], + new_server_path, + monitoring_port, + new_server_id, + ) + elif root_create_data["create_type"] == "import_zip": + ServersController.set_import(new_server_id) + + elif data["create_type"] == "minecraft_bedrock": + if root_create_data["create_type"] == "download_exe": + ServersController.set_import(new_server_id) + self.import_helper.download_bedrock_server( + new_server_path, new_server_id + ) + elif root_create_data["create_type"] == "import_server": + ServersController.set_import(new_server_id) + full_exe_path = os.path.join(new_server_path, create_data["executable"]) + self.import_helper.import_bedrock_server( + create_data["existing_server_path"], + new_server_path, + monitoring_port, + full_exe_path, + new_server_id, + ) + elif root_create_data["create_type"] == "import_zip": + ServersController.set_import(new_server_id) + full_exe_path = os.path.join(new_server_path, create_data["executable"]) + self.import_helper.import_bedrock_zip_server( + create_data["zip_path"], + new_server_path, + os.path.join(create_data["zip_root"], create_data["executable"]), + monitoring_port, + new_server_id, + ) + + exec_user = self.users.get_user_by_id(int(user_id)) + captured_roles = data.get("roles", []) + # These lines create a new Role for the Server with full permissions + # and add the user to it if he's not a superuser + if len(captured_roles) == 0: + if not exec_user["superuser"]: + new_server_uuid = self.servers.get_server_data_by_id(new_server_id).get( + "server_uuid" + ) + role_id = self.roles.add_role( + f"Creator of Server with uuid={new_server_uuid}", + exec_user["user_id"], + ) + self.server_perms.add_role_server(new_server_id, role_id, "11111111") + self.users.add_role_to_user(exec_user["user_id"], role_id) + + else: + for role in captured_roles: + role_id = role + self.server_perms.add_role_server(new_server_id, role_id, "11111111") return new_server_id, server_fs_uuid - def create_jar_server( - self, - jar: str, - server: str, - version: str, - name: str, - min_mem: int, - max_mem: int, - port: int, - user_id: int, - ): - server_id = Helpers.create_uuid() - server_dir = os.path.join(self.helper.servers_dir, server_id) - backup_path = os.path.join(self.helper.backup_path, server_id) - if Helpers.is_os_windows(): - server_dir = Helpers.wtol_path(server_dir) - backup_path = Helpers.wtol_path(backup_path) - server_dir.replace(" ", "^ ") - backup_path.replace(" ", "^ ") - - server_file = f"{server}-{version}.jar" - - # make the dir - perhaps a UUID? - Helpers.ensure_dir_exists(server_dir) - Helpers.ensure_dir_exists(backup_path) - - try: - # do a eula.txt - with open( - os.path.join(server_dir, "eula.txt"), "w", encoding="utf-8" - ) as file: - file.write("eula=false") - file.close() - - # setup server.properties with the port - with open( - os.path.join(server_dir, "server.properties"), "w", encoding="utf-8" - ) as file: - file.write(f"server-port={port}") - file.close() - - except Exception as e: - logger.error(f"Unable to create required server files due to :{e}") - return False - - if Helpers.is_os_windows(): - # Let's check for and setup for install server commands - if server == "forge": - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f'-jar "{server_file}" --installServer' - ) - else: - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f'-jar "{server_file}" nogui' - ) - else: - if server == "forge": - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f"-jar {server_file} --installServer" - ) - else: - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f"-jar {server_file} nogui" - ) - server_log_file = "./logs/latest.log" - server_stop = "stop" - - new_id = self.register_server( - name, - server_id, - server_dir, - backup_path, - server_command, - server_file, - server_log_file, - server_stop, - port, - 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 - ) - - return new_id - @staticmethod def verify_jar_server(server_path: str, server_jar: str): server_path = Helpers.get_os_understandable_path(server_path) @@ -592,123 +594,6 @@ class Controller: return False return True - def import_jar_server( - self, - server_name: str, - server_path: str, - server_jar: str, - min_mem: int, - max_mem: int, - port: int, - user_id: int, - ): - server_id = Helpers.create_uuid() - new_server_dir = os.path.join(self.helper.servers_dir, server_id) - backup_path = os.path.join(self.helper.backup_path, server_id) - if Helpers.is_os_windows(): - new_server_dir = Helpers.wtol_path(new_server_dir) - backup_path = Helpers.wtol_path(backup_path) - new_server_dir.replace(" ", "^ ") - backup_path.replace(" ", "^ ") - - Helpers.ensure_dir_exists(new_server_dir) - Helpers.ensure_dir_exists(backup_path) - server_path = Helpers.get_os_understandable_path(server_path) - - full_jar_path = os.path.join(new_server_dir, server_jar) - - if Helpers.is_os_windows(): - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f'-jar "{full_jar_path}" nogui' - ) - else: - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f"-jar {full_jar_path} nogui" - ) - server_log_file = "./logs/latest.log" - server_stop = "stop" - - new_id = self.register_server( - server_name, - server_id, - new_server_dir, - backup_path, - server_command, - server_jar, - server_log_file, - server_stop, - port, - user_id, - server_type="minecraft-java", - ) - ServersController.set_import(new_id) - self.import_helper.import_jar_server(server_path, new_server_dir, port, new_id) - return new_id - - def import_zip_server( - self, - server_name: str, - zip_path: str, - server_jar: str, - min_mem: int, - max_mem: int, - port: int, - user_id: int, - ): - server_id = Helpers.create_uuid() - new_server_dir = os.path.join(self.helper.servers_dir, server_id) - backup_path = os.path.join(self.helper.backup_path, server_id) - if Helpers.is_os_windows(): - new_server_dir = Helpers.wtol_path(new_server_dir) - backup_path = Helpers.wtol_path(backup_path) - new_server_dir.replace(" ", "^ ") - backup_path.replace(" ", "^ ") - - temp_dir = Helpers.get_os_understandable_path(zip_path) - Helpers.ensure_dir_exists(new_server_dir) - Helpers.ensure_dir_exists(backup_path) - - full_jar_path = os.path.join(new_server_dir, server_jar) - - if Helpers.is_os_windows(): - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f'-jar "{full_jar_path}" nogui' - ) - else: - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f"-jar {full_jar_path} nogui" - ) - logger.debug("command: " + server_command) - server_log_file = "./logs/latest.log" - server_stop = "stop" - - new_id = self.register_server( - server_name, - server_id, - new_server_dir, - backup_path, - server_command, - server_jar, - server_log_file, - server_stop, - port, - user_id, - server_type="minecraft-java", - ) - ServersController.set_import(new_id) - self.import_helper.import_java_zip_server( - temp_dir, new_server_dir, port, new_id - ) - return new_id - # ********************************************************************************** # BEDROCK IMPORTS # ********************************************************************************** @@ -1065,6 +950,8 @@ class Controller: "the new directory." }, ) + self.helper.dir_migration = False + return # set the cached serve dir self.helper.servers_dir = new_server_path diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index d35cf8dd..3bb3336a 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -42,10 +42,10 @@ scheduler_intervals = { class TasksManager: controller: Controller - def __init__(self, helper, controller): + def __init__(self, helper, controller, file_helper): self.helper: Helpers = helper self.controller: Controller = controller - self.tornado: Webserver = Webserver(helper, controller, self) + self.tornado: Webserver = Webserver(helper, controller, self, file_helper) try: self.tz = get_localzone() except ZoneInfoNotFoundError as e: @@ -727,12 +727,21 @@ class TasksManager: def check_for_updates(self): logger.info("Checking for Crafty updates...") self.helper.update_available = self.helper.check_remote_version() + remote = self.helper.update_available if self.helper.update_available: logger.info(f"Found new version {self.helper.update_available}") else: logger.info( "No updates found! You are on the most up to date Crafty version." ) + if self.helper.update_available: + self.helper.update_available = { + "id": str(remote), + "title": f"{remote} Update Available", + "date": "", + "desc": "Release notes are available by clicking this notification.", + "link": "https://gitlab.com/crafty-controller/crafty-4/-/releases", + } logger.info("Refreshing Gravatar PFPs...") for user in HelperUsers.get_all_users(): if user.email: diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py deleted file mode 100644 index 451f16d0..00000000 --- a/app/classes/web/ajax_handler.py +++ /dev/null @@ -1,700 +0,0 @@ -import os -import html -import pathlib -import re -import logging -import time -import urllib.parse -import nh3 -import tornado.web -import tornado.escape - -from app.classes.models.server_permissions import EnumPermissionsServer -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.server import ServerOutBuf -from app.classes.web.base_handler import BaseHandler -from app.classes.shared.websocket_manager import WebSocketManager - -logger = logging.getLogger(__name__) - - -class AjaxHandler(BaseHandler): - def render_page(self, template, page_data): - self.render( - template, - data=page_data, - translate=self.translator.translate, - ) - - @tornado.web.authenticated - def get(self, page): - _, _, exec_user = self.current_user - error = nh3.clean(self.get_argument("error", "WTF Error!")) - - template = "panel/denied.html" - - page_data = {"user_data": exec_user, "error": error} - - if page == "error": - template = "public/error.html" - self.render_page(template, page_data) - - elif page == "server_log": - server_id = self.get_argument("id", None) - full_log = self.get_argument("full", False) - - if server_id is None: - logger.warning("Server ID not found in server_log ajax call") - self.redirect("/panel/error?error=Server ID Not Found") - return - - server_id = nh3.clean(server_id) - - server_data = self.controller.servers.get_server_data_by_id(server_id) - if not server_data: - logger.warning("Server Data not found in server_log ajax call") - self.redirect("/panel/error?error=Server ID Not Found") - return - - if not server_data["log_path"]: - logger.warning( - f"Log path not found in server_log ajax call ({server_id})" - ) - - if full_log: - log_lines = self.helper.get_setting("max_log_lines") - data = Helpers.tail_file( - # If the log path is absolute it returns it as is - # If it is relative it joins the paths below like normal - pathlib.Path(server_data["path"], server_data["log_path"]), - log_lines, - ) - else: - data = ServerOutBuf.lines.get(server_id, []) - - for line in data: - try: - 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(d.encode("utf-8")) - - except Exception as e: - logger.warning(f"Skipping Log Line due to error: {e}") - - elif page == "announcements": - data = Helpers.get_announcements() - page_data["notify_data"] = data - self.render_page("ajax/notify.html", page_data) - - elif page == "get_zip_tree": - path = self.get_argument("path", None) - - self.write( - Helpers.get_os_understandable_path(path) - + "\n" - + Helpers.generate_zip_tree(path) - ) - self.finish() - - elif page == "get_zip_dir": - path = self.get_argument("path", None) - - self.write( - Helpers.get_os_understandable_path(path) - + "\n" - + Helpers.generate_zip_dir(path) - ) - self.finish() - - elif page == "get_backup_tree": - server_id = self.get_argument("id", None) - folder = self.get_argument("path", None) - - output = "" - - dir_list = [] - unsorted_files = [] - file_list = os.listdir(folder) - for item in file_list: - if os.path.isdir(os.path.join(folder, item)): - dir_list.append(item) - else: - unsorted_files.append(item) - file_list = sorted(dir_list, key=str.casefold) + sorted( - unsorted_files, key=str.casefold - ) - output += f"""