From e59b62402534e634db5fd02b6d06178d96c441ea Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 8 Feb 2024 20:25:32 -0500 Subject: [PATCH 01/68] Move backup path from servers to backup Add uuid field to backups --- app/classes/controllers/servers_controller.py | 2 -- app/classes/models/management.py | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 86e17802..a61c563b 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -48,7 +48,6 @@ class ServersController(metaclass=Singleton): name: str, server_uuid: str, server_dir: str, - backup_path: str, server_command: str, server_file: str, server_log_file: str, @@ -83,7 +82,6 @@ class ServersController(metaclass=Singleton): name, server_uuid, server_dir, - backup_path, server_command, server_file, server_log_file, diff --git a/app/classes/models/management.py b/app/classes/models/management.py index e86e3209..de5e3561 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -1,3 +1,4 @@ +import uuid import logging import datetime from peewee import ( @@ -9,6 +10,7 @@ from peewee import ( TextField, AutoField, BooleanField, + UUIDField, ) from playhouse.shortcuts import model_to_dict @@ -120,6 +122,9 @@ class Schedules(BaseModel): # Backups Class # ********************************************************************************** class Backups(BaseModel): + backup_id = UUIDField(primary_key=True, default=uuid.uuid4) + backup_name = CharField(default="New Backup") + backup_location = CharField(default="") excluded_dirs = CharField(null=True) max_backups = IntegerField() server_id = ForeignKeyField(Servers, backref="backups_server") From 548a439f14972a8a2db11953412add69df5b16e8 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 8 Mar 2024 22:06:33 -0500 Subject: [PATCH 02/68] Initial commit for backup migration. Kind of broken :/ --- app/classes/models/servers.py | 4 - app/migrations/20240308_multi-backup.py | 105 ++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 app/migrations/20240308_multi-backup.py diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 13d9096a..e5d85c69 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -26,7 +26,6 @@ class Servers(BaseModel): created = DateTimeField(default=datetime.datetime.now) server_name = CharField(default="Server", index=True) path = CharField(default="") - backup_path = CharField(default="") executable = CharField(default="") log_path = CharField(default="") execution_command = CharField(default="") @@ -65,7 +64,6 @@ class HelperServers: server_id: str, name: str, server_dir: str, - backup_path: str, server_command: str, server_file: str, server_log_file: str, @@ -81,7 +79,6 @@ class HelperServers: name: The name of the server server_uuid: This is the UUID of the server server_dir: The directory where the server is located - backup_path: The path to the backup folder server_command: The command to start the server server_file: The name of the server file server_log_file: The path to the server log file @@ -111,7 +108,6 @@ class HelperServers: server_port=server_port, server_ip=server_host, stop_command=server_stop, - backup_path=backup_path, type=server_type, created_by=created_by, ).server_id diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py new file mode 100644 index 00000000..1c5694e8 --- /dev/null +++ b/app/migrations/20240308_multi-backup.py @@ -0,0 +1,105 @@ +import datetime +import uuid +import peewee +import logging + +from app.classes.shared.console import Console +from app.classes.shared.migration import Migrator, MigrateHistory +from app.classes.models.management import Backups + +logger = logging.getLogger(__name__) + + +def migrate(migrator: Migrator, database, **kwargs): + """ + Write your migrations here. + """ + print("pee pee") + db = database + + migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4)) + migrator.add_columns("backups", backup_name=peewee.CharField(default="Default")) + migrator.add_columns("backups", backup_location=peewee.CharField(default="")) + + class Servers(peewee.Model): + server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4())) + created = peewee.DateTimeField(default=datetime.datetime.now) + server_name = peewee.CharField(default="Server", index=True) + path = peewee.CharField(default="") + backup_path = peewee.CharField(default="") + executable = peewee.CharField(default="") + log_path = peewee.CharField(default="") + execution_command = peewee.CharField(default="") + auto_start = peewee.BooleanField(default=0) + auto_start_delay = peewee.IntegerField(default=10) + crash_detection = peewee.BooleanField(default=0) + stop_command = peewee.CharField(default="stop") + executable_update_url = peewee.CharField(default="") + server_ip = peewee.CharField(default="127.0.0.1") + server_port = peewee.IntegerField(default=25565) + logs_delete_after = peewee.IntegerField(default=0) + type = peewee.CharField(default="minecraft-java") + show_status = peewee.BooleanField(default=1) + created_by = peewee.IntegerField(default=-100) + shutdown_timeout = peewee.IntegerField(default=60) + ignored_exits = peewee.CharField(default="0") + + class Meta: + table_name = "servers" + database = db + + class NewBackups(peewee.Model): + backup_id = peewee.UUIDField(primary_key=True, default=uuid.uuid4) + backup_name = peewee.CharField(default="New Backup") + backup_location = peewee.CharField(default="") + excluded_dirs = peewee.CharField(null=True) + max_backups = peewee.IntegerField() + server_id = peewee.ForeignKeyField(Servers, backref="backups_server") + compress = peewee.BooleanField(default=False) + shutdown = peewee.BooleanField(default=False) + before = peewee.CharField(default="") + after = peewee.CharField(default="") + + class Meta: + table_name = "new_backups" + database = db + + migrator.create_table(NewBackups) + + migrator.run() + + # Copy data from the existing backups table to the new one + for backup in Backups.select(): + print(backup) + # Fetch the related server entry from the Servers table + server = Servers.get(Servers.server_id == backup.server_id) + + # Create a new backup entry with data from the old backup entry and related server + NewBackups.create( + backup_name="Default", + backup_location=server.backup_path, # Set backup_location equal to backup_path + excluded_dirs=backup.excluded_dirs, + max_backups=backup.max_backups, + server_id=server.server_id, + compress=backup.compress, + shutdown=backup.shutdown, + before=backup.before, + after=backup.after, + ) + + # Drop the existing backups table + migrator.drop_table("backups") + + # Rename the new table to backups + migrator.rename_table("new_backups", "backups") + migrator.drop_columns("servers", ["backup_path"]) + + +def rollback(migrator: Migrator, database, **kwargs): + """ + Write your rollback migrations here. + """ + db = database + + migrator.drop_columns("backups", ["name", "backup_id", "backup_location"]) + migrator.add_columns("servers", backup_path=peewee.CharField(default="")) From 1a8d351fbd4e2480c38963b4b8bf0e0497145368 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 8 Mar 2024 22:45:08 -0500 Subject: [PATCH 03/68] Remove print statements --- app/classes/models/users.py | 1 - app/migrations/20240308_multi-backup.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/classes/models/users.py b/app/classes/models/users.py index e44d06fb..1963bf3b 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -119,7 +119,6 @@ class HelperUsers: @staticmethod def get_user_total(): count = Users.select().where(Users.username != "system").count() - print(count) return count @staticmethod diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index 1c5694e8..85106e16 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -14,7 +14,6 @@ def migrate(migrator: Migrator, database, **kwargs): """ Write your migrations here. """ - print("pee pee") db = database migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4)) @@ -70,7 +69,6 @@ def migrate(migrator: Migrator, database, **kwargs): # Copy data from the existing backups table to the new one for backup in Backups.select(): - print(backup) # Fetch the related server entry from the Servers table server = Servers.get(Servers.server_id == backup.server_id) From eec943211835bcf1a7d1ccfebf3fcc65b32f9ce2 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 8 Mar 2024 23:22:54 -0500 Subject: [PATCH 04/68] Update migration Add backup return function --- .../controllers/management_controller.py | 4 +++ app/classes/models/management.py | 26 ++++++++++++++++++- app/migrations/20240308_multi-backup.py | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 7085b503..4a3f0aef 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -181,6 +181,10 @@ class ManagementController: def get_backup_config(server_id): return HelpersManagement.get_backup_config(server_id) + @staticmethod + def get_backups_by_server(server_id, model=False): + return HelpersManagement.get_backups_by_server(server_id, model) + def set_backup_config( self, server_id: int, diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 6bb94e28..52ea9e1e 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -132,6 +132,7 @@ class Backups(BaseModel): shutdown = BooleanField(default=False) before = CharField(default="") after = CharField(default="") + enabled = BooleanField(default=True) class Meta: table_name = "backups" @@ -390,7 +391,7 @@ class HelpersManagement: Backups.select().where(Backups.server_id == server_id).join(Servers)[0] ) conf = { - "backup_path": row.server_id.backup_path, + "backup_path": row.backup_location, "excluded_dirs": row.excluded_dirs, "max_backups": row.max_backups, "server_id": row.server_id_id, @@ -412,6 +413,29 @@ class HelpersManagement: } return conf + @staticmethod + def get_backups_by_server(server_id, model): + if not model: + data = {} + for backup in ( + Backups.select().where(Backups.server_id == server_id).execute() + ): + data[str(backup.backup_id)] = { + "backup_id": backup.backup_id, + "backup_name": backup.backup_name, + "backup_path": backup.backup_location, + "excluded_dirs": backup.excluded_dirs, + "max_backups": backup.max_backups, + "server_id": backup.server_id_id, + "compress": backup.compress, + "shutdown": backup.shutdown, + "before": backup.before, + "after": backup.after, + } + else: + data = Backups.select().where(Backups.server_id == server_id).execute() + return data + @staticmethod def remove_backup_config(server_id): Backups.delete().where(Backups.server_id == server_id).execute() diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index 85106e16..49c7643c 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -19,6 +19,7 @@ def migrate(migrator: Migrator, database, **kwargs): migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4)) migrator.add_columns("backups", backup_name=peewee.CharField(default="Default")) migrator.add_columns("backups", backup_location=peewee.CharField(default="")) + migrator.add_columns("backups", enabled=peewee.BooleanField(default=True)) class Servers(peewee.Model): server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4())) @@ -58,6 +59,7 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown = peewee.BooleanField(default=False) before = peewee.CharField(default="") after = peewee.CharField(default="") + enabled = peewee.BooleanField(default=True) class Meta: table_name = "new_backups" @@ -83,6 +85,7 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown=backup.shutdown, before=backup.before, after=backup.after, + enabled=True, ) # Drop the existing backups table From 99ccaa925fe339302dd4f41686b0c5fa689cfbec Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 8 Mar 2024 23:23:36 -0500 Subject: [PATCH 05/68] Add backups to page --- app/classes/web/panel_handler.py | 9 +- app/frontend/static/assets/css/crafty.css | 10 ++ .../templates/panel/server_backup.html | 108 +++++++++++++++++- app/translations/en_EN.json | 8 +- 4 files changed, 130 insertions(+), 5 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d61e3c0e..5532fdf5 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -680,6 +680,11 @@ class PanelHandler(BaseHandler): page_data["backup_config"] = ( self.controller.management.get_backup_config(server_id) ) + page_data["backups"] = self.controller.management.get_backups_by_server( + server_id, model=True + ) + for backup in page_data["backups"]: + print(backup) exclusions = [] page_data["exclusions"] = ( self.controller.management.get_excluded_backup_dirs(server_id) @@ -706,7 +711,9 @@ class PanelHandler(BaseHandler): page_data["backup_list"] = server.list_backups() except: page_data["backup_list"] = [] - page_data["backup_path"] = Helpers.wtol_path(server_info["backup_path"]) + page_data["backup_path"] = Helpers.wtol_path( + page_data["backup_config"]["backup_path"] + ) if subpage == "metrics": try: diff --git a/app/frontend/static/assets/css/crafty.css b/app/frontend/static/assets/css/crafty.css index 43dd2e6a..8a80f6d8 100644 --- a/app/frontend/static/assets/css/crafty.css +++ b/app/frontend/static/assets/css/crafty.css @@ -12,6 +12,16 @@ nav.sidebar { position: fixed; } +td { + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ +} + +td::-webkit-scrollbar { + display: none; +} @media (min-width: 992px) { nav.sidebar { diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 2a9263ba..7d5216f2 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -39,7 +39,111 @@ {% include "parts/m_server_controls_list.html %} - +
+
+
+
+

{{ translate('backups', 'backups', data['lang']) }}

+ {% if data['user_data']['hints'] %} + + {% end %} + +
+
+ {% if len(data['backups']) == 0 %} +
+ {{ translate('backups', 'no-backup', data['lang']) }} {{ translate('backups', 'newbackup',data['lang']) }}. +
+ {% end %} + {% if len(data['backups']) > 0 %} +
+ + + + + + + + + + + + {% for backup in data['backups'] %} + + + + + + + + {% end %} + +
{{ translate('serverBackups', 'enabled', data['lang']) }}{{ translate('serverBackups', 'name', data['lang']) }} {{ translate('serverBackups', 'storageLocation', data['lang']) }}{{ translate('serverBackups', 'maxBackups', data['lang']) }}{{ translate('serverBackups', 'actions', data['lang']) }}
+ + +

{{backup.backup_name}}

+
+

{{backup.backup_location}}

+
+

{{backup.max_backups}}

+
+ + + +
+
+
+ + + + + + + + + + {% for backup in data['backups'] %} + + + + + + {% end %} + +
{{ translate('backups', 'enabled', + data['lang']) }}Name + {{ translate('backups', 'edit', data['lang']) + }}
+ + +

{{backup.backup_name}}

+
+ + + +
+
+ {% end %} +
+
+
+

@@ -69,7 +173,7 @@ class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }} {% end %}
diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 5c48b873..b0edd92e 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -323,7 +323,11 @@ "shutdown": "Shutdown server for duration of backup", "size": "Size", "storageLocation": "Storage Location", - "storageLocationDesc": "Where do you want to store backups?" + "storageLocationDesc": "Where do you want to store backups?", + "enabled": "Enabled", + "name": "Name", + "storage": "Storage Location", + "action": "Actions" }, "serverConfig": { "bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.", @@ -671,4 +675,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From 1381cf77ef8020aadec970560260738188f930bc Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 9 Mar 2024 12:49:51 -0500 Subject: [PATCH 06/68] Fix most translations --- app/frontend/templates/panel/server_backup.html | 14 +++++++------- app/translations/en_EN.json | 6 +++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 7d5216f2..0ffba3c4 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -43,16 +43,16 @@
-

{{ translate('backups', 'backups', data['lang']) }}

+

{{ translate('serverBackups', 'backups', data['lang']) }}

{% if data['user_data']['hints'] %} {% end %} - +
{% if len(data['backups']) == 0 %}
- {{ translate('backups', 'no-backup', data['lang']) }} {{ translate('backups', 'newbackup',data['lang']) }}. + {{ translate('serverBackups', 'no-backup', data['lang']) }} {{ translate('serverBackups', 'newBackup',data['lang']) }}.
{% end %} {% if len(data['backups']) > 0 %} @@ -91,7 +91,7 @@ - @@ -104,11 +104,11 @@ - - @@ -130,7 +130,7 @@ - diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index b0edd92e..7425208f 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -327,7 +327,11 @@ "enabled": "Enabled", "name": "Name", "storage": "Storage Location", - "action": "Actions" + "actions": "Actions", + "newBackup": "Create New Backup", + "edit": "Edit", + "run": "Run Backup", + "backups": "Server Backups" }, "serverConfig": { "bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.", From a4da773d254bf4f7e4d6c4f0ba36a586e6bd64a9 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 9 Mar 2024 12:49:59 -0500 Subject: [PATCH 07/68] Remove print statements --- app/classes/web/panel_handler.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 5532fdf5..afb23fd2 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -683,8 +683,6 @@ class PanelHandler(BaseHandler): page_data["backups"] = self.controller.management.get_backups_by_server( server_id, model=True ) - for backup in page_data["backups"]: - print(backup) exclusions = [] page_data["exclusions"] = ( self.controller.management.get_excluded_backup_dirs(server_id) From b898595371b0bf5dad2006fb864b54e5975ade2e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 17:30:37 -0400 Subject: [PATCH 08/68] Refactor backup config methods to add/update --- .../controllers/management_controller.py | 12 +++-- app/classes/models/management.py | 52 ++++++++++++------- app/classes/shared/main_controller.py | 5 +- .../servers/server/backups/backup/index.py | 2 +- .../api/servers/server/backups/index.py | 34 ++---------- 5 files changed, 47 insertions(+), 58 deletions(-) diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 4a3f0aef..42df3e11 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -185,18 +185,22 @@ class ManagementController: def get_backups_by_server(server_id, model=False): return HelpersManagement.get_backups_by_server(server_id, model) - def set_backup_config( + @staticmethod + def update_backup_config(backup_id, updates): + return HelpersManagement.update_backup_config(backup_id, updates) + + def add_backup_config( self, server_id: int, - backup_path: str = None, - max_backups: int = None, + backup_path: str = "", + max_backups: int = 0, excluded_dirs: list = None, compress: bool = False, shutdown: bool = False, before: str = "", after: str = "", ): - return self.management_helper.set_backup_config( + return self.management_helper.add_backup_config( server_id, backup_path, max_backups, diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 52ea9e1e..a8c69fa2 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -385,15 +385,14 @@ class HelpersManagement: # Backups Methods # ********************************************************************************** @staticmethod - def get_backup_config(server_id): + def get_backup_config(backup_id): try: - row = ( - Backups.select().where(Backups.server_id == server_id).join(Servers)[0] - ) + row = Backups.select().where(Backups.backup_id == backup_id) conf = { "backup_path": row.backup_location, "excluded_dirs": row.excluded_dirs, "max_backups": row.max_backups, + "backup_location": row.backup_location, "server_id": row.server_id_id, "compress": row.compress, "shutdown": row.shutdown, @@ -401,16 +400,7 @@ class HelpersManagement: "after": row.after, } except IndexError: - conf = { - "backup_path": None, - "excluded_dirs": None, - "max_backups": 0, - "server_id": server_id, - "compress": False, - "shutdown": False, - "before": "", - "after": "", - } + return None return conf @staticmethod @@ -440,7 +430,31 @@ class HelpersManagement: def remove_backup_config(server_id): Backups.delete().where(Backups.server_id == server_id).execute() - def set_backup_config( + 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, + } + Backups.create(**conf) + logger.debug("Creating new backup record.") + + def update_backup_config( self, server_id: int, backup_path: str = None, @@ -503,8 +517,8 @@ class HelpersManagement: logger.debug("Creating new backup record.") @staticmethod - def get_excluded_backup_dirs(server_id: int): - excluded_dirs = HelpersManagement.get_backup_config(server_id)["excluded_dirs"] + def get_excluded_backup_dirs(backup_id: int): + excluded_dirs = HelpersManagement.get_backup_config(backup_id)["excluded_dirs"] if excluded_dirs is not None and excluded_dirs != "": dir_list = excluded_dirs.split(",") else: @@ -516,7 +530,7 @@ class HelpersManagement: if dir_to_add not in dir_list: dir_list.append(dir_to_add) excluded_dirs = ",".join(dir_list) - self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) + self.update_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: logger.debug( f"Not adding {dir_to_add} to excluded directories - " @@ -528,7 +542,7 @@ class HelpersManagement: if dir_to_del in dir_list: dir_list.remove(dir_to_del) excluded_dirs = ",".join(dir_list) - self.set_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) + self.update_backup_config(server_id=server_id, excluded_dirs=excluded_dirs) else: logger.debug( f"Not removing {dir_to_del} from excluded directories - " diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 9c3219ff..9d99e4f8 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -552,7 +552,6 @@ class Controller: name=data["name"], server_uuid=server_fs_uuid, server_dir=new_server_path, - backup_path=backup_path, server_command=server_command, server_file=server_file, server_log_file=log_location, @@ -562,7 +561,7 @@ class Controller: server_host=monitoring_host, server_type=monitoring_type, ) - self.management.set_backup_config( + self.management.add_backup_config( new_server_id, backup_path, ) @@ -905,7 +904,6 @@ class Controller: name: str, server_uuid: str, server_dir: str, - backup_path: str, server_command: str, server_file: str, server_log_file: str, @@ -920,7 +918,6 @@ class Controller: name, server_uuid, server_dir, - backup_path, server_command, server_file, server_log_file, 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 70ceb2b2..5dc301bb 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 @@ -189,7 +189,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): bu_path = os.path.relpath(item_path, loop_backup_path) bu_path = os.path.join(new_server_obj.path, bu_path) excluded_dirs.append(bu_path) - self.controller.management.set_backup_config( + self.controller.management.add_backup_config( new_server_id, new_server_obj.backup_path, backup_config["max_backups"], 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 9e47bcfc..0a95bff0 100644 --- a/app/classes/web/routes/api/servers/server/backups/index.py +++ b/app/classes/web/routes/api/servers/server/backups/index.py @@ -52,7 +52,7 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) self.finish_json(200, self.controller.management.get_backup_config(server_id)) - def patch(self, server_id: str): + def patch(self, backup_id: str): auth_data = self.authenticate_user() if not auth_data: return @@ -78,7 +78,8 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): "error_data": str(e), }, ) - + backup_conf = self.controller.management.get_backup_config(backup_id) + server_id = backup_conf["server_id"] 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"}) @@ -92,32 +93,5 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): # if the user doesn't have Schedule permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) - self.controller.management.set_backup_config( - server_id, - data.get( - "backup_path", - self.controller.management.get_backup_config(server_id)["backup_path"], - ), - data.get( - "max_backups", - self.controller.management.get_backup_config(server_id)["max_backups"], - ), - data.get("exclusions"), - data.get( - "compress", - self.controller.management.get_backup_config(server_id)["compress"], - ), - data.get( - "shutdown", - self.controller.management.get_backup_config(server_id)["shutdown"], - ), - data.get( - "backup_before", - self.controller.management.get_backup_config(server_id)["before"], - ), - data.get( - "backup_after", - self.controller.management.get_backup_config(server_id)["after"], - ), - ) + self.controller.management.update_backup_config(server_id, data) return self.finish_json(200, {"status": "ok"}) From 3bba043cf016a3399ef784e50a5d359de4a4b604 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 18:14:45 -0400 Subject: [PATCH 09/68] Get backup configs --- app/classes/models/management.py | 84 +++----------------------------- 1 file changed, 7 insertions(+), 77 deletions(-) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index a8c69fa2..5a3df8b1 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -386,22 +386,7 @@ class HelpersManagement: # ********************************************************************************** @staticmethod def get_backup_config(backup_id): - try: - row = Backups.select().where(Backups.backup_id == backup_id) - conf = { - "backup_path": row.backup_location, - "excluded_dirs": row.excluded_dirs, - "max_backups": row.max_backups, - "backup_location": row.backup_location, - "server_id": row.server_id_id, - "compress": row.compress, - "shutdown": row.shutdown, - "before": row.before, - "after": row.after, - } - except IndexError: - return None - return conf + return model_to_dict(Backups.get(Backups.backup_id == backup_id)) @staticmethod def get_backups_by_server(server_id, model): @@ -454,67 +439,12 @@ class HelpersManagement: Backups.create(**conf) logger.debug("Creating new backup record.") - def update_backup_config( - self, - server_id: int, - backup_path: str = None, - max_backups: int = None, - excluded_dirs: list = None, - compress: bool = False, - shutdown: bool = False, - before: str = "", - after: str = "", - ): - logger.debug(f"Updating server {server_id} backup config with {locals()}") - if Backups.select().where(Backups.server_id == server_id).exists(): - new_row = False - conf = {} - else: - conf = { - "excluded_dirs": None, - "max_backups": 0, - "server_id": server_id, - "compress": False, - "shutdown": False, - "before": "", - "after": "", - } - new_row = True - if max_backups is not None: - conf["max_backups"] = max_backups - if excluded_dirs is not None: - dirs_to_exclude = ",".join(excluded_dirs) - conf["excluded_dirs"] = dirs_to_exclude - conf["compress"] = compress - conf["shutdown"] = shutdown - conf["before"] = before - conf["after"] = after - if not new_row: - with self.database.atomic(): - if backup_path is not None: - server_rows = ( - Servers.update(backup_path=backup_path) - .where(Servers.server_id == server_id) - .execute() - ) - else: - server_rows = 0 - backup_rows = ( - Backups.update(conf).where(Backups.server_id == server_id).execute() - ) - logger.debug( - f"Updating existing backup record. " - f"{server_rows}+{backup_rows} rows affected" - ) - else: - with self.database.atomic(): - conf["server_id"] = server_id - if backup_path is not None: - Servers.update(backup_path=backup_path).where( - Servers.server_id == server_id - ) - Backups.create(**conf) - logger.debug("Creating new backup record.") + @staticmethod + def update_backup_config(backup_id, data): + if "excluded_dirs" in data: + dirs_to_exclude = ",".join(data["excluded_dirs"]) + data["excluded_dirs"] = dirs_to_exclude + Backups.update(**data).where(Backups.backup_id == backup_id).execute() @staticmethod def get_excluded_backup_dirs(backup_id: int): From f2e00040bd5bb84aa4437004fd37086b32397b77 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 18:15:06 -0400 Subject: [PATCH 10/68] Make backups list page load --- app/classes/web/panel_handler.py | 31 +- .../templates/panel/server_backup.html | 411 +++------ .../templates/panel/server_backup_edit.html | 820 ++++++++++++++++++ 3 files changed, 952 insertions(+), 310 deletions(-) create mode 100644 app/frontend/templates/panel/server_backup_edit.html diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d436a72a..514ea494 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -677,16 +677,10 @@ class PanelHandler(BaseHandler): page_data["java_versions"] = page_java if subpage == "backup": server_info = self.controller.servers.get_server_data_by_id(server_id) - page_data["backup_config"] = ( - self.controller.management.get_backup_config(server_id) - ) + page_data["backups"] = self.controller.management.get_backups_by_server( server_id, model=True ) - exclusions = [] - page_data["exclusions"] = ( - self.controller.management.get_excluded_backup_dirs(server_id) - ) page_data["backing_up"] = ( self.controller.servers.get_server_instance_by_id( server_id @@ -698,20 +692,8 @@ class PanelHandler(BaseHandler): ).send_backup_status() ) # makes it so relative path is the only thing shown - for file in page_data["exclusions"]: - if Helpers.is_os_windows(): - exclusions.append(file.replace(server_info["path"] + "\\", "")) - else: - exclusions.append(file.replace(server_info["path"] + "/", "")) - page_data["exclusions"] = exclusions + self.controller.servers.refresh_server_settings(server_id) - try: - page_data["backup_list"] = server.list_backups() - except: - page_data["backup_list"] = [] - page_data["backup_path"] = Helpers.wtol_path( - page_data["backup_config"]["backup_path"] - ) if subpage == "metrics": try: @@ -1260,6 +1242,15 @@ class PanelHandler(BaseHandler): template = "panel/server_schedule_edit.html" + elif page == "edit_backup": + exclusions = [] + for file in page_data["exclusions"]: + if Helpers.is_os_windows(): + exclusions.append(file.replace(server_info["path"] + "\\", "")) + else: + exclusions.append(file.replace(server_info["path"] + "/", "")) + page_data["exclusions"] = exclusions + 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/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 0ffba3c4..6a8098c4 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -43,35 +43,51 @@
-

{{ translate('serverBackups', 'backups', data['lang']) }}

+

{{ translate('serverBackups', 'backups', + data['lang']) }}

{% if data['user_data']['hints'] %} - + {% end %} - +
{% if len(data['backups']) == 0 %}
- {{ translate('serverBackups', 'no-backup', data['lang']) }} {{ translate('serverBackups', 'newBackup',data['lang']) }}. + {{ translate('serverBackups', 'no-backup', data['lang']) }} {{ + translate('serverBackups', 'newBackup',data['lang']) }}.
{% end %} {% if len(data['backups']) > 0 %}
-
{{ translate('backups', 'enabled', + {{ translate('serverBackups', 'enabled', data['lang']) }} Name {{ translate('backups', 'edit', data['lang']) + {{ translate('serverBackups', 'edit', data['lang']) }}
+
- - - - - + + + + + {% for backup in data['backups'] %} @@ -85,13 +101,17 @@

{{backup.max_backups}}

@@ -101,7 +121,8 @@
{{ translate('serverBackups', 'enabled', data['lang']) }}{{ translate('serverBackups', 'name', data['lang']) }} {{ translate('serverBackups', 'storageLocation', data['lang']) }}{{ translate('serverBackups', 'maxBackups', data['lang']) }}{{ translate('serverBackups', 'actions', data['lang']) }}{{ translate('serverBackups', 'enabled', + data['lang']) }}{{ translate('serverBackups', 'name', + data['lang']) }} {{ translate('serverBackups', + 'storageLocation', data['lang']) }}{{ translate('serverBackups', + 'maxBackups', data['lang']) }}{{ translate('serverBackups', 'actions', + data['lang']) }}
- - -
- +
@@ -124,13 +148,17 @@

{{backup.backup_name}}

@@ -144,207 +172,6 @@ -
-
-
-
- {% if data['backing_up'] %} -
-
{{ - data['backup_stats']['percent'] }}%
-
-

Backing up {{data['server_stats']['world_size']}}

- {% end %} - -
- {% if not data['backing_up'] %} -
- -
- {% end %} -
-
- {% if data['super_user'] %} - - - {% end %} -
- -
- - -
-
- - {% if data['backup_config']['compress'] %} - {{ translate('serverBackups', 'compress', data['lang']) }} - {% else %} - {{ - translate('serverBackups', 'compress', data['lang']) }} - {% end %} -
-
- - {% if data['backup_config']['shutdown'] %} - {{ translate('serverBackups', 'shutdown', data['lang']) }} - {% else %} - {{ - translate('serverBackups', 'shutdown', data['lang']) }} - {% end %} -
-
- - {% if data['backup_config']['before'] %} - {{ - translate('serverBackups', 'before', data['lang']) }} -
- - {% else %} - {{ - translate('serverBackups', 'before', data['lang']) }} -
- - {% end %} -
-
- - {% if data['backup_config']['after'] %} - {{ - translate('serverBackups', 'after', data['lang']) }} -
- - {% else %} - {{ - translate('serverBackups', 'after', data['lang']) }} -
- - {% end %} -
-
- -
- -
- - - - - -
- -
-
- -
{{ translate('serverBackups', 'enabled', @@ -116,7 +137,10 @@ {% for backup in data['backups'] %}
- - -
-

{{ translate('serverBackups', 'currentBackups', data['lang']) }}

- - - - - - - - - {% for backup in data['backup_list'] %} - - - - - - {% end %} - - -
{{ translate('serverBackups', 'options', data['lang']) }}{{ translate('serverBackups', 'path', data['lang']) }}{{ translate('serverBackups', 'size', data['lang']) }}
- - - {{ translate('serverBackups', 'download', data['lang']) }} - -
-
- - -
{{ backup['path'] }}{{ backup['size'] }}
- -
-
-
-
-
-
-
-

{{ translate('serverBackups', 'excludedBackups', - data['lang']) }}

-
-
-
    - {% for item in data['exclusions'] %} -
  • {{item}}
  • -
    - {% end %} -
-
@@ -414,15 +241,15 @@ async function backup_started() { const token = getCookie("_xsrf") let res = await fetch(`/api/v2/servers/${server_id}/action/backup_server`, { - method: 'POST', - headers: { - 'X-XSRFToken': token - } - }); - let responseData = await res.json(); - if (responseData.status === "ok") { - console.log(responseData); - $("#backup_button").html(`
+ method: 'POST', + headers: { + 'X-XSRFToken': token + } + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + console.log(responseData); + $("#backup_button").html(`
{{ @@ -430,18 +257,18 @@

Backing up {{data['server_stats']['world_size']}}

`); - } else { + } else { - bootbox.alert({ - title: responseData.status, - message: responseData.error - }); - } + bootbox.alert({ + title: responseData.status, + message: responseData.error + }); + } return; } async function del_backup(filename, id) { const token = getCookie("_xsrf") - let contents = JSON.stringify({"filename": filename}) + let contents = JSON.stringify({ "filename": filename }) let res = await fetch(`/api/v2/servers/${id}/backups/backup/`, { method: 'DELETE', headers: { @@ -452,15 +279,17 @@ let responseData = await res.json(); if (responseData.status === "ok") { window.location.reload(); - }else{ - bootbox.alert({"title": responseData.status, - "message": responseData.error}) + } else { + bootbox.alert({ + "title": responseData.status, + "message": responseData.error + }) } } async function restore_backup(filename, id) { const token = getCookie("_xsrf") - let contents = JSON.stringify({"filename": filename}) + let contents = JSON.stringify({ "filename": filename }) var dialog = bootbox.dialog({ message: " {{ translate('serverBackups', 'restoring', data['lang']) }}", closeButton: false @@ -475,9 +304,11 @@ let responseData = await res.json(); if (responseData.status === "ok") { window.location.href = "/panel/dashboard"; - }else{ - bootbox.alert({"title": responseData.status, - "message": responseData.error}) + } else { + bootbox.alert({ + "title": responseData.status, + "message": responseData.error + }) } } @@ -529,7 +360,7 @@ $('input.excluded:checkbox:checked').each(function () { excluded.push($(this).val()); }); - if ($("#root_files_button").hasClass("clicked")){ + if ($("#root_files_button").hasClass("clicked")) { formDataObject.exclusions = excluded; } delete formDataObject.root_path @@ -711,54 +542,54 @@ }); } - function getDirView(event){ + function getDirView(event) { let path = event.target.parentElement.getAttribute("data-path"); if (document.getElementById(path).classList.contains('clicked')) { return; - }else{ + } else { getTreeView(path); } } - async function getTreeView(path){ + async function getTreeView(path) { console.log(path) const token = getCookie("_xsrf"); let res = await fetch(`/api/v2/servers/${server_id}/files`, { - method: 'POST', - headers: { - 'X-XSRFToken': token - }, - body: JSON.stringify({"page": "backups", "path": path}), + method: 'POST', + headers: { + 'X-XSRFToken': token + }, + body: JSON.stringify({ "page": "backups", "path": path }), + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + console.log(responseData); + process_tree_response(responseData); + + } else { + + bootbox.alert({ + title: responseData.status, + message: responseData.error }); - let responseData = await res.json(); - if (responseData.status === "ok") { - console.log(responseData); - process_tree_response(responseData); - - } else { - - bootbox.alert({ - title: responseData.status, - message: responseData.error - }); - } + } } function process_tree_response(response) { let path = response.data.root_path.path; let text = `
    `; - Object.entries(response.data).forEach(([key, value]) => { - if (key === "root_path" || key === "db_stats"){ - //continue is not valid in for each. Return acts as a continue. - return; - } + Object.entries(response.data).forEach(([key, value]) => { + if (key === "root_path" || key === "db_stats") { + //continue is not valid in for each. Return acts as a continue. + return; + } let checked = "" let dpath = value.path; let filename = key; - if (value.excluded){ + if (value.excluded) { checked = "checked" } - if (value.dir){ + if (value.dir) { text += `
  • \n
    @@ -768,7 +599,7 @@ ${filename}
  • ` - }else{ + } else { text += `
  • `; - if(response.data.root_path.top){ + if (response.data.root_path.top) { try { - document.getElementById('main-tree-div').innerHTML += text; - document.getElementById('main-tree').parentElement.classList.add("clicked"); - } catch { - document.getElementById('files-tree').innerHTML = text; - } - }else{ + document.getElementById('main-tree-div').innerHTML += text; + document.getElementById('main-tree').parentElement.classList.add("clicked"); + } catch { + document.getElementById('files-tree').innerHTML = text; + } + } else { try { - document.getElementById(path + "span").classList.add('tree-caret-down'); - document.getElementById(path).innerHTML += text; - document.getElementById(path).classList.add("clicked"); - } catch { - console.log("Bad") - } + document.getElementById(path + "span").classList.add('tree-caret-down'); + document.getElementById(path).innerHTML += text; + document.getElementById(path).classList.add("clicked"); + } catch { + console.log("Bad") + } - var toggler = document.getElementById(path + "span"); + var toggler = document.getElementById(path + "span"); - if (toggler.classList.contains('files-tree-title')) { - document.getElementById(path + "span").addEventListener("click", function caretListener() { - document.getElementById(path + "ul").classList.toggle("d-block"); - document.getElementById(path + "span").classList.toggle("tree-caret-down"); - }); - } + if (toggler.classList.contains('files-tree-title')) { + document.getElementById(path + "span").addEventListener("click", function caretListener() { + document.getElementById(path + "ul").classList.toggle("d-block"); + document.getElementById(path + "span").classList.toggle("tree-caret-down"); + }); + } } } diff --git a/app/frontend/templates/panel/server_backup_edit.html b/app/frontend/templates/panel/server_backup_edit.html new file mode 100644 index 00000000..0ffba3c4 --- /dev/null +++ b/app/frontend/templates/panel/server_backup_edit.html @@ -0,0 +1,820 @@ +{% extends ../base.html %} + +{% block meta %} +{% end %} + +{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails', data['lang']) }}{% end %} + +{% block content %} + +
    + + +
    +
    + +
    + +
    + + + {% include "parts/details_stats.html %} + +
    + +
    +
    +
    + + + {% include "parts/server_controls_list.html %} + + + {% include "parts/m_server_controls_list.html %} + +
    +
    +
    +
    +

    {{ translate('serverBackups', 'backups', data['lang']) }}

    + {% if data['user_data']['hints'] %} + + {% end %} + +
    +
    + {% if len(data['backups']) == 0 %} +
    + {{ translate('serverBackups', 'no-backup', data['lang']) }} {{ translate('serverBackups', 'newBackup',data['lang']) }}. +
    + {% end %} + {% if len(data['backups']) > 0 %} +
    + + + + + + + + + + + + {% for backup in data['backups'] %} + + + + + + + + {% end %} + +
    {{ translate('serverBackups', 'enabled', data['lang']) }}{{ translate('serverBackups', 'name', data['lang']) }} {{ translate('serverBackups', 'storageLocation', data['lang']) }}{{ translate('serverBackups', 'maxBackups', data['lang']) }}{{ translate('serverBackups', 'actions', data['lang']) }}
    + + +

    {{backup.backup_name}}

    +
    +

    {{backup.backup_location}}

    +
    +

    {{backup.max_backups}}

    +
    + + + +
    +
    +
    + + + + + + + + + + {% for backup in data['backups'] %} + + + + + + {% end %} + +
    {{ translate('serverBackups', 'enabled', + data['lang']) }}Name + {{ translate('serverBackups', 'edit', data['lang']) + }}
    + + +

    {{backup.backup_name}}

    +
    + + + +
    +
    + {% end %} +
    +
    +
    +
    +
    +
    +
    +
    + {% if data['backing_up'] %} +
    +
    {{ + data['backup_stats']['percent'] }}%
    +
    +

    Backing up {{data['server_stats']['world_size']}}

    + {% end %} + +
    + {% if not data['backing_up'] %} +
    + +
    + {% end %} +
    +
    + {% if data['super_user'] %} + + + {% end %} +
    + +
    + + +
    +
    + + {% if data['backup_config']['compress'] %} + {{ translate('serverBackups', 'compress', data['lang']) }} + {% else %} + {{ + translate('serverBackups', 'compress', data['lang']) }} + {% end %} +
    +
    + + {% if data['backup_config']['shutdown'] %} + {{ translate('serverBackups', 'shutdown', data['lang']) }} + {% else %} + {{ + translate('serverBackups', 'shutdown', data['lang']) }} + {% end %} +
    +
    + + {% if data['backup_config']['before'] %} + {{ + translate('serverBackups', 'before', data['lang']) }} +
    + + {% else %} + {{ + translate('serverBackups', 'before', data['lang']) }} +
    + + {% end %} +
    +
    + + {% if data['backup_config']['after'] %} + {{ + translate('serverBackups', 'after', data['lang']) }} +
    + + {% else %} + {{ + translate('serverBackups', 'after', data['lang']) }} +
    + + {% end %} +
    +
    + +
    + +
    + + + + +
    +
    + +
    +
    + + +

    {{ translate('serverBackups', 'currentBackups', data['lang']) }}

    + + + + + + + + + {% for backup in data['backup_list'] %} + + + + + + {% end %} + + +
    {{ translate('serverBackups', 'options', data['lang']) }}{{ translate('serverBackups', 'path', data['lang']) }}{{ translate('serverBackups', 'size', data['lang']) }}
    + + + {{ translate('serverBackups', 'download', data['lang']) }} + +
    +
    + + +
    {{ backup['path'] }}{{ backup['size'] }}
    + +
    +
    +
    +
    +
    +
    +
    +

    {{ translate('serverBackups', 'excludedBackups', + data['lang']) }}

    +
    +
    +
      + {% for item in data['exclusions'] %} +
    • {{item}}
    • +
      + {% end %} +
    +
    + +
    +
    +
    +
    + + + +
    + + + +{% end %} + +{% block js %} + + +{% end %} \ No newline at end of file From eebf68a37603d6a923f5101db335c9e5f565b581 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 21 Apr 2024 11:26:16 -0400 Subject: [PATCH 11/68] Front end loading of backup edit page --- .../controllers/management_controller.py | 8 +- app/classes/models/management.py | 4 +- app/classes/web/panel_handler.py | 65 +++++ .../templates/panel/server_backup.html | 14 +- .../templates/panel/server_backup_edit.html | 263 ++++++------------ 5 files changed, 161 insertions(+), 193 deletions(-) diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 42df3e11..7fea8a34 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -178,8 +178,8 @@ class ManagementController: # Backups Methods # ********************************************************************************** @staticmethod - def get_backup_config(server_id): - return HelpersManagement.get_backup_config(server_id) + def get_backup_config(backup_id): + return HelpersManagement.get_backup_config(backup_id) @staticmethod def get_backups_by_server(server_id, model=False): @@ -212,8 +212,8 @@ class ManagementController: ) @staticmethod - def get_excluded_backup_dirs(server_id: int): - return HelpersManagement.get_excluded_backup_dirs(server_id) + def get_excluded_backup_dirs(backup_id: int): + return HelpersManagement.get_excluded_backup_dirs(backup_id) def add_excluded_backup_dir(self, server_id: int, dir_to_add: str): self.management_helper.add_excluded_backup_dir(server_id, dir_to_add) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 5a3df8b1..dc754c72 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -455,7 +455,7 @@ class HelpersManagement: dir_list = [] return dir_list - def add_excluded_backup_dir(self, server_id: int, dir_to_add: str): + def add_excluded_backup_dir(self, server_id: str, dir_to_add: str): dir_list = self.get_excluded_backup_dirs(server_id) if dir_to_add not in dir_list: dir_list.append(dir_to_add) @@ -467,7 +467,7 @@ class HelpersManagement: f"already in the excluded directory list for server ID {server_id}" ) - def del_excluded_backup_dir(self, server_id: int, dir_to_del: str): + def del_excluded_backup_dir(self, server_id: str, dir_to_del: str): dir_list = self.get_excluded_backup_dirs(server_id) if dir_to_del in dir_list: dir_list.remove(dir_to_del) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 514ea494..ac5a8b41 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1243,7 +1243,66 @@ class PanelHandler(BaseHandler): template = "panel/server_schedule_edit.html" elif page == "edit_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"] = self.controller.management.get_backup_config( + backup_id + ) + page_data["backups"] = self.controller.management.get_backups_by_server( + server_id, model=True + ) exclusions = [] + page_data["backing_up"] = self.controller.servers.get_server_instance_by_id( + server_id + ).is_backingup + 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) + try: + page_data["backup_list"] = server.list_backups() + except: + 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"] = ( + self.controller.management.get_excluded_backup_dirs(backup_id) + ) + # Make exclusion paths relative for page for file in page_data["exclusions"]: if Helpers.is_os_windows(): exclusions.append(file.replace(server_info["path"] + "\\", "")) @@ -1251,6 +1310,12 @@ class PanelHandler(BaseHandler): exclusions.append(file.replace(server_info["path"] + "/", "")) page_data["exclusions"] = 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/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 6a8098c4..c5c0cdaf 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -85,7 +85,8 @@ - - diff --git a/app/frontend/templates/panel/server_backup_edit.html b/app/frontend/templates/panel/server_backup_edit.html index 0ffba3c4..c68d521f 100644 --- a/app/frontend/templates/panel/server_backup_edit.html +++ b/app/frontend/templates/panel/server_backup_edit.html @@ -39,111 +39,6 @@ {% include "parts/m_server_controls_list.html %} -
    -
    -
    -
    -

    {{ translate('serverBackups', 'backups', data['lang']) }}

    - {% if data['user_data']['hints'] %} - - {% end %} - -
    -
    - {% if len(data['backups']) == 0 %} -
    - {{ translate('serverBackups', 'no-backup', data['lang']) }} {{ translate('serverBackups', 'newBackup',data['lang']) }}. -
    - {% end %} - {% if len(data['backups']) > 0 %} -
    - - - - - - - - - - - - {% for backup in data['backups'] %} - - - - - - - - {% end %} - -
    {{ translate('serverBackups', 'enabled', data['lang']) }}{{ translate('serverBackups', 'name', data['lang']) }} {{ translate('serverBackups', 'storageLocation', data['lang']) }}{{ translate('serverBackups', 'maxBackups', data['lang']) }}{{ translate('serverBackups', 'actions', data['lang']) }}
    - - -

    {{backup.backup_name}}

    -
    -

    {{backup.backup_location}}

    -
    -

    {{backup.max_backups}}

    -
    - - - -
    -
    -
    - - - - - - - - - - {% for backup in data['backups'] %} - - - - - - {% end %} - -
    {{ translate('serverBackups', 'enabled', - data['lang']) }}Name - {{ translate('serverBackups', 'edit', data['lang']) - }}
    - - -

    {{backup.backup_name}}

    -
    - - - -
    -
    - {% end %} -
    -
    -
    -

    @@ -173,7 +68,7 @@ class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc', data['lang']) }} {% end %}
    @@ -272,8 +167,10 @@
@@ -309,7 +206,7 @@

- -

{{backup.backup_name}}

@@ -126,8 +115,6 @@ width="100%" style="table-layout:fixed;"> - {{ translate('serverBackups', 'enabled', - data['lang']) }} Name {{ translate('serverBackups', 'edit', data['lang']) @@ -137,15 +124,6 @@ {% for backup in data['backups'] %} - - -

{{backup.backup_name}}

diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index 49c7643c..85106e16 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -19,7 +19,6 @@ def migrate(migrator: Migrator, database, **kwargs): migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4)) migrator.add_columns("backups", backup_name=peewee.CharField(default="Default")) migrator.add_columns("backups", backup_location=peewee.CharField(default="")) - migrator.add_columns("backups", enabled=peewee.BooleanField(default=True)) class Servers(peewee.Model): server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4())) @@ -59,7 +58,6 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown = peewee.BooleanField(default=False) before = peewee.CharField(default="") after = peewee.CharField(default="") - enabled = peewee.BooleanField(default=True) class Meta: table_name = "new_backups" @@ -85,7 +83,6 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown=backup.shutdown, before=backup.before, after=backup.after, - enabled=True, ) # Drop the existing backups table From f8626633cfcaf2c31304cb18371b768e09506a49 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 13:51:40 -0400 Subject: [PATCH 13/68] Add action ID option to schedules --- .../controllers/management_controller.py | 2 + app/classes/models/management.py | 4 ++ app/classes/shared/tasks.py | 1 + app/migrations/20240308_multi-backup.py | 58 ++++++++++++++++++- 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index fc661e84..6f4da3cb 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -123,6 +123,7 @@ class ManagementController: cron_string="* * * * *", parent=None, delay=0, + action_id=None, ): return HelpersManagement.create_scheduled_task( server_id, @@ -137,6 +138,7 @@ class ManagementController: cron_string, parent, delay, + action_id, ) @staticmethod diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 3390e428..24505471 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -89,6 +89,7 @@ class Schedules(BaseModel): interval_type = CharField() start_time = CharField(null=True) command = CharField(null=True) + action_id = CharField(null=True) name = CharField() one_time = BooleanField(default=False) cron_string = CharField(default="") @@ -114,6 +115,7 @@ class Backups(BaseModel): shutdown = BooleanField(default=False) before = CharField(default="") after = CharField(default="") + enabled = BooleanField(default=True) class Meta: table_name = "backups" @@ -268,6 +270,7 @@ class HelpersManagement: cron_string="* * * * *", parent=None, delay=0, + action_id=None, ): sch_id = Schedules.insert( { @@ -278,6 +281,7 @@ class HelpersManagement: Schedules.interval_type: interval_type, Schedules.start_time: start_time, Schedules.command: command, + Schedules.action_id: action_id, Schedules.name: name, Schedules.one_time: one_time, Schedules.cron_string: cron_string, diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index b9513441..57e3c4d0 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -337,6 +337,7 @@ class TasksManager: job_data["cron_string"], job_data["parent"], job_data["delay"], + job_data.get("action_id", None), ) # Checks to make sure some doofus didn't actually make the newly diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index 85106e16..4aa854b9 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -5,7 +5,7 @@ import logging from app.classes.shared.console import Console from app.classes.shared.migration import Migrator, MigrateHistory -from app.classes.models.management import Backups +from app.classes.models.management import Backups, Schedules logger = logging.getLogger(__name__) @@ -19,6 +19,10 @@ def migrate(migrator: Migrator, database, **kwargs): migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4)) migrator.add_columns("backups", backup_name=peewee.CharField(default="Default")) migrator.add_columns("backups", backup_location=peewee.CharField(default="")) + migrator.add_columns("backups", enabled=peewee.BooleanField(default=True)) + migrator.add_columns( + "schedules", action_id=peewee.CharField(null=True, default=None) + ) class Servers(peewee.Model): server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4())) @@ -58,12 +62,34 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown = peewee.BooleanField(default=False) before = peewee.CharField(default="") after = peewee.CharField(default="") + enabled = peewee.BooleanField(default=True) class Meta: table_name = "new_backups" database = db + class NewSchedules(peewee.Model): + schedule_id = peewee.IntegerField(unique=True, primary_key=True) + server_id = peewee.ForeignKeyField(Servers, backref="schedule_server") + enabled = peewee.BooleanField() + action = peewee.CharField() + interval = peewee.IntegerField() + interval_type = peewee.CharField() + start_time = peewee.CharField(null=True) + command = peewee.CharField(null=True) + action_id = peewee.CharField(null=True) + name = peewee.CharField() + one_time = peewee.BooleanField(default=False) + cron_string = peewee.CharField(default="") + parent = peewee.IntegerField(null=True) + delay = peewee.IntegerField(default=0) + next_run = peewee.CharField(default="") + + class Meta: + table_name = "new_schedules" + migrator.create_table(NewBackups) + migrator.create_table(NewSchedules) migrator.run() @@ -83,6 +109,7 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown=backup.shutdown, before=backup.before, after=backup.after, + enabled=True, ) # Drop the existing backups table @@ -92,6 +119,35 @@ def migrate(migrator: Migrator, database, **kwargs): migrator.rename_table("new_backups", "backups") migrator.drop_columns("servers", ["backup_path"]) + for schedule in Schedules.select(): + action_id = None + if schedule.command == "backup_server": + backup = NewBackups.get(NewBackups.server_id == schedule.server_id) + action_id = backup.backup_id + NewSchedules.create( + schedule_id=schedule.schedule_id, + server_id=schedule.server_id, + enabled=schedule.enabled, + action=schedule.action, + interval=schedule.interval, + interval_type=schedule.interval_type, + start_time=schedule.start_time, + command=schedule.command, + action_id=action_id, + name=schedule.name, + one_time=schedule.one_time, + cron_string=schedule.cron_string, + parent=schedule.parent, + delay=schedule.delay, + next_run=schedule.next_run, + ) + + # Drop the existing backups table + migrator.drop_table("schedules") + + # Rename the new table to backups + migrator.rename_table("new_schedules", "schedules") + def rollback(migrator: Migrator, database, **kwargs): """ From 60d3ee1aa8daca53273a2ebd4afbc57ae4b381d9 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 14:40:14 -0400 Subject: [PATCH 14/68] Working default backup --- .../controllers/management_controller.py | 9 ++- app/classes/shared/server.py | 75 ++++++++----------- app/classes/shared/tasks.py | 2 +- app/classes/web/routes/api/api_handlers.py | 2 +- .../web/routes/api/servers/server/action.py | 5 +- .../templates/panel/server_backup.html | 60 ++------------- 6 files changed, 49 insertions(+), 104 deletions(-) diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 6f4da3cb..057ffd5e 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -75,7 +75,7 @@ class ManagementController: # Commands Methods # ********************************************************************************** - def send_command(self, user_id, server_id, remote_ip, command): + def send_command(self, user_id, server_id, remote_ip, command, action_id=None): server_name = HelperServers.get_server_friendly_name(server_id) # Example: Admin issued command start_server for server Survival @@ -86,7 +86,12 @@ class ManagementController: remote_ip, ) self.queue_command( - {"server_id": server_id, "user_id": user_id, "command": command} + { + "server_id": server_id, + "user_id": user_id, + "command": command, + "action_id": action_id, + } ) def queue_command(self, command_data): diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index a6c98b89..0302b803 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -207,9 +207,6 @@ class ServerInstance: self.server_scheduler.start() self.dir_scheduler.start() self.start_dir_calc_task() - self.backup_thread = threading.Thread( - target=self.backup_server, daemon=True, name=f"backup_{self.name}" - ) self.is_backingup = False # Reset crash and update at initialization self.stats_helper.server_crash_reset() @@ -940,8 +937,7 @@ class ServerInstance: WebSocketManager().broadcast_user(user, "send_start_reload", {}) def restart_threaded_server(self, user_id): - bu_conf = HelpersManagement.get_backup_config(self.server_id) - if self.is_backingup and bu_conf["shutdown"]: + if self.is_backingup: logger.info( "Restart command detected. Supressing - server has" " backup shutdown enabled and server is currently backing up." @@ -1111,12 +1107,12 @@ class ServerInstance: f.write("eula=true") self.run_threaded_server(user_id) - def a_backup_server(self): - if self.settings["backup_path"] == "": - logger.critical("Backup path is None. Canceling Backup!") - return + def a_backup_server(self, backup_id): backup_thread = threading.Thread( - target=self.backup_server, daemon=True, name=f"backup_{self.name}" + target=self.backup_server, + daemon=True, + name=f"backup_{self.name}", + args=[backup_id], ) logger.info( f"Starting Backup Thread for server {self.settings['server_name']}." @@ -1144,7 +1140,7 @@ class ServerInstance: logger.info(f"Backup Thread started for server {self.settings['server_name']}.") @callback - def backup_server(self): + def backup_server(self, backup_id): was_server_running = None logger.info(f"Starting server {self.name} (ID {self.server_id}) backup") server_users = PermissionsServers.get_server_user_list(self.server_id) @@ -1157,7 +1153,12 @@ class ServerInstance: ).format(self.name), ) time.sleep(3) - conf = HelpersManagement.get_backup_config(self.server_id) + conf = HelpersManagement.get_backup_config(backup_id) + backup_location = conf["backup_location"] + if not backup_location: + Console.critical("No backup path found. Canceling") + self.is_backingup = False + return None if conf["before"]: if self.check_running(): logger.debug( @@ -1177,10 +1178,10 @@ class ServerInstance: self.stop_server() was_server_running = True - self.helper.ensure_dir_exists(self.settings["backup_path"]) + self.helper.ensure_dir_exists(backup_location) try: backup_filename = ( - f"{self.settings['backup_path']}/" + f"{backup_location}/" f"{datetime.datetime.now().astimezone(self.tz).strftime('%Y-%m-%d_%H-%M-%S')}" # pylint: disable=line-too-long ) logger.info( @@ -1188,36 +1189,24 @@ class ServerInstance: f" (ID#{self.server_id}, path={self.server_path}) " f"at '{backup_filename}'" ) - excluded_dirs = HelpersManagement.get_excluded_backup_dirs(self.server_id) + excluded_dirs = HelpersManagement.get_excluded_backup_dirs(backup_id) server_dir = Helpers.get_os_understandable_path(self.settings["path"]) - if conf["compress"]: - logger.debug( - "Found compress backup to be true. Calling compressed archive" - ) - self.file_helper.make_compressed_backup( - Helpers.get_os_understandable_path(backup_filename), - server_dir, - excluded_dirs, - self.server_id, - ) - else: - logger.debug( - "Found compress backup to be false. Calling NON-compressed archive" - ) - self.file_helper.make_backup( - Helpers.get_os_understandable_path(backup_filename), - server_dir, - excluded_dirs, - self.server_id, - ) + + self.file_helper.make_backup( + Helpers.get_os_understandable_path(backup_filename), + server_dir, + excluded_dirs, + self.server_id, + conf["compress"], + ) while ( - len(self.list_backups()) > conf["max_backups"] + len(self.list_backups(backup_location)) > conf["max_backups"] and conf["max_backups"] > 0 ): backup_list = self.list_backups() oldfile = backup_list[0] - oldfile_path = f"{conf['backup_path']}/{oldfile['path']}" + oldfile_path = f"{backup_location}/{oldfile['path']}" logger.info(f"Removing old backup '{oldfile['path']}'") os.remove(Helpers.get_os_understandable_path(oldfile_path)) @@ -1297,28 +1286,26 @@ class ServerInstance: except: return {"percent": 0, "total_files": 0} - def list_backups(self): - if not self.settings["backup_path"]: + def list_backups(self, backup_location): + if not backup_location: logger.info( f"Error putting backup file list for server with ID: {self.server_id}" ) return [] if not Helpers.check_path_exists( - Helpers.get_os_understandable_path(self.settings["backup_path"]) + Helpers.get_os_understandable_path(backup_location) ): return [] files = Helpers.get_human_readable_files_sizes( Helpers.list_dir_by_date( - Helpers.get_os_understandable_path(self.settings["backup_path"]) + Helpers.get_os_understandable_path(backup_location) ) ) return [ { "path": os.path.relpath( f["path"], - start=Helpers.get_os_understandable_path( - self.settings["backup_path"] - ), + start=Helpers.get_os_understandable_path(backup_location), ), "size": f["size"], } diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 57e3c4d0..af3d8227 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -140,7 +140,7 @@ class TasksManager: ) elif command == "backup_server": - svr.a_backup_server() + svr.a_backup_server(cmd["action_id"]) elif command == "update_executable": svr.jar_update() diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index a30350a5..20586b1a 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -273,7 +273,7 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/?", + r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/([a-z0-9-]+)/?", ApiServersServerActionHandler, handler_args, ), diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index aba06da3..3608058f 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) class ApiServersServerActionHandler(BaseApiHandler): - def post(self, server_id: str, action: str): + def post(self, server_id: str, action: str, action_id=None): auth_data = self.authenticate_user() if not auth_data: return @@ -54,7 +54,7 @@ class ApiServersServerActionHandler(BaseApiHandler): return self._agree_eula(server_id, auth_data[4]["user_id"]) self.controller.management.send_command( - auth_data[4]["user_id"], server_id, self.get_remote_ip(), action + auth_data[4]["user_id"], server_id, self.get_remote_ip(), action, action_id ) self.finish_json( @@ -93,7 +93,6 @@ class ApiServersServerActionHandler(BaseApiHandler): new_server_name, new_server_id, new_server_path, - new_backup_path, new_server_command, server_data.get("executable"), new_server_log_path, diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 63cfaca2..2a34902c 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -101,8 +101,8 @@ @@ -218,9 +218,10 @@ return r ? r[1] : undefined; } - async function backup_started() { + async function backup_started(backup_id) { const token = getCookie("_xsrf") - let res = await fetch(`/api/v2/servers/${server_id}/action/backup_server`, { + console.log(backup_id) + let res = await fetch(`/api/v2/servers/${server_id}/action/backup_server/${backup_id}/`, { method: 'POST', headers: { 'X-XSRFToken': token @@ -322,53 +323,6 @@ } $(document).ready(function () { - $("#backup-form").on("submit", async function (e) { - e.preventDefault(); - const token = getCookie("_xsrf") - let backupForm = document.getElementById("backup-form"); - - let formData = new FormData(backupForm); - //Remove checks that we don't need in form data. - formData.delete("after-check"); - formData.delete("before-check"); - //Create an object from the form data entries - let formDataObject = Object.fromEntries(formData.entries()); - //We need to make sure these are sent regardless of whether or not they're checked - formDataObject.compress = $("#compress").prop('checked'); - formDataObject.shutdown = $("#shutdown").prop('checked'); - let excluded = []; - $('input.excluded:checkbox:checked').each(function () { - excluded.push($(this).val()); - }); - if ($("#root_files_button").hasClass("clicked")) { - formDataObject.exclusions = excluded; - } - delete formDataObject.root_path - console.log(excluded); - console.log(formDataObject); - // Format the plain form data as JSON - let formDataJsonString = JSON.stringify(formDataObject, replacer); - - console.log(formDataJsonString); - - let res = await fetch(`/api/v2/servers/${server_id}/backups/`, { - method: 'PATCH', - headers: { - 'X-XSRFToken': token - }, - body: formDataJsonString, - }); - let responseData = await res.json(); - if (responseData.status === "ok") { - window.location.reload(); - } else { - - bootbox.alert({ - title: responseData.error, - message: responseData.error_data - }); - } - }); try { if ($('#backup_path').val() == '') { @@ -461,8 +415,8 @@ } }); }); - $("#backup_now_button").click(function () { - backup_started(); + $(".backup_now_button").click(function () { + backup_started($(this).data('backup')); }); }); From d55e7c9e64e252b05b9fa025a4c28a0bd58a19c3 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 14:40:21 -0400 Subject: [PATCH 15/68] Remove repeated code --- app/classes/shared/file_helpers.py | 78 ++++-------------------------- 1 file changed, 10 insertions(+), 68 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 90d8e65c..d6c1199f 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -229,74 +229,14 @@ class FileHelpers: return True - def make_compressed_backup( - self, path_to_destination, path_to_zip, excluded_dirs, server_id, comment="" - ): - # create a ZipFile object - path_to_destination += ".zip" - ex_replace = [p.replace("\\", "/") for p in excluded_dirs] - total_bytes = 0 - dir_bytes = Helpers.get_dir_size(path_to_zip) - results = { - "percent": 0, - "total_files": self.helper.human_readable_file_size(dir_bytes), - } - WebSocketManager().broadcast_page_params( - "/panel/server_detail", - {"id": str(server_id)}, - "backup_status", - results, - ) - with ZipFile(path_to_destination, "w", ZIP_DEFLATED) as zip_file: - zip_file.comment = bytes( - comment, "utf-8" - ) # comments over 65535 bytes will be truncated - for root, dirs, files in os.walk(path_to_zip, topdown=True): - for l_dir in dirs: - if str(os.path.join(root, l_dir)).replace("\\", "/") in ex_replace: - dirs.remove(l_dir) - ziproot = path_to_zip - for file in files: - if ( - str(os.path.join(root, file)).replace("\\", "/") - not in ex_replace - and file != "crafty.sqlite" - ): - try: - logger.info(f"backing up: {os.path.join(root, file)}") - if os.name == "nt": - zip_file.write( - os.path.join(root, file), - os.path.join(root.replace(ziproot, ""), file), - ) - else: - zip_file.write( - os.path.join(root, file), - os.path.join(root.replace(ziproot, "/"), file), - ) - - except Exception as e: - logger.warning( - f"Error backing up: {os.path.join(root, file)}!" - f" - Error was: {e}" - ) - total_bytes += os.path.getsize(os.path.join(root, file)) - percent = round((total_bytes / dir_bytes) * 100, 2) - results = { - "percent": percent, - "total_files": self.helper.human_readable_file_size(dir_bytes), - } - WebSocketManager().broadcast_page_params( - "/panel/server_detail", - {"id": str(server_id)}, - "backup_status", - results, - ) - - return True - def make_backup( - self, path_to_destination, path_to_zip, excluded_dirs, server_id, comment="" + self, + path_to_destination, + path_to_zip, + excluded_dirs, + server_id, + comment="", + compressed=None, ): # create a ZipFile object path_to_destination += ".zip" @@ -313,7 +253,9 @@ class FileHelpers: "backup_status", results, ) - with ZipFile(path_to_destination, "w") as zip_file: + # Set the compression mode based on the `compressed` parameter + compression_mode = ZIP_DEFLATED if compressed else None + with ZipFile(path_to_destination, "w", compression_mode) as zip_file: zip_file.comment = bytes( comment, "utf-8" ) # comments over 65535 bytes will be truncated From c037f1d1afdee99fc6f2cd722b966e60e771a4c5 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 15:11:46 -0400 Subject: [PATCH 16/68] Lint --- app/migrations/20240308_multi-backup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index 4aa854b9..dd80ba77 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -98,10 +98,12 @@ def migrate(migrator: Migrator, database, **kwargs): # Fetch the related server entry from the Servers table server = Servers.get(Servers.server_id == backup.server_id) - # Create a new backup entry with data from the old backup entry and related server + # Create a new backup entry with data from the + # old backup entry and related server NewBackups.create( backup_name="Default", - backup_location=server.backup_path, # Set backup_location equal to backup_path + # Set backup_location equal to backup_path + backup_location=server.backup_path, excluded_dirs=backup.excluded_dirs, max_backups=backup.max_backups, server_id=server.server_id, From 41147266ad52fc2f2c5f10e1ba0c63eb5b8c53b8 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 15:11:58 -0400 Subject: [PATCH 17/68] Fix issue with backup compression --- app/classes/shared/file_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index d6c1199f..3c14447c 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -4,7 +4,7 @@ import logging import pathlib import tempfile import zipfile -from zipfile import ZipFile, ZIP_DEFLATED +from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED import urllib.request import ssl import time @@ -254,7 +254,7 @@ class FileHelpers: results, ) # Set the compression mode based on the `compressed` parameter - compression_mode = ZIP_DEFLATED if compressed else None + compression_mode = ZIP_DEFLATED if compressed else ZIP_STORED with ZipFile(path_to_destination, "w", compression_mode) as zip_file: zip_file.comment = bytes( comment, "utf-8" From 97de58f31d0c9732171155056ff018c48b7d2167 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 15:12:28 -0400 Subject: [PATCH 18/68] Add action ID to tasks --- app/classes/shared/tasks.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index af3d8227..2f37e274 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -240,6 +240,7 @@ class TasksManager: "system" ), "command": schedule.command, + "action_id": schedule.action_id, } ], ) @@ -268,6 +269,7 @@ class TasksManager: "system" ), "command": schedule.command, + "action_id": schedule.action_id, } ], ) @@ -284,6 +286,7 @@ class TasksManager: "system" ), "command": schedule.command, + "action_id": schedule.action_id, } ], ) @@ -303,6 +306,7 @@ class TasksManager: "system" ), "command": schedule.command, + "action_id": schedule.action_id, } ], ) @@ -337,7 +341,7 @@ class TasksManager: job_data["cron_string"], job_data["parent"], job_data["delay"], - job_data.get("action_id", None), + job_data["action_id"], ) # Checks to make sure some doofus didn't actually make the newly @@ -368,6 +372,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -394,6 +399,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -410,6 +416,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -429,6 +436,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -521,6 +529,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -544,6 +553,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -560,6 +570,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -579,6 +590,7 @@ class TasksManager: "system" ), "command": job_data["command"], + "action_id": job_data["action_id"], } ], ) @@ -654,6 +666,7 @@ class TasksManager: "system" ), "command": schedule.command, + "action_id": schedule.action_id, } ], ) From 334d4b69c8979b34c7c80ae5ebf47e4b8dddad02 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 15:12:46 -0400 Subject: [PATCH 19/68] Allow three arguments on server actions --- app/classes/web/routes/api/api_handlers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 20586b1a..3bdf27ef 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -273,7 +273,8 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/([a-z0-9-]+)/?", + # optional third argument when we need a action ID + r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)(?:/([a-z0-9-]+))?/?", ApiServersServerActionHandler, handler_args, ), From b061ebf5e523ca511cd06ec745e3b17cb95d804b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 15:12:55 -0400 Subject: [PATCH 20/68] Use zip note --- app/classes/shared/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 0302b803..0182afc5 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -1197,6 +1197,7 @@ class ServerInstance: server_dir, excluded_dirs, self.server_id, + conf["backup_name"], conf["compress"], ) From 3cf4ebf0734b6d08ecacf6a85b4992dc1a78fe37 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 16:33:28 -0400 Subject: [PATCH 21/68] Backups are editable! --- app/classes/models/management.py | 2 +- app/classes/shared/file_helpers.py | 2 + app/classes/shared/server.py | 28 +-- app/classes/web/panel_handler.py | 4 +- app/classes/web/routes/api/api_handlers.py | 2 +- .../servers/server/backups/backup/index.py | 170 ++++++++++++++++-- .../api/servers/server/backups/index.py | 18 +- .../templates/panel/server_backup_edit.html | 45 +++-- 8 files changed, 213 insertions(+), 58 deletions(-) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 24505471..495d3e38 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -357,7 +357,7 @@ class HelpersManagement: data[str(backup.backup_id)] = { "backup_id": backup.backup_id, "backup_name": backup.backup_name, - "backup_path": backup.backup_location, + "backup_location": backup.backup_location, "excluded_dirs": backup.excluded_dirs, "max_backups": backup.max_backups, "server_id": backup.server_id_id, diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 3c14447c..a7e27555 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -235,6 +235,7 @@ class FileHelpers: path_to_zip, excluded_dirs, server_id, + backup_id, comment="", compressed=None, ): @@ -306,6 +307,7 @@ class FileHelpers: results = { "percent": percent, "total_files": self.helper.human_readable_file_size(dir_bytes), + "backup_id": backup_id, } # send status results to page. WebSocketManager().broadcast_page_params( diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 0182afc5..e265b0ca 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -1197,6 +1197,7 @@ class ServerInstance: server_dir, excluded_dirs, self.server_id, + backup_id, conf["backup_name"], conf["compress"], ) @@ -1205,7 +1206,7 @@ class ServerInstance: len(self.list_backups(backup_location)) > conf["max_backups"] and conf["max_backups"] > 0 ): - backup_list = self.list_backups() + backup_list = self.list_backups(conf["backup_location"]) oldfile = backup_list[0] oldfile_path = f"{backup_location}/{oldfile['path']}" logger.info(f"Removing old backup '{oldfile['path']}'") @@ -1213,7 +1214,12 @@ class ServerInstance: self.is_backingup = False logger.info(f"Backup of server: {self.name} completed") - results = {"percent": 100, "total_files": 0, "current_file": 0} + results = { + "percent": 100, + "total_files": 0, + "current_file": 0, + "backup_id": backup_id, + } if len(WebSocketManager().clients) > 0: WebSocketManager().broadcast_page_params( "/panel/server_detail", @@ -1251,7 +1257,12 @@ class ServerInstance: logger.exception( f"Failed to create backup of server {self.name} (ID {self.server_id})" ) - results = {"percent": 100, "total_files": 0, "current_file": 0} + results = { + "percent": 100, + "total_files": 0, + "current_file": 0, + "backup_id": backup_id, + } if len(WebSocketManager().clients) > 0: WebSocketManager().broadcast_page_params( "/panel/server_detail", @@ -1267,17 +1278,6 @@ class ServerInstance: self.run_threaded_server(HelperUsers.get_user_id_by_name("system")) self.last_backup_failed = True - def backup_status(self, source_path, dest_path): - results = Helpers.calc_percent(source_path, dest_path) - self.backup_stats = results - if len(WebSocketManager().clients) > 0: - WebSocketManager().broadcast_page_params( - "/panel/server_detail", - {"id": str(self.server_id)}, - "backup_status", - results, - ) - def last_backup_status(self): return self.last_backup_failed diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 962a5abb..4461f0d3 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1286,7 +1286,9 @@ class PanelHandler(BaseHandler): ) self.controller.servers.refresh_server_settings(server_id) try: - page_data["backup_list"] = server.list_backups() + page_data["backup_list"] = server.list_backups( + page_data["backup_config"]["backup_location"] + ) except: page_data["backup_list"] = [] page_data["backup_path"] = Helpers.wtol_path( diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 3bdf27ef..78df3ed5 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -218,7 +218,7 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/servers/([a-z0-9-]+)/backups/backup/?", + r"/api/v2/servers/([a-z0-9-]+)/backups/backup/([a-z0-9-]+)/?", ApiServersServerBackupsBackupIndexHandler, 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 cfe8f4b1..fba7bad8 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 @@ -11,7 +11,7 @@ from app.classes.shared.helpers import Helpers logger = logging.getLogger(__name__) -backup_schema = { +BACKUP_SCHEMA = { "type": "object", "properties": { "filename": {"type": "string", "minLength": 5}, @@ -19,11 +19,40 @@ backup_schema = { "additionalProperties": False, "minProperties": 1, } +BACKUP_PATCH_SCHEMA = { + "type": "object", + "properties": { + "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"}, + }, + "additionalProperties": False, + "minProperties": 1, +} + +BASIC_BACKUP_PATCH_SCHEMA = { + "type": "object", + "properties": { + "max_backups": {"type": "integer"}, + "compress": {"type": "boolean"}, + "shutdown": {"type": "boolean"}, + "before": {"type": "string"}, + "after": {"type": "string"}, + "exclusions": {"type": "array"}, + }, + "additionalProperties": False, + "minProperties": 1, +} class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): - def get(self, server_id: str): + def get(self, server_id: str, backup_id: str): auth_data = self.authenticate_user() + backup_conf = self.controller.management.get_backup_config(backup_id) if not auth_data: return mask = self.controller.server_perms.get_lowest_api_perm_mask( @@ -32,15 +61,40 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): ), auth_data[5], ) + 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", + }, + ) 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"}) - self.finish_json(200, self.controller.management.get_backup_config(server_id)) + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT_AUTHORIZED", + "error_data": "Authorization Error", + }, + ) + self.finish_json(200, backup_conf) - def delete(self, server_id: str): + def delete(self, server_id: str, backup_id: str): auth_data = self.authenticate_user() - backup_conf = self.controller.management.get_backup_config(server_id) + 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( @@ -52,7 +106,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): 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"}) + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT_AUTHORIZED", + "error_data": "Authorization Error", + }, + ) try: data = json.loads(self.request.body) @@ -61,7 +122,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) try: - validate(data, backup_schema) + validate(data, BACKUP_SCHEMA) except ValidationError as e: return self.finish_json( 400, @@ -74,7 +135,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): try: FileHelpers.del_file( - os.path.join(backup_conf["backup_path"], data["filename"]) + os.path.join(backup_conf["backup_location"], data["filename"]) ) except Exception as e: return self.finish_json( @@ -89,7 +150,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): return self.finish_json(200, {"status": "ok"}) - def post(self, server_id: str): + def post(self, server_id: str, backup_id: str): auth_data = self.authenticate_user() if not auth_data: return @@ -102,7 +163,24 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): 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"}) + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT_AUTHORIZED", + "error_data": "Authorization Error", + }, + ) + backup_config = self.controller.management.get_backup_config(backup_id) + if backup_config["server_id"] != server_id: + return self.finish_json( + 400, + { + "status": "error", + "error": "ID_MISMATCH", + "error_data": "Server ID backup server ID different", + }, + ) try: data = json.loads(self.request.body) @@ -111,7 +189,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) try: - validate(data, backup_schema) + validate(data, BACKUP_SCHEMA) except ValidationError as e: return self.finish_json( 400, @@ -184,7 +262,6 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): self.controller.servers.update_server(new_server_obj) # preserve backup config - backup_config = self.controller.management.get_backup_config(server_id) excluded_dirs = [] server_obj = self.controller.servers.get_server_obj(server_id) loop_backup_path = self.helper.wtol_path(server_obj.path) @@ -221,3 +298,70 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): ) return self.finish_json(200, {"status": "ok"}) + + def patch(self, server_id: str, backup_id: str): + auth_data = self.authenticate_user() + if not auth_data: + return + + 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: + if auth_data[4]["superuser"]: + validate(data, BACKUP_PATCH_SCHEMA) + else: + validate(data, BASIC_BACKUP_PATCH_SCHEMA) + except ValidationError as e: + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID_JSON_SCHEMA", + "error_data": str(e), + }, + ) + backup_conf = self.controller.management.get_backup_config(backup_id) + 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", + "error_data": "Authorization Error", + }, + ) + 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", + }, + ) + 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", + }, + ) + + self.controller.management.update_backup_config(backup_id, data) + 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 55744ea1..51b69473 100644 --- a/app/classes/web/routes/api/servers/server/backups/index.py +++ b/app/classes/web/routes/api/servers/server/backups/index.py @@ -10,12 +10,12 @@ logger = logging.getLogger(__name__) backup_patch_schema = { "type": "object", "properties": { - "backup_path": {"type": "string", "minLength": 1}, + "backup_location": {"type": "string", "minLength": 1}, "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, - "backup_before": {"type": "string"}, - "backup_after": {"type": "string"}, + "before": {"type": "string"}, + "after": {"type": "string"}, "exclusions": {"type": "array"}, }, "additionalProperties": False, @@ -28,8 +28,8 @@ basic_backup_patch_schema = { "max_backups": {"type": "integer"}, "compress": {"type": "boolean"}, "shutdown": {"type": "boolean"}, - "backup_before": {"type": "string"}, - "backup_after": {"type": "string"}, + "before": {"type": "string"}, + "after": {"type": "string"}, "exclusions": {"type": "array"}, }, "additionalProperties": False, @@ -52,9 +52,11 @@ 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.finish_json(200, self.controller.management.get_backup_config(server_id)) + self.finish_json( + 200, self.controller.management.get_backups_by_server(server_id) + ) - def patch(self, backup_id: str): + def post(self, server_id: str): auth_data = self.authenticate_user() if not auth_data: return @@ -80,8 +82,6 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): "error_data": str(e), }, ) - backup_conf = self.controller.management.get_backup_config(backup_id) - server_id = backup_conf["server_id"] 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"}) diff --git a/app/frontend/templates/panel/server_backup_edit.html b/app/frontend/templates/panel/server_backup_edit.html index c68d521f..1ec54d48 100644 --- a/app/frontend/templates/panel/server_backup_edit.html +++ b/app/frontend/templates/panel/server_backup_edit.html @@ -67,7 +67,7 @@ - {% end %} @@ -107,14 +107,14 @@ {{ translate('serverBackups', 'before', data['lang']) }}
- {% else %} {{ translate('serverBackups', 'before', data['lang']) }}
- {% end %} @@ -124,14 +124,14 @@ {{ translate('serverBackups', 'after', data['lang']) }}
- {% else %} {{ translate('serverBackups', 'after', data['lang']) }}
- {% end %} @@ -206,7 +206,8 @@

-