diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 057ffd5e..8a7c020d 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -5,6 +5,7 @@ from prometheus_client import CollectorRegistry, Gauge from app.classes.models.management import HelpersManagement, HelpersWebhooks from app.classes.models.servers import HelperServers +from app.classes.shared.helpers import Helpers logger = logging.getLogger(__name__) @@ -189,30 +190,34 @@ class ManagementController: def get_backups_by_server(server_id, model=False): return HelpersManagement.get_backups_by_server(server_id, model) + @staticmethod + def delete_backup_config(backup_id): + HelpersManagement.remove_backup_config(backup_id) + @staticmethod def update_backup_config(backup_id, updates): + if "backup_location" in updates: + updates["backup_location"] = Helpers.wtol_path(updates["backup_location"]) return HelpersManagement.update_backup_config(backup_id, updates) - def add_backup_config( - self, - server_id: int, - backup_path: str = "", - max_backups: int = 0, - excluded_dirs: list = None, - compress: bool = False, - shutdown: bool = False, - before: str = "", - after: str = "", - ): + def add_backup_config(self, data): + if "backup_location" in data: + data["backup_location"] = Helpers.wtol_path(data["backup_location"]) + return self.management_helper.add_backup_config(data) + + def add_default_backup_config(self, server_id, backup_path): return self.management_helper.add_backup_config( - server_id, - backup_path, - max_backups, - excluded_dirs, - compress, - shutdown, - before, - after, + { + "backup_name": "afdgahah", + "backup_location": Helpers.wtol_path(backup_path), + "max_backups": 0, + "before": "", + "after": "", + "compress": False, + "shutdown": False, + "server_id": server_id, + "excluded_dirs": [], + } ) @staticmethod diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 495d3e38..75c93432 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -371,30 +371,10 @@ class HelpersManagement: return data @staticmethod - def remove_backup_config(server_id): - Backups.delete().where(Backups.server_id == server_id).execute() + def remove_backup_config(backup_id): + Backups.delete().where(Backups.backup_id == backup_id).execute() - def add_backup_config( - self, - server_id: str, - backup_path: str = "", - max_backups: int = 0, - excluded_dirs: list = None, - compress: bool = False, - shutdown: bool = False, - before: str = "", - after: str = "", - ): - conf = { - "excluded_dirs": excluded_dirs, - "max_backups": max_backups, - "server_id": server_id, - "backup_location": backup_path, - "compress": compress, - "shutdown": shutdown, - "before": before, - "after": after, - } + def add_backup_config(self, conf): Backups.create(**conf) logger.debug("Creating new backup record.") diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index d693d96b..46762c1f 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -561,7 +561,7 @@ class Controller: server_host=monitoring_host, server_type=monitoring_type, ) - self.management.add_backup_config( + self.management.add_default_backup_config( new_server_id, backup_path, ) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 4461f0d3..04713878 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1320,6 +1320,70 @@ class PanelHandler(BaseHandler): return template = "panel/server_backup_edit.html" + elif page == "add_backup": + server_id = self.get_argument("id", None) + backup_id = self.get_argument("backup_id", None) + page_data["active_link"] = "backups" + page_data["permissions"] = { + "Commands": EnumPermissionsServer.COMMANDS, + "Terminal": EnumPermissionsServer.TERMINAL, + "Logs": EnumPermissionsServer.LOGS, + "Schedule": EnumPermissionsServer.SCHEDULE, + "Backup": EnumPermissionsServer.BACKUP, + "Files": EnumPermissionsServer.FILES, + "Config": EnumPermissionsServer.CONFIG, + "Players": EnumPermissionsServer.PLAYERS, + } + if not self.failed_server: + server_obj = self.controller.servers.get_server_instance_by_id( + server_id + ) + page_data["backup_failed"] = server_obj.last_backup_status() + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) + ) + server_info = self.controller.servers.get_server_data_by_id(server_id) + page_data["backup_config"] = { + "excluded_dirs": [], + "max_backups": 0, + "server_id": server_id, + "backup_location": os.path.join(self.helper.backup_path, server_id), + "compress": False, + "shutdown": False, + "before": "", + "after": "", + } + page_data["backing_up"] = False + page_data["backup_stats"] = ( + self.controller.servers.get_server_instance_by_id( + server_id + ).send_backup_status() + ) + self.controller.servers.refresh_server_settings(server_id) + + page_data["backup_list"] = [] + page_data["backup_path"] = Helpers.wtol_path( + page_data["backup_config"]["backup_location"] + ) + page_data["server_data"] = self.controller.servers.get_server_data_by_id( + server_id + ) + page_data["server_stats"] = self.controller.servers.get_server_stats_by_id( + server_id + ) + page_data["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) + page_data["exclusions"] = [] + + if not EnumPermissionsServer.BACKUP in page_data["user_permissions"]: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access To Schedules") + return + template = "panel/server_backup_edit.html" + elif page == "edit_user": user_id = self.get_argument("id", None) role_servers = self.controller.servers.get_authorized_servers(user_id) diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 78df3ed5..e6646c9e 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -38,6 +38,7 @@ from app.classes.web.routes.api.servers.server.backups.index import ( ) from app.classes.web.routes.api.servers.server.backups.backup.index import ( ApiServersServerBackupsBackupIndexHandler, + ApiServersServerBackupsBackupFilesIndexHandler, ) from app.classes.web.routes.api.servers.server.files import ( ApiServersServerFilesIndexHandler, @@ -223,7 +224,12 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/servers/([a-z0-9-]+)/files/?", + r"/api/v2/servers/([a-z0-9-]+)/backups/backup/([a-z0-9-]+)/files/?", + ApiServersServerBackupsBackupFilesIndexHandler, + handler_args, + ), + ( + r"/api/v2/servers/([a-z0-9-]+)/files(?:/([a-zA-Z0-9-]+))?/?", ApiServersServerFilesIndexHandler, handler_args, ), diff --git a/app/classes/web/routes/api/servers/server/backups/backup/index.py b/app/classes/web/routes/api/servers/server/backups/backup/index.py index fba7bad8..9296ad9d 100644 --- a/app/classes/web/routes/api/servers/server/backups/backup/index.py +++ b/app/classes/web/routes/api/servers/server/backups/backup/index.py @@ -22,13 +22,14 @@ BACKUP_SCHEMA = { BACKUP_PATCH_SCHEMA = { "type": "object", "properties": { + "backup_name": {"type": "string", "minLength": 3}, "backup_location": {"type": "string", "minLength": 1}, "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, "before": {"type": "string"}, "after": {"type": "string"}, - "exclusions": {"type": "array"}, + "excluded_dirs": {"type": "array"}, }, "additionalProperties": False, "minProperties": 1, @@ -37,12 +38,13 @@ BACKUP_PATCH_SCHEMA = { BASIC_BACKUP_PATCH_SCHEMA = { "type": "object", "properties": { + "backup_name": {"type": "string", "minLength": 3}, "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, "before": {"type": "string"}, "after": {"type": "string"}, - "exclusions": {"type": "array"}, + "excluded_dirs": {"type": "array"}, }, "additionalProperties": False, "minProperties": 1, @@ -115,38 +117,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): }, ) - try: - data = json.loads(self.request.body) - except json.decoder.JSONDecodeError as e: - return self.finish_json( - 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} - ) - try: - validate(data, BACKUP_SCHEMA) - except ValidationError as e: - return self.finish_json( - 400, - { - "status": "error", - "error": "INVALID_JSON_SCHEMA", - "error_data": str(e), - }, - ) - - try: - FileHelpers.del_file( - os.path.join(backup_conf["backup_location"], data["filename"]) - ) - except Exception as e: - return self.finish_json( - 400, {"status": "error", "error": f"DELETE FAILED with error {e}"} - ) self.controller.management.add_to_audit_log( auth_data[4]["user_id"], - f"Edited server {server_id}: removed backup {data['filename']}", + f"Edited server {server_id}: removed backup config" + f" {backup_conf['backup_name']}", server_id, self.get_remote_ip(), ) + self.controller.management.delete_backup_config(backup_id) return self.finish_json(200, {"status": "ok"}) @@ -275,10 +253,6 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): self.controller.management.add_backup_config( new_server_id, new_server_obj.backup_path, - backup_config["max_backups"], - excluded_dirs, - backup_config["compress"], - backup_config["shutdown"], ) # remove old server's tasks try: @@ -362,6 +336,74 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): "error_data": "Authorization Error", }, ) - self.controller.management.update_backup_config(backup_id, data) return self.finish_json(200, {"status": "ok"}) + + +class ApiServersServerBackupsBackupFilesIndexHandler(BaseApiHandler): + def delete(self, server_id: str, backup_id: str): + auth_data = self.authenticate_user() + backup_conf = self.controller.management.get_backup_config(backup_id) + if backup_conf["server_id"]["server_id"] != server_id: + return self.finish_json( + 400, + { + "status": "error", + "error": "ID_MISMATCH", + "error_data": "Server ID backup server ID different", + }, + ) + if not auth_data: + return + mask = self.controller.server_perms.get_lowest_api_perm_mask( + self.controller.server_perms.get_user_permissions_mask( + auth_data[4]["user_id"], server_id + ), + auth_data[5], + ) + server_permissions = self.controller.server_perms.get_permissions(mask) + if EnumPermissionsServer.BACKUP not in server_permissions: + # if the user doesn't have Schedule permission, return an error + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT_AUTHORIZED", + "error_data": "Authorization Error", + }, + ) + + try: + data = json.loads(self.request.body) + except json.decoder.JSONDecodeError as e: + return self.finish_json( + 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} + ) + try: + validate(data, BACKUP_SCHEMA) + except ValidationError as e: + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID_JSON_SCHEMA", + "error_data": str(e), + }, + ) + + try: + FileHelpers.del_file( + os.path.join(backup_conf["backup_location"], data["filename"]) + ) + except Exception as e: + return self.finish_json( + 400, {"status": "error", "error": f"DELETE FAILED with error {e}"} + ) + self.controller.management.add_to_audit_log( + auth_data[4]["user_id"], + f"Edited server {server_id}: removed backup {data['filename']}", + server_id, + self.get_remote_ip(), + ) + + return self.finish_json(200, {"status": "ok"}) diff --git a/app/classes/web/routes/api/servers/server/backups/index.py b/app/classes/web/routes/api/servers/server/backups/index.py index 51b69473..de82f98a 100644 --- a/app/classes/web/routes/api/servers/server/backups/index.py +++ b/app/classes/web/routes/api/servers/server/backups/index.py @@ -10,13 +10,14 @@ logger = logging.getLogger(__name__) backup_patch_schema = { "type": "object", "properties": { + "backup_name": {"type": "string", "minLength": 3}, "backup_location": {"type": "string", "minLength": 1}, "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, "before": {"type": "string"}, "after": {"type": "string"}, - "exclusions": {"type": "array"}, + "excluded_dirs": {"type": "array"}, }, "additionalProperties": False, "minProperties": 1, @@ -25,12 +26,13 @@ backup_patch_schema = { basic_backup_patch_schema = { "type": "object", "properties": { + "backup_name": {"type": "string", "minLength": 3}, "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, "before": {"type": "string"}, "after": {"type": "string"}, - "exclusions": {"type": "array"}, + "excluded_dirs": {"type": "array"}, }, "additionalProperties": False, "minProperties": 1, @@ -95,6 +97,8 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): if EnumPermissionsServer.BACKUP not in server_permissions: # if the user doesn't have Schedule permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) - - self.controller.management.update_backup_config(server_id, data) + data["server_id"] = server_id + if not data.get("excluded_dirs", None): + data["excluded_dirs"] = [] + self.controller.management.add_backup_config(data) return self.finish_json(200, {"status": "ok"}) diff --git a/app/classes/web/routes/api/servers/server/files.py b/app/classes/web/routes/api/servers/server/files.py index 2951ff25..6e864a69 100644 --- a/app/classes/web/routes/api/servers/server/files.py +++ b/app/classes/web/routes/api/servers/server/files.py @@ -72,7 +72,7 @@ file_delete_schema = { class ApiServersServerFilesIndexHandler(BaseApiHandler): - def post(self, server_id: str): + def post(self, server_id: str, backup_id=None): auth_data = self.authenticate_user() if not auth_data: return @@ -149,21 +149,35 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler): filename = html.escape(raw_filename) rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) - if str(dpath) in self.controller.management.get_excluded_backup_dirs( - server_id - ): - if os.path.isdir(rel): - return_json[filename] = { - "path": dpath, - "dir": True, - "excluded": True, - } + if backup_id: + if str( + dpath + ) in self.controller.management.get_excluded_backup_dirs(backup_id): + if os.path.isdir(rel): + return_json[filename] = { + "path": dpath, + "dir": True, + "excluded": True, + } + else: + return_json[filename] = { + "path": dpath, + "dir": False, + "excluded": True, + } else: - return_json[filename] = { - "path": dpath, - "dir": False, - "excluded": True, - } + if os.path.isdir(rel): + return_json[filename] = { + "path": dpath, + "dir": True, + "excluded": False, + } + else: + return_json[filename] = { + "path": dpath, + "dir": False, + "excluded": False, + } else: if os.path.isdir(rel): return_json[filename] = { diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 2a34902c..ee5dd674 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -247,15 +247,14 @@ } return; } - async function del_backup(filename, id) { + async function del_backup(backup_id) { const token = getCookie("_xsrf") - let contents = JSON.stringify({ "filename": filename }) - let res = await fetch(`/api/v2/servers/${id}/backups/backup/`, { + let res = await fetch(`/api/v2/servers/${server_id}/backups/backup/${backup_id}`, { method: 'DELETE', headers: { 'token': token, }, - body: contents + body: {} }); let responseData = await res.json(); if (responseData.status === "ok") { @@ -268,90 +267,8 @@ } } - async function restore_backup(filename, id) { - const token = getCookie("_xsrf") - let contents = JSON.stringify({ "filename": filename }) - var dialog = bootbox.dialog({ - message: " {{ translate('serverBackups', 'restoring', data['lang']) }}", - closeButton: false - }); - let res = await fetch(`/api/v2/servers/${id}/backups/backup/`, { - method: 'POST', - headers: { - 'token': token, - }, - body: contents - }); - let responseData = await res.json(); - if (responseData.status === "ok") { - window.location.href = "/panel/dashboard"; - } else { - bootbox.alert({ - "title": responseData.status, - "message": responseData.error - }) - } - } - - $("#before-check").on("click", function () { - if ($("#before-check:checked").val()) { - $("#backup_before").css("display", "inline-block"); - } else { - $("#backup_before").css("display", "none"); - $("#backup_before").val(""); - } - }); - $("#after-check").on("click", function () { - if ($("#after-check:checked").val()) { - $("#backup_after").css("display", "inline-block"); - } else { - $("#backup_after").css("display", "none"); - $("#backup_after").val(""); - } - }); - - function replacer(key, value) { - if (key != "backup_before" && key != "backup_after") { - if (typeof value == "boolean" || key === "executable_update_url") { - return value - } else { - return (isNaN(value) ? value : +value); - } - } else { - return value; - } - } - $(document).ready(function () { - - try { - if ($('#backup_path').val() == '') { - console.log('true') - try { - document.getElementById('backup_now_button').disabled = true; - } catch { - - } - } else { - document.getElementById('backup_now_button').disabled = false; - } - } catch { - try { - document.getElementById('backup_now_button').disabled = false; - } catch { - - } - } console.log("ready!"); - $("#backup_config_box").hide(); - $("#backup_save_note").hide(); - - $("#show_config").click(function () { - $("#backup_config_box").toggle(); - $('#backup_button').hide(); - $('#backup_save_note').show(); - $('#backup_data').hide(); - }); $('#backup_table').DataTable({ "order": [[1, "desc"]], @@ -365,6 +282,7 @@ }); $(".del_button").click(function () { + let backup = $(this).data('backup'); var file_to_del = $(this).data("file"); var backup_path = $(this).data('backup_path'); @@ -384,8 +302,8 @@ callback: function (result) { console.log(result); if (result == true) { - var full_path = backup_path + '/' + file_to_del; - del_backup(file_to_del, server_id); + + del_backup(backup); } } }); diff --git a/app/frontend/templates/panel/server_backup_edit.html b/app/frontend/templates/panel/server_backup_edit.html index 1ec54d48..e7e0cfe0 100644 --- a/app/frontend/templates/panel/server_backup_edit.html +++ b/app/frontend/templates/panel/server_backup_edit.html @@ -63,6 +63,16 @@ {% end %}
+ + {% if data["backup_config"].get("backup_id", None) %} + + {% else %} + + {% end %} +
+
{% if data['super_user'] %}