diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 408f9ebd..a6ce2ab0 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -10,7 +10,6 @@ from peewee import ( TextField, AutoField, BooleanField, - UUIDField, ) from playhouse.shortcuts import model_to_dict @@ -18,6 +17,7 @@ from app.classes.models.base_model import BaseModel from app.classes.models.users import HelperUsers from app.classes.models.servers import Servers from app.classes.models.server_permissions import PermissionsServers +from app.classes.shared.helpers import Helpers from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) @@ -105,7 +105,7 @@ class Schedules(BaseModel): # Backups Class # ********************************************************************************** class Backups(BaseModel): - backup_id = UUIDField(primary_key=True, default=uuid.uuid4) + backup_id = CharField(primary_key=True, default=Helpers.create_uuid()) backup_name = CharField(default="New Backup") backup_location = CharField(default="") excluded_dirs = CharField(null=True) @@ -115,6 +115,8 @@ class Backups(BaseModel): shutdown = BooleanField(default=False) before = CharField(default="") after = CharField(default="") + default = BooleanField(default=False) + status = CharField(default='{"status": "Standby", "message": ""}') enabled = BooleanField(default=True) class Meta: @@ -348,7 +350,7 @@ class HelpersManagement: return model_to_dict(Backups.get(Backups.backup_id == backup_id)) @staticmethod - def get_backups_by_server(server_id, model): + def get_backups_by_server(server_id, model=False): if not model: data = {} for backup in ( @@ -365,11 +367,26 @@ class HelpersManagement: "shutdown": backup.shutdown, "before": backup.before, "after": backup.after, + "default": backup.default, + "enabled": backup.enabled, } else: data = Backups.select().where(Backups.server_id == server_id).execute() return data + @staticmethod + def get_default_server_backup(server_id: str) -> dict: + bu_query = Backups.select().where( + Backups.server_id == server_id & Backups.default == True + ) + + backup_model = bu_query.first() + + if backup_model: + return model_to_dict(backup_model) + else: + raise IndexError + @staticmethod def remove_backup_config(backup_id): Backups.delete().where(Backups.backup_id == backup_id).execute() diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index e265b0ca..c12cb00e 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -1107,12 +1107,12 @@ class ServerInstance: f.write("eula=true") self.run_threaded_server(user_id) - def a_backup_server(self, backup_id): + def server_backup_threader(self, backup_id, update=False): backup_thread = threading.Thread( target=self.backup_server, daemon=True, name=f"backup_{self.name}", - args=[backup_id], + args=[backup_id, update], ) logger.info( f"Starting Backup Thread for server {self.settings['server_name']}." @@ -1140,7 +1140,7 @@ class ServerInstance: logger.info(f"Backup Thread started for server {self.settings['server_name']}.") @callback - def backup_server(self, backup_id): + def backup_server(self, backup_id, update): 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) @@ -1174,9 +1174,12 @@ class ServerInstance: "Found shutdown preference. Delaying" + "backup start. Shutting down server." ) - if self.check_running(): - self.stop_server() - was_server_running = True + if not update: + if self.check_running(): + self.stop_server() + was_server_running = True + else: + was_server_running = False self.helper.ensure_dir_exists(backup_location) try: @@ -1318,7 +1321,7 @@ class ServerInstance: def jar_update(self): self.stats_helper.set_update(True) update_thread = threading.Thread( - target=self.a_jar_update, daemon=True, name=f"exe_update_{self.name}" + target=self.threaded_jar_update, daemon=True, name=f"exe_update_{self.name}" ) update_thread.start() @@ -1359,10 +1362,13 @@ class ServerInstance: def check_update(self): return self.stats_helper.get_server_stats()["updating"] - def a_jar_update(self): + def threaded_jar_update(self): server_users = PermissionsServers.get_server_user_list(self.server_id) was_started = "-1" - self.a_backup_server() + # Get default backup configuration + backup_config = HelpersManagement.get_default_server_backup(self.server_id) + # start threaded backup + self.server_backup_threader(backup_config["backup_id"], True) # checks if server is running. Calls shutdown if it is running. if self.check_running(): was_started = True @@ -1516,12 +1522,6 @@ class ServerInstance: WebSocketManager().broadcast_user_page( user, "/panel/dashboard", "send_start_reload", {} ) - WebSocketManager().broadcast_user( - user, - "notification", - "Executable update finished for " + self.name, - ) - self.management_helper.add_to_audit_log_raw( "Alert", "-1", diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 2f37e274..80efa87d 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(cmd["action_id"]) + svr.server_backup_threader(cmd["action_id"]) elif command == "update_executable": svr.jar_update() 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 9296ad9d..3a76dfa4 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 @@ -124,6 +124,15 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): server_id, self.get_remote_ip(), ) + if backup_conf["default"]: + return self.finish_json( + 405, + { + "status": "error", + "error": "NOT_ALLOWED", + "error_data": "Cannot delete default backup", + }, + ) self.controller.management.delete_backup_config(backup_id) return self.finish_json(200, {"status": "ok"}) diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index ee5dd674..331f940c 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -70,7 +70,9 @@ {{ translate('serverBackups', 'name', data['lang']) }} - {{ translate('serverBackups', + {{ translate('serverBackups', 'status', + data['lang']) }} + {{ translate('serverBackups', 'storageLocation', data['lang']) }} {{ translate('serverBackups', 'maxBackups', data['lang']) }} @@ -83,6 +85,19 @@

{{backup.backup_name}}

+
+ {% if backup.default %} + {{ translate('serverBackups', 'default', + data['lang']) }} + {% end %} + + +
+ +

{{backup.backup_location}}

@@ -96,9 +111,11 @@ class="btn btn-info"> + {% if not backup.default %} + {% end %} + {% end %} + {% if not backup.default %} + {% end %} + {% end %} + {% if data["backup_config"].get("backup_id", None) %} @@ -188,7 +195,8 @@ - @@ -431,6 +439,13 @@ } $(document).ready(function () { + $(".backup-explain").on("click", function (e) { + e.preventDefault(); + bootbox.alert($(this).data("explain")); + }); + $(".cancel-button").on("click", function () { + location.href = `/panel/server_detail?id=${server_id}&subpage=backup` + }); $("#backup-form").on("submit", async function (e) { e.preventDefault(); const token = getCookie("_xsrf") diff --git a/app/migrations/20240308_multi-backup.py b/app/migrations/20240308_multi-backup.py index dd80ba77..fc6650c7 100644 --- a/app/migrations/20240308_multi-backup.py +++ b/app/migrations/20240308_multi-backup.py @@ -3,9 +3,11 @@ import uuid import peewee import logging + +from app.classes.models.management import Backups, Schedules +from app.classes.shared.helpers import Helpers from app.classes.shared.console import Console from app.classes.shared.migration import Migrator, MigrateHistory -from app.classes.models.management import Backups, Schedules logger = logging.getLogger(__name__) @@ -15,11 +17,20 @@ def migrate(migrator: Migrator, database, **kwargs): Write your migrations here. """ db = database - + Console.info("Starting Backups migrations") + Console.info( + "Migrations: Adding columns [backup_id, " + "backup_name, backup_location, enabled, default, action_id, backup_status]" + ) 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("backups", default=peewee.BooleanField(default=False)) + migrator.add_columns( + "backups", + status=peewee.CharField(default='{"status": "Standby", "message": ""}'), + ) migrator.add_columns( "schedules", action_id=peewee.CharField(null=True, default=None) ) @@ -52,7 +63,7 @@ def migrate(migrator: Migrator, database, **kwargs): database = db class NewBackups(peewee.Model): - backup_id = peewee.UUIDField(primary_key=True, default=uuid.uuid4) + backup_id = peewee.CharField(primary_key=True, default=Helpers.create_uuid()) backup_name = peewee.CharField(default="New Backup") backup_location = peewee.CharField(default="") excluded_dirs = peewee.CharField(null=True) @@ -62,6 +73,8 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown = peewee.BooleanField(default=False) before = peewee.CharField(default="") after = peewee.CharField(default="") + default = peewee.BooleanField(default=False) + status = peewee.CharField(default='{"status": "Standby", "message": ""}') enabled = peewee.BooleanField(default=True) class Meta: @@ -97,11 +110,11 @@ def migrate(migrator: Migrator, database, **kwargs): for backup in Backups.select(): # Fetch the related server entry from the Servers table server = Servers.get(Servers.server_id == backup.server_id) - + Console.info(f"Migrations: Migrating backup for server {server.server_name}") # Create a new backup entry with data from the # old backup entry and related server NewBackups.create( - backup_name="Default", + backup_name=f"{server.server_name} Backup", # Set backup_location equal to backup_path backup_location=server.backup_path, excluded_dirs=backup.excluded_dirs, @@ -111,19 +124,27 @@ def migrate(migrator: Migrator, database, **kwargs): shutdown=backup.shutdown, before=backup.before, after=backup.after, + default=True, enabled=True, ) + Console.debug("Migrations: Dropping old backup table") # Drop the existing backups table migrator.drop_table("backups") + Console.debug("Migrations: Renaming new_backups to backups") # Rename the new table to backups migrator.rename_table("new_backups", "backups") + + Console.debug("Migrations: Dropping backup_path from servers table") migrator.drop_columns("servers", ["backup_path"]) for schedule in Schedules.select(): action_id = None if schedule.command == "backup_server": + Console.info( + f"Migrations: Adding backup ID to task with name {schedule.name}" + ) backup = NewBackups.get(NewBackups.server_id == schedule.server_id) action_id = backup.backup_id NewSchedules.create( @@ -144,9 +165,11 @@ def migrate(migrator: Migrator, database, **kwargs): next_run=schedule.next_run, ) + Console.debug("Migrations: dropping old schedules table") # Drop the existing backups table migrator.drop_table("schedules") + Console.debug("Migrations: renaming new_schedules to schedules") # Rename the new table to backups migrator.rename_table("new_schedules", "schedules") diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 8c5813af..a9da4c7e 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -335,7 +335,10 @@ "newBackup": "Create New Backup", "edit": "Edit", "run": "Run Backup", - "backups": "Server Backups" + "backups": "Server Backups", + "default": "Default Backup", + "defaultExplain": "The backup that Crafty will use before updates. This cannot be changed or deleted.", + "status": "Status" }, "serverConfig": { "bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",