diff --git a/CHANGELOG.md b/CHANGELOG.md index abd77b2a..996acb40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,20 @@ # Changelog -## --- [4.2.4] - 2023/TBD +## --- [4.3.1] - 2023/TBD ### New features TBD +### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ +## --- [4.3.0] - 2023/03/09 +### Breaking Changes +- This release includes database migrations that are not revertable. Once you update to this version you will not be able to rollback to a previous version. +- In this release, we've implemented a breaking change to enhance server identification within Crafty: instead of relying on numerical integers (1, 2, 3, etc.), Servers are now uniquely identified by their UUIDs. Please adapt your API clients accordingly. + ### Refactor - Refactor remote file downloads ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719)) ### Bug fixes @@ -10,9 +23,10 @@ TBD - Do not allow users at server limit to clone servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/718)) - Fix bug where you cannot get to config with unloaded server ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/9de08973b6bb2ddf91283c5c6b0e189ff34f7e24)) - Fix forge install v1.20, 1.20.1 and 1.20.2 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/710)) -- Fix Sanitisation on Passwords ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715)) +- Fix Sanitisation on Passwords ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/725)) - Fix `Upload Imports` on unix systems, that have a space in the root dir name ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/722)) - Fix Bedrock downloads, add `www` to download URL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/723)) +- Fire backup webhook 'after' backup has finished ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/727)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) - Bump cryptography for CVE-2024-26130 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/724)) diff --git a/README.md b/README.md index b1b401d7..bc621c7d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) -# Crafty Controller 4.2.4 +# Crafty Controller 4.3.1 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 86e17802..6a5cce4e 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -80,8 +80,8 @@ class ServersController(metaclass=Singleton): PeeweeException: If the server already exists """ return HelperServers.create_server( - name, server_uuid, + name, server_dir, backup_path, server_command, @@ -161,9 +161,9 @@ class ServersController(metaclass=Singleton): # Servers Methods # ********************************************************************************** - def get_server_instance_by_id(self, server_id: t.Union[str, int]) -> ServerInstance: + def get_server_instance_by_id(self, server_id: t.Union[str, str]) -> ServerInstance: for server in self.servers_list: - if int(server["server_id"]) == int(server_id): + if server["server_id"] == server_id: return server["server_obj"] logger.warning(f"Unable to find server object for server id {server_id}") diff --git a/app/classes/models/management.py b/app/classes/models/management.py index e86e3209..ffe207c2 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -31,9 +31,9 @@ class AuditLog(BaseModel): user_name = CharField(default="") user_id = IntegerField(default=0, index=True) source_ip = CharField(default="127.0.0.1") - server_id = IntegerField( - default=None, index=True - ) # When auditing global events, use server ID 0 + server_id = ForeignKeyField( + Servers, backref="audit_server", null=True + ) # When auditing global events, use server ID null log_msg = TextField(default="") class Meta: @@ -79,7 +79,7 @@ class HostStats(BaseModel): # ********************************************************************************** class Webhooks(BaseModel): id = AutoField() - server_id = IntegerField(null=True) + server_id = ForeignKeyField(Servers, backref="webhook_server", null=True) name = CharField(default="Custom Webhook", max_length=64) url = CharField(default="") webhook_type = CharField(default="Custom") @@ -337,7 +337,7 @@ class HelpersManagement: @staticmethod def delete_scheduled_task_by_server(server_id): - Schedules.delete().where(Schedules.server_id == int(server_id)).execute() + Schedules.delete().where(Schedules.server_id == server_id).execute() @staticmethod def get_scheduled_task(schedule_id): diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py index 8473ed12..64258c8c 100644 --- a/app/classes/models/server_stats.py +++ b/app/classes/models/server_stats.py @@ -71,7 +71,7 @@ class HelperServerStats: database = None def __init__(self, server_id): - self.server_id = int(server_id) + self.server_id = server_id self.init_database(self.server_id) def init_database(self, server_id): diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 00bc5002..13d9096a 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -3,7 +3,6 @@ import datetime import typing as t from peewee import ( CharField, - AutoField, DateTimeField, BooleanField, IntegerField, @@ -13,6 +12,9 @@ from playhouse.shortcuts import model_to_dict from app.classes.shared.main_models import DatabaseShortcuts from app.classes.models.base_model import BaseModel +# from app.classes.models.users import Users +from app.classes.shared.helpers import Helpers + logger = logging.getLogger(__name__) @@ -20,9 +22,8 @@ logger = logging.getLogger(__name__) # Servers Model # ********************************************************************************** class Servers(BaseModel): - server_id = AutoField() + server_id = CharField(primary_key=True, default=Helpers.create_uuid()) created = DateTimeField(default=datetime.datetime.now) - server_uuid = CharField(default="", index=True) server_name = CharField(default="Server", index=True) path = CharField(default="") backup_path = CharField(default="") @@ -40,6 +41,7 @@ class Servers(BaseModel): type = CharField(default="minecraft-java") show_status = BooleanField(default=1) created_by = IntegerField(default=-100) + # created_by = ForeignKeyField(Users, backref="creator_server", null=True) shutdown_timeout = IntegerField(default=60) ignored_exits = CharField(default="0") count_players = BooleanField(default=True) @@ -60,8 +62,8 @@ class HelperServers: # ********************************************************************************** @staticmethod def create_server( + server_id: str, name: str, - server_uuid: str, server_dir: str, backup_path: str, server_command: str, @@ -95,25 +97,24 @@ class HelperServers: Raises: PeeweeException: If the server already exists """ - return Servers.insert( - { - Servers.server_name: name, - Servers.server_uuid: server_uuid, - Servers.path: server_dir, - Servers.executable: server_file, - Servers.execution_command: server_command, - Servers.auto_start: False, - Servers.auto_start_delay: 10, - Servers.crash_detection: False, - Servers.log_path: server_log_file, - Servers.server_port: server_port, - Servers.server_ip: server_host, - Servers.stop_command: server_stop, - Servers.backup_path: backup_path, - Servers.type: server_type, - Servers.created_by: created_by, - } - ).execute() + return Servers.create( + server_id=server_id, + server_uuid=server_id, + server_name=name, + path=server_dir, + executable=server_file, + execution_command=server_command, + auto_start=False, + auto_start_delay=10, + crash_detection=False, + log_path=server_log_file, + 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 @staticmethod def get_server_obj(server_id): diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index 95a83047..4b7abbc3 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -18,7 +18,12 @@ logger = logging.getLogger(__name__) class MainPrompt(cmd.Cmd): def __init__( - self, helper, tasks_manager, migration_manager, main_controller, import3 + self, + helper, + tasks_manager, + migration_manager, + main_controller, + import3, ): super().__init__() self.helper: Helpers = helper diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index cfb3a10a..b6e824a5 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -239,7 +239,7 @@ class Controller: try: os.mkdir(final_path) except FileExistsError: - final_path += "_" + server["server_uuid"] + final_path += "_" + server["server_id"] os.mkdir(final_path) try: FileHelpers.copy_file( @@ -632,11 +632,11 @@ class Controller: # and add the user to it if he's not a superuser if len(captured_roles) == 0: if not exec_user["superuser"]: - new_server_uuid = self.servers.get_server_data_by_id(new_server_id).get( - "server_uuid" + new_server_id = self.servers.get_server_data_by_id(new_server_id).get( + "server_id" ) role_id = self.roles.add_role( - f"Creator of Server with uuid={new_server_uuid}", + f"Creator of Server with id={new_server_id}", exec_user["user_id"], ) self.server_perms.add_role_server(new_server_id, role_id, "11111111") @@ -647,7 +647,7 @@ class Controller: role_id = role self.server_perms.add_role_server(new_server_id, role_id, "11111111") - return new_server_id, server_fs_uuid + return new_server_id @staticmethod def verify_jar_server(server_path: str, server_jar: str): @@ -1095,7 +1095,7 @@ class Controller: for server in servers: server_path = server.get("path") new_local_server_path = os.path.join( - new_server_path, server.get("server_uuid") + new_server_path, server.get("server_id") ) if os.path.isdir(server_path): WebSocketManager().broadcast_page( diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index c31542a2..1adea4c6 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -200,6 +200,21 @@ class Migrator(object): ) return model + @get_model + def alter_column_type( + self, + model: peewee.Model, + column_name: str, + field: peewee.Field, + ) -> peewee.Model: + """ + Alter field data type in database. + """ + self.operations.append( + self.migrator.alter_column_type(model._meta.table_name, column_name, field) + ) + return model + @get_model def rename_table(self, model: peewee.Model, new_name: str) -> peewee.Model: """ diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 8d07af23..a31cc891 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -208,7 +208,7 @@ class ServerInstance: self.dir_scheduler.start() self.start_dir_calc_task() self.backup_thread = threading.Thread( - target=self.a_backup_server, daemon=True, name=f"backup_{self.name}" + target=self.backup_server, daemon=True, name=f"backup_{self.name}" ) self.is_backingup = False # Reset crash and update at initialization @@ -1110,13 +1110,12 @@ class ServerInstance: f.write("eula=true") self.run_threaded_server(user_id) - @callback - def backup_server(self): + def a_backup_server(self): if self.settings["backup_path"] == "": logger.critical("Backup path is None. Canceling Backup!") return backup_thread = threading.Thread( - target=self.a_backup_server, daemon=True, name=f"backup_{self.name}" + target=self.backup_server, daemon=True, name=f"backup_{self.name}" ) logger.info( f"Starting Backup Thread for server {self.settings['server_name']}." @@ -1143,7 +1142,8 @@ class ServerInstance: return False logger.info(f"Backup Thread started for server {self.settings['server_name']}.") - def a_backup_server(self): + @callback + def backup_server(self): 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) @@ -1373,7 +1373,7 @@ class ServerInstance: def a_jar_update(self): server_users = PermissionsServers.get_server_user_list(self.server_id) was_started = "-1" - self.backup_server() + self.a_backup_server() # checks if server is running. Calls shutdown if it is running. if self.check_running(): was_started = True diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index a5ea32ac..d1b786b9 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -140,7 +140,7 @@ class TasksManager: ) elif command == "backup_server": - svr.backup_server() + svr.a_backup_server() elif command == "update_executable": svr.jar_update() diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index bb9ded4d..8ecaaf7a 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -174,7 +174,7 @@ class PanelHandler(BaseHandler): self.redirect("/panel/error?error=Invalid Server ID") return None for server in self.controller.servers.failed_servers: - if int(server_id) == server["server_id"]: + if server_id == server["server_id"]: self.failed_server = True return server_id # Does this server exist? @@ -557,7 +557,7 @@ class PanelHandler(BaseHandler): "server_id": { "server_id": server_id, "server_name": server_temp_obj["server_name"], - "server_uuid": server_temp_obj["server_uuid"], + "server_uuid": server_temp_obj["server_id"], "path": server_temp_obj["path"], "log_path": server_temp_obj["log_path"], "executable": server_temp_obj["executable"], diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 21c78c04..a30350a5 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -208,92 +208,92 @@ def api_handlers(handler_args): handler_args, ), ( - r"/api/v2/servers/([0-9]+)/?", + r"/api/v2/servers/([a-z0-9-]+)/?", ApiServersServerIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/backups/?", + r"/api/v2/servers/([a-z0-9-]+)/backups/?", ApiServersServerBackupsIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/backups/backup/?", + r"/api/v2/servers/([a-z0-9-]+)/backups/backup/?", ApiServersServerBackupsBackupIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/files/?", + r"/api/v2/servers/([a-z0-9-]+)/files/?", ApiServersServerFilesIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/files/create/?", + r"/api/v2/servers/([a-z0-9-]+)/files/create/?", ApiServersServerFilesCreateHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/files/zip/?", + r"/api/v2/servers/([a-z0-9-]+)/files/zip/?", ApiServersServerFilesZipHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/tasks/?", + r"/api/v2/servers/([a-z0-9-]+)/tasks/?", ApiServersServerTasksIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/?", + r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/?", ApiServersServerTasksTaskIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/tasks/([0-9]+)/children/?", + r"/api/v2/servers/([a-z0-9-]+)/tasks/([0-9]+)/children/?", ApiServersServerTasksTaskChildrenHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/stats/?", + r"/api/v2/servers/([a-z0-9-]+)/stats/?", ApiServersServerStatsHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/history/?", + r"/api/v2/servers/([a-z0-9-]+)/history/?", ApiServersServerHistoryHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/webhook/([0-9]+)/?", + r"/api/v2/servers/([a-z0-9-]+)/webhook/([0-9]+)/?", ApiServersServerWebhooksManagementIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/webhook/?", + r"/api/v2/servers/([a-z0-9-]+)/webhook/?", ApiServersServerWebhooksIndexHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/action/([a-z_]+)/?", + r"/api/v2/servers/([a-z0-9-]+)/action/([a-z_]+)/?", ApiServersServerActionHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/logs/?", + r"/api/v2/servers/([a-z0-9-]+)/logs/?", ApiServersServerLogsHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/users/?", + r"/api/v2/servers/([a-z0-9-]+)/users/?", ApiServersServerUsersHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/public/?", + r"/api/v2/servers/([a-z0-9-]+)/public/?", ApiServersServerPublicHandler, handler_args, ), ( - r"/api/v2/servers/([0-9]+)/stdin/?", + r"/api/v2/servers/([a-z0-9-]+)/stdin/?", ApiServersServerStdinHandler, handler_args, ), diff --git a/app/classes/web/routes/api/servers/index.py b/app/classes/web/routes/api/servers/index.py index 31dbd4c6..3c14f604 100644 --- a/app/classes/web/routes/api/servers/index.py +++ b/app/classes/web/routes/api/servers/index.py @@ -723,9 +723,7 @@ class ApiServersIndexHandler(BaseApiHandler): 405, {"status": "error", "error": "DATA CONSTRAINT FAILED"} ) return - new_server_id, new_server_uuid = self.controller.create_api_server( - data, user["user_id"] - ) + new_server_id = self.controller.create_api_server(data, user["user_id"]) self.controller.servers.stats.record_stats() @@ -734,7 +732,7 @@ class ApiServersIndexHandler(BaseApiHandler): ( f"created server {data['name']}" f" (ID: {new_server_id})" - f" (UUID: {new_server_uuid})" + f" (UUID: {new_server_id})" ), server_id=new_server_id, source_ip=self.get_remote_ip(), @@ -746,7 +744,7 @@ class ApiServersIndexHandler(BaseApiHandler): "status": "ok", "data": { "new_server_id": str(new_server_id), - "new_server_uuid": new_server_uuid, + "new_server_uuid": new_server_id, }, }, ) diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index a30ab410..01ce45c4 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -3,7 +3,6 @@ import os from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.models.servers import Servers from app.classes.shared.file_helpers import FileHelpers -from app.classes.shared.helpers import Helpers from app.classes.web.base_api_handler import BaseApiHandler @@ -68,10 +67,20 @@ class ApiServersServerActionHandler(BaseApiHandler): name_counter += 1 new_server_name = server_data.get("server_name") + f" (Copy {name_counter})" - new_server_uuid = Helpers.create_uuid() - while os.path.exists(os.path.join(self.helper.servers_dir, new_server_uuid)): - new_server_uuid = Helpers.create_uuid() - new_server_path = os.path.join(self.helper.servers_dir, new_server_uuid) + new_server_id = self.controller.servers.create_server( + new_server_name, + None, + "", + None, + server_data.get("executable"), + None, + server_data.get("stop_command"), + server_data.get("type"), + user_id, + server_data.get("server_port"), + ) + + new_server_path = os.path.join(self.helper.servers_dir, new_server_id) self.controller.management.add_to_audit_log( user_id, @@ -89,19 +98,12 @@ class ApiServersServerActionHandler(BaseApiHandler): self.helper.get_os_understandable_path(server_data.get("log_path")) ) - new_server_id = self.controller.servers.create_server( - new_server_name, - new_server_uuid, - new_server_path, - "", - new_server_command, - server_data.get("executable"), - new_server_log_file, - server_data.get("stop_command"), - server_data.get("type"), - user_id, - server_data.get("server_port"), - ) + server: Servers = self.controller.servers.get_server_obj(new_server_id) + server.path = new_server_path + server.log_path = new_server_log_file + server.execution_command = new_server_command + self.controller.servers.update_server(server) + for role in self.controller.server_perms.get_server_roles(server_id): mask = self.controller.server_perms.get_permissions_mask( role.role_id, server_id 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 9a4ecc30..05edd3a9 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 @@ -145,7 +145,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): new_server_id = new_server new_server = self.controller.servers.get_server_data(new_server) self.controller.rename_backup_dir( - server_id, new_server_id, new_server["server_uuid"] + server_id, new_server_id, new_server["server_id"] ) # preserve current schedules for schedule in self.controller.management.get_schedules_by_server( diff --git a/app/classes/web/routes/api/servers/server/index.py b/app/classes/web/routes/api/servers/server/index.py index 06db3158..81035bd0 100644 --- a/app/classes/web/routes/api/servers/server/index.py +++ b/app/classes/web/routes/api/servers/server/index.py @@ -176,7 +176,7 @@ class ApiServersServerIndexHandler(BaseApiHandler): self.tasks_manager.remove_all_server_tasks(server_id) failed = False for item in self.controller.servers.failed_servers[:]: - if item["server_id"] == int(server_id): + if item["server_id"] == server_id: self.controller.servers.failed_servers.remove(item) failed = True diff --git a/app/classes/web/routes/metrics/metrics_handlers.py b/app/classes/web/routes/metrics/metrics_handlers.py index fa43a909..7fb1e31d 100644 --- a/app/classes/web/routes/metrics/metrics_handlers.py +++ b/app/classes/web/routes/metrics/metrics_handlers.py @@ -17,7 +17,7 @@ def metrics_handlers(handler_args): handler_args, ), ( - r"/metrics/servers/([0-9]+)/?", + r"/metrics/servers/([a-z0-9-]+)/?", ApiOpenMetricsServersHandler, handler_args, ), diff --git a/app/config/version.json b/app/config/version.json index 3c001e77..9a8d1a7f 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, - "minor": 2, - "sub": 4 + "minor": 3, + "sub": 1 } diff --git a/app/frontend/templates/panel/server_admin_controls.html b/app/frontend/templates/panel/server_admin_controls.html index c021f310..b136a650 100644 --- a/app/frontend/templates/panel/server_admin_controls.html +++ b/app/frontend/templates/panel/server_admin_controls.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 3606d4af..2a9263ba 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} @@ -454,7 +454,7 @@ }); } }); - + try { if ($('#backup_path').val() == '') { console.log('true') diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index 392f38a2..46e144c3 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index ce188d2a..2d99c6bb 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_logs.html b/app/frontend/templates/panel/server_logs.html index 32f3cb66..3605adba 100644 --- a/app/frontend/templates/panel/server_logs.html +++ b/app/frontend/templates/panel/server_logs.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_metrics.html b/app/frontend/templates/panel/server_metrics.html index 5067e709..be7712e5 100644 --- a/app/frontend/templates/panel/server_metrics.html +++ b/app/frontend/templates/panel/server_metrics.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_schedule_edit.html b/app/frontend/templates/panel/server_schedule_edit.html index fac8242a..7b116f7f 100644 --- a/app/frontend/templates/panel/server_schedule_edit.html +++ b/app/frontend/templates/panel/server_schedule_edit.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} @@ -235,7 +235,7 @@ if (key != "start_time" && key != "cron_string" && key != "interval_type") { if (typeof value == "boolean") { return value - } + } console.log(key) if (key === "interval" && value === ""){ return 0; @@ -298,7 +298,7 @@ title: responseData.status, message: responseData.error }); - } + } }); $("#schedule_form").on("submit", async function (e) { @@ -332,7 +332,7 @@ method: 'PATCH', headers: { 'X-XSRFToken': token, - "Content-Type": "application/json", + "Content-Type": "application/json", }, body: formDataJsonString, }); @@ -345,7 +345,7 @@ title: responseData.error, message: responseData.error_data }); - } + } }); }); diff --git a/app/frontend/templates/panel/server_schedules.html b/app/frontend/templates/panel/server_schedules.html index 85bc077d..1fcb30df 100644 --- a/app/frontend/templates/panel/server_schedules.html +++ b/app/frontend/templates/panel/server_schedules.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_term.html b/app/frontend/templates/panel/server_term.html index a127e7aa..7467781a 100644 --- a/app/frontend/templates/panel/server_term.html +++ b/app/frontend/templates/panel/server_term.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/frontend/templates/panel/server_webhook_edit.html b/app/frontend/templates/panel/server_webhook_edit.html index fd556833..80610a49 100644 --- a/app/frontend/templates/panel/server_webhook_edit.html +++ b/app/frontend/templates/panel/server_webhook_edit.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} @@ -161,7 +161,7 @@ if (key != "start_time" && key != "cron_string" && key != "interval_type") { if (typeof value == "boolean") { return value - } + } console.log(key) if (key === "interval" && value === ""){ return 0; @@ -221,7 +221,7 @@ title: responseData.status, message: responseData.error }); - } + } }); $("#webhook_form").on("submit", async function (e) { @@ -249,7 +249,7 @@ method: 'PATCH', headers: { 'X-XSRFToken': token, - "Content-Type": "application/json", + "Content-Type": "application/json", }, body: formDataJsonString, }); @@ -262,15 +262,15 @@ title: responseData.status, message: responseData.error }); - } + } }); }); - + function hexToDiscordInt(hexColor) { // Remove the hash at the start if it's there const sanitizedHex = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor; - + // Convert the hex to an integer return parseInt(sanitizedHex, 16); } diff --git a/app/frontend/templates/panel/server_webhooks.html b/app/frontend/templates/panel/server_webhooks.html index 25a61257..432b0148 100644 --- a/app/frontend/templates/panel/server_webhooks.html +++ b/app/frontend/templates/panel/server_webhooks.html @@ -17,7 +17,7 @@ {{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
- UUID: {{ data['server_stats']['server_id']['server_uuid'] }} + UUID: {{ data['server_stats']['server_id']['server_id'] }} diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py new file mode 100644 index 00000000..c741a84c --- /dev/null +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -0,0 +1,244 @@ +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 ( + AuditLog, + Webhooks, + Schedules, + Backups, +) +from app.classes.models.server_permissions import RoleServers + +logger = logging.getLogger(__name__) + + +def migrate(migrator: Migrator, database, **kwargs): + """ + Write your migrations here. + """ + db = database + + # ********************************************************************************** + # Servers New Model from Old (easier to migrate without dunmping Database) + # ********************************************************************************** + class Servers(peewee.Model): + server_id = peewee.CharField(primary_key=True, default=str(uuid.uuid4())) + created = peewee.DateTimeField(default=datetime.datetime.now) + server_uuid = peewee.CharField(default="", index=True) + 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 + + try: + logger.info("Migrating Data from Int to UUID (Type Change)") + Console.info("Migrating Data from Int to UUID (Type Change)") + + # Changes on Server Table + migrator.alter_column_type( + Servers, + "server_id", + peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ) + + # Changes on Audit Log Table + migrator.alter_column_type( + AuditLog, + "server_id", + peewee.ForeignKeyField( + Servers, + backref="audit_server", + null=True, + field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ), + ) + # Changes on Webhook Table + migrator.alter_column_type( + Webhooks, + "server_id", + peewee.ForeignKeyField( + Servers, + backref="webhook_server", + null=True, + field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ), + ) + + migrator.run() + + logger.info("Migrating Data from Int to UUID (Type Change) : SUCCESS") + Console.info("Migrating Data from Int to UUID (Type Change) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID (Type Change)") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID (Type Change)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + try: + logger.info("Migrating Data from Int to UUID (Foreign Keys)") + Console.info("Migrating Data from Int to UUID (Foreign Keys)") + # Changes on Audit Log Table + for audit_log in AuditLog.select(): + old_server_id = audit_log.server_id_id + if old_server_id == "0" or old_server_id is None: + server_uuid = None + else: + try: + server = Servers.get_by_id(old_server_id) + server_uuid = server.server_uuid + except: + server_uuid = old_server_id + AuditLog.update(server_id=server_uuid).where( + AuditLog.audit_id == audit_log.audit_id + ).execute() + + # Changes on Webhooks Log Table + for webhook in Webhooks.select(): + old_server_id = webhook.server_id_id + try: + server = Servers.get_by_id(old_server_id) + server_uuid = server.server_uuid + except: + server_uuid = old_server_id + Webhooks.update(server_id=server_uuid).where( + Webhooks.id == webhook.id + ).execute() + + # Changes on Schedules Log Table + for schedule in Schedules.select(): + old_server_id = schedule.server_id_id + try: + server = Servers.get_by_id(old_server_id) + server_uuid = server.server_uuid + except: + server_uuid = old_server_id + Schedules.update(server_id=server_uuid).where( + Schedules.schedule_id == schedule.schedule_id + ).execute() + + # Changes on Backups Log Table + for backup in Backups.select(): + old_server_id = backup.server_id_id + try: + server = Servers.get_by_id(old_server_id) + server_uuid = server.server_uuid + except: + server_uuid = old_server_id + Backups.update(server_id=server_uuid).where( + Backups.server_id == old_server_id + ).execute() + + # Changes on RoleServers Log Table + for role_servers in RoleServers.select(): + old_server_id = role_servers.server_id_id + try: + server = Servers.get_by_id(old_server_id) + server_uuid = server.server_uuid + except: + server_uuid = old_server_id + RoleServers.update(server_id=server_uuid).where( + RoleServers.role_id == role_servers.id + and RoleServers.server_id == old_server_id + ).execute() + + logger.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS") + Console.info("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID (Foreign Keys)") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID (Foreign Keys)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + try: + logger.info("Migrating Data from Int to UUID (Primary Keys)") + Console.info("Migrating Data from Int to UUID (Primary Keys)") + # Migrating servers from the old id type to the new one + for server in Servers.select(): + Servers.update(server_id=server.server_uuid).where( + Servers.server_id == server.server_id + ).execute() + + logger.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS") + Console.info("Migrating Data from Int to UUID (Primary Keys) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID (Primary Keys)") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID (Primary Keys)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + # Changes on Server Table + logger.info("Migrating Data from Int to UUID (Removing UUID Field from Servers)") + Console.info("Migrating Data from Int to UUID (Removing UUID Field from Servers)") + migrator.drop_columns("servers", ["server_uuid"]) + migrator.run() + logger.info( + "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" + ) + Console.info( + "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" + ) + + return + + +def rollback(migrator: Migrator, database, **kwargs): + """ + Write your rollback migrations here. + """ + db = database + + # Changes on Server Table + migrator.alter_column_type( + "servers", + "server_id", + peewee.AutoField(), + ) + + # Changes on Audit Log Table + migrator.alter_column_type( + AuditLog, + "server_id", + peewee.IntegerField(default=None, index=True), + ) + + # Changes on Webhook Table + migrator.alter_column_type( + Webhooks, + "server_id", + peewee.IntegerField(null=True), + ) diff --git a/sonar-project.properties b/sonar-project.properties index 635324ef..b6fa060c 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization=crafty-controller # This is the name and version displayed in the SonarCloud UI. sonar.projectName=Crafty 4 -sonar.projectVersion=4.2.4 +sonar.projectVersion=4.3.1 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**