From aa7f47a4f446046127b32c055071c140bdefcbad Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Wed, 1 Nov 2023 18:44:56 +0100 Subject: [PATCH 001/342] Reworking id to use uuid --- app/classes/controllers/servers_controller.py | 6 +- app/classes/models/management.py | 10 +- app/classes/models/server_stats.py | 2 +- app/classes/models/servers.py | 47 +++---- app/classes/shared/command.py | 13 +- app/classes/shared/main_controller.py | 10 +- app/classes/shared/migrate_uuid.py | 116 ++++++++++++++++++ app/classes/shared/migration.py | 15 +++ app/classes/web/panel_handler.py | 4 +- app/classes/web/routes/api/api_handlers.py | 36 +++--- app/classes/web/routes/api/servers/index.py | 4 +- .../web/routes/api/servers/server/action.py | 37 +++--- .../servers/server/backups/backup/index.py | 2 +- .../web/routes/api/servers/server/index.py | 2 +- .../web/routes/metrics/metrics_handlers.py | 2 +- .../panel/server_admin_controls.html | 2 +- .../templates/panel/server_backup.html | 4 +- .../templates/panel/server_config.html | 2 +- .../templates/panel/server_files.html | 2 +- app/frontend/templates/panel/server_logs.html | 2 +- .../templates/panel/server_metrics.html | 2 +- .../templates/panel/server_schedule_edit.html | 10 +- .../templates/panel/server_schedules.html | 2 +- app/frontend/templates/panel/server_term.html | 2 +- .../templates/panel/server_webhook_edit.html | 14 +-- .../templates/panel/server_webhooks.html | 2 +- .../20231022_rework_servers_uuid.py | 103 ++++++++++++++++ main.py | 4 +- 28 files changed, 354 insertions(+), 103 deletions(-) create mode 100644 app/classes/shared/migrate_uuid.py create mode 100644 app/migrations/20231022_rework_servers_uuid.py diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index c0bae7b0..f36ce73f 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -77,8 +77,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, @@ -158,9 +158,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 a83fd0a2..d7061e09 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -3,15 +3,18 @@ import datetime import typing as t from peewee import ( CharField, - AutoField, + # AutoField, DateTimeField, BooleanField, IntegerField, + ForeignKeyField, ) 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,7 +23,7 @@ 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) @@ -40,6 +43,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") @@ -59,8 +63,8 @@ class HelperServers: # ********************************************************************************** @staticmethod def create_server( + server_id: str, name: str, - server_uuid: str, server_dir: str, backup_path: str, server_command: str, @@ -94,25 +98,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 155fe083..c8bd5a8d 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -6,6 +6,7 @@ import logging import getpass from app.classes.shared.console import Console from app.classes.shared.import3 import Import3 +from app.classes.shared.migrate_uuid import MigrateUUID from app.classes.shared.helpers import Helpers from app.classes.shared.tasks import TasksManager @@ -18,7 +19,13 @@ 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, + migrate_uuid, ): super().__init__() self.helper: Helpers = helper @@ -26,6 +33,7 @@ class MainPrompt(cmd.Cmd): self.migration_manager: MigrationManager = migration_manager self.controller: Controller = main_controller self.import3: Import3 = import3 + self.migrate_uuid: MigrateUUID = migrate_uuid # overrides the default Prompt self.prompt = "" @@ -114,6 +122,9 @@ class MainPrompt(cmd.Cmd): def do_import3(self, _line): self.import3.start_import() + def do_migrate_uuid(self, _line): + self.migrate_uuid.start_migrate() + def universal_exit(self): logger.info("Stopping all server daemons / threads") Console.info( diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 23586696..aca0bd45 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -175,7 +175,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( @@ -560,11 +560,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") @@ -1023,7 +1023,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/migrate_uuid.py b/app/classes/shared/migrate_uuid.py new file mode 100644 index 00000000..82aa76d2 --- /dev/null +++ b/app/classes/shared/migrate_uuid.py @@ -0,0 +1,116 @@ +import logging + +from app.classes.shared.console import Console + +from app.classes.shared.main_controller import Controller +from app.classes.models.servers import Servers +from app.classes.models.management import ( + AuditLog, + Webhooks, + Schedules, + Backups, +) +from app.classes.models.server_permissions import RoleServers + +logger = logging.getLogger(__name__) + + +class MigrateUUID: + def __init__(self, helper, controller: Controller): + self.helper = helper + self.controller = controller + + def start_migrate(self): + success = self.update_backreferences_tables() + if success: + self.migrate_servers() + + def update_backreferences_tables(self): + try: + # 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() + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID") + Console.error(ex) + return False + return True + + def migrate_servers(self): + try: + # 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() + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID") + Console.error(ex) + return False + return True 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/web/panel_handler.py b/app/classes/web/panel_handler.py index e1d21f03..eb540ded 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -153,7 +153,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? @@ -518,7 +518,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 706c346f..7c0ba192 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -202,92 +202,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..8c4c1669 100644 --- a/app/classes/web/routes/api/servers/index.py +++ b/app/classes/web/routes/api/servers/index.py @@ -734,7 +734,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 +746,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 153b889d..9f98c553 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 @@ -60,10 +59,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, @@ -81,19 +90,11 @@ 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) self.controller.servers.init_all_servers() 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 b92e1e9f..41ae74b0 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 afe02a0b..98b545bf 100644 --- a/app/classes/web/routes/api/servers/server/index.py +++ b/app/classes/web/routes/api/servers/server/index.py @@ -174,7 +174,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/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 d79e710e..27b45a8a 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'] }} @@ -453,7 +453,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 46f7b4dc..4d38e41f 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 9a4033a1..bc56816a 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 bb63fbe7..0272ca4f 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 e7087771..57b483dc 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'] }} @@ -159,7 +159,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; @@ -219,7 +219,7 @@ title: responseData.status, message: responseData.error }); - } + } }); $("#webhook_form").on("submit", async function (e) { @@ -247,7 +247,7 @@ method: 'PATCH', headers: { 'X-XSRFToken': token, - "Content-Type": "application/json", + "Content-Type": "application/json", }, body: formDataJsonString, }); @@ -260,15 +260,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/20231022_rework_servers_uuid.py b/app/migrations/20231022_rework_servers_uuid.py new file mode 100644 index 00000000..2cf1614e --- /dev/null +++ b/app/migrations/20231022_rework_servers_uuid.py @@ -0,0 +1,103 @@ +import datetime +import uuid +import peewee + +from app.classes.shared.migration import Migrator +from app.classes.models.management import AuditLog +from app.classes.models.management import Webhooks + + +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 + + # 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())), + ), + ) + + +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/main.py b/main.py index 143dfb4f..3379af5b 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,7 @@ from packaging import version as pkg_version from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.import3 import Import3 +from app.classes.shared.migrate_uuid import MigrateUUID from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers @@ -174,6 +175,7 @@ if __name__ == "__main__": Console.info("Remote change complete.") import3 = Import3(helper, controller) + migrate_uuid = MigrateUUID(helper, controller) tasks_manager = TasksManager(helper, controller, file_helper) tasks_manager.start_webserver() @@ -259,7 +261,7 @@ if __name__ == "__main__": controller.clear_support_status() crafty_prompt = MainPrompt( - helper, tasks_manager, migration_manager, controller, import3 + helper, tasks_manager, migration_manager, controller, import3, migrate_uuid ) controller_setup_thread = Thread(target=controller_setup, name="controller_setup") From 586b2038b64a740cd5e3c1977655b7048f69035f Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 10 Nov 2023 23:16:04 +0100 Subject: [PATCH 002/342] Fixing warnings --- app/classes/models/servers.py | 5 ++--- app/classes/shared/main_controller.py | 2 +- app/classes/web/routes/api/servers/index.py | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index d7061e09..28cd8998 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -3,17 +3,16 @@ import datetime import typing as t from peewee import ( CharField, - # AutoField, DateTimeField, BooleanField, IntegerField, - ForeignKeyField, ) 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.models.users import Users from app.classes.shared.helpers import Helpers logger = logging.getLogger(__name__) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index aca0bd45..6f85c25e 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -575,7 +575,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): diff --git a/app/classes/web/routes/api/servers/index.py b/app/classes/web/routes/api/servers/index.py index 8c4c1669..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() From 1b05562a9ed6d4d324b8d1557a504afa6812aa78 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 9 Feb 2024 00:10:26 +0100 Subject: [PATCH 003/342] Fix Forge Install bug for 1.20, 1.20.1 and 1.20.2 --- app/classes/shared/server.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index e47fe19c..c3cee9fc 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -697,6 +697,10 @@ class ServerInstance: version_param = version[0][0].split(".") version_major = int(version_param[0]) version_minor = int(version_param[1]) + if version_param.length > 2: + version_sub = int(version_param[2]) + else: + version_sub = 0 # Checking which version we are with if version_major <= 1 and version_minor < 17: @@ -730,8 +734,8 @@ class ServerInstance: server_obj.execution_command = execution_command Console.debug(SUCCESSMSG) - elif version_major <= 1 and version_minor < 20: - # NEW VERSION >= 1.17 and <= 1.20 + elif version_major <= 1 and version_minor <= 20 and version_sub < 3: + # NEW VERSION >= 1.17 and <= 1.20.2 # (no jar file in server dir, only run.bat and run.sh) run_file_path = "" From a7f7a50ae023e42ae84ad72faccc1face29f72c3 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 9 Feb 2024 00:22:42 +0100 Subject: [PATCH 004/342] really fixing the bug --- app/classes/shared/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index c3cee9fc..ce97cade 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -697,7 +697,7 @@ class ServerInstance: version_param = version[0][0].split(".") version_major = int(version_param[0]) version_minor = int(version_param[1]) - if version_param.length > 2: + if len(version_param) > 2: version_sub = int(version_param[2]) else: version_sub = 0 @@ -782,7 +782,7 @@ class ServerInstance: server_obj.execution_command = execution_command Console.debug(SUCCESSMSG) else: - # NEW VERSION >= 1.20 + # NEW VERSION >= 1.20.3 # (executable jar is back in server dir) # Retrieving the executable jar filename From e59b62402534e634db5fd02b6d06178d96c441ea Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 8 Feb 2024 20:25:32 -0500 Subject: [PATCH 005/342] 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 ad6e53a5d1c974f3b4bfc4e107aacd43fb45535a Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 19:29:14 +0100 Subject: [PATCH 006/342] Adding all migration steps to a migration.py file --- app/classes/shared/migrate_uuid.py | 116 ------------------ .../20231022_rework_servers_uuid.py | 103 +++++++++++++++- 2 files changed, 101 insertions(+), 118 deletions(-) delete mode 100644 app/classes/shared/migrate_uuid.py diff --git a/app/classes/shared/migrate_uuid.py b/app/classes/shared/migrate_uuid.py deleted file mode 100644 index 82aa76d2..00000000 --- a/app/classes/shared/migrate_uuid.py +++ /dev/null @@ -1,116 +0,0 @@ -import logging - -from app.classes.shared.console import Console - -from app.classes.shared.main_controller import Controller -from app.classes.models.servers import Servers -from app.classes.models.management import ( - AuditLog, - Webhooks, - Schedules, - Backups, -) -from app.classes.models.server_permissions import RoleServers - -logger = logging.getLogger(__name__) - - -class MigrateUUID: - def __init__(self, helper, controller: Controller): - self.helper = helper - self.controller = controller - - def start_migrate(self): - success = self.update_backreferences_tables() - if success: - self.migrate_servers() - - def update_backreferences_tables(self): - try: - # 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() - - except Exception as ex: - logger.error("Error while migrating Data from Int to UUID") - logger.error(ex) - Console.error("Error while migrating Data from Int to UUID") - Console.error(ex) - return False - return True - - def migrate_servers(self): - try: - # 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() - - except Exception as ex: - logger.error("Error while migrating Data from Int to UUID") - logger.error(ex) - Console.error("Error while migrating Data from Int to UUID") - Console.error(ex) - return False - return True diff --git a/app/migrations/20231022_rework_servers_uuid.py b/app/migrations/20231022_rework_servers_uuid.py index 2cf1614e..3d09d902 100644 --- a/app/migrations/20231022_rework_servers_uuid.py +++ b/app/migrations/20231022_rework_servers_uuid.py @@ -1,10 +1,19 @@ import datetime import uuid import peewee +import logging +from app.classes.shared.console import Console from app.classes.shared.migration import Migrator -from app.classes.models.management import AuditLog -from app.classes.models.management import Webhooks +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): @@ -74,6 +83,96 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) + try: + # 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() + 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) + return False + + try: + # 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() + + 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) + return False + + # Changes on Server Table + migrator.drop_columns("servers", ["server_uuid"]) + + return True + def rollback(migrator: Migrator, database, **kwargs): """ From ccec2a3400ca7ce27b5fd1b602ba8a7491064b6e Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 19:32:23 +0100 Subject: [PATCH 007/342] rename migration file to have a more recent one --- ...022_rework_servers_uuid.py => 20240216_rework_servers_uuid.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/migrations/{20231022_rework_servers_uuid.py => 20240216_rework_servers_uuid.py} (100%) diff --git a/app/migrations/20231022_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py similarity index 100% rename from app/migrations/20231022_rework_servers_uuid.py rename to app/migrations/20240216_rework_servers_uuid.py From bb3901f7835653653d265e020607e35f9f281d25 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 19:45:08 +0100 Subject: [PATCH 008/342] remove forgoten use of migration_uuid --- main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.py b/main.py index dcbf27e5..ebaf7806 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,6 @@ from packaging import version as pkg_version from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.import3 import Import3 -from app.classes.shared.migrate_uuid import MigrateUUID from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers @@ -384,7 +383,6 @@ if __name__ == "__main__": controller.set_project_root(APPLICATION_PATH) tasks_manager = TasksManager(helper, controller, file_helper) import3 = Import3(helper, controller) - migrate_uuid = MigrateUUID(helper, controller) # Check to see if client config.json version is different than the # Master config.json in helpers.py @@ -417,7 +415,7 @@ if __name__ == "__main__": # start the Crafty console. crafty_prompt = MainPrompt( - helper, tasks_manager, migration_manager, controller, import3, migrate_uuid + helper, tasks_manager, migration_manager, controller, import3 ) # set up all controllers From 875c9058b03a40821ff22fb4c7ef6ea2f0c9b063 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 19:46:22 +0100 Subject: [PATCH 009/342] there were still use -_- --- app/classes/shared/command.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index c8bd5a8d..ab7a494a 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -6,7 +6,6 @@ import logging import getpass from app.classes.shared.console import Console from app.classes.shared.import3 import Import3 -from app.classes.shared.migrate_uuid import MigrateUUID from app.classes.shared.helpers import Helpers from app.classes.shared.tasks import TasksManager @@ -25,7 +24,6 @@ class MainPrompt(cmd.Cmd): migration_manager, main_controller, import3, - migrate_uuid, ): super().__init__() self.helper: Helpers = helper @@ -33,7 +31,6 @@ class MainPrompt(cmd.Cmd): self.migration_manager: MigrationManager = migration_manager self.controller: Controller = main_controller self.import3: Import3 = import3 - self.migrate_uuid: MigrateUUID = migrate_uuid # overrides the default Prompt self.prompt = "" @@ -122,9 +119,6 @@ class MainPrompt(cmd.Cmd): def do_import3(self, _line): self.import3.start_import() - def do_migrate_uuid(self, _line): - self.migrate_uuid.start_migrate() - def universal_exit(self): logger.info("Stopping all server daemons / threads") Console.info( From 90e0777b9b986473551c512bcd753365adcbe552 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 20:04:12 +0100 Subject: [PATCH 010/342] try to revert back to old migrate history if failed --- app/classes/models/servers.py | 2 +- .../20240216_rework_servers_uuid.py | 78 ++++++++++++------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 92270d91..8802236a 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) class Servers(BaseModel): server_id = CharField(primary_key=True, default=Helpers.create_uuid()) created = DateTimeField(default=datetime.datetime.now) - server_uuid = CharField(default="", index=True) + # server_uuid = CharField(default="", index=True) server_name = CharField(default="Server", index=True) path = CharField(default="") backup_path = CharField(default="") diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index 3d09d902..30ec45a2 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -4,7 +4,7 @@ import peewee import logging from app.classes.shared.console import Console -from app.classes.shared.migration import Migrator +from app.classes.shared.migration import Migrator, MigrateHistory from app.classes.models.management import ( AuditLog, Webhooks, @@ -53,35 +53,47 @@ def migrate(migrator: Migrator, database, **kwargs): table_name = "servers" database = db - # Changes on Server Table - migrator.alter_column_type( - Servers, - "server_id", - peewee.CharField(primary_key=True, default=str(uuid.uuid4())), - ) + try: + # 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())), - ), - ) + # 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())), + ), + ) + + 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.select().order_by(MigrateHistory.id.desc()).get() + ) + last_migration.delete() + return False try: # Changes on Audit Log Table @@ -152,6 +164,10 @@ def migrate(migrator: Migrator, database, **kwargs): logger.error(ex) Console.error("Error while migrating Data from Int to UUID (Foreign Keys)") Console.error(ex) + last_migration = ( + MigrateHistory.select().order_by(MigrateHistory.id.desc()).get() + ) + last_migration.delete() return False try: @@ -166,6 +182,10 @@ def migrate(migrator: Migrator, database, **kwargs): logger.error(ex) Console.error("Error while migrating Data from Int to UUID (Primary Keys)") Console.error(ex) + last_migration = ( + MigrateHistory.select().order_by(MigrateHistory.id.desc()).get() + ) + last_migration.delete() return False # Changes on Server Table From 12627cec8e20ec1d424e5690be74a822a57268f5 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 20:21:38 +0100 Subject: [PATCH 011/342] Fixed the why it was not working --- .../20240216_rework_servers_uuid.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index 30ec45a2..b4c2dee0 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -54,6 +54,9 @@ def migrate(migrator: Migrator, database, **kwargs): database = db try: + logger.error("Migrating Data from Int to UUID (Type Change)") + Console.error("Migrating Data from Int to UUID (Type Change)") + # Changes on Server Table migrator.alter_column_type( Servers, @@ -84,18 +87,20 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) + migrator.run() + 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.select().order_by(MigrateHistory.id.desc()).get() - ) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() return False try: + logger.error("Migrating Data from Int to UUID (Foreign Keys)") + Console.error("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 @@ -164,13 +169,13 @@ def migrate(migrator: Migrator, database, **kwargs): logger.error(ex) Console.error("Error while migrating Data from Int to UUID (Foreign Keys)") Console.error(ex) - last_migration = ( - MigrateHistory.select().order_by(MigrateHistory.id.desc()).get() - ) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() return False try: + logger.error("Migrating Data from Int to UUID (Primary Keys)") + Console.error("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( @@ -182,14 +187,13 @@ def migrate(migrator: Migrator, database, **kwargs): logger.error(ex) Console.error("Error while migrating Data from Int to UUID (Primary Keys)") Console.error(ex) - last_migration = ( - MigrateHistory.select().order_by(MigrateHistory.id.desc()).get() - ) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() return False # Changes on Server Table migrator.drop_columns("servers", ["server_uuid"]) + migrator.run() return True From b30d7b627cec94488cb15d75ab2b99f0b159fb60 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 20:23:23 +0100 Subject: [PATCH 012/342] removing uuid from server model --- app/classes/models/servers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index 8802236a..13d9096a 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -24,7 +24,6 @@ logger = logging.getLogger(__name__) class Servers(BaseModel): 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="") From 3529c9f7fead3a3409912c313635f39946f4f12d Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 20:27:30 +0100 Subject: [PATCH 013/342] Fixing loog messages --- .../20240216_rework_servers_uuid.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index b4c2dee0..ccfce915 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -54,8 +54,8 @@ def migrate(migrator: Migrator, database, **kwargs): database = db try: - logger.error("Migrating Data from Int to UUID (Type Change)") - Console.error("Migrating Data from Int to UUID (Type Change)") + 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( @@ -89,6 +89,9 @@ def migrate(migrator: Migrator, database, **kwargs): 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) @@ -164,6 +167,10 @@ def migrate(migrator: Migrator, database, **kwargs): RoleServers.role_id == role_servers.id and RoleServers.server_id == old_server_id ).execute() + + logger.error("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS") + Console.error("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) @@ -182,6 +189,9 @@ def migrate(migrator: Migrator, database, **kwargs): Servers.server_id == server.server_id ).execute() + logger.error("Migrating Data from Int to UUID (Primary Keys) : SUCCESS") + Console.error("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) @@ -192,8 +202,16 @@ def migrate(migrator: Migrator, database, **kwargs): return False # Changes on Server Table + logger.error("Migrating Data from Int to UUID (Removing UUID Field from Servers)") + Console.error("Migrating Data from Int to UUID (Removing UUID Field from Servers)") migrator.drop_columns("servers", ["server_uuid"]) migrator.run() + logger.error( + "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" + ) + Console.error( + "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" + ) return True From 3bd5de5bf602f6a05fbffeac4513bd887382a1b1 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Fri, 16 Feb 2024 20:36:22 +0100 Subject: [PATCH 014/342] update cryptography in requirements.txt too fix vulnerability --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7b1adcfc..13e46226 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ argon2-cffi==23.1.0 cached_property==1.5.2 colorama==0.4.6 croniter==1.4.1 -cryptography==41.0.7 +cryptography==42.0.0 libgravatar==1.0.4 nh3==0.2.14 packaging==23.2 From d673908ce4fd1662793c8b1de0be15ded5644db8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 17 Feb 2024 22:33:50 -0500 Subject: [PATCH 015/342] Encode password base64 to server --- app/classes/web/public_handler.py | 17 +++++++++++------ app/frontend/templates/public/login.html | 11 ++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 762d3fb1..93db9332 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -1,5 +1,7 @@ import logging import nh3 +import base64 +import binascii from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers @@ -112,10 +114,13 @@ class PublicHandler(BaseHandler): if self.request.query: next_page = "/login?" + self.request.query - # pylint: disable=no-member - entered_username = nh3.clean(self.get_argument("username")) - entered_password = self.get_argument("password") - # pylint: enable=no-member + entered_username = nh3.clean(self.get_argument("username")) # pylint: disable=no-member + try: + entered_password = base64.b64decode(self.get_argument("encPassword")) + except binascii.Error: + error_msg = ("Hello? Hello? Anybody home?" + " Go straight to jail. Do not pass go.") + return self.redirect(f"/login?error_msg={error_msg}") try: user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) @@ -132,7 +137,8 @@ class PublicHandler(BaseHandler): # self.clear_cookie("user_data") self.clear_cookie("token") if self.request.query: - self.redirect(f"/login?error_msg={error_msg}&{self.request.query}") + self.redirect(f"/login?err or_msg={error_msg}" + f"&{self.request.query}") else: self.redirect(f"/login?error_msg={error_msg}") return @@ -175,7 +181,6 @@ class PublicHandler(BaseHandler): else: self.redirect(f"/login?error_msg={error_msg}") return - login_result = self.helper.verify_pass(entered_password, user_data.password) # Valid Login diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 1b39d8c4..45ef5a6f 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -78,9 +78,9 @@ } {% if data['query'] %} -
+ {% else %} - + {% end %} {% raw xsrf_form_html() %}
@@ -170,7 +170,9 @@ bootbox.alert(responseData.data) } - + function encodePass(){ + $("#encPassword").val(btoa($("#password").val())) + } + \ No newline at end of file From f6bb6eafcdc6f730d599e4545270194cfec71459 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 17 Feb 2024 23:08:23 -0500 Subject: [PATCH 016/342] Add default path password check Change decode to utf-8 --- app/classes/web/public_handler.py | 16 ++++++++++++++-- app/translations/en_EN.json | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 93db9332..5280644a 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -116,7 +116,8 @@ class PublicHandler(BaseHandler): entered_username = nh3.clean(self.get_argument("username")) # pylint: disable=no-member try: - entered_password = base64.b64decode(self.get_argument("encPassword")) + entered_password = base64.b64decode( + self.get_argument("encPassword")).decode("utf-8") except binascii.Error: error_msg = ("Hello? Hello? Anybody home?" " Go straight to jail. Do not pass go.") @@ -220,7 +221,18 @@ class PublicHandler(BaseHandler): # self.clear_cookie("user") # self.clear_cookie("user_data") self.clear_cookie("token") - error_msg = "Incorrect username or password. Please try again." + error_msg = ( + self.helper.translation.translate("login", + "incorrect", + self.helper.get_setting("language")) + ) + if entered_password == "app/config/default-creds.txt": + error_msg += ". " + error_msg += ( + self.helper.translation.translate("login", + "defaultPath", + self.helper.get_setting("language")) + ) # log this failed login attempt self.controller.management.add_to_audit_log( user_data.user_id, "Tried to log in", 0, self.get_remote_ip() diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 7d2b2cbb..0b7bf022 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -219,7 +219,9 @@ "login": "Log In", "password": "Password", "username": "Username", - "viewStatus": "View Public Status Page" + "viewStatus": "View Public Status Page", + "incorrect": "Incorrect username or password", + "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location." }, "notify": { "activityLog": "Activity Logs", From 166e3c50887c6477bbfa7ac4807cd245ced3dd67 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 12:03:02 -0500 Subject: [PATCH 017/342] Black codebase --- app/classes/web/public_handler.py | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 5280644a..ebc2929e 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -1,7 +1,7 @@ import logging -import nh3 -import base64 import binascii +import base64 +import nh3 from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers @@ -114,13 +114,18 @@ class PublicHandler(BaseHandler): if self.request.query: next_page = "/login?" + self.request.query - entered_username = nh3.clean(self.get_argument("username")) # pylint: disable=no-member + entered_username = nh3.clean( + self.get_argument("username") + ) # pylint: disable=no-member try: entered_password = base64.b64decode( - self.get_argument("encPassword")).decode("utf-8") + self.get_argument("encPassword") + ).decode("utf-8") except binascii.Error: - error_msg = ("Hello? Hello? Anybody home?" - " Go straight to jail. Do not pass go.") + error_msg = ( + "Hello? Hello? Anybody home?" + " Go straight to jail. Do not pass go." + ) return self.redirect(f"/login?error_msg={error_msg}") try: @@ -138,8 +143,9 @@ class PublicHandler(BaseHandler): # self.clear_cookie("user_data") self.clear_cookie("token") if self.request.query: - self.redirect(f"/login?err or_msg={error_msg}" - f"&{self.request.query}") + self.redirect( + f"/login?err or_msg={error_msg}" f"&{self.request.query}" + ) else: self.redirect(f"/login?error_msg={error_msg}") return @@ -221,17 +227,13 @@ class PublicHandler(BaseHandler): # self.clear_cookie("user") # self.clear_cookie("user_data") self.clear_cookie("token") - error_msg = ( - self.helper.translation.translate("login", - "incorrect", - self.helper.get_setting("language")) + error_msg = self.helper.translation.translate( + "login", "incorrect", self.helper.get_setting("language") ) if entered_password == "app/config/default-creds.txt": error_msg += ". " - error_msg += ( - self.helper.translation.translate("login", - "defaultPath", - self.helper.get_setting("language")) + error_msg += self.helper.translation.translate( + "login", "defaultPath", self.helper.get_setting("language") ) # log this failed login attempt self.controller.management.add_to_audit_log( From d168a7f8f03a5f1bbefedf20a071ba4e75243de8 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 19:19:30 -0500 Subject: [PATCH 018/342] Remove max password length --- app/classes/web/routes/api/auth/login.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/auth/login.py b/app/classes/web/routes/api/auth/login.py index b91b295d..7a27c6f8 100644 --- a/app/classes/web/routes/api/auth/login.py +++ b/app/classes/web/routes/api/auth/login.py @@ -17,7 +17,7 @@ login_schema = { "minLength": 4, "pattern": "^[a-z0-9_]+$", }, - "password": {"type": "string", "maxLength": 20, "minLength": 4}, + "password": {"type": "string", "minLength": 4}, }, "required": ["username", "password"], "additionalProperties": False, From aac35b14d9a786121bdcc6015f0935c920578b31 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:18:47 -0500 Subject: [PATCH 019/342] Change login payload --- app/classes/web/public_handler.py | 163 +++++++++++++---------- app/frontend/templates/public/login.html | 126 ++++++++++-------- app/translations/en_EN.json | 5 +- 3 files changed, 172 insertions(+), 122 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index ebc2929e..87745220 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -1,7 +1,11 @@ import logging import binascii import base64 +import urllib +import json import nh3 +from jsonschema import validate +from jsonschema.exceptions import ValidationError from app.classes.shared.helpers import Helpers from app.classes.models.users import HelperUsers @@ -47,7 +51,7 @@ class PublicHandler(BaseHandler): } if self.request.query: - page_data["query"] = self.request.query + page_data["query"] = self.request.query_arguments.get("next")[0].decode() # sensible defaults template = "public/404.html" @@ -77,11 +81,7 @@ class PublicHandler(BaseHandler): # if we have no page, let's go to login else: - if self.request.query: - self.redirect("/login?" + self.request.query) - else: - self.redirect("/login") - return + return self.redirect("/login") self.render( template, @@ -91,42 +91,74 @@ class PublicHandler(BaseHandler): ) def post(self, page=None): - # pylint: disable=no-member - error = nh3.clean(self.get_argument("error", "Invalid Login!")) - error_msg = nh3.clean(self.get_argument("error_msg", "")) - # pylint: enable=no-member + login_schema = { + "type": "object", + "properties": { + "username": { + "type": "string", + "pattern": "^[a-z0-9_]+$", + }, + "password": {"type": "string"}, + }, + "required": ["username", "password"], + "additionalProperties": False, + } + try: + data = json.loads(self.request.body) + except json.decoder.JSONDecodeError as e: + logger.error( + "Invalid JSON schema for API" + f" login attempt from {self.get_remote_ip()}" + ) + return self.finish_json( + 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} + ) + + try: + validate(data, login_schema) + except ValidationError as e: + logger.error( + "Invalid JSON schema for API" + f" login attempt from {self.get_remote_ip()}" + ) + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID_JSON_SCHEMA", + "error_data": str(e), + }, + ) page_data = { "version": self.helper.get_version_string(), - "error": error, "lang": self.helper.get_setting("language"), "lang_page": self.helper.get_lang_page(self.helper.get_setting("language")), "query": "", } if self.request.query: - page_data["query"] = self.request.query + page_data["query"] = self.request.query_arguments.get("next")[0].decode() if page == "login": + data = json.loads(self.request.body) + auth_log.info( f"User attempting to authenticate from {self.get_remote_ip()}" ) - next_page = "/login" - if self.request.query: - next_page = "/login?" + self.request.query - - entered_username = nh3.clean( - self.get_argument("username") - ) # pylint: disable=no-member + entered_username = nh3.clean(data["username"]) # pylint: disable=no-member try: - entered_password = base64.b64decode( - self.get_argument("encPassword") - ).decode("utf-8") - except binascii.Error: - error_msg = ( - "Hello? Hello? Anybody home?" - " Go straight to jail. Do not pass go." + entered_password = urllib.parse.unquote( + base64.b64decode(data["password"]).decode("utf-8") + ) + except binascii.Error: + return self.finish_json( + 403, + { + "status": "error", + "error": "Hello? Hello? Anybody home?" + " Go straight to jail. Do not pass go.", + }, ) - return self.redirect(f"/login?error_msg={error_msg}") try: user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) @@ -138,18 +170,18 @@ class PublicHandler(BaseHandler): f" Authentication failed from remote IP {self.get_remote_ip()}" " Users does not exist." ) - error_msg = "Incorrect username or password. Please try again." + self.finish_json( + 403, + { + "status": "error", + "error": self.helper.translation.translate( + "login", "incorrect", self.helper.get_setting("language") + ), + }, + ) # self.clear_cookie("user") # self.clear_cookie("user_data") - self.clear_cookie("token") - if self.request.query: - self.redirect( - f"/login?err or_msg={error_msg}" f"&{self.request.query}" - ) - else: - self.redirect(f"/login?error_msg={error_msg}") - return - + return self.clear_cookie("token") # if we don't have a user if not user_data: auth_log.error( @@ -158,15 +190,18 @@ class PublicHandler(BaseHandler): " User does not exist." ) self.controller.log_attempt(self.get_remote_ip(), entered_username) - error_msg = "Incorrect username or password. Please try again." + self.finish_json( + 403, + { + "status": "error", + "error": self.helper.translation.translate( + "login", "incorrect", self.helper.get_setting("language") + ), + }, + ) # self.clear_cookie("user") # self.clear_cookie("user_data") - self.clear_cookie("token") - if self.request.query: - self.redirect(f"/login?error_msg={error_msg}&{self.request.query}") - else: - self.redirect(f"/login?error_msg={error_msg}") - return + return self.clear_cookie("token") # if they are disabled if not user_data.enabled: @@ -176,18 +211,18 @@ class PublicHandler(BaseHandler): " User account disabled" ) self.controller.log_attempt(self.get_remote_ip(), entered_username) - error_msg = ( - "User account disabled. Please contact " - "your system administrator for more info." + self.finish_json( + 403, + { + "status": "error", + "error": self.helper.translation.translate( + "login", "disabled", self.helper.get_setting("language") + ), + }, ) # self.clear_cookie("user") # self.clear_cookie("user_data") - self.clear_cookie("token") - if self.request.query: - self.redirect(f"/login?error_msg={error_msg}&{self.request.query}") - else: - self.redirect(f"/login?error_msg={error_msg}") - return + return self.clear_cookie("token") login_result = self.helper.verify_pass(entered_password, user_data.password) # Valid Login @@ -212,12 +247,9 @@ class PublicHandler(BaseHandler): user_data.user_id, "Logged in", 0, self.get_remote_ip() ) - if self.request.query_arguments.get("next"): - next_page = self.request.query_arguments.get("next")[0].decode() - else: - next_page = "/panel/dashboard" - - self.redirect(next_page) + return self.finish_json( + 200, {"status": "ok", "data": {"message": "login successful!"}} + ) else: auth_log.error( f"User attempted to log into {entered_username}." @@ -239,12 +271,9 @@ class PublicHandler(BaseHandler): self.controller.management.add_to_audit_log( user_data.user_id, "Tried to log in", 0, self.get_remote_ip() ) - if self.request.query: - self.redirect(f"/login?error_msg={error_msg}&{self.request.query}") - else: - self.redirect(f"/login?error_msg={error_msg}") + return self.finish_json( + 403, + {"status": "error", "error": error_msg}, + ) else: - if self.request.query: - self.redirect("/login?" + self.request.query) - else: - self.redirect("/login") + self.redirect("/login?") diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 45ef5a6f..2d4e6c84 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -77,55 +77,49 @@ box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4); } - {% if data['query'] %} - - {% else %} - - {% end %} - {% raw xsrf_form_html() %} -
- -
- -
+ + {% raw xsrf_form_html() %} +
+ +
+
-
- -
- -
+
+
+ +
+
-
- -
- {% if error_msg is not None %} -
- {{error_msg}} -
- {% end %} -
-
-   -
- +
+
+ +
+
+
+
+
+  
+ +
- - - - + + + +
@@ -155,13 +149,13 @@ document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')'; //Register Service worker for mobile app if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', {scope: '/'}) + navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', { scope: '/' }) .then(function (registration) { console.log('Service Worker Registered'); }); } }); - async function resetPass(){ + async function resetPass() { let res = await fetch(`/api/v2/crafty/resetPass/`, { method: 'GET', }); @@ -170,9 +164,38 @@ bootbox.alert(responseData.data) } - function encodePass(){ - $("#encPassword").val(btoa($("#password").val())) - } + $("#login-form").on("submit", async function (e) { + e.preventDefault(); + let loginForm = document.getElementById("login-form"); + + let formData = new FormData(loginForm); + + //Create an object from the form data entries + let formDataObject = Object.fromEntries(formData.entries()); + console.log(formDataObject) + let res = await fetch(`/login`, { + method: 'POST', + headers: { + 'X-XSRFToken': formDataObject._xsrf, + "Content-Type": "application/json" + }, + body: JSON.stringify({ + "username": formDataObject.username, + "password": btoa(encodeURIComponent(formDataObject.password)) + }), + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + console.log("OK") + if ($("#login-form").data("query")) { + location.href = `${$("#login-form").data("query")}`; + } else { + location.href = `/panel/dashboard` + } + } else { + $("#error-field").html(responseData.error); + } + }); - \ No newline at end of file diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 0b7bf022..3ec3b9ed 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -221,7 +221,8 @@ "username": "Username", "viewStatus": "View Public Status Page", "incorrect": "Incorrect username or password", - "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location." + "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.", + "disabled": "User account disabled. Please contact your system administrator for more info." }, "notify": { "activityLog": "Activity Logs", @@ -670,4 +671,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From 1cf773bd40bd7e4fb89b5865305ffff050dab7fc Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:32:25 -0500 Subject: [PATCH 020/342] Update error message --- app/classes/web/public_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 87745220..fc4f940e 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -156,7 +156,8 @@ class PublicHandler(BaseHandler): { "status": "error", "error": "Hello? Hello? Anybody home?" - " Go straight to jail. Do not pass go.", + " Go straight to jail. Do not pass go." + " Uh oh! Stinky 💩", }, ) From 1a7c76dca78e21bfe01909723f706b91b479c62a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:35:36 -0500 Subject: [PATCH 021/342] Add unicode except --- app/classes/web/public_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index fc4f940e..0843e761 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -150,7 +150,7 @@ class PublicHandler(BaseHandler): entered_password = urllib.parse.unquote( base64.b64decode(data["password"]).decode("utf-8") ) - except binascii.Error: + except (binascii.Error, UnicodeDecodeError): return self.finish_json( 403, { From c2c10d46097221ef68c50f5c5688a304b1fe6bf8 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:39:24 -0500 Subject: [PATCH 022/342] Separate errors for unicode/binascii --- app/classes/web/public_handler.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 0843e761..2c04873c 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -150,7 +150,7 @@ class PublicHandler(BaseHandler): entered_password = urllib.parse.unquote( base64.b64decode(data["password"]).decode("utf-8") ) - except (binascii.Error, UnicodeDecodeError): + except binascii.Error: return self.finish_json( 403, { @@ -160,6 +160,14 @@ class PublicHandler(BaseHandler): " Uh oh! Stinky 💩", }, ) + except UnicodeDecodeError: + return self.finish_json( + 403, + { + "status": "error", + "error": "VWggb2ghIFN0aW5reSDwn5Kp", + }, + ) try: user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) From dfe9aeb21203df148923b7245ad5b6eed45cb36d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:40:07 -0500 Subject: [PATCH 023/342] Remove the stinky --- app/classes/web/public_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 2c04873c..2613f732 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -156,8 +156,7 @@ class PublicHandler(BaseHandler): { "status": "error", "error": "Hello? Hello? Anybody home?" - " Go straight to jail. Do not pass go." - " Uh oh! Stinky 💩", + " Go straight to jail. Do not pass go.", }, ) except UnicodeDecodeError: From 5712d56e0e4ce6de44bcd052b2b2111f2bc8f792 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:43:59 -0500 Subject: [PATCH 024/342] Needed to plunge some errors --- app/classes/web/public_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 2613f732..b857508a 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -164,7 +164,7 @@ class PublicHandler(BaseHandler): 403, { "status": "error", - "error": "VWggb2ghIFN0aW5reSDwn5Kp", + "error": "VWggb2ghIFN0aW5reSDwn5Kp 🪠", }, ) From d5c009960b36c67135c5f7369d6d4207f8741583 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:46:19 -0500 Subject: [PATCH 025/342] Clean up forgot to purge --- app/classes/web/public_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index b857508a..21e8aa44 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -164,7 +164,7 @@ class PublicHandler(BaseHandler): 403, { "status": "error", - "error": "VWggb2ghIFN0aW5reSDwn5Kp 🪠", + "error": "VWggb2ghIFN0aW5reS 🪠", }, ) From bfa0c724126ca01944d3cabf70faf2e19cdc6bd0 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 20:52:35 -0500 Subject: [PATCH 026/342] Appease black --- app/classes/models/server_permissions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index eb5e3f35..56f9d8ac 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -172,9 +172,9 @@ class PermissionsServers: RoleServers.server_id, RoleServers.permissions ).where(RoleServers.role_id == role_id) for role_server in role_servers: - permissions_dict[ - role_server.server_id_id - ] = PermissionsServers.get_permissions(role_server.permissions) + permissions_dict[role_server.server_id_id] = ( + PermissionsServers.get_permissions(role_server.permissions) + ) return permissions_dict @staticmethod From 842e576b5c0ed9a41f253718f13124062e556fd6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 21:14:15 -0500 Subject: [PATCH 027/342] Refactor minimum password length --- app/classes/controllers/users_controller.py | 2 +- app/classes/shared/command.py | 10 +++++----- app/classes/shared/helpers.py | 1 + app/classes/shared/main_models.py | 11 +++++++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 5c6dd3d2..5425fbf8 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -52,7 +52,7 @@ class UsersController: }, "password": { "type": "string", - "minLength": 8, + "minLength": self.helper.minimum_password_length, "examples": ["crafty"], "title": "Password", }, diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index 155fe083..95a83047 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -77,11 +77,11 @@ class MainPrompt(cmd.Cmd): # get new password from user new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ") # check to make sure it fits our requirements. - if len(new_pass) > 512: - Console.warning("Passwords must be greater than 6char long and under 512") - return False - if len(new_pass) < 6: - Console.warning("Passwords must be greater than 6char long and under 512") + if len(new_pass) < self.helper.minimum_password_length: + Console.warning( + "Passwords must be greater than" + f" {self.helper.minimum_password_length} char long" + ) return False # grab repeated password input new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ") diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 9c63a323..9dff8356 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -81,6 +81,7 @@ class Helpers: self.update_available = False self.ignored_names = ["crafty_managed.txt", "db_stats"] self.crafty_starting = False + self.minimum_password_length = 8 @staticmethod def auto_installer_fix(ex): diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index c166b7fb..0cced56f 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -18,13 +18,20 @@ class DatabaseBuilder: logger.info("Fresh Install Detected - Creating Default Settings") Console.info("Fresh Install Detected - Creating Default Settings") default_data = self.helper.find_default_password() - if password not in default_data: + if "password" not in default_data: Console.help( "No default password found. Using password created " "by Crafty. Find it in app/config/default-creds.txt" ) username = default_data.get("username", "admin") - password = default_data.get("password", password) + if self.helper.minimum_password_length > default_data.get("password", password): + Console.critical( + "Default password too short" + " using Crafty's created default." + " Find it in app/config/default-creds.txt" + ) + else: + password = default_data.get("password", password) self.users_helper.add_user( username=username, From fcb8b02907ebb355ed7975b42248830c54fc03bb Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 19 Feb 2024 21:14:28 -0500 Subject: [PATCH 028/342] Black codebase --- app/classes/web/panel_handler.py | 248 +++++++++--------- .../web/routes/api/crafty/config/index.py | 20 +- .../routes/api/crafty/config/server_dir.py | 10 +- app/classes/web/routes/api/roles/index.py | 10 +- .../web/routes/api/roles/role/servers.py | 10 +- app/classes/web/server_handler.py | 20 +- 6 files changed, 170 insertions(+), 148 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index a7e54974..e8c93c68 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -345,15 +345,17 @@ class PanelHandler(BaseHandler): self.controller.users.get_user_lang_by_id(exec_user["user_id"]) ), "super_user": superuser, - "api_key": { - "name": api_key.name, - "created": api_key.created, - "server_permissions": api_key.server_permissions, - "crafty_permissions": api_key.crafty_permissions, - "superuser": api_key.superuser, - } - if api_key is not None - else None, + "api_key": ( + { + "name": api_key.name, + "created": api_key.created, + "server_permissions": api_key.server_permissions, + "crafty_permissions": api_key.crafty_permissions, + "superuser": api_key.superuser, + } + if api_key is not None + else None + ), "superuser": superuser, } try: @@ -417,14 +419,14 @@ class PanelHandler(BaseHandler): self.controller.first_login = False if superuser: # TODO: Figure out a better solution try: - page_data[ - "servers" - ] = self.controller.servers.get_all_servers_stats() + page_data["servers"] = ( + self.controller.servers.get_all_servers_stats() + ) except IndexError: self.controller.servers.stats.record_stats() - page_data[ - "servers" - ] = self.controller.servers.get_all_servers_stats() + page_data["servers"] = ( + self.controller.servers.get_all_servers_stats() + ) else: try: user_auth = self.controller.servers.get_authorized_servers_stats( @@ -454,19 +456,19 @@ class PanelHandler(BaseHandler): for server_id in user_order[:]: for server in un_used_servers[:]: if flag == 0: - server["stats"][ - "importing" - ] = self.controller.servers.get_import_status( - str(server["stats"]["server_id"]["server_id"]) + server["stats"]["importing"] = ( + self.controller.servers.get_import_status( + str(server["stats"]["server_id"]["server_id"]) + ) ) server["stats"]["crashed"] = self.controller.servers.is_crashed( str(server["stats"]["server_id"]["server_id"]) ) try: - server["stats"][ - "waiting_start" - ] = self.controller.servers.get_waiting_start( - str(server["stats"]["server_id"]["server_id"]) + server["stats"]["waiting_start"] = ( + self.controller.servers.get_waiting_start( + str(server["stats"]["server_id"]["server_id"]) + ) ) except Exception as e: logger.error(f"Failed to get server waiting to start: {e}") @@ -543,9 +545,9 @@ class PanelHandler(BaseHandler): server_id ) if not self.failed_server: - page_data[ - "server_stats" - ] = self.controller.servers.get_server_stats_by_id(server_id) + page_data["server_stats"] = ( + self.controller.servers.get_server_stats_by_id(server_id) + ) else: server_temp_obj = self.controller.servers.get_server_data_by_id( server_id @@ -611,19 +613,19 @@ class PanelHandler(BaseHandler): "Config": EnumPermissionsServer.CONFIG, "Players": EnumPermissionsServer.PLAYERS, } - page_data[ - "user_permissions" - ] = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) ) if not self.failed_server: - page_data["server_stats"][ - "crashed" - ] = self.controller.servers.is_crashed(server_id) + page_data["server_stats"]["crashed"] = ( + self.controller.servers.is_crashed(server_id) + ) if not self.failed_server: - page_data["server_stats"][ - "server_type" - ] = self.controller.servers.get_server_type_by_id(server_id) + page_data["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) if not subpage: for spage, perm in SUBPAGE_PERMS.items(): @@ -674,23 +676,23 @@ 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["backup_config"] = ( + self.controller.management.get_backup_config(server_id) + ) 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 - ).is_backingup - page_data[ - "backup_stats" - ] = self.controller.servers.get_server_instance_by_id( - server_id - ).send_backup_status() + 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 + ).is_backingup + ) + page_data["backup_stats"] = ( + self.controller.servers.get_server_instance_by_id( + server_id + ).send_backup_status() + ) # makes it so relative path is the only thing shown for file in page_data["exclusions"]: if Helpers.is_os_windows(): @@ -723,10 +725,10 @@ class PanelHandler(BaseHandler): server_id, hours=(days * 24) ) if subpage == "webhooks": - page_data[ - "webhooks" - ] = self.controller.management.get_webhooks_by_server( - server_id, model=True + page_data["webhooks"] = ( + self.controller.management.get_webhooks_by_server( + server_id, model=True + ) ) page_data["triggers"] = WebhookFactory.get_monitored_events() @@ -758,9 +760,9 @@ class PanelHandler(BaseHandler): if not superuser: self.redirect("/panel/error?error=Unauthorized access") page_data["banned_players_html"] = get_banned_players_html() - page_data[ - "banned_players" - ] = self.controller.servers.get_banned_players(server_id) + page_data["banned_players"] = ( + self.controller.servers.get_banned_players(server_id) + ) server_instance = self.controller.servers.get_server_instance_by_id( server_id ) @@ -925,9 +927,9 @@ class PanelHandler(BaseHandler): if item not in page_data["backgrounds"]: page_data["backgrounds"].append(item) page_data["background"] = self.controller.cached_login - page_data[ - "login_opacity" - ] = self.controller.management.get_login_opacity() + page_data["login_opacity"] = ( + self.controller.management.get_login_opacity() + ) page_data["active_link"] = "custom_login" template = "panel/custom_login.html" @@ -959,13 +961,11 @@ class PanelHandler(BaseHandler): page_data["servers"] = [] page_data["servers_all"] = self.controller.servers.get_all_defined_servers() page_data["role-servers"] = [] - page_data[ - "permissions_all" - ] = self.controller.crafty_perms.list_defined_crafty_permissions() + page_data["permissions_all"] = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) page_data["permissions_list"] = set() - page_data[ - "quantity_server" - ] = ( + page_data["quantity_server"] = ( self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long ) page_data["languages"] = [] @@ -1007,10 +1007,10 @@ class PanelHandler(BaseHandler): page_data["server_data"] = self.controller.servers.get_server_data_by_id( server_id ) - page_data[ - "user_permissions" - ] = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) ) page_data["permissions"] = { "Commands": EnumPermissionsServer.COMMANDS, @@ -1025,9 +1025,9 @@ class PanelHandler(BaseHandler): 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["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) page_data["new_webhook"] = True page_data["webhook"] = {} page_data["webhook"]["webhook_type"] = "Custom" @@ -1061,10 +1061,10 @@ class PanelHandler(BaseHandler): page_data["server_data"] = self.controller.servers.get_server_data_by_id( server_id ) - page_data[ - "user_permissions" - ] = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) ) page_data["permissions"] = { "Commands": EnumPermissionsServer.COMMANDS, @@ -1079,9 +1079,9 @@ class PanelHandler(BaseHandler): 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["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) page_data["new_webhook"] = False page_data["webhook"] = self.controller.management.get_webhook_by_id( webhook_id @@ -1121,10 +1121,10 @@ class PanelHandler(BaseHandler): "Config": EnumPermissionsServer.CONFIG, "Players": EnumPermissionsServer.PLAYERS, } - page_data[ - "user_permissions" - ] = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) ) page_data["server_data"] = self.controller.servers.get_server_data_by_id( server_id @@ -1132,9 +1132,9 @@ class PanelHandler(BaseHandler): 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["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) page_data["new_schedule"] = True page_data["schedule"] = {} page_data["schedule"]["children"] = [] @@ -1189,10 +1189,10 @@ class PanelHandler(BaseHandler): "Config": EnumPermissionsServer.CONFIG, "Players": EnumPermissionsServer.PLAYERS, } - page_data[ - "user_permissions" - ] = self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id + page_data["user_permissions"] = ( + self.controller.server_perms.get_user_id_permissions_list( + exec_user["user_id"], server_id + ) ) page_data["server_data"] = self.controller.servers.get_server_data_by_id( server_id @@ -1200,9 +1200,9 @@ class PanelHandler(BaseHandler): 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["server_stats"]["server_type"] = ( + self.controller.servers.get_server_type_by_id(server_id) + ) page_data["new_schedule"] = False page_data["schedule"] = {} page_data["schedule"]["server_id"] = server_id @@ -1212,9 +1212,9 @@ class PanelHandler(BaseHandler): page_data["schedule"]["name"] = schedule.name else: page_data["schedule"]["name"] = "" - page_data["schedule"][ - "children" - ] = self.controller.management.get_child_schedules(sch_id) + page_data["schedule"]["children"] = ( + self.controller.management.get_child_schedules(sch_id) + ) # We check here to see if the command is any of the default ones. # We do not want a user changing to a custom command # and seeing our command there. @@ -1280,16 +1280,16 @@ class PanelHandler(BaseHandler): } if exec_user["superuser"]: page_data["users"] = self.controller.users.get_all_users() - page_data[ - "permissions_all" - ] = self.controller.crafty_perms.list_defined_crafty_permissions() - page_data[ - "permissions_list" - ] = self.controller.crafty_perms.get_crafty_permissions_list(user_id) - page_data[ - "quantity_server" - ] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits( - user_id + page_data["permissions_all"] = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) + page_data["permissions_list"] = ( + self.controller.crafty_perms.get_crafty_permissions_list(user_id) + ) + page_data["quantity_server"] = ( + self.controller.crafty_perms.list_crafty_permissions_quantity_limits( + user_id + ) ) page_data["languages"] = [] page_data["languages"].append( @@ -1349,12 +1349,12 @@ class PanelHandler(BaseHandler): page_data["user"] = self.controller.users.get_user_by_id(user_id) page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id) # self.controller.crafty_perms.list_defined_crafty_permissions() - page_data[ - "server_permissions_all" - ] = self.controller.server_perms.list_defined_permissions() - page_data[ - "crafty_permissions_all" - ] = self.controller.crafty_perms.list_defined_crafty_permissions() + page_data["server_permissions_all"] = ( + self.controller.server_perms.list_defined_permissions() + ) + page_data["crafty_permissions_all"] = ( + self.controller.crafty_perms.list_defined_crafty_permissions() + ) if user_id is None: self.redirect("/panel/error?error=Invalid User ID") @@ -1442,9 +1442,9 @@ class PanelHandler(BaseHandler): DatabaseShortcuts.get_data_obj(server.server_object) ) page_data["servers_all"] = page_servers - page_data[ - "permissions_all" - ] = self.controller.server_perms.list_defined_permissions() + page_data["permissions_all"] = ( + self.controller.server_perms.list_defined_permissions() + ) page_data["permissions_dict"] = {} template = "panel/panel_edit_role.html" @@ -1467,12 +1467,12 @@ class PanelHandler(BaseHandler): DatabaseShortcuts.get_data_obj(server.server_object) ) page_data["servers_all"] = page_servers - page_data[ - "permissions_all" - ] = self.controller.server_perms.list_defined_permissions() - page_data[ - "permissions_dict" - ] = self.controller.server_perms.get_role_permissions_dict(role_id) + page_data["permissions_all"] = ( + self.controller.server_perms.list_defined_permissions() + ) + page_data["permissions_dict"] = ( + self.controller.server_perms.get_role_permissions_dict(role_id) + ) page_data["user-roles"] = user_roles page_data["users"] = self.controller.users.get_all_users() diff --git a/app/classes/web/routes/api/crafty/config/index.py b/app/classes/web/routes/api/crafty/config/index.py index a2bff723..c901732c 100644 --- a/app/classes/web/routes/api/crafty/config/index.py +++ b/app/classes/web/routes/api/crafty/config/index.py @@ -80,9 +80,13 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler): 200, { "status": "ok", - "data": self.controller.roles.get_all_role_ids() - if get_only_ids - else [model_to_dict(r) for r in self.controller.roles.get_all_roles()], + "data": ( + self.controller.roles.get_all_role_ids() + if get_only_ids + else [ + model_to_dict(r) for r in self.controller.roles.get_all_roles() + ] + ), }, ) @@ -158,9 +162,13 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler): 200, { "status": "ok", - "data": self.controller.roles.get_all_role_ids() - if get_only_ids - else [model_to_dict(r) for r in self.controller.roles.get_all_roles()], + "data": ( + self.controller.roles.get_all_role_ids() + if get_only_ids + else [ + model_to_dict(r) for r in self.controller.roles.get_all_roles() + ] + ), }, ) diff --git a/app/classes/web/routes/api/crafty/config/server_dir.py b/app/classes/web/routes/api/crafty/config/server_dir.py index 4e41be14..91c4cc89 100644 --- a/app/classes/web/routes/api/crafty/config/server_dir.py +++ b/app/classes/web/routes/api/crafty/config/server_dir.py @@ -36,9 +36,13 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler): 200, { "status": "ok", - "data": self.controller.roles.get_all_role_ids() - if get_only_ids - else [model_to_dict(r) for r in self.controller.roles.get_all_roles()], + "data": ( + self.controller.roles.get_all_role_ids() + if get_only_ids + else [ + model_to_dict(r) for r in self.controller.roles.get_all_roles() + ] + ), }, ) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index b0c773a7..dce6f453 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -87,9 +87,13 @@ class ApiRolesIndexHandler(BaseApiHandler): 200, { "status": "ok", - "data": self.controller.roles.get_all_role_ids() - if get_only_ids - else [model_to_dict(r) for r in self.controller.roles.get_all_roles()], + "data": ( + self.controller.roles.get_all_role_ids() + if get_only_ids + else [ + model_to_dict(r) for r in self.controller.roles.get_all_roles() + ] + ), }, ) diff --git a/app/classes/web/routes/api/roles/role/servers.py b/app/classes/web/routes/api/roles/role/servers.py index b9b920ca..0a0eff6f 100644 --- a/app/classes/web/routes/api/roles/role/servers.py +++ b/app/classes/web/routes/api/roles/role/servers.py @@ -25,8 +25,12 @@ class ApiRolesRoleServersHandler(BaseApiHandler): 200, { "status": "ok", - "data": PermissionsServers.get_server_ids_from_role(role_id) - if get_only_ids - else self.controller.roles.get_server_ids_and_perms_from_role(role_id), + "data": ( + PermissionsServers.get_server_ids_from_role(role_id) + if get_only_ids + else self.controller.roles.get_server_ids_and_perms_from_role( + role_id + ) + ), }, ) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index e940352e..545029aa 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -118,15 +118,17 @@ class ServerHandler(BaseHandler): "lang_page": Helpers.get_lang_page( self.controller.users.get_user_lang_by_id(exec_user["user_id"]) ), - "api_key": { - "name": api_key.name, - "created": api_key.created, - "server_permissions": api_key.server_permissions, - "crafty_permissions": api_key.crafty_permissions, - "superuser": api_key.superuser, - } - if api_key is not None - else None, + "api_key": ( + { + "name": api_key.name, + "created": api_key.created, + "server_permissions": api_key.server_permissions, + "crafty_permissions": api_key.crafty_permissions, + "superuser": api_key.superuser, + } + if api_key is not None + else None + ), "superuser": superuser, } From 647e3b1111e501c983b2056138063d10ed42162a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Tue, 20 Feb 2024 17:32:34 -0500 Subject: [PATCH 029/342] Remove encoding --- app/classes/web/public_handler.py | 4 +--- app/frontend/templates/public/login.html | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 21e8aa44..26a040e2 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -147,9 +147,7 @@ class PublicHandler(BaseHandler): ) entered_username = nh3.clean(data["username"]) # pylint: disable=no-member try: - entered_password = urllib.parse.unquote( - base64.b64decode(data["password"]).decode("utf-8") - ) + entered_password = data["password"] except binascii.Error: return self.finish_json( 403, diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 2d4e6c84..5a54ecca 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -181,7 +181,7 @@ }, body: JSON.stringify({ "username": formDataObject.username, - "password": btoa(encodeURIComponent(formDataObject.password)) + "password": formDataObject.password }), }); let responseData = await res.json(); From f6242d65072199b4a643c956332381b281edf43a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Tue, 20 Feb 2024 18:47:00 -0500 Subject: [PATCH 030/342] Remove username regex --- app/classes/web/public_handler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 26a040e2..cb2c2593 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -96,7 +96,6 @@ class PublicHandler(BaseHandler): "properties": { "username": { "type": "string", - "pattern": "^[a-z0-9_]+$", }, "password": {"type": "string"}, }, From f4f4f9e0b6e029622044214da6a1dfe6aa1afdbe Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Tue, 20 Feb 2024 18:50:32 -0500 Subject: [PATCH 031/342] Remove extraneous error messaging --- app/classes/web/public_handler.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index cb2c2593..3f46780f 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -124,7 +124,7 @@ class PublicHandler(BaseHandler): 400, { "status": "error", - "error": "INVALID_JSON_SCHEMA", + "error": "VWggb2ghIFN0aW5reS 🪠", "error_data": str(e), }, ) @@ -145,25 +145,7 @@ class PublicHandler(BaseHandler): f"User attempting to authenticate from {self.get_remote_ip()}" ) entered_username = nh3.clean(data["username"]) # pylint: disable=no-member - try: - entered_password = data["password"] - except binascii.Error: - return self.finish_json( - 403, - { - "status": "error", - "error": "Hello? Hello? Anybody home?" - " Go straight to jail. Do not pass go.", - }, - ) - except UnicodeDecodeError: - return self.finish_json( - 403, - { - "status": "error", - "error": "VWggb2ghIFN0aW5reS 🪠", - }, - ) + entered_password = data["password"] try: user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) From 62b55792e5147d94596cf65bc1d014673ab953d6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Tue, 20 Feb 2024 22:28:29 -0500 Subject: [PATCH 032/342] Update spanish translation Sort english translation --- app/translations/en_EN.json | 10 +++++----- .../{es_ES_incomplete.json => es_ES.json} | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) rename app/translations/{es_ES_incomplete.json => es_ES.json} (96%) diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 3ec3b9ed..5c48b873 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -215,14 +215,14 @@ "version": "Version" }, "login": { + "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.", + "disabled": "User account disabled. Please contact your system administrator for more info.", "forgotPassword": "Forgot Password", + "incorrect": "Incorrect username or password", "login": "Log In", "password": "Password", "username": "Username", - "viewStatus": "View Public Status Page", - "incorrect": "Incorrect username or password", - "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.", - "disabled": "User account disabled. Please contact your system administrator for more info." + "viewStatus": "View Public Status Page" }, "notify": { "activityLog": "Activity Logs", @@ -671,4 +671,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} \ No newline at end of file +} diff --git a/app/translations/es_ES_incomplete.json b/app/translations/es_ES.json similarity index 96% rename from app/translations/es_ES_incomplete.json rename to app/translations/es_ES.json index 63f3da36..5f750fc7 100644 --- a/app/translations/es_ES_incomplete.json +++ b/app/translations/es_ES.json @@ -111,6 +111,7 @@ "starting": "Inicio-retrasado", "status": "Estado", "stop": "Detener", + "storage": "Almacenamiento", "version": "Versión", "welcome": "Bienvenido a Crafty Controller" }, @@ -214,7 +215,10 @@ "version": "Versión" }, "login": { + "defaultPath": "La contraseña introducida es la ruta default de las credenciales, no la contraseña. Busca la contraseña accediendo a la carpeta de la ruta", + "disabled": "Cuenta del usuario desactivada. Porfavor contacta al administrador para mas informacion.", "forgotPassword": "Olvidé mi contraseña", + "incorrect": "El nombre de usuario o contraseña es incorrecto", "login": "Iniciar Sesión", "password": "Contraseña", "username": "Usuario", @@ -326,6 +330,7 @@ "bePatientDeleteFiles": "Tenga paciencia mientras eliminamos su servidor del panel de Crafty y eliminamos todos los archivos. Esta pantalla se cerrará en unos momentos.", "bePatientUpdate": "Tenga paciencia mientras actualizamos el servidor. El tiempo de descarga puede variar según la velocidad del Internet...
Esta pantalla se actualizará en unos momentos.", "cancel": "Cancelar", + "countPlayers": "Incluir el servidor en la cuenta total de jugadores", "crashTime": "Tiempo de espera por crasheo", "crashTimeDesc": "¿Cuanto tiempo esperar para considerar el servidor como crasheado?", "deleteFilesQuestion": "¿Eliminar archivos del servidor del host?", @@ -510,6 +515,7 @@ "cpuUsage": "Uso de CPU", "description": "Descripción", "errorCalculatingUptime": "Error calculando tiempo de actividad", + "loadingMotd": "Cargando MOTD", "memUsage": "Uso de memoria", "offline": "Desconectado", "online": "En línea", @@ -577,6 +583,7 @@ "serverUpload": "Subir servidor comprimido", "serverVersion": "Versión del servidor", "sizeInGB": "Tamaño en GB", + "unsupported": "Versiones de Minecraft inferiores a la 1.8 no estan soportadas por Crafty. Es posible instalarlas. Resultados pueden variar.", "uploadButton": "Subir", "uploadZip": "Subir archivo Zip para importar servidor", "zipPath": "Ruta del servidor" @@ -591,6 +598,15 @@ "newServer": "Crear nuevo Servidor", "servers": "Servidores" }, + "startup": { + "almost": "Terminando. Espera un momento...", + "internals": "Configurando e inicializando los componentes internos de Crafty", + "internet": "Verificando conexion a internet", + "server": "Inicializando ", + "serverInit": "Inicializando Servidores", + "starting": "Crafty esta iniciando...", + "tasks": "Iniciando el programador de tareas" + }, "userConfig": { "apiKey": "Claves API", "auth": "¿Autorizado? ", From 900ec15e4c4f28f4cf8d70102d0b851be3c5792f Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Tue, 20 Feb 2024 22:30:32 -0500 Subject: [PATCH 033/342] Fix bug where you cannot get to config with unloaded server --- app/classes/web/panel_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index e8c93c68..077769b2 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -574,6 +574,7 @@ class PanelHandler(BaseHandler): "crash_detection": server_temp_obj["crash_detection"], "show_status": server_temp_obj["show_status"], "ignored_exits": server_temp_obj["ignored_exits"], + "count_players": server_temp_obj["count_players"], }, "running": False, "crashed": False, From 4df7858da6c505a75e0890fe9516f64480325ea0 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 21 Feb 2024 00:04:07 -0500 Subject: [PATCH 034/342] Fix code quality issues --- app/classes/web/public_handler.py | 52 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 3f46780f..467765ea 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -1,7 +1,4 @@ import logging -import binascii -import base64 -import urllib import json import nh3 from jsonschema import validate @@ -237,30 +234,31 @@ class PublicHandler(BaseHandler): return self.finish_json( 200, {"status": "ok", "data": {"message": "login successful!"}} ) - else: - auth_log.error( - f"User attempted to log into {entered_username}." - f" Authentication failed from remote IP {self.get_remote_ip()}" - ) - self.controller.log_attempt(self.get_remote_ip(), entered_username) - # self.clear_cookie("user") - # self.clear_cookie("user_data") - self.clear_cookie("token") - error_msg = self.helper.translation.translate( - "login", "incorrect", self.helper.get_setting("language") - ) - if entered_password == "app/config/default-creds.txt": - error_msg += ". " - error_msg += self.helper.translation.translate( - "login", "defaultPath", self.helper.get_setting("language") - ) - # log this failed login attempt - self.controller.management.add_to_audit_log( - user_data.user_id, "Tried to log in", 0, self.get_remote_ip() - ) - return self.finish_json( - 403, - {"status": "error", "error": error_msg}, + + # We'll continue on and handle unsuccessful logins + auth_log.error( + f"User attempted to log into {entered_username}." + f" Authentication failed from remote IP {self.get_remote_ip()}" + ) + self.controller.log_attempt(self.get_remote_ip(), entered_username) + # self.clear_cookie("user") + # self.clear_cookie("user_data") + self.clear_cookie("token") + error_msg = self.helper.translation.translate( + "login", "incorrect", self.helper.get_setting("language") + ) + if entered_password == "app/config/default-creds.txt": + error_msg += ". " + error_msg += self.helper.translation.translate( + "login", "defaultPath", self.helper.get_setting("language") ) + # log this failed login attempt + self.controller.management.add_to_audit_log( + user_data.user_id, "Tried to log in", 0, self.get_remote_ip() + ) + return self.finish_json( + 403, + {"status": "error", "error": error_msg}, + ) else: self.redirect("/login?") From 6759d840f31d41625ba9f1902e7ccea8c0139ffa Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 21 Feb 2024 09:20:50 +0000 Subject: [PATCH 035/342] Update changelog 900ec15e --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf1ed17..4a9c9fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ TBD - Fix Bedrock cert issues ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719)) - Make sure default.json is read from correct location ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/714)) - 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)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) ### Lang From fb22a753c197b5580a9530496147015297515acc Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 21 Feb 2024 15:37:03 -0500 Subject: [PATCH 036/342] Update de_DE, lol_EN, lv_LV, and tr_TR translations --- app/translations/de_DE.json | 3 +++ app/translations/lol_EN.json | 3 +++ app/translations/lv_LV.json | 3 +++ app/translations/tr_TR.json | 3 +++ 4 files changed, 12 insertions(+) diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index 695264a9..c9ffea15 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -215,7 +215,10 @@ "version": "Version" }, "login": { + "defaultPath": "Der eingegebene Text ist der Pfad zum Passwort, nicht das Passwort selbst. Das Standartpasswort kann unter diesen Pfad eingesehen werden.", + "disabled": "Account gesperrt. Für weitere Informationen den Serveradministrator kontaktieren", "forgotPassword": "Passwort vergessen", + "incorrect": "Benutzername oder Passwort falsch", "login": "Einloggen", "password": "Passwort", "username": "Nutzername", diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index 854e7a09..aa07ff5f 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -215,7 +215,10 @@ "version": "VERSHUN" }, "login": { + "defaultPath": "Silleh hooman, dat iz da dafault secret path, not da passwurd. Plz find da default passwurd in dat spot.", + "disabled": "User account no play. Plz boop ur system hooman for moar infoz.", "forgotPassword": "FORGWOTS YOUR SEEKRET", + "incorrect": "U gotz wrong name or passwurd", "login": "WOG INZ", "password": "SEEKRET", "username": "USERNAEM", diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index 7756cab0..d337d38f 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -216,7 +216,10 @@ "version": "Versija" }, "login": { + "defaultPath": "Parole ko ievadijāt ir celš uz noklusētās paroles vietu, nevis noklusētā parole. Lūdzu apskatiet noklusēto paroli šajā vietā.", + "disabled": "Lietotāja konts atspējots. Lūdzu sazinieties ar savu sistēmas administratoru priekš papildus informācijas.", "forgotPassword": "Aizmirsu Paroli", + "incorrect": "Nepareizs lietotājvārds vai parole", "login": "Ieiet", "password": "Parole", "username": "Lietotājvārds", diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index 3f12fd79..cddd2a50 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -215,7 +215,10 @@ "version": "Sürüm" }, "login": { + "defaultPath": "Girdiğiniz şifre varsayılan şifrenin konumudur, varsayılan şifre değil. Lütfen o konumda bulunan varsayılan şifreyi bulunuz.", + "disabled": "Bu kullanıcı hesabı engellenmiştir. Daha fazla bilgi için lütfen sunucu yöneticiniz ile konuşunuz.", "forgotPassword": "Şifremi Unuttum", + "incorrect": "Kullanıcı adınız veya şifreniz yanlış.", "login": "Oturum Aç", "password": "Şifre", "username": "Kullanıcı Adı", From 669ac6389651fc9c01b09a4bb37deb8cf308363c Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 21 Feb 2024 20:11:13 -0500 Subject: [PATCH 037/342] Add script to fix linux perms --- .gitlab/scripts/linux_perms_fix.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .gitlab/scripts/linux_perms_fix.sh diff --git a/.gitlab/scripts/linux_perms_fix.sh b/.gitlab/scripts/linux_perms_fix.sh new file mode 100644 index 00000000..24b92176 --- /dev/null +++ b/.gitlab/scripts/linux_perms_fix.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Prompt the user for the directory path +read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path + +# Check if the script is running within a Docker container +if [ -f "/.dockerenv" ]; then + echo "Script is running within a Docker container. Exiting with error." + exit 1 # Exit with an error code if running in Docker +else + echo "Script is not running within a Docker container. Executing permissions changes..." + # Run the commands to set permissions + sudo chmod 700 $(find "$directory_path" -type d) + sudo chmod 644 $(find "$directory_path" -type f) +fi \ No newline at end of file From f8f428a8d962980be5517c28bc1554755fec2cd4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 21 Feb 2024 20:19:47 -0500 Subject: [PATCH 038/342] Update french and chinese translations --- app/translations/fr_FR.json | 3 +++ app/translations/zh_CN.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index a1649fbc..be429c83 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -215,7 +215,10 @@ "version": "Version" }, "login": { + "defaultPath": "Ce que tu as renseigné n'est pas le mot de passe, mais le chemin du fichier où le trouver.", + "disabled": "Ce compte est désactivé. Merci de contacter l'administrateur de ton serveur pour plus d'informations.", "forgotPassword": "Mot de Passe Oublié", + "incorrect": "Identifiant et/ou mot de passe incorrect.", "login": "Connexion", "password": "Mot de Passe", "username": "Nom d'Utilisateur", diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 1c800502..0b817b7e 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -215,7 +215,10 @@ "version": "版本" }, "login": { + "defaultPath": "您输入的密码是默认凭据的路径,不是其中的密码。请在此路径中找到默认密码。", + "disabled": "用户账号已禁用。请联系您的系统管理员以了解更多信息。", "forgotPassword": "忘记密码", + "incorrect": "用户名或密码错误", "login": "登录", "password": "密码", "username": "用户名", From 69dc7dfedb2e298b3aee9452c46c51062af0d78c Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 22 Feb 2024 13:06:49 -0500 Subject: [PATCH 039/342] Update Hebrew translation --- app/translations/he_IL.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index f87142ce..7e2b1403 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -215,7 +215,10 @@ "version": "גרסה" }, "login": { + "defaultPath": "הסיסמה שהזנת היא נתיב האישורים המוגדר כברירת מחדל, ולא הסיסמה עצמה. אנא מצא את הסיסמה המוגדרת כברירת מחדל במיקום זה.", + "disabled": "חשבון המשתמש מושבת. אנא פנה למנהל המערכת שלך לקבלת מידע נוסף.", "forgotPassword": "שכחתי סיסמה", + "incorrect": "שם משתמש או סיסמה שגויים", "login": "התחברות", "password": "סיסמה", "username": "שם משתמש", From fdf3221754c36b682eece75cb7e271b546ca8144 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 22 Feb 2024 20:21:54 +0000 Subject: [PATCH 040/342] Fix bug where unix installs could not have a space in file name --- app/frontend/templates/server/bedrock_wizard.html | 2 +- app/frontend/templates/server/wizard.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/frontend/templates/server/bedrock_wizard.html b/app/frontend/templates/server/bedrock_wizard.html index 823a4585..4b11e9d2 100644 --- a/app/frontend/templates/server/bedrock_wizard.html +++ b/app/frontend/templates/server/bedrock_wizard.html @@ -556,7 +556,7 @@ xmlHttpRequest.addEventListener('load', (event) => { if (event.target.responseText == 'success') { console.log('Upload for file', file.name, 'was successful!') - document.getElementById("upload_input").innerHTML = `
🔒
`; + $("#upload_input").html(`
🔒
`); document.getElementById("lower_half").style.visibility = "visible"; } else { diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index 2d84e6aa..d6d64da3 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -881,7 +881,7 @@ xmlHttpRequest.addEventListener('load', (event) => { if (event.target.responseText == 'success') { console.log('Upload for file', file.name, 'was successful!') - document.getElementById("upload_input").innerHTML = `
🔒
`; + $("#upload_input").html(`
🔒
`); document.getElementById("lower_half").style.visibility = "visible"; document.getElementById("lower_half").hidden = false; } From 85a6fca65dc1e20b7c0f5e2264cfcb519f06dbdd Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Fri, 23 Feb 2024 07:44:10 -0600 Subject: [PATCH 041/342] updated polish translation --- app/translations/pl_PL.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 4f430cfe..6c049668 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -215,7 +215,10 @@ "version": "Wersja" }, "login": { + "defaultPath": "Hasło które wprowadziłeś jest podstawową ścieżką w której przechowywane są dane logowania. Znajdź podstawowe hasło w tej lokalizacji.", + "disabled": "Konto tego użytkownika jest wyłączone. Skontaktuj się z administratorem by uzyskać więcej informacji.", "forgotPassword": "Zapomniałem hasła", + "incorrect:": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło", "login": "Zaloguj się", "password": "Hasło", "username": "Nazwa użytkownika", From 5a019c0f27e9431f170abc695d2a2b6b325dc1f8 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Fri, 23 Feb 2024 07:44:41 -0600 Subject: [PATCH 042/342] updated dutch translation --- app/translations/nl_BE.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index 8650fb1a..d20e209c 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -215,7 +215,10 @@ "version": "Versie" }, "login": { + "defaultPath": "Het ingevoerde wachtwoord is het pad naar de standaardreferentie, niet het wachtwoord zelf. Raadpleeg de standaardwachtwoord op de aangegeven locatie.", + "disabled": "Gebruikersaccount uitgeschakeld. Neem voor meer informatie contact op met uw systeembeheerder.", "forgotPassword": "Wachtwoord vergeten", + "incorrect": "Verkeerde gebruikersnaam of wachtwoord", "login": "Log In", "password": "Wachtwoord", "username": "gebruikersnaam", From 5fc24f225382b161ca482841c49e9799f4326a95 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Fri, 23 Feb 2024 07:45:00 -0600 Subject: [PATCH 043/342] updated thai translation --- app/translations/th_TH.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index ba03a856..bbb82dae 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -215,7 +215,10 @@ "version": "เวอร์ชั่น" }, "login": { + "defaultPath": "รหัสผ่านที่คุณกรอกคือเส้นทางข้อมูลเริ่มต้น ไม่ใช่รหัสผ่าน กรุณาค้นหารหัสผ่านเริ่มต้นในตำแหน่งนั้น", + "disabled": "บัญชีผู้ใช้ถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบของคุณสำหรับข้อมูลเพิ่มเติม", "forgotPassword": "ลืมรหัสผ่าน", + "incorrect": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง", "login": "เข้าสู่ระบบ", "password": "รหัสผ่าน", "username": "ชื่อผู้ใช้", From 1ead20b9b5bb4c68f74baab067c78f831f5973fe Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Fri, 23 Feb 2024 07:45:23 -0600 Subject: [PATCH 044/342] updated Ukranian translation --- app/translations/uk_UA.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index 36b95da2..74b683dd 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -85,7 +85,7 @@ "cpuCurFreq": "Швидкість CPU", "cpuMaxFreq": "Максимальна швидкість CPU", "cpuUsage": "Використання CPU", - "crashed": "Аварійне завершення", + "crashed": "Краш", "dashboard": "Панель", "delay-explained": "Служба/агент нещодавно запущено та затримує запуск серверів minecraft", "host": "Хост", @@ -215,7 +215,10 @@ "version": "Версія" }, "login": { + "defaultPath": "Пароль, який ви ввели, є шляхом до облікових даних за умовчанням, а не паролем. Будь ласка, знайдіть стандартний пароль у цьому місці.", + "disabled": "Користувача вимкнено. Зверніться до вашого системного адміністратора за допомогою.", "forgotPassword": "Забули пароль", + "incorrect": "Неправильний логін або пароль", "login": "Вхід", "password": "Пароль", "username": "Логін", @@ -351,7 +354,7 @@ "sendingRequest": "Надсилання вашого запиту...", "serverAutoStart": "Сервер Авто-старт", "serverAutostartDelay": "Сервер Авто-старт затримка", - "serverAutostartDelayDesc": "Затримка Авто-старту сервера (Якщо увімкнуто раніше)", + "serverAutostartDelayDesc": "Затримка Авто-старту сервера (Після запуску Crafty))", "serverCrashDetection": "Детектор крашу сервера", "serverExecutable": "Виконуваний файл Серверу", "serverExecutableDesc": "Це виконуваний файл для запуску сервера", @@ -369,7 +372,7 @@ "serverPortDesc": "Цей порт призначений для статистики Crafty", "serverStopCommand": "Команда зупинки сервера", "serverStopCommandDesc": "Команда яка буде надсилатись, щоб зупинити сервер", - "showStatus": "Показувати на публічній сторінці статус", + "showStatus": "Показувати статус на публічній сторінці", "shutdownTimeout": "Час відклику зупинки", "statsHint1": "Цей порт на якому працює сервер. Це потрібно лиш для того щоб Crafty міг виводити статистику для цього сервера.", "statsHint2": "Це не змінює порт вашого сервера. Ви мусите власноруч змінити налаштування в server.properties або іншому конфігураційному файлі.", @@ -406,7 +409,7 @@ "logs": "Логи", "metrics": "Графік", "playerControls": "Керування Гравцями", - "reset": "Повернутись нагору", + "reset": "Вниз", "schedule": "Розклад", "serverDetails": "Деталі сервера", "terminal": "Термінал" From 440dee7ef5378bd031a0c440522f1281e4b6f737 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Fri, 23 Feb 2024 09:01:40 -0600 Subject: [PATCH 045/342] really fixed polish translation --- app/translations/pl_PL.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 6c049668..ade8c5a5 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -218,7 +218,7 @@ "defaultPath": "Hasło które wprowadziłeś jest podstawową ścieżką w której przechowywane są dane logowania. Znajdź podstawowe hasło w tej lokalizacji.", "disabled": "Konto tego użytkownika jest wyłączone. Skontaktuj się z administratorem by uzyskać więcej informacji.", "forgotPassword": "Zapomniałem hasła", - "incorrect:": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło", + "incorrect": "Niepoprawny login lub hasło/Niepoprawna nazwa użytkownika lub hasło", "login": "Zaloguj się", "password": "Hasło", "username": "Nazwa użytkownika", From 6e28da4195308d3521384b59143d01f7ff4ced28 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 2 Mar 2024 16:53:17 -0500 Subject: [PATCH 046/342] Fix bedrock url --- app/classes/shared/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 1ed3d71f..abe48b2a 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -117,7 +117,7 @@ class Helpers: Get latest bedrock executable url \n\n returns url if successful, False if not """ - url = "https://minecraft.net/en-us/download/server/bedrock/" + url = "https://www.minecraft.net/en-us/download/server/bedrock/" headers = { "Accept-Encoding": "identity", "Accept-Language": "en", @@ -127,8 +127,8 @@ class Helpers: "Chrome/104.0.0.0 Safari/537.36" ), } - target_win = 'https://minecraft.azureedge.net/bin-win/[^"]*' - target_linux = 'https://minecraft.azureedge.net/bin-linux/[^"]*' + target_win = 'https://www.minecraft.azureedge.net/bin-win/[^"]*' + target_linux = 'https://www.minecraft.azureedge.net/bin-linux/[^"]*' try: # Get minecraft server download page From 9f524ff1131bfbfbeec1358a4b6383d6624ec0ab Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 2 Mar 2024 17:00:52 -0500 Subject: [PATCH 047/342] Too many www's --- app/classes/shared/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index abe48b2a..7bf280c4 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -127,8 +127,8 @@ class Helpers: "Chrome/104.0.0.0 Safari/537.36" ), } - target_win = 'https://www.minecraft.azureedge.net/bin-win/[^"]*' - target_linux = 'https://www.minecraft.azureedge.net/bin-linux/[^"]*' + target_win = 'https://minecraft.azureedge.net/bin-win/[^"]*' + target_linux = 'https://minecraft.azureedge.net/bin-linux/[^"]*' try: # Get minecraft server download page From 9134f19a43364d8cb15d5b0a7896b937bd903fd7 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 2 Mar 2024 22:53:55 +0000 Subject: [PATCH 048/342] Update changelog !710 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9c9fc6..97c4cf70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TBD - Make sure default.json is read from correct location ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/714)) - 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)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) ### Lang From a9f2ea0a14dd52b0554daf99dabca05a09571b37 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 2 Mar 2024 23:24:34 +0000 Subject: [PATCH 049/342] Update changelog !715 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c4cf70..d110e9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,11 @@ 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)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) ### Lang -TBD +- Update `de_DE, en_EN, es_ES, fr_FR, he_IL, lol_EN, lv_LV, nl_BE pl_PL, th_TH, tr_TR, uk_UA, zh_CN` translations for `4.3.0` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))

## --- [4.2.3] - 2023/02/02 From 2b2926a1000b87e747286de1eca5d1f6bee2eb59 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 2 Mar 2024 23:47:14 +0000 Subject: [PATCH 050/342] Update changelog !722 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d110e9d5..d57407dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ TBD - 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 `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)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) ### Lang From 270f3308482aa2d93c3c2b57dab75ee233b3ee68 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 00:04:17 +0000 Subject: [PATCH 051/342] Update changelog !723 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d57407dc..bbf7856e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ TBD - 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 `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)) ### Tweaks - Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716)) ### Lang From 8268a769ee19464922a2ad6bb8455d7ba7b32674 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 00:26:23 +0000 Subject: [PATCH 052/342] Bump cryptography for CVE-2024-26130 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30414b35..e3a58bad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ argon2-cffi==23.1.0 cached_property==1.5.2 colorama==0.4.6 croniter==1.4.1 -cryptography==42.0.2 +cryptography==42.0.4 libgravatar==1.0.4 nh3==0.2.14 packaging==23.2 From f8b3c6410511e8f6bca5d738f5f1fb6ce550a48c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 00:27:24 +0000 Subject: [PATCH 053/342] Update changelog !724 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf7856e..abd77b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ TBD - Fix Bedrock downloads, add `www` to download URL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/723)) ### 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)) ### Lang - Update `de_DE, en_EN, es_ES, fr_FR, he_IL, lol_EN, lv_LV, nl_BE pl_PL, th_TH, tr_TR, uk_UA, zh_CN` translations for `4.3.0` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))

From 8d08084ac2a3b0db0ca3c7598758836115430fb9 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 01:10:48 +0000 Subject: [PATCH 054/342] Change target of min pass length check to 'int' of pass length Fixes default password min length check, crashing crafty on fresh install --- app/classes/shared/main_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index 0cced56f..77633a4c 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -24,7 +24,9 @@ class DatabaseBuilder: "by Crafty. Find it in app/config/default-creds.txt" ) username = default_data.get("username", "admin") - if self.helper.minimum_password_length > default_data.get("password", password): + if self.helper.minimum_password_length > len( + default_data.get("password", password) + ): Console.critical( "Default password too short" " using Crafty's created default." From 0353c17d942c9becb45e19491efc61ef3bd78570 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 01:26:53 +0000 Subject: [PATCH 055/342] Update changelog !725 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf7856e..9cf26471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ 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)) ### Tweaks From c20c6b35b8cbfbe574128385c4dda4a639e2ecfb Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 3 Mar 2024 01:36:53 +0000 Subject: [PATCH 056/342] Re-tag release to 4.3.0 BREAKING: Contains db changes w/ no avail rollback --- CHANGELOG.md | 2 +- README.md | 2 +- app/config/version.json | 4 ++-- sonar-project.properties | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abd77b2a..aee7e09d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -## --- [4.2.4] - 2023/TBD +## --- [4.3.0] - 2023/TBD ### New features TBD ### Refactor diff --git a/README.md b/README.md index b1b401d7..75da23a7 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.0 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/config/version.json b/app/config/version.json index 3c001e77..db68adb0 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, - "minor": 2, - "sub": 4 + "minor": 3, + "sub": 0 } diff --git a/sonar-project.properties b/sonar-project.properties index 635324ef..bf2d9c5c 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.0 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/** From 2c289133f5e83f95547409b76e46cf608e97bcae Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 3 Mar 2024 10:00:18 +0100 Subject: [PATCH 057/342] Fixing logging errors to info to display properly logs Remove return true/false values as the function shouldn't return values --- .../20240216_rework_servers_uuid.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index ccfce915..c741a84c 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -99,11 +99,11 @@ def migrate(migrator: Migrator, database, **kwargs): Console.error(ex) last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() - return False + return try: - logger.error("Migrating Data from Int to UUID (Foreign Keys)") - Console.error("Migrating Data from Int to UUID (Foreign Keys)") + 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 @@ -168,8 +168,8 @@ def migrate(migrator: Migrator, database, **kwargs): and RoleServers.server_id == old_server_id ).execute() - logger.error("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS") - Console.error("Migrating Data from Int to UUID (Foreign Keys) : SUCCESS") + 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)") @@ -178,19 +178,19 @@ def migrate(migrator: Migrator, database, **kwargs): Console.error(ex) last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() - return False + return try: - logger.error("Migrating Data from Int to UUID (Primary Keys)") - Console.error("Migrating Data from Int to UUID (Primary Keys)") + 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.error("Migrating Data from Int to UUID (Primary Keys) : SUCCESS") - Console.error("Migrating Data from Int to UUID (Primary Keys) : SUCCESS") + 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)") @@ -199,21 +199,21 @@ def migrate(migrator: Migrator, database, **kwargs): Console.error(ex) last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) last_migration.delete() - return False + return # Changes on Server Table - logger.error("Migrating Data from Int to UUID (Removing UUID Field from Servers)") - Console.error("Migrating Data from Int to UUID (Removing UUID Field from Servers)") + 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.error( + logger.info( "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" ) - Console.error( + Console.info( "Migrating Data from Int to UUID (Removing UUID Field from Servers) : SUCCESS" ) - return True + return def rollback(migrator: Migrator, database, **kwargs): From 0cd3acca132ed7e34083a1e4e2fd28ad570d792a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 7 Mar 2024 11:06:39 -0500 Subject: [PATCH 058/342] Alert webhook AFTER backup has finished --- app/classes/shared/server.py | 12 ++++++------ app/classes/shared/tasks.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) 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() From 19e3f758aaba59ca43cf9087de33962aff4097a3 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Fri, 8 Mar 2024 00:21:57 +0000 Subject: [PATCH 059/342] Update changelog !727 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aee7e09d..82131e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ TBD - Fix Sanitisation on Passwords ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715)) - 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)) From 374984fe1cedb5035cd44901b1b6970f7e0f3cb8 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Fri, 8 Mar 2024 00:59:15 +0000 Subject: [PATCH 060/342] Update changelog -Breaking Change- --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009a27c7..6e08eb96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Changelog -## --- [4.3.0] - 2023/TBD -### New features -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 From 128be0a352beb6501c5b43b5d23fd94bc00d1321 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Fri, 8 Mar 2024 21:32:25 +0000 Subject: [PATCH 061/342] Prepare 4.3.1 release base --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- app/config/version.json | 2 +- sonar-project.properties | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e08eb96..996acb40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog +## --- [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. diff --git a/README.md b/README.md index 75da23a7..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.3.0 +# Crafty Controller 4.3.1 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/config/version.json b/app/config/version.json index db68adb0..9a8d1a7f 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 3, - "sub": 0 + "sub": 1 } diff --git a/sonar-project.properties b/sonar-project.properties index bf2d9c5c..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.3.0 +sonar.projectVersion=4.3.1 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/** From 548a439f14972a8a2db11953412add69df5b16e8 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 8 Mar 2024 22:06:33 -0500 Subject: [PATCH 062/342] 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 063/342] 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 064/342] 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 065/342] 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 066/342] 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 067/342] 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 986576f243228dc380ebca9036ec415e5e134a37 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 10 Mar 2024 12:43:52 +0000 Subject: [PATCH 068/342] Adding migration to fix the rework servers uuid issue --- .../20240310_fixing_rework_servers_uuid.py | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 app/migrations/20240310_fixing_rework_servers_uuid.py diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py new file mode 100644 index 00000000..3c613e52 --- /dev/null +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -0,0 +1,132 @@ +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 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_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 (Fixing Issue)") + Console.info("Migrating Data from Int to UUID (Fixing Issue)") + + # Changes on Servers Roles Table + migrator.alter_column_type( + RoleServers, + "server_id", + peewee.ForeignKeyField( + Servers, + backref="role_server", + null=True, + field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ), + ) + + # Changes on Backups Table + migrator.alter_column_type( + Backups, + "server_id", + peewee.ForeignKeyField( + Servers, + backref="backup_server", + null=True, + field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ), + ) + + # Changes on SChedule Table + migrator.alter_column_type( + Schedules, + "server_id", + peewee.ForeignKeyField( + Servers, + backref="schedule_server", + null=True, + field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())), + ), + ) + + migrator.run() + + logger.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") + Console.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from Int to UUID (Fixing Issue)") + logger.error(ex) + Console.error("Error while migrating Data from Int to UUID (Fixing Issue)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + return + + +def rollback(migrator: Migrator, database, **kwargs): + """ + Write your rollback migrations here. + """ + db = database + + # Changes on Webhook Table + migrator.alter_column_type( + RoleServers, + "server_id", + peewee.IntegerField(null=True), + ) + + # Changes on Webhook Table + migrator.alter_column_type( + Backups, + "server_id", + peewee.IntegerField(null=True), + ) + + # Changes on Webhook Table + migrator.alter_column_type( + Schedules, + "server_id", + peewee.IntegerField(null=True), + ) From 1e2ec6b6438b3975388679d1c960089cadf2bb32 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 10 Mar 2024 12:49:35 +0000 Subject: [PATCH 069/342] Removing the up_one in the constructor to prevent running all migration each time we got a new one. It's handled by migration.up() function. This function was not creating fake migrations. --- app/classes/shared/migration.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 1adea4c6..12d072c9 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -369,11 +369,12 @@ class MigrationManager(object): @cached_property def migrator(self) -> Migrator: """ - Create migrator and setup it with fake migrations. + Create migrator """ migrator = Migrator(self.database) - for name in self.done: - self.up_one(name, migrator, True) + # Removing the up_one to prevent running all migration each time we got a new one. it's handled by migration.up + #for name in self.done: + # self.up_one(name, migrator, True) return migrator def compile(self, name, migrate="", rollback=""): From ae7d0991e40fcddddc507ceaa9afe5f60faf677b Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 10 Mar 2024 12:59:04 +0000 Subject: [PATCH 070/342] Fixint lint issue --- app/classes/shared/migration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 12d072c9..c840e257 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -372,7 +372,8 @@ class MigrationManager(object): Create migrator """ migrator = Migrator(self.database) - # Removing the up_one to prevent running all migration each time we got a new one. it's handled by migration.up + # Removing the up_one to prevent running all migrations + # each time we got a new one. It's handled by migration.up() function. #for name in self.done: # self.up_one(name, migrator, True) return migrator From 36de32e5b1c00a19c77eb537715d484bbb8335c8 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 10 Mar 2024 13:15:17 +0000 Subject: [PATCH 071/342] Trying to appease lint one more time --- app/classes/shared/migration.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index c840e257..8f081982 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -372,9 +372,10 @@ class MigrationManager(object): Create migrator """ migrator = Migrator(self.database) - # Removing the up_one to prevent running all migrations - # each time we got a new one. It's handled by migration.up() function. - #for name in self.done: + # Removing the up_one to prevent running all + # migrations each time we got a new one. + # It's handled by migration.up() function. + # for name in self.done: # self.up_one(name, migrator, True) return migrator From dbe5581e5a6b477a75b297c344f57da445524624 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 10 Mar 2024 22:52:23 +0000 Subject: [PATCH 072/342] Appease Black --- app/classes/shared/migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 8f081982..6a93802b 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -372,8 +372,8 @@ class MigrationManager(object): Create migrator """ migrator = Migrator(self.database) - # Removing the up_one to prevent running all - # migrations each time we got a new one. + # Removing the up_one to prevent running all + # migrations each time we got a new one. # It's handled by migration.up() function. # for name in self.done: # self.up_one(name, migrator, True) From 9704016c54db5858cc31061506cf4abb0dc4a35b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 13 Mar 2024 17:47:07 -0400 Subject: [PATCH 073/342] Update schema for str uuid --- app/classes/web/routes/api/roles/index.py | 4 ++-- app/classes/web/routes/api/roles/role/index.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index dce6f453..266afb23 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -17,7 +17,7 @@ create_role_schema = { "type": "object", "properties": { "server_id": { - "type": "integer", + "type": "string", "minimum": 1, }, "permissions": { @@ -47,7 +47,7 @@ basic_create_role_schema = { "type": "object", "properties": { "server_id": { - "type": "integer", + "type": "string", "minimum": 1, }, "permissions": { diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index 0dd7d6c8..5ed12d69 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -16,7 +16,7 @@ modify_role_schema = { "type": "object", "properties": { "server_id": { - "type": "integer", + "type": "string", "minimum": 1, }, "permissions": { @@ -46,7 +46,7 @@ basic_modify_role_schema = { "type": "object", "properties": { "server_id": { - "type": "integer", + "type": "string", "minimum": 1, }, "permissions": { From c9a46a1f9b5f933e219a683fd77a4c54c7251e2b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 15 Mar 2024 14:47:30 -0400 Subject: [PATCH 074/342] Remove http handler from codebase --- app/classes/web/http_handler.py | 42 ---------------------------- app/classes/web/http_handler_page.py | 33 ---------------------- app/classes/web/tornado_handler.py | 27 ------------------ 3 files changed, 102 deletions(-) delete mode 100644 app/classes/web/http_handler.py delete mode 100644 app/classes/web/http_handler_page.py diff --git a/app/classes/web/http_handler.py b/app/classes/web/http_handler.py deleted file mode 100644 index 32676d59..00000000 --- a/app/classes/web/http_handler.py +++ /dev/null @@ -1,42 +0,0 @@ -import logging -import requests - -from app.classes.web.base_handler import BaseHandler - -logger = logging.getLogger(__name__) - - -class HTTPHandler(BaseHandler): - def get(self): - url = str(self.request.host) - port = 443 - url_list = url.split(":") - if url_list[0] != "": - url = "https://" + url_list[0] - else: - url = "https://" + url - db_port = self.helper.get_setting("https_port") - try: - resp = requests.head(url + ":" + str(port), timeout=(0.5, 5)) - resp.raise_for_status() - except Exception: - port = db_port - self.redirect(url + ":" + str(port)) - - -class HTTPHandlerPage(BaseHandler): - def get(self): - url = str(self.request.host) - port = 443 - url_list = url.split(":") - if url_list[0] != "": - url = "https://" + url_list[0] - else: - url = "https://" + url - db_port = self.helper.get_setting("https_port") - try: - resp = requests.head(url + ":" + str(port), timeout=(0.5, 5)) - resp.raise_for_status() - except Exception: - port = db_port - self.redirect(url + ":" + str(port)) diff --git a/app/classes/web/http_handler_page.py b/app/classes/web/http_handler_page.py deleted file mode 100644 index 77161577..00000000 --- a/app/classes/web/http_handler_page.py +++ /dev/null @@ -1,33 +0,0 @@ -import logging -import requests -from app.classes.web.base_handler import BaseHandler - -logger = logging.getLogger(__name__) - - -class HTTPHandlerPage(BaseHandler): - def get(self): - url = self.request.full_url - port = 443 - if url[len(url) - 1] == "/": - url = url.strip(url[len(url) - 1]) - url_list = url.split("/") - if url_list[0] != "": - primary_url = url_list[0] + ":" + str(port) + "/" - backup_url = ( - url_list[0] + ":" + str(self.helper.get_setting("https_port")) + "/" - ) - for i in range(len(url_list) - 1): - primary_url += url_list[i + 1] - backup_url += url_list[i + 1] - else: - primary_url = url + str(port) - backup_url = url + str(self.helper.get_setting("https_port")) - - try: - resp = requests.head(primary_url, timeout=(0.5, 5)) - resp.raise_for_status() - url = primary_url - except Exception: - url = backup_url - self.redirect("https://" + url + ":" + str(port)) diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index fbcf970f..f65f4fca 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -25,7 +25,6 @@ from app.classes.web.server_handler import ServerHandler from app.classes.web.websocket_handler import WebSocketHandler from app.classes.web.static_handler import CustomStaticHandler from app.classes.web.upload_handler import UploadHandler -from app.classes.web.http_handler import HTTPHandler, HTTPHandlerPage from app.classes.web.status_handler import StatusHandler @@ -44,7 +43,6 @@ class Webserver: file_helper: FileHelpers, ): self.ioloop = None - self.http_server = None self.https_server = None self.helper = helper self.controller = controller @@ -173,30 +171,6 @@ class Webserver: static_handler_class=CustomStaticHandler, serve_traceback=debug_errors, ) - http_handers = [ - (r"/", HTTPHandler, handler_args), - (r"/(.+)", HTTPHandlerPage, handler_args), - ] - http_app = tornado.web.Application( - http_handers, - template_path=os.path.join(self.helper.webroot, "templates"), - static_path=os.path.join(self.helper.webroot, "static"), - debug=debug_errors, - cookie_secret=cookie_secret, - xsrf_cookies=True, - autoreload=False, - log_function=self.log_function, - default_handler_class=HTTPHandler, - login_url="/login", - serve_traceback=debug_errors, - ) - - if http_port != 0: - self.http_server = tornado.httpserver.HTTPServer(http_app) - self.http_server.listen(http_port) - else: - logger.info("http port disabled by config") - self.https_server = tornado.httpserver.HTTPServer(app, ssl_options=cert_objects) self.https_server.listen(https_port) @@ -218,7 +192,6 @@ class Webserver: logger.info("Shutting Down Web Server") Console.info("Shutting Down Web Server") self.ioloop.stop() - self.http_server.stop() self.https_server.stop() logger.info("Web Server Stopped") Console.info("Web Server Stopped") From 6eea6a15d7c0b8c73831bc21101967a71c620702 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 15 Mar 2024 14:49:31 -0400 Subject: [PATCH 075/342] Remove http port from config --- app/classes/shared/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 6b821f9d..0427da11 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -496,7 +496,6 @@ class Helpers: # Config.json was removed from the repo to make it easier for users # To make non-breaking changes to the file. return { - "http_port": 8000, "https_port": 8443, "language": "en_EN", "cookie_expire": 30, From 0a572fba92875f06eff49446a9a6a561b61d9728 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 17 Mar 2024 12:50:11 -0400 Subject: [PATCH 076/342] Refactor API keys "super user" to "full access" --- app/classes/models/crafty_permissions.py | 2 +- app/classes/models/server_permissions.py | 2 +- app/classes/models/users.py | 6 +++--- app/classes/web/base_handler.py | 2 +- app/classes/web/panel_handler.py | 6 +++--- app/classes/web/routes/api/users/user/api.py | 6 +++--- app/classes/web/server_handler.py | 4 ++-- app/classes/web/upload_handler.py | 6 +++--- .../panel/panel_edit_user_apikeys.html | 8 ++++---- app/migrations/20240317_apikey_full_access.py | 17 +++++++++++++++++ 10 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 app/migrations/20240317_apikey_full_access.py diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 7430f332..e7a159d9 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -187,7 +187,7 @@ class PermissionsCrafty: @staticmethod def get_api_key_permissions_list(key: ApiKeys): user = HelperUsers.get_user(key.user_id) - if user["superuser"] and key.superuser: + if user["superuser"] and key.full_access: return PermissionsCrafty.get_permissions_list() if user["superuser"]: # User is superuser but API key isn't diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index 56f9d8ac..12301e30 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -264,7 +264,7 @@ class PermissionsServers: @staticmethod def get_api_key_permissions_list(key: ApiKeys, server_id: str): user = HelperUsers.get_user(key.user_id) - if user["superuser"] and key.superuser: + if user["superuser"] and key.full_access: return PermissionsServers.get_permissions_list() roles_list = HelperUsers.get_user_roles_id(user["user_id"]) role_server = ( diff --git a/app/classes/models/users.py b/app/classes/models/users.py index e44d06fb..3f96e651 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -71,7 +71,7 @@ class ApiKeys(BaseModel): user_id = ForeignKeyField(Users, backref="api_token", index=True) server_permissions = CharField(default="00000000") crafty_permissions = CharField(default="000") - superuser = BooleanField(default=False) + full_access = BooleanField(default=False) class Meta: table_name = "api_keys" @@ -408,7 +408,7 @@ class HelperUsers: def add_user_api_key( name: str, user_id: str, - superuser: bool = False, + full_access: bool = False, server_permissions_mask: t.Optional[str] = None, crafty_permissions_mask: t.Optional[str] = None, ): @@ -426,7 +426,7 @@ class HelperUsers: if crafty_permissions_mask is not None else {} ), - ApiKeys.superuser: superuser, + ApiKeys.full_access: full_access, } ).execute() diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index ced6cb97..6967ebe2 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -191,7 +191,7 @@ class BaseHandler(tornado.web.RequestHandler): superuser = user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access exec_user_role = set() if superuser: diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d61e3c0e..0fe7c8ae 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -168,7 +168,7 @@ class PanelHandler(BaseHandler): # Commented out because there is no server access control for API keys, # they just inherit from the host user # if api_key is not None: - # superuser = superuser and api_key.superuser + # superuser = superuser and api_key.full_access if server_id is None: self.redirect("/panel/error?error=Invalid Server ID") @@ -242,7 +242,7 @@ class PanelHandler(BaseHandler): api_key, _token_data, exec_user = self.current_user superuser = exec_user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access if superuser: # TODO: Figure out a better solution defined_servers = self.controller.servers.list_defined_servers() @@ -351,7 +351,7 @@ class PanelHandler(BaseHandler): "created": api_key.created, "server_permissions": api_key.server_permissions, "crafty_permissions": api_key.crafty_permissions, - "superuser": api_key.superuser, + "full_access": api_key.full_access, } if api_key is not None else None diff --git a/app/classes/web/routes/api/users/user/api.py b/app/classes/web/routes/api/users/user/api.py index 1c7635f2..960cfe32 100644 --- a/app/classes/web/routes/api/users/user/api.py +++ b/app/classes/web/routes/api/users/user/api.py @@ -75,7 +75,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): "name": key.name, "server_permissions": key.server_permissions, "crafty_permissions": key.crafty_permissions, - "superuser": key.superuser, + "full_access": key.full_access, } ) self.finish_json( @@ -99,7 +99,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): "type": "string", "pattern": "^[01]{3}$", # 8 bits, see EnumPermissionsCrafty }, - "superuser": {"type": "boolean"}, + "full_access": {"type": "boolean"}, }, "additionalProperties": False, "minProperties": 1, @@ -163,7 +163,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): key_id = self.controller.users.add_user_api_key( data["name"], user_id, - data["superuser"], + data["full_access"], data["server_permissions_mask"], data["crafty_permissions_mask"], ) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index 545029aa..085d8626 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -30,7 +30,7 @@ class ServerHandler(BaseHandler): ) = self.current_user superuser = exec_user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access if superuser: defined_servers = self.controller.servers.list_defined_servers() @@ -124,7 +124,7 @@ class ServerHandler(BaseHandler): "created": api_key.created, "server_permissions": api_key.server_permissions, "crafty_permissions": api_key.crafty_permissions, - "superuser": api_key.superuser, + "full_access": api_key.full_access, } if api_key is not None else None diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 0667dd12..747fa63b 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -42,7 +42,7 @@ class UploadHandler(BaseHandler): if self.upload_type == "server_import": superuser = exec_user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access user_id = exec_user["user_id"] stream_size_value = self.helper.get_setting("stream_size_GB") @@ -133,7 +133,7 @@ class UploadHandler(BaseHandler): elif self.upload_type == "background": superuser = exec_user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access user_id = exec_user["user_id"] stream_size_value = self.helper.get_setting("stream_size_GB") @@ -212,7 +212,7 @@ class UploadHandler(BaseHandler): server_id = self.get_argument("server_id", None) superuser = exec_user["superuser"] if api_key is not None: - superuser = superuser and api_key.superuser + superuser = superuser and api_key.full_access user_id = exec_user["user_id"] stream_size_value = self.helper.get_setting("stream_size_GB") diff --git a/app/frontend/templates/panel/panel_edit_user_apikeys.html b/app/frontend/templates/panel/panel_edit_user_apikeys.html index 084db0c3..772a07b7 100644 --- a/app/frontend/templates/panel/panel_edit_user_apikeys.html +++ b/app/frontend/templates/panel/panel_edit_user_apikeys.html @@ -70,7 +70,7 @@
{{ translate('backups', 'enabled', + {{ translate('serverBackups', 'enabled', data['lang']) }} Name {{ translate('backups', 'edit', data['lang']) + {{ translate('serverBackups', 'edit', data['lang']) }}
{{ apikey.name }} {{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }} - {% if apikey.superuser %} + {% if apikey.full_access %} {{ translate('apiKeys', 'yes', data['lang']) }} @@ -158,8 +158,8 @@
- - + +
@@ -240,7 +240,7 @@ "name": formDataObject.name, "server_permissions_mask": server_permissions, "crafty_permissions_mask": crafty_permissions, - "superuser": $("#superuser").prop('checked'), + "full_access": $("#full_access").prop('checked'), }); console.log(formDataJsonString); diff --git a/app/migrations/20240317_apikey_full_access.py b/app/migrations/20240317_apikey_full_access.py new file mode 100644 index 00000000..6ae223e1 --- /dev/null +++ b/app/migrations/20240317_apikey_full_access.py @@ -0,0 +1,17 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.rename_column("api_keys", "superuser", "full_access") + + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.rename_column("api_keys", "full_access", "superuser") + """ + Write your rollback migrations here. + """ From 5190fd51d5c9bdfa804ed6e5ee3df54039e6bb9c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Mon, 18 Mar 2024 22:32:25 +0000 Subject: [PATCH 077/342] Update changelog !729 Also fix minor date discrepancy. --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 996acb40..1bee1408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,16 @@ # Changelog -## --- [4.3.1] - 2023/TBD +## --- [4.3.1] - 2024/TBD ### New features TBD ### Bug fixes -TBD +- Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729)) ### Tweaks TBD ### Lang TBD

-## --- [4.3.0] - 2023/03/09 +## --- [4.3.0] - 2024/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. @@ -34,7 +34,7 @@ TBD - Update `de_DE, en_EN, es_ES, fr_FR, he_IL, lol_EN, lv_LV, nl_BE pl_PL, th_TH, tr_TR, uk_UA, zh_CN` translations for `4.3.0` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/715))

-## --- [4.2.3] - 2023/02/02 +## --- [4.2.3] - 2024/02/02 ### New features - Use Papermc Group's API for `paper` & `folia` builds in server builder ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/688)) - Allow omission of player count from Dashboard (e.g. for proxy servers) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/692)) From 68d9ed03e32d22c09edc8d72d0f65bea0db5372e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Mon, 18 Mar 2024 23:38:23 +0000 Subject: [PATCH 078/342] Update changelog !730 --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bee1408..56251f24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,10 @@ # Changelog ## --- [4.3.1] - 2024/TBD -### New features -TBD ### Bug fixes - Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729)) ### Tweaks -TBD -### Lang -TBD +- Remove http re-direct handler. Users should implement nginx configurations for port 80 redirects ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/730)) +

## --- [4.3.0] - 2024/03/09 From a748f37b625999566f122b581c8c8f8adf8353ac Mon Sep 17 00:00:00 2001 From: Zedifus Date: Mon, 18 Mar 2024 23:50:42 +0000 Subject: [PATCH 079/342] Close changelog v4.3.1 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56251f24..46128266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -## --- [4.3.1] - 2024/TBD +## --- [4.3.1] - 2024/03/18 ### Bug fixes - Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729)) ### Tweaks From 9dffc25cc8c37fa6a0df18c79f8fad99e11fee15 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Tue, 19 Mar 2024 00:08:55 +0000 Subject: [PATCH 080/342] Prepare 4.3.2 release base --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- app/config/version.json | 2 +- sonar-project.properties | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46128266..44750a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog +## --- [4.3.2] - 2024/TBD +### New features +TBD +### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ ## --- [4.3.1] - 2024/03/18 ### Bug fixes - Fix Server ID Rework for backups, schedules, and roles (INT ID to UUID migration) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/729)) diff --git a/README.md b/README.md index bc621c7d..ae70bd0e 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.3.1 +# Crafty Controller 4.3.2 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/config/version.json b/app/config/version.json index 9a8d1a7f..8dffa727 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 3, - "sub": 1 + "sub": 2 } diff --git a/sonar-project.properties b/sonar-project.properties index b6fa060c..a5f5e258 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.3.1 +sonar.projectVersion=4.3.2 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/** From 912785b28014f7817b584eea606e517bb42f4aa7 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 21 Mar 2024 18:10:39 -0400 Subject: [PATCH 081/342] Remove http references from codebase --- app/classes/web/routes/api/crafty/config/index.py | 1 - app/classes/web/tornado_handler.py | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/classes/web/routes/api/crafty/config/index.py b/app/classes/web/routes/api/crafty/config/index.py index c901732c..4a9cbd63 100644 --- a/app/classes/web/routes/api/crafty/config/index.py +++ b/app/classes/web/routes/api/crafty/config/index.py @@ -9,7 +9,6 @@ from app.classes.web.base_api_handler import BaseApiHandler config_json_schema = { "type": "object", "properties": { - "http_port": {"type": "integer"}, "https_port": {"type": "integer"}, "language": { "type": "string", diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index f65f4fca..6285edfc 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -98,7 +98,6 @@ class Webserver: # let's verify we have an SSL cert self.helper.create_self_signed_cert() - http_port = self.helper.get_setting("http_port") https_port = self.helper.get_setting("https_port") debug_errors = self.helper.get_setting("show_errors") @@ -110,9 +109,6 @@ class Webserver: cookie_secret = self.helper.random_string_generator(32) HelpersManagement.set_cookie_secret(cookie_secret) - if not http_port and http_port != 0: - http_port = 8000 - if not https_port: https_port = 8443 @@ -125,7 +121,7 @@ class Webserver: ), } - logger.info(f"Starting Web Server on ports http:{http_port} https:{https_port}") + logger.info(f"Starting Web Server on ports https:{https_port}") asyncio.set_event_loop(asyncio.new_event_loop()) From d39f93bfa5f80eeea9faef0276ac830272b24906 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 11:25:05 +0100 Subject: [PATCH 082/342] trying loading only the diff migrations at constructor --- app/classes/shared/migration.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 6a93802b..9cf52857 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -372,11 +372,9 @@ class MigrationManager(object): Create migrator """ migrator = Migrator(self.database) - # Removing the up_one to prevent running all - # migrations each time we got a new one. - # It's handled by migration.up() function. - # for name in self.done: - # self.up_one(name, migrator, True) + # Creating false migrations from the + for name in self.diff: + self.up_one(name, migrator, True) return migrator def compile(self, name, migrate="", rollback=""): From c00cb1a61d2615354b36db0469dd979fac744a19 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 11:30:24 +0100 Subject: [PATCH 083/342] Splitting migration in 2 files to remove migrator.run from it --- .../20240216_rework_servers_uuid.py | 114 ----------- .../20240216_rework_servers_uuid_part2.py | 184 ++++++++++++++++++ 2 files changed, 184 insertions(+), 114 deletions(-) create mode 100644 app/migrations/20240216_rework_servers_uuid_part2.py diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index c741a84c..eea3f336 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -87,8 +87,6 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) - migrator.run() - logger.info("Migrating Data from Int to UUID (Type Change) : SUCCESS") Console.info("Migrating Data from Int to UUID (Type Change) : SUCCESS") @@ -101,118 +99,6 @@ def migrate(migrator: Migrator, database, **kwargs): 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 diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py new file mode 100644 index 00000000..6cba7298 --- /dev/null +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -0,0 +1,184 @@ +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 (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 + + 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), + ) From 8f8feef6de222fbcadf5ea185e66f6bb9529cdd4 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 12:11:43 +0100 Subject: [PATCH 084/342] Solution is disabling update if already done --- app/classes/shared/migration.py | 6 ++++-- app/migrations/20240216_rework_servers_uuid_part2.py | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/classes/shared/migration.py b/app/classes/shared/migration.py index 9cf52857..287bb4f3 100644 --- a/app/classes/shared/migration.py +++ b/app/classes/shared/migration.py @@ -372,8 +372,10 @@ class MigrationManager(object): Create migrator """ migrator = Migrator(self.database) - # Creating false migrations from the - for name in self.diff: + # Running false migrations to retrives the schemes of + # the precedents created tables in the table_dict element + # It's useful to run the new migrations + for name in self.done: self.up_one(name, migrator, True) return migrator diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py index 6cba7298..58f2ebb8 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -22,6 +22,13 @@ def migrate(migrator: Migrator, database, **kwargs): """ db = database + this_migration = MigrateHistory.get(MigrateHistory.name == "20240216_rework_servers_uuid_part2") + if this_migration: + Console.debug("Update database already done, skipping this migration") + return + else : + Console.debug("Update database not done, doing : Migration Data from Int to UUID") + # ********************************************************************************** # Servers New Model from Old (easier to migrate without dunmping Database) # ********************************************************************************** From 5e4dbb1902c93f0ab1051ccfeaaf266f52c34e38 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 12:28:22 +0100 Subject: [PATCH 085/342] Disabling code if already updated to prevent unnecessary errors messages --- app/migrations/20240216_rework_servers_uuid_part2.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py index 58f2ebb8..a3e30cd8 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -22,12 +22,10 @@ def migrate(migrator: Migrator, database, **kwargs): """ db = database - this_migration = MigrateHistory.get(MigrateHistory.name == "20240216_rework_servers_uuid_part2") - if this_migration: - Console.debug("Update database already done, skipping this migration") + this_migration = MigrateHistory.get_or_none(MigrateHistory.name == "20240216_rework_servers_uuid_part2") + if this_migration is not None: + Console.debug("Update database already done, skipping this part") return - else : - Console.debug("Update database not done, doing : Migration Data from Int to UUID") # ********************************************************************************** # Servers New Model from Old (easier to migrate without dunmping Database) From 64a1defb9558bc90da86f24e35873110bd58a24b Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 12:29:16 +0100 Subject: [PATCH 086/342] Removing logging and console to prevent unnecessary messages --- app/migrations/20240216_rework_servers_uuid.py | 6 ------ app/migrations/20240310_fixing_rework_servers_uuid.py | 8 -------- 2 files changed, 14 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index eea3f336..facd0e42 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -54,9 +54,6 @@ def migrate(migrator: Migrator, database, **kwargs): 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, @@ -87,9 +84,6 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) - 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) diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py index 3c613e52..111b43f9 100644 --- a/app/migrations/20240310_fixing_rework_servers_uuid.py +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -48,9 +48,6 @@ def migrate(migrator: Migrator, database, **kwargs): database = db try: - logger.info("Migrating Data from Int to UUID (Fixing Issue)") - Console.info("Migrating Data from Int to UUID (Fixing Issue)") - # Changes on Servers Roles Table migrator.alter_column_type( RoleServers, @@ -87,11 +84,6 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) - migrator.run() - - logger.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") - Console.info("Migrating Data from Int to UUID (Fixing Issue) : SUCCESS") - except Exception as ex: logger.error("Error while migrating Data from Int to UUID (Fixing Issue)") logger.error(ex) From f0086c70f789be4048896149cecd0b77947967fe Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 12:37:46 +0100 Subject: [PATCH 087/342] importing class instead of copy pasting it --- .../20240216_rework_servers_uuid.py | 32 +--------------- .../20240216_rework_servers_uuid_part2.py | 32 +--------------- .../20240310_fixing_rework_servers_uuid.py | 38 ++++--------------- 3 files changed, 10 insertions(+), 92 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index facd0e42..7e93ebf8 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -12,6 +12,7 @@ from app.classes.models.management import ( Backups, ) from app.classes.models.server_permissions import RoleServers +from app.classes.models.servers import Servers logger = logging.getLogger(__name__) @@ -22,37 +23,6 @@ def migrate(migrator: Migrator, database, **kwargs): """ 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: # Changes on Server Table migrator.alter_column_type( diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py index a3e30cd8..a778ce28 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -12,6 +12,7 @@ from app.classes.models.management import ( Backups, ) from app.classes.models.server_permissions import RoleServers +from app.classes.models.servers import Servers logger = logging.getLogger(__name__) @@ -27,37 +28,6 @@ def migrate(migrator: Migrator, database, **kwargs): Console.debug("Update database already done, skipping this part") return - # ********************************************************************************** - # 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 (Foreign Keys)") Console.info("Migrating Data from Int to UUID (Foreign Keys)") diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py index 111b43f9..cae0dec4 100644 --- a/app/migrations/20240310_fixing_rework_servers_uuid.py +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -7,6 +7,7 @@ from app.classes.shared.console import Console from app.classes.shared.migration import Migrator, MigrateHistory from app.classes.models.management import Schedules, Backups from app.classes.models.server_permissions import RoleServers +from app.classes.models.servers import Servers logger = logging.getLogger(__name__) @@ -17,36 +18,6 @@ def migrate(migrator: Migrator, database, **kwargs): """ 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_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: # Changes on Servers Roles Table migrator.alter_column_type( @@ -84,6 +55,9 @@ def migrate(migrator: Migrator, database, **kwargs): ), ) + # Drop Column after migration + migrator.drop_columns("servers", ["server_uuid"]) + except Exception as ex: logger.error("Error while migrating Data from Int to UUID (Fixing Issue)") logger.error(ex) @@ -122,3 +96,7 @@ def rollback(migrator: Migrator, database, **kwargs): "server_id", peewee.IntegerField(null=True), ) + + migrator.add_columns( + "servers", server_uuid=peewee.CharField(default="", index=True) + ) # Recreating the column for roll back From 3a4037a3323e9bb8dc6b15c46b483ef05a7acf6f Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 12:57:41 +0100 Subject: [PATCH 088/342] trying to add rollback methods --- .../20240216_rework_servers_uuid_part2.py | 160 ++++++++++++++++-- 1 file changed, 142 insertions(+), 18 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py index a778ce28..94c78454 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -13,6 +13,7 @@ from app.classes.models.management import ( ) from app.classes.models.server_permissions import RoleServers from app.classes.models.servers import Servers +from app.classes.models.base_model import BaseModel logger = logging.getLogger(__name__) @@ -137,23 +138,146 @@ def rollback(migrator: Migrator, database, **kwargs): """ db = database - # Changes on Server Table - migrator.alter_column_type( - "servers", - "server_id", - peewee.AutoField(), - ) + # Condition to prevent running rollback each time we've got a rollback to do + this_migration = MigrateHistory.get_or_none(MigrateHistory.name == "20240216_rework_servers_uuid_part2") + if this_migration is None: + Console.debug("Update database already done, skipping this part") + return - # Changes on Audit Log Table - migrator.alter_column_type( - AuditLog, - "server_id", - peewee.IntegerField(default=None, index=True), - ) + # ********************************************************************************** + # Old Servers Model + # ********************************************************************************** + class OldServers(BaseModel): + server_id = peewee.AutoField() + created = peewee.DateTimeField(default=datetime.datetime.now) + server_uuid = peewee.CharField(default="", 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) + # created_by = ForeignKeyField(Users, backref="creator_server", null=True) + shutdown_timeout = peewee.IntegerField(default=60) + ignored_exits = peewee.CharField(default="0") + count_players = peewee.BooleanField(default=True) - # Changes on Webhook Table - migrator.alter_column_type( - Webhooks, - "server_id", - peewee.IntegerField(null=True), - ) + class Meta: + table_name = "servers" + + try: + logger.info("Migrating Data from UUID to Int (Primary Keys)") + Console.info("Migrating Data from UUID to Int (Primary Keys)") + # Migrating servers from the old id type to the new one + new_id = 0 + for server in Servers.select(): + new_id += 1 + Servers.update(server_uuid=server.server_id).where( + Servers.server_id == server.server_id + ).execute() + Servers.update(server_id=new_id).where( + Servers.server_id == server.server_id + ).execute() + + logger.info("Migrating Data from UUID to Int (Primary Keys) : SUCCESS") + Console.info("Migrating Data from UUID to Int (Primary Keys) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from UUID to Int (Primary Keys)") + logger.error(ex) + Console.error("Error while migrating Data from UUID to Int (Primary Keys)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + try: + logger.info("Migrating Data from UUID to Int (Foreign Keys)") + Console.info("Migrating Data from UUID to Int (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 is None: + new_server_id = 0 + else: + try: + server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + new_server_id = server.server_id + except: + new_server_id = old_server_id + AuditLog.update(server_id=new_server_id).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 = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + new_server_id = server.server_id + except: + new_server_id = old_server_id + Webhooks.update(server_id=new_server_id).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 = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + new_server_id = server.server_id + except: + new_server_id = old_server_id + Schedules.update(server_id=new_server_id).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 = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + new_server_id = server.server_id + except: + new_server_id = old_server_id + Backups.update(server_id=new_server_id).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 = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + new_server_id = server.server_id + except: + new_server_id = old_server_id + RoleServers.update(server_id=new_server_id).where( + RoleServers.role_id == role_servers.id + and RoleServers.server_id == old_server_id + ).execute() + + logger.info("Migrating Data from UUID to Int (Foreign Keys) : SUCCESS") + Console.info("Migrating Data from UUID to Int (Foreign Keys) : SUCCESS") + + except Exception as ex: + logger.error("Error while migrating Data from UUID to Int (Foreign Keys)") + logger.error(ex) + Console.error("Error while migrating Data from UUID to Int (Foreign Keys)") + Console.error(ex) + last_migration = MigrateHistory.get_by_id(MigrateHistory.select().count()) + last_migration.delete() + return + + return From 8ff83fa04546d9e5e15101962e6befec973eaa87 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 13:14:47 +0100 Subject: [PATCH 089/342] appease black lint --- app/migrations/20240216_rework_servers_uuid_part2.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240216_rework_servers_uuid_part2.py index 94c78454..b2e91f92 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240216_rework_servers_uuid_part2.py @@ -24,7 +24,9 @@ def migrate(migrator: Migrator, database, **kwargs): """ db = database - this_migration = MigrateHistory.get_or_none(MigrateHistory.name == "20240216_rework_servers_uuid_part2") + this_migration = MigrateHistory.get_or_none( + MigrateHistory.name == "20240216_rework_servers_uuid_part2" + ) if this_migration is not None: Console.debug("Update database already done, skipping this part") return @@ -139,7 +141,9 @@ def rollback(migrator: Migrator, database, **kwargs): db = database # Condition to prevent running rollback each time we've got a rollback to do - this_migration = MigrateHistory.get_or_none(MigrateHistory.name == "20240216_rework_servers_uuid_part2") + this_migration = MigrateHistory.get_or_none( + MigrateHistory.name == "20240216_rework_servers_uuid_part2" + ) if this_migration is None: Console.debug("Update database already done, skipping this part") return @@ -211,7 +215,9 @@ def rollback(migrator: Migrator, database, **kwargs): new_server_id = 0 else: try: - server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + server = OldServers.get_or_none( + OldServers.server_uuid == old_server_id + ) new_server_id = server.server_id except: new_server_id = old_server_id From d30b3356378e512f2fb86f72e3f4c7382b9aa7f1 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 13:32:54 +0100 Subject: [PATCH 090/342] renaming the file to run part 1 before part 2 --- ...rs_uuid_part2.py => 20240217_rework_servers_uuid_part2.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename app/migrations/{20240216_rework_servers_uuid_part2.py => 20240217_rework_servers_uuid_part2.py} (98%) diff --git a/app/migrations/20240216_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py similarity index 98% rename from app/migrations/20240216_rework_servers_uuid_part2.py rename to app/migrations/20240217_rework_servers_uuid_part2.py index b2e91f92..7414dfef 100644 --- a/app/migrations/20240216_rework_servers_uuid_part2.py +++ b/app/migrations/20240217_rework_servers_uuid_part2.py @@ -25,7 +25,7 @@ def migrate(migrator: Migrator, database, **kwargs): db = database this_migration = MigrateHistory.get_or_none( - MigrateHistory.name == "20240216_rework_servers_uuid_part2" + MigrateHistory.name == "20240217_rework_servers_uuid_part2" ) if this_migration is not None: Console.debug("Update database already done, skipping this part") @@ -142,7 +142,7 @@ def rollback(migrator: Migrator, database, **kwargs): # Condition to prevent running rollback each time we've got a rollback to do this_migration = MigrateHistory.get_or_none( - MigrateHistory.name == "20240216_rework_servers_uuid_part2" + MigrateHistory.name == "20240217_rework_servers_uuid_part2" ) if this_migration is None: Console.debug("Update database already done, skipping this part") From f71aaeb870ade8e8d8f9f33672034fa994aca7b7 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 13:47:57 +0100 Subject: [PATCH 091/342] Adding back the Servers Model --- .../20240216_rework_servers_uuid.py | 32 ++++++++++- .../20240217_rework_servers_uuid_part2.py | 54 ++++++++++++++----- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index 7e93ebf8..facd0e42 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -12,7 +12,6 @@ from app.classes.models.management import ( Backups, ) from app.classes.models.server_permissions import RoleServers -from app.classes.models.servers import Servers logger = logging.getLogger(__name__) @@ -23,6 +22,37 @@ def migrate(migrator: Migrator, database, **kwargs): """ 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: # Changes on Server Table migrator.alter_column_type( diff --git a/app/migrations/20240217_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py index 7414dfef..2303960c 100644 --- a/app/migrations/20240217_rework_servers_uuid_part2.py +++ b/app/migrations/20240217_rework_servers_uuid_part2.py @@ -12,7 +12,6 @@ from app.classes.models.management import ( Backups, ) from app.classes.models.server_permissions import RoleServers -from app.classes.models.servers import Servers from app.classes.models.base_model import BaseModel logger = logging.getLogger(__name__) @@ -24,6 +23,37 @@ def migrate(migrator: Migrator, database, **kwargs): """ 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 + this_migration = MigrateHistory.get_or_none( MigrateHistory.name == "20240217_rework_servers_uuid_part2" ) @@ -149,12 +179,13 @@ def rollback(migrator: Migrator, database, **kwargs): return # ********************************************************************************** - # Old Servers Model + # Servers New Model from Old (easier to migrate without dunmping Database) # ********************************************************************************** - class OldServers(BaseModel): - server_id = peewee.AutoField() + 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="") @@ -171,13 +202,12 @@ def rollback(migrator: Migrator, database, **kwargs): type = peewee.CharField(default="minecraft-java") show_status = peewee.BooleanField(default=1) created_by = peewee.IntegerField(default=-100) - # created_by = ForeignKeyField(Users, backref="creator_server", null=True) shutdown_timeout = peewee.IntegerField(default=60) ignored_exits = peewee.CharField(default="0") - count_players = peewee.BooleanField(default=True) class Meta: table_name = "servers" + database = db try: logger.info("Migrating Data from UUID to Int (Primary Keys)") @@ -215,8 +245,8 @@ def rollback(migrator: Migrator, database, **kwargs): new_server_id = 0 else: try: - server = OldServers.get_or_none( - OldServers.server_uuid == old_server_id + server = Servers.get_or_none( + Servers.server_uuid == old_server_id ) new_server_id = server.server_id except: @@ -229,7 +259,7 @@ def rollback(migrator: Migrator, database, **kwargs): for webhook in Webhooks.select(): old_server_id = webhook.server_id_id try: - server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + server = Servers.get_or_none(Servers.server_uuid == old_server_id) new_server_id = server.server_id except: new_server_id = old_server_id @@ -241,7 +271,7 @@ def rollback(migrator: Migrator, database, **kwargs): for schedule in Schedules.select(): old_server_id = schedule.server_id_id try: - server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + server = Servers.get_or_none(Servers.server_uuid == old_server_id) new_server_id = server.server_id except: new_server_id = old_server_id @@ -253,7 +283,7 @@ def rollback(migrator: Migrator, database, **kwargs): for backup in Backups.select(): old_server_id = backup.server_id_id try: - server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + server = Servers.get_or_none(Servers.server_uuid == old_server_id) new_server_id = server.server_id except: new_server_id = old_server_id @@ -265,7 +295,7 @@ def rollback(migrator: Migrator, database, **kwargs): for role_servers in RoleServers.select(): old_server_id = role_servers.server_id_id try: - server = OldServers.get_or_none(OldServers.server_uuid == old_server_id) + server = Servers.get_or_none(Servers.server_uuid == old_server_id) new_server_id = server.server_id except: new_server_id = old_server_id From 2bb8e185de2dd77fadd4d6685a3352f27fffe82d Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 14:13:45 +0100 Subject: [PATCH 092/342] Checking if the column is already deleted --- app/migrations/20240310_fixing_rework_servers_uuid.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py index cae0dec4..e524ab75 100644 --- a/app/migrations/20240310_fixing_rework_servers_uuid.py +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -56,7 +56,11 @@ def migrate(migrator: Migrator, database, **kwargs): ) # Drop Column after migration - migrator.drop_columns("servers", ["server_uuid"]) + + servers_columns = db.get_columns('servers') + if not any(column_data.name == "server_uuid" for column_data in servers_columns): + Console.debug("Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part") + migrator.drop_columns("servers", ["server_uuid"]) except Exception as ex: logger.error("Error while migrating Data from Int to UUID (Fixing Issue)") From 9aad5e3d8fb718323f688fbdf93ae1e04bd4d0d7 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 14:20:31 +0100 Subject: [PATCH 093/342] trying something to works form differents update version --- app/migrations/20240217_rework_servers_uuid_part2.py | 5 +++++ app/migrations/20240310_fixing_rework_servers_uuid.py | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/migrations/20240217_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py index 2303960c..c55334e7 100644 --- a/app/migrations/20240217_rework_servers_uuid_part2.py +++ b/app/migrations/20240217_rework_servers_uuid_part2.py @@ -60,6 +60,11 @@ def migrate(migrator: Migrator, database, **kwargs): if this_migration is not None: Console.debug("Update database already done, skipping this part") return + else: + servers_columns = db.get_columns('servers') + if not any(column_data.name == "server_uuid" for column_data in servers_columns): + Console.debug("Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part") + return try: logger.info("Migrating Data from Int to UUID (Foreign Keys)") diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py index e524ab75..2dd5111e 100644 --- a/app/migrations/20240310_fixing_rework_servers_uuid.py +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -56,10 +56,9 @@ def migrate(migrator: Migrator, database, **kwargs): ) # Drop Column after migration - servers_columns = db.get_columns('servers') - if not any(column_data.name == "server_uuid" for column_data in servers_columns): - Console.debug("Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part") + if any(column_data.name == "server_uuid" for column_data in servers_columns): + Console.debug("Servers.server_uuid not deleted before Crafty version 4.3.2, skipping this part") migrator.drop_columns("servers", ["server_uuid"]) except Exception as ex: From 7a7f2371879b91de905beb007fde88ccc9c8cd42 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 14:30:48 +0100 Subject: [PATCH 094/342] appease lint --- .../20240217_rework_servers_uuid_part2.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/migrations/20240217_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py index c55334e7..eab04c28 100644 --- a/app/migrations/20240217_rework_servers_uuid_part2.py +++ b/app/migrations/20240217_rework_servers_uuid_part2.py @@ -61,9 +61,13 @@ def migrate(migrator: Migrator, database, **kwargs): Console.debug("Update database already done, skipping this part") return else: - servers_columns = db.get_columns('servers') - if not any(column_data.name == "server_uuid" for column_data in servers_columns): - Console.debug("Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part") + servers_columns = db.get_columns("servers") + if not any( + column_data.name == "server_uuid" for column_data in servers_columns + ): + Console.debug( + "Servers.server_uuid already deleted in Crafty version 4.3.0, skipping this part" + ) return try: @@ -250,9 +254,7 @@ def rollback(migrator: Migrator, database, **kwargs): new_server_id = 0 else: try: - server = Servers.get_or_none( - Servers.server_uuid == old_server_id - ) + server = Servers.get_or_none(Servers.server_uuid == old_server_id) new_server_id = server.server_id except: new_server_id = old_server_id From a87daa6ef3be1e6c956f0b1266ca67f72f2597dc Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 23 Mar 2024 14:32:39 +0100 Subject: [PATCH 095/342] Format with Black for lint apeasing --- app/migrations/20240310_fixing_rework_servers_uuid.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/migrations/20240310_fixing_rework_servers_uuid.py b/app/migrations/20240310_fixing_rework_servers_uuid.py index 2dd5111e..cbde833b 100644 --- a/app/migrations/20240310_fixing_rework_servers_uuid.py +++ b/app/migrations/20240310_fixing_rework_servers_uuid.py @@ -56,9 +56,11 @@ def migrate(migrator: Migrator, database, **kwargs): ) # Drop Column after migration - servers_columns = db.get_columns('servers') + servers_columns = db.get_columns("servers") if any(column_data.name == "server_uuid" for column_data in servers_columns): - Console.debug("Servers.server_uuid not deleted before Crafty version 4.3.2, skipping this part") + Console.debug( + "Servers.server_uuid not deleted before Crafty version 4.3.2, skipping this part" + ) migrator.drop_columns("servers", ["server_uuid"]) except Exception as ex: From 3edfd4320dda7b473920ed7e7c64f4f598aa0dce Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sun, 24 Mar 2024 20:39:10 +0100 Subject: [PATCH 096/342] Replacing server_id=0 to server_id=None for AuditLog --- app/classes/web/panel_handler.py | 2 +- app/classes/web/public_handler.py | 4 ++-- app/classes/web/routes/api/auth/login.py | 4 ++-- app/classes/web/routes/api/crafty/config/index.py | 4 ++-- app/classes/web/routes/api/crafty/config/server_dir.py | 2 +- app/classes/web/routes/api/roles/index.py | 2 +- app/classes/web/routes/api/roles/role/index.py | 4 ++-- app/classes/web/routes/api/users/index.py | 2 +- app/classes/web/routes/api/users/user/api.py | 6 +++--- app/classes/web/routes/api/users/user/index.py | 4 ++-- app/classes/web/websocket_handler.py | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d61e3c0e..716671c8 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1403,7 +1403,7 @@ class PanelHandler(BaseHandler): self.controller.management.add_to_audit_log( exec_user["user_id"], f"Removed user {target_user['username']} (UID:{user_id})", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) self.redirect("/panel/panel_config") diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 467765ea..21e2d495 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -228,7 +228,7 @@ class PublicHandler(BaseHandler): ) # log this login self.controller.management.add_to_audit_log( - user_data.user_id, "Logged in", 0, self.get_remote_ip() + user_data.user_id, "Logged in", None, self.get_remote_ip() ) return self.finish_json( @@ -254,7 +254,7 @@ class PublicHandler(BaseHandler): ) # log this failed login attempt self.controller.management.add_to_audit_log( - user_data.user_id, "Tried to log in", 0, self.get_remote_ip() + user_data.user_id, "Tried to log in", None, self.get_remote_ip() ) return self.finish_json( 403, diff --git a/app/classes/web/routes/api/auth/login.py b/app/classes/web/routes/api/auth/login.py index 7a27c6f8..7e8131f3 100644 --- a/app/classes/web/routes/api/auth/login.py +++ b/app/classes/web/routes/api/auth/login.py @@ -101,7 +101,7 @@ class ApiAuthLoginHandler(BaseApiHandler): # log this login self.controller.management.add_to_audit_log( - user_data.user_id, "logged in via the API", 0, self.get_remote_ip() + user_data.user_id, "logged in via the API", None, self.get_remote_ip() ) self.finish_json( @@ -119,7 +119,7 @@ class ApiAuthLoginHandler(BaseApiHandler): else: # log this failed login attempt self.controller.management.add_to_audit_log( - user_data.user_id, "Tried to log in", 0, self.get_remote_ip() + user_data.user_id, "Tried to log in", None, self.get_remote_ip() ) self.finish_json( 401, diff --git a/app/classes/web/routes/api/crafty/config/index.py b/app/classes/web/routes/api/crafty/config/index.py index c901732c..e7e0b42f 100644 --- a/app/classes/web/routes/api/crafty/config/index.py +++ b/app/classes/web/routes/api/crafty/config/index.py @@ -129,7 +129,7 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], "edited config.json", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) @@ -226,7 +226,7 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"customized login photo: {data['photo']}/{data['opacity']}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) self.controller.management.set_login_opacity(int(data["opacity"])) diff --git a/app/classes/web/routes/api/crafty/config/server_dir.py b/app/classes/web/routes/api/crafty/config/server_dir.py index 91c4cc89..87835d35 100644 --- a/app/classes/web/routes/api/crafty/config/server_dir.py +++ b/app/classes/web/routes/api/crafty/config/server_dir.py @@ -109,7 +109,7 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler): self.controller.management.add_to_audit_log( auth_data[4]["user_id"], f"updated master servers dir to {new_dir}/servers", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index 266afb23..0f656dbb 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -161,7 +161,7 @@ class ApiRolesIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"created role {role_name} (RID:{role_id})", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index 5ed12d69..a45c297f 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -112,7 +112,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"deleted role with ID {role_id}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) @@ -172,7 +172,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"modified role with ID {role_id}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index f7341d38..fef154a0 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -177,7 +177,7 @@ class ApiUsersIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"added user {username} (UID:{user_id}) with roles {roles}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/routes/api/users/user/api.py b/app/classes/web/routes/api/users/user/api.py index 1c7635f2..9bdafadf 100644 --- a/app/classes/web/routes/api/users/user/api.py +++ b/app/classes/web/routes/api/users/user/api.py @@ -43,7 +43,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): auth_data[4]["user_id"], f"Generated a new API token for the key {key.name} " f"from user with UID: {key.user_id}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) data_key = self.controller.authentication.generate( @@ -173,7 +173,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): f"Added API key {data['name']} with crafty permissions " f"{data['crafty_permissions_mask']}" f" and {data['server_permissions_mask']} for user with UID: {user_id}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) self.finish_json(200, {"status": "ok", "data": {"id": key_id}}) @@ -233,7 +233,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): auth_data[4]["user_id"], f"Removed API key {target_key} " f"(ID: {key_id}) from user {auth_data[4]['user_id']}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index 1b7f6f91..6efee93e 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -94,7 +94,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler): self.controller.management.add_to_audit_log( user["user_id"], f"deleted the user {user_id}", - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) @@ -283,7 +283,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler): f"edited user {user_obj.username} (UID: {user_id})" f"with roles {user_obj.roles}" ), - server_id=0, + server_id=None, source_ip=self.get_remote_ip(), ) diff --git a/app/classes/web/websocket_handler.py b/app/classes/web/websocket_handler.py index cde97584..3e426797 100644 --- a/app/classes/web/websocket_handler.py +++ b/app/classes/web/websocket_handler.py @@ -55,7 +55,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): self.controller.management.add_to_audit_log_raw( "unknown", 0, - 0, + None, "Someone tried to connect via WebSocket without proper authentication", self.get_remote_ip(), ) From 25b64e353c15d40525a7d00da2d601b433a04454 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 24 Mar 2024 17:55:58 -0400 Subject: [PATCH 097/342] Fix backup restores after uuid server IDs --- app/classes/controllers/server_perms_controller.py | 2 +- app/classes/shared/main_controller.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index f6632cd7..37893e9e 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -47,7 +47,7 @@ class ServerPermsController: new_server_id, role.role_id, PermissionsServers.get_permissions_mask( - int(role.role_id), int(old_server_id) + int(role.role_id), old_server_id ), ) # Permissions_Servers.add_role_server( diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index b6e824a5..938ff748 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -1131,7 +1131,7 @@ class Controller: server_obj.path = new_local_server_path failed = False for s in self.servers.failed_servers: - if int(s["server_id"]) == int(server.get("server_id")): + if s["server_id"] == server.get("server_id"): failed = True if not failed: self.servers.update_server(server_obj) From 3639ab92601ffd6474ac048dbe684d8a360f65fa Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 24 Mar 2024 17:56:10 -0400 Subject: [PATCH 098/342] Catch more specific exception --- .../web/routes/api/servers/server/backups/backup/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 05edd3a9..70ceb2b2 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 @@ -203,7 +203,7 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): except JobLookupError as e: logger.info("No active tasks found for server: {e}") self.controller.remove_server(server_id, True) - except Exception as e: + except (FileNotFoundError, NotADirectoryError) as e: return self.finish_json( 400, {"status": "error", "error": f"NO BACKUP FOUND {e}"} ) From 4a134278aeb725bcdb98fe60f2109b96a4602789 Mon Sep 17 00:00:00 2001 From: Dylan Oonk Date: Sun, 24 Mar 2024 22:49:47 +0000 Subject: [PATCH 099/342] remove version disclosure from login --- app/frontend/templates/public/login.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 5a54ecca..275d2000 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -111,8 +111,7 @@
From 089b49c85ed73f76d75fe3543cd8aeca4fbfdb32 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 25 Mar 2024 13:04:09 -0400 Subject: [PATCH 100/342] Disable crafty perms user does not have access to --- app/classes/web/panel_handler.py | 3 +++ app/frontend/templates/panel/panel_edit_user_apikeys.html | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 0fe7c8ae..691e1806 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1356,6 +1356,9 @@ class PanelHandler(BaseHandler): page_data["crafty_permissions_all"] = ( self.controller.crafty_perms.list_defined_crafty_permissions() ) + page_data["user_crafty_permissions"] = ( + self.controller.crafty_perms.get_crafty_permissions_list(user_id) + ) if user_id is None: self.redirect("/panel/error?error=Invalid User ID") diff --git a/app/frontend/templates/panel/panel_edit_user_apikeys.html b/app/frontend/templates/panel/panel_edit_user_apikeys.html index 772a07b7..b1b179ef 100644 --- a/app/frontend/templates/panel/panel_edit_user_apikeys.html +++ b/app/frontend/templates/panel/panel_edit_user_apikeys.html @@ -148,9 +148,15 @@ }} + {% if permission in data['user_crafty_permissions'] %} + {% else %} + + {% end %} {% end %} From 95490d50677318743a9305d92a278281827b8c6d Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Sun, 31 Mar 2024 13:25:17 -0500 Subject: [PATCH 101/342] Updating Italian translation --- .../{it_IT_incomplete.json => it_IT.json} | 140 +++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) rename app/translations/{it_IT_incomplete.json => it_IT.json} (78%) diff --git a/app/translations/it_IT_incomplete.json b/app/translations/it_IT.json similarity index 78% rename from app/translations/it_IT_incomplete.json rename to app/translations/it_IT.json index 99b29d7e..adb59c7d 100644 --- a/app/translations/it_IT_incomplete.json +++ b/app/translations/it_IT.json @@ -53,6 +53,20 @@ "translationTitle": "Traduttore in lingua", "translator": "Traduttori" }, + "customLogin": { + "apply": "Applica", + "backgroundUpload": "Carica sfondo", + "customLoginPage": "Personalizza la pagina di accesso", + "delete": "Cancella", + "labelLoginImage": "Scegli lo sfondo della finestra di accesso", + "loginBackground": "Immagini di sfondo della finestra di accesso", + "loginImage": "Carica un'immagine di sfondo per la schermata di accesso.", + "loginOpacity": "Seleziona l'opacità della finestra di accesso", + "pageTitle": "Pagina di accesso personalizzata", + "preview": "Anteprima", + "select": "Seleziona", + "selectImage": "Seleziona un'immagine" + }, "dashboard": { "actions": "Azioni", "allServers": "Tutti i Server", @@ -75,6 +89,7 @@ "dashboard": "Pannello di Controllo", "delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft", "host": "Host", + "installing": "Installazione...", "kill": "Termina il Processo", "killing": "Terminando il processo...", "lastBackup": "Ultimo:", @@ -96,6 +111,7 @@ "starting": "Avvio ritardato", "status": "Stato", "stop": "Stop", + "storage": "Archiviazione", "version": "Versione", "welcome": "Benvenuto su Crafty Controller" }, @@ -164,20 +180,33 @@ } }, "error": { + "agree": "Conferma", + "bedrockError": "I download di Bedrock non sono disponibili. Si prega di controllare", + "cancel": "Annulla", "contact": "Contact Crafty Control Support via Discord", + "craftyStatus": "Stato di Crafty", + "cronFormat": "Rilevato formato Cron non valido", "embarassing": "Oh my, well, this is embarrassing.", "error": "Error!", "eulaAgree": "Do you agree?", "eulaMsg": "You must agree to the EULA. A copy of the Minecraft EULA is linked under this message.", "eulaTitle": "Agree To EULA", + "fileError": "Il tipo di file deve essere un'immagine.", "fileTooLarge": "Caricamento fallito. File da caricare troppo grande. Contatta un amministratore di sistema per assistenza.", "hereIsTheError": "Here is the error", + "installerJava": "Installazione fallita {} : L'installazione di server Forge richiede Java. Abbiamo rilevato che Java non è installato. Installa Java e riprova.", "internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.", + "migration": "Lo spazio di archiviazione del server principale di Crafty verrà migrato in una nuova posizione. Durante questo periodo tutti gli avvii dei server sono stati sospesi. Aspetta mentre completiamo la migrazione", "no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?", + "noInternet": "Crafty ha problemi ad accedere a internet. La creazione di server è stata disabilitata. Controlla la tua connessione a internet e aggiorna la pagina.", "noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.", "not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?", "portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.", + "privMsg": "e il ", + "serverJars1": "API JAR del server non raggiungibile. Si prega di controllare", + "serverJars2": "per informazioni più aggiornate.", "start-error": "Server {} failed to start with error code: {}", + "superError": "Devi essere un super utente per eseguire questa azione.", "terribleFailure": "What a Terrible Failure!" }, "footer": { @@ -186,10 +215,14 @@ "version": "Versione" }, "login": { + "defaultPath": "La password immessa è il percorso delle credenziali predefinite, non la password. Trova la password predefinita in quella posizione.", + "disabled": "Account utente disabilitato. Per ulteriori informazioni, contatta l'amministratore di sistema.", "forgotPassword": "Password dimenticata", + "incorrect": "nome utente o password errati", "login": "Accedi", "password": "Password", - "username": "Nome utente" + "username": "Nome utente", + "viewStatus": "Visualizza la pagina di stato pubblica" }, "notify": { "activityLog": "Registri di Attività", @@ -201,24 +234,38 @@ "preparingLogs": "Per favore aspetta mentre prepariamo i tuoi registri... Ti invieremo una notifica quando saranno pronti. Potrebbe volerci un po' per installazioni grosse.", "supportLogs": "Registri di supporto" }, + "offline": { + "offline": "Disconnesso", + "pleaseConnect": "Connettiti a internet per usare Crafty." + }, "panelConfig": { "adminControls": "Controllo admin", "allowedServers": "Server permessi", + "apply": "Applica", "assignedRoles": "Ruoli assegnati", "cancel": "Cancella", "clearComms": "Pulisci i comandi non eseguiti", + "custom": "Personalizza Crafty", "delete": "Elimina", "edit": "Modifica", + "enableLang": "Abilita tutte le lingue", "enabled": "Abilitato", + "globalExplain": "Dove Crafty memorizza tutti i file del tuo server. (Aggiungeremo il percorso con /servers/[uuid of server])", + "globalServer": "Directory dei server globali", + "json": "Config.json", + "match": "Le password devono corrispondere", "newRole": "Aggiungi nuovo ruolo", "newUser": "Aggiungi nuovo utente", + "noMounts": "Non mostrare supporti su Dash", "pageTitle": "Configurazioni del pannello", "role": "Ruolo", "roleUsers": "Utenti del ruolo", "roles": "Ruoli", "save": "Salva", + "select": "Seleziona", "superConfirm": "Procedi solo se vuoi che questo utente abbia accesso a TUTTO (tutti gli utenti, server, impostazioni del pannello, ecc...). Può anche revocare i tuoi poteri da superuser.", "superConfirmTitle": "Abilitare lo superuser? Sei sicuro?", + "title": "Configurazione di Crafty", "user": "Utente", "users": "Utenti" }, @@ -242,14 +289,17 @@ "roleTitle": "Impostazioni del ruolo", "roleUserName": "Nome Utente", "roleUsers": "Utenti con il ruolo: ", + "selectManager": "Seleziona un gestore per questo ruolo", "serverAccess": "Abilitato?", "serverName": "Nome del Server", "serversDesc": "Server a cui questo ruolo è consentito l'accesso" }, "serverBackups": { + "after": "Esegui il comando prima del backup", "backupAtMidnight": "Auto-backup a mezzanotte?", "backupNow": "Effettua il Backup Ora!", "backupTask": "Un'azione di backup è cominciata.", + "before": "Esegui il comando dopo il backup", "cancel": "Cancella", "clickExclude": "Clicca per selezionare le esclusioni", "compress": "Comprimi backup", @@ -280,6 +330,7 @@ "bePatientDeleteFiles": "Per favore sii paziente mentre rimuoviamo il tuo server dal pannello di Crafty e cancelliamo tutti i files. Questa schermata si chiuderà in pochi istanti.", "bePatientUpdate": "Per favore sii paziente mentre aggiorniamo il server. I tempi di download possono variare dalla tua velocità di internet.
Questa schermata si aggiornerà a breve", "cancel": "Cancella", + "countPlayers": "Includi il server nel conteggio totale dei giocatori", "crashTime": "Crash Timeout", "crashTimeDesc": "Quanto dobbiamo aspettare per considerare il tuo server crashato per colpa di un timeout?", "deleteFilesQuestion": "Eliminare i file del server dalla macchina?", @@ -289,6 +340,8 @@ "deleteServerQuestionMessage": "Sei sicuro di voler eliminare questo server? Dopo la conferma non puoi tornare indietro...", "exeUpdateURL": "URL di aggiornamento dell'eseguibile del server", "exeUpdateURLDesc": "URL di download diretto per gli aggiornamenti del server.", + "ignoredExits": "Codici di arresto anomalo ignorati", + "ignoredExitsExplain": "I codici di arresto anomalo per il rilevamento degli incidenti di Crafty dovrebbero essere ignorati come un normale 'stop' (separato da virgole)", "javaNoChange": "Non cambiare la versione di Java", "javaVersion": "Cambia la versione di Java attualmente in uso", "javaVersionDesc": "Se vuoi cambiare versione di Java, assicurati che la path dell'eseguibile di Java sia immessa tra 'apostrofi' ('java' di default è esclusa)", @@ -319,7 +372,13 @@ "serverPortDesc": "La Porta a cui Crafty si dovrà collegare per le statistiche", "serverStopCommand": "Comando d'arresto del server", "serverStopCommandDesc": "Comando inviato al server per l'arresto", + "showStatus": "Mostra nella pagina di stato pubblica", + "shutdownTimeout": "Tempo di attesa dello spegnimento", + "statsHint1": "la porta che sta usando il server dovrebbe essere qui. Questo è il modo in cui Crafty apre una connessione al tuo server per le statistiche.", + "statsHint2": "Ciò non modifica la porta del tuo server. È comunque necessario modificare la porta nel file di configurazione del server.", "stopBeforeDeleting": "Per favore arresta il server prima di eliminarlo", + "timeoutExplain1": "Per quanto tempo Crafty attenderà lo spegnimento del tuo server dopo averlo eseguito", + "timeoutExplain2": "comando prima di forzare l'arresto del processo.", "update": "Aggiorna l'eseguibile", "yesDelete": "Sì, elimina", "yesDeleteFiles": "Sì, elimina i files" @@ -345,8 +404,12 @@ "backup": "Backup", "config": "Configura", "files": "Files", + "filter": "Filtra registri", + "filterList": "Parole filtrate", "logs": "Registri", + "metrics": "Metrica", "playerControls": "Gestisci i giocatori", + "reset": "Ripristina scorrimento", "schedule": "Programma", "serverDetails": "Dettagli del Server", "terminal": "Terminale" @@ -383,6 +446,11 @@ "waitUpload": "Per favore aspetta mentre carichiamo i tuoi files... Potrebbe volerci un momento.", "yesDelete": "Sì, conosco le conseguenze" }, + "serverMetrics": { + "resetZoom": "Ripristina ingrandimento", + "zoomHint1": "Per ingrandire il grafico, tieni premuto il tasto Maiusc, quindi usa la rotella di scorrimento.", + "zoomHint2": "In alternativa, tieni premuto il tasto Maiusc, quindi fai clic e trascina l'area su cui desideri ingrandire." + }, "serverPlayerManagement": { "bannedPlayers": "Giocatori Banditi", "loadingBannedPlayers": "Carico i giocatori banditi", @@ -410,23 +478,44 @@ "parent-explain": "Quale azione dovrebbe far eseguire questa?", "reaction": "Reazione", "restart": "Riavvia il Server", + "select": "Seleziona Base / Cron / Reazione a catena", "start": "Avvia il server", "stop": "Arresta il Server", "time": "Orario", "time-explain": "A che ora vuoi eseguire la tua azione programmata?" }, "serverSchedules": { + "action": "Azione", "areYouSure": "Eliminare l'azione programmata?", "cancel": "Cancella", "cannotSee": "Non vedi tutto?", "cannotSeeOnMobile": "Prova a cliccare su un'azione programmata per tutti i dettagli.", + "child": "Schedario secondario con ID ", + "close": "Chiudi", + "command": "Comando", "confirm": "Conferma", - "confirmDelete": "Vuoi eliminare l'azione programmata? Non puoi tornare indietro." + "confirmDelete": "Vuoi eliminare l'azione programmata? Non puoi tornare indietro.", + "create": "Crea nuovo schedario", + "cron": "Stringa Cron", + "delete": "Cancella", + "details": "Dettagli dello schedario", + "edit": "Modifica", + "enabled": "Abilitato", + "every": "Tutto", + "interval": "Intervallo", + "name": "Nome", + "newSchedule": "Nuovo schedario", + "nextRun": "Prossima operazione", + "no": "No", + "no-schedule": "Non sono attualmente presenti schedari per questo server. Per iniziare, clicca", + "scheduledTasks": "Attività schedate", + "yes": "Sì" }, "serverStats": { "cpuUsage": "Utilizzo del Processore", "description": "Descrizione", "errorCalculatingUptime": "Errore nel calcolo dei tempi di operazione", + "loadingMotd": "Caricamento MOTD", "memUsage": "Utilizzo della memoria", "offline": "Offline", "online": "Online", @@ -444,6 +533,8 @@ "commandInput": "Inserisci il comando", "delay-explained": "Il servizio/agente è stato avviato di recente e sta ritardando l'avvio del server di Minecraft", "downloading": "Scaricando...", + "importing": "Importazione...", + "installing": "Installazione...", "restart": "Riavvia", "sendCommand": "Invia il comando", "start": "Avvia", @@ -468,6 +559,7 @@ "importServerButton": "Importa Server!", "importZip": "Importa da un File Zip", "importing": "Importando il Server...", + "labelZipFile": "Scegli il tuo file zip", "maxMem": "Memoria Massima", "minMem": "Memoria minima", "myNewServer": "Il mio nuovo Server", @@ -478,6 +570,7 @@ "save": "Salva", "selectRole": "Seleziona i Ruoli", "selectRoot": "Seleziona la directory radice dell'archivio", + "selectServer": "Seleziona un server", "selectType": "Seleziona un Tipo", "selectVersion": "Seleziona una versione", "selectZipDir": "Seleziona il percorso in memoria in cui scompatteremo i file", @@ -485,9 +578,14 @@ "serverName": "Nome Server", "serverPath": "Percorso del Server", "serverPort": "Porta del Server", + "serverSelect": "Server selezionato", "serverType": "Tipo di Server", + "serverUpload": "Carica server zippato", "serverVersion": "Versione del Server", "sizeInGB": "Dimensione in GB", + "unsupported": "Le versioni di Minecraft inferiori alla 1.8 non sono supportate da Crafty. Puoi comunque installarlo. I risultati varieranno.", + "uploadButton": "Carica", + "uploadZip": "Carica file zip per l'importazione del server", "zipPath": "Percorso del Server" }, "sidebar": { @@ -495,10 +593,20 @@ "credits": "Crediti", "dashboard": "Pannello di controllo", "documentation": "Documentazione", + "inApp": "Documenti in app", "navigation": "Navigazione", "newServer": "Crea un Nuovo Server", "servers": "Servers" }, + "startup": { + "almost": "Finalizzazione. Tieniti forte...", + "internals": "Configurazione e avvio dei componenti interni di Crafty", + "internet": "Controllo connessione a internet", + "server": "Inizializzazione ", + "serverInit": "Inizializzazione server", + "starting": "Avvio di Crafty...", + "tasks": "Avvio attività schedate" + }, "userConfig": { "apiKey": "Chiavi API", "auth": "Autorizzato? ", @@ -519,6 +627,7 @@ "lastLogin": "Ultimo accesso: ", "lastUpdate": "Ultimo aggiornamento: ", "leaveBlank": "Per modificare l'utente senza cambiare la passowrd, lascia il campo in bianco.", + "manager": "Gestore", "member": "È membro?", "notExist": "Non puoi eliminare qualcosa che non esiste!", "pageTitle": "Modifica Utente", @@ -527,6 +636,7 @@ "permName": "Nome del Permesso", "repeat": "Ripeti la Password", "roleName": "Nome del ruolo", + "selectManager": "Seleziona un gestore per l'utente", "super": "Super User", "userLang": "Linguaggio dell'utente", "userName": "Nome utente", @@ -534,6 +644,32 @@ "userRoles": "Ruoli dell'utente", "userRolesDesc": "Ruoli di cui l'utente fa parte.", "userSettings": "Impostazioni Utente", + "userTheme": "Tema IU", "uses": "Numero di usi permessi (-1==Nessun limite)" + }, + "webhooks": { + "areYouSureDel": "Sei sicuro di voler eliminare questo webhook?", + "areYouSureRun": "Sei sicuro di voler testare questo webhook?", + "backup_server": "Backup del server completato", + "bot_name": "Nome del bot", + "color": "Seleziona l'accentuazione del colore", + "crash_detected": "Il server si è arrestato in modo anomalo", + "edit": "Modifica", + "enabled": "Abilitato", + "jar_update": "Eseguibile del server aggiornato", + "kill": "Server arrestato", + "name": "Nome", + "new": "Nuovo webhook", + "newWebhook": "Nuovo webhook", + "no-webhook": "Attualmente non ci sono webhook in questo server. Per iniziare, clicca", + "run": "Testa il webhook", + "send_command": "Comando del server ricevuto", + "start_server": "Server avviato", + "stop_server": "Server spento", + "trigger": "Attivazione a", + "type": "Tipo di Webhook", + "url": "URL del Webhook", + "webhook_body": "Corpo del Webhook", + "webhooks": "Webhook" } } From 50d4b045e702c1ef0e2c4ac91731affebd22e23d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 1 Apr 2024 16:16:33 -0400 Subject: [PATCH 102/342] Update permissions script to include progress --- .gitlab/scripts/linux_perms_fix.sh | 39 +++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/.gitlab/scripts/linux_perms_fix.sh b/.gitlab/scripts/linux_perms_fix.sh index 24b92176..41993fc3 100644 --- a/.gitlab/scripts/linux_perms_fix.sh +++ b/.gitlab/scripts/linux_perms_fix.sh @@ -3,13 +3,44 @@ # Prompt the user for the directory path read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path +# Count the total number of directories +total_dirs=$(find "$directory_path" -type d | wc -l) + +# Count the total number of files +total_files=$(find "$directory_path" -type f | wc -l) + +# Initialize a counter for directories and files +dir_count=0 +file_count=0 + +# Function to print progress +print_progress() { + echo -ne "\rDirectories: $dir_count/$total_dirs Files: $file_count/$total_files" +} + # Check if the script is running within a Docker container if [ -f "/.dockerenv" ]; then echo "Script is running within a Docker container. Exiting with error." exit 1 # Exit with an error code if running in Docker else echo "Script is not running within a Docker container. Executing permissions changes..." - # Run the commands to set permissions - sudo chmod 700 $(find "$directory_path" -type d) - sudo chmod 644 $(find "$directory_path" -type f) -fi \ No newline at end of file + + # Run the commands to set permissions for directories + echo "Changing permissions for directories:" + for dir in $(find "$directory_path" -type d); do + sudo chmod 700 "$dir" + ((dir_count++)) + print_progress + done + + # Run the commands to set permissions for files + echo -e "\nChanging permissions for files:" + for file in $(find "$directory_path" -type f); do + sudo chmod 644 "$file" + ((file_count++)) + print_progress + done + echo "You will now need to execute a chmod +x on all bedrock executables" +fi + +echo "" # Adding a new line after the loop for better readability \ No newline at end of file From 91a601fb5f13efa73d0d7a444abd0242f522f911 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 1 Apr 2024 16:30:12 -0400 Subject: [PATCH 103/342] Remove console spam --- .gitlab/scripts/linux_perms_fix.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitlab/scripts/linux_perms_fix.sh b/.gitlab/scripts/linux_perms_fix.sh index 41993fc3..d727b16b 100644 --- a/.gitlab/scripts/linux_perms_fix.sh +++ b/.gitlab/scripts/linux_perms_fix.sh @@ -4,10 +4,10 @@ read -p "Enter the directory path to set permissions (/var/opt/minecraft/crafty): " directory_path # Count the total number of directories -total_dirs=$(find "$directory_path" -type d | wc -l) +total_dirs=$(find "$directory_path" -type d 2>/dev/null | wc -l) # Count the total number of files -total_files=$(find "$directory_path" -type f | wc -l) +total_files=$(find "$directory_path" -type f 2>/dev/null | wc -l) # Initialize a counter for directories and files dir_count=0 @@ -27,17 +27,19 @@ else # Run the commands to set permissions for directories echo "Changing permissions for directories:" - for dir in $(find "$directory_path" -type d); do - sudo chmod 700 "$dir" - ((dir_count++)) + for dir in $(find "$directory_path" -type d 2>/dev/null); do + if [ -e "$dir" ]; then + sudo chmod 700 "$dir" && ((dir_count++)) + fi print_progress done # Run the commands to set permissions for files echo -e "\nChanging permissions for files:" - for file in $(find "$directory_path" -type f); do - sudo chmod 644 "$file" - ((file_count++)) + for file in $(find "$directory_path" -type f 2>/dev/null); do + if [ -e "$file" ]; then + sudo chmod 644 "$file" && ((file_count++)) + fi print_progress done echo "You will now need to execute a chmod +x on all bedrock executables" From 432421f36241cfe62174f906f079140c7f2b986b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 1 Apr 2024 17:02:27 -0400 Subject: [PATCH 104/342] Fix server clones following uuid migration --- .../web/routes/api/servers/server/action.py | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index 01ce45c4..7de2697d 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -67,20 +67,29 @@ class ApiServersServerActionHandler(BaseApiHandler): name_counter += 1 new_server_name = server_data.get("server_name") + f" (Copy {name_counter})" - 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_id = self.helper.create_uuid() + new_server_path = os.path.join(self.helper.servers_dir, new_server_id) + new_backup_path = os.path.join(self.helper.backup_path, new_server_id) + new_server_command = str(server_data.get("execution_command")).replace( + server_id, new_server_id + ) + new_server_log_path = server_data.get("log_path").replace( + server_id, new_server_id ) - new_server_path = os.path.join(self.helper.servers_dir, new_server_id) + self.controller.register_server( + new_server_name, + new_server_id, + new_server_path, + new_backup_path, + new_server_command, + server_data.get("executable"), + new_server_log_path, + server_data.get("stop_command"), + server_data.get("server_port"), + user_id, + server_data.get("type"), + ) self.controller.management.add_to_audit_log( user_id, @@ -92,18 +101,6 @@ class ApiServersServerActionHandler(BaseApiHandler): # copy the old server FileHelpers.copy_dir(server_data.get("path"), new_server_path) - # TODO get old server DB data to individual variables - new_server_command = str(server_data.get("execution_command")) - new_server_log_file = str( - self.helper.get_os_understandable_path(server_data.get("log_path")) - ) - - 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 From 50ccac8ea5faa0550f1f6176a8859e9fdb49d692 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Mon, 1 Apr 2024 16:58:37 -0500 Subject: [PATCH 105/342] update czech language --- .../{cs_CS_incomplete.json => cs_CS.json} | 90 ++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) rename app/translations/{cs_CS_incomplete.json => cs_CS.json} (85%) diff --git a/app/translations/cs_CS_incomplete.json b/app/translations/cs_CS.json similarity index 85% rename from app/translations/cs_CS_incomplete.json rename to app/translations/cs_CS.json index 879b78cd..2229d5cb 100644 --- a/app/translations/cs_CS_incomplete.json +++ b/app/translations/cs_CS.json @@ -53,6 +53,20 @@ "translationTitle": "Překlad do jazyků", "translator": "Překladatelé" }, + "customLogin": { + "apply": "použít", + "backgroundUpload": "Nahrání pozadí", + "customLoginPage": "Udělej si vlastní přihlašovací stránku", + "delete": "Smazat", + "labelLoginImage": "Vyber si svoji přihlašovací stránku", + "loginBackground": "Pozadí pro přihlášeni", + "loginImage": "Nahrání pozadí pro přihlašovací stránku.", + "loginOpacity": "Nastavení transparentnosti přihlašovacího okna", + "pageTitle": "Vlastní přihlašovací stránka", + "preview": "Náhled", + "select": "Vybrat", + "selectImage": "Vybrat obrázek" + }, "dashboard": { "actions": "Akce", "allServers": "Všechny servery", @@ -97,6 +111,7 @@ "starting": "Zpožděný start", "status": "Stav", "stop": "Zastavit", + "storage": "úložiště", "version": "Verze", "welcome": "Vítejte v Crafty Controlleru" }, @@ -181,8 +196,11 @@ }, "error": { "agree": "Souhlasím", + "bedrockError": "Stažení Bedrocku není dostupné. Prosím zkontrolujte", "cancel": "Zrušit", "contact": "Kontaktujte podporu Crafty přes Discord", + "craftyStatus": "Crafty stav systémů", + "cronFormat": "Neplatný formát Cron", "embarassing": "Ach jo, no, to je trapné.", "error": "Chyba!", "eulaAgree": "Souhlasíte?", @@ -191,12 +209,17 @@ "fileError": "Typ souboru musí být obrázek.", "fileTooLarge": "Odeslání se nezdařilo. Příliš velký nahraný soubor. Obraťte se na správce systému.", "hereIsTheError": "Zde je chyba", + "installerJava": "Selhala instalace {} : Forge servery potřebují k instalaci Javu. Zjistili jsme že java není nainstalovaná. Prosím nainstalujte java na server.", "internet": "Zjistili jsme, že počítač se spuštěným programem Crafty není připojen k internetu. Připojení klientů k serveru může být omezeno.", + "migration": "Crafty's hlavní úložiště se přesouvá do jiné lokace. Všechny servry budou pozastaveny v tento čas. Prosím počkejte než se dokončí migrace", "no-file": "Zdá se, že nemůžeme najít požadovaný soubor. Překontrolujte cestu. Má Crafty správné oprávnění?", + "noInternet": "Crafty se nemůže připojit k internetu. Vytváření serverů je pozastaveno. Prosím zkontrolujte připojení k internetu a potom obnovte tuto stránku.", "noJava": "Server {} se nepodařilo spustit s kódem chyby: Zjistili jsme, že Java není nainstalována. Nainstalujte prosím Javu a poté spusťte server.", "not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?", "portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.", "privMsg": "a ", + "serverJars1": "Server JAR api je nepřístupná. Prosím zkontrolujte", + "serverJars2": "pro aktualní informace.", "start-error": "Server {} se nepodařilo spustit s kódem chyby: {}", "superError": "Pro dokončení této akce musíte být super uživatel.", "terribleFailure": "Jaké strašné selhání!" @@ -207,10 +230,14 @@ "version": "Verze" }, "login": { + "defaultPath": "Heslo které jste vložili je výchozí cesta k pověření. Prosím najděte výchozí heslo v této lokaci.", + "disabled": "Uživatelský účet byl pozastaven. Prosím kontaktujte vašeho správce systému pro více informací.", "forgotPassword": "Zapomenuté heslo", + "incorrect": "Nesprávné uživatelské jméno nebo heslo", "login": "Přihlásit se", "password": "Heslo", - "username": "Uživatelské jméno" + "username": "Uživatelské jméno", + "viewStatus": "Podívejte se na statusy systému" }, "notify": { "activityLog": "Záznamy o činnosti", @@ -222,21 +249,32 @@ "preparingLogs": " Počkejte prosím, než připravíme vaše protokoly... Až budou připraveny, pošleme vám oznámení. U rozsáhlých zavádění to může chvíli trvat.", "supportLogs": "Protokoly podpory" }, + "offline": { + "offline": "Offline", + "pleaseConnect": "Prosím připojte se k internetu ať můžete používat Crafty." + }, "panelConfig": { "adminControls": "Ovládání správce", "allowedServers": "Povolené servery", + "apply": "Použít", "assignedRoles": "Přidělené role", "backgroundUpload": "Nahrání pozadí", "cancel": "Zrušit", "clearComms": "Vymazat nevykonané příkazy", + "custom": "Upravit Crafty", "delete": "Smazat", "edit": "Upravit", + "enableLang": "Povolit všechny jazyky", "enabled": "Zapnuto", + "globalExplain": "Kde má Crafty ukládat všechny soubory serverů. (Cestu doplníme o /server/[uuid serveru])", + "globalServer": "Globalní cesta k serverům", + "json": "Config.json", "loginBackground": "Přihlašovací obrázek na pozadí", "loginImage": "Nahrajte obrázek na pozadí přihlašovací obrazovky.", "match": "Hesla musí být stejná", "newRole": "Přidat novou roli", "newUser": "Přidat nového uživatele", + "noMounts": "Ukazatel zaplněnosti disků na hlavní stránce", "pageTitle": "Nastavení panelu", "preview": "Náhled", "role": "Role", @@ -247,6 +285,7 @@ "selectImage": "Vyberte obrázek", "superConfirm": "Postupujte pouze v případě, že chcete, aby měl tento uživatel přístup ke VŠEM (ke všem uživatelským účtům, serverům, nastavení panelu atd.). Může vám dokonce odebrat práva superuživatele.", "superConfirmTitle": "Povolit superuživatele? Jste si jisti?", + "title": "Crafty Konfigurace", "user": "Uživatel", "users": "Uživatelé" }, @@ -276,9 +315,11 @@ "serversDesc": "servery, ke kterým má tato role přístup" }, "serverBackups": { + "after": "Spustit příkaz po záloze", "backupAtMidnight": "Automatické zálohování o půlnoci?", "backupNow": "Zálohovat nyní!", "backupTask": "Bylo spuštěno zálohování.", + "before": "Spustit příkaz před zálohou", "cancel": "Zrušit", "clickExclude": "Kliknutím vyberete výjimku", "compress": "Komprimovat zálohu", @@ -309,6 +350,7 @@ "bePatientDeleteFiles": "Buďte prosím trpěliví, než odstraníme váš server z panelu Crafty a všechny jeho soubory. Tato obrazovka se za chvíli zavře.", "bePatientUpdate": "Prosím, buďte trpěliví, dokud server neaktualizujeme. Doba stahování se může lišit v závislosti na rychlosti vašeho internetu.
Tato obrazovka se za chvíli aktualizuje", "cancel": "Zrušit", + "countPlayers": "Počítat server v globálních statistikách", "crashTime": "Časový limit havárie", "crashTimeDesc": "Jak dlouho bychom měli čekat, než budeme váš server považovat za havarovaný?", "deleteFilesQuestion": "Odstranit soubory serveru z přístroje?", @@ -318,6 +360,8 @@ "deleteServerQuestionMessage": "Opravdu chcete tento server odstranit? Po tomto kroku již není cesty zpět...", "exeUpdateURL": "Adresa URL pro aktualizaci spustitelných souborů serveru", "exeUpdateURLDesc": "Adresa URL pro přímé stahování aktualizací.", + "ignoredExits": "Ignorovat kódy pádu serveru", + "ignoredExitsExplain": "Ukončovací kódy kteŕe Crafty bude ignorovat a brát jako normálně ukončený server (odělte čárkou)", "javaNoChange": "Nepřepisujte", "javaVersion": "Přepsat aktuální verzi Javy", "javaVersionDesc": "Pokud se chystáte přepsat Javu, ujistěte se, že je aktuální cesta k Javě v příkazu 'execution command' zabalena do uvozovek (výchozí proměnná 'java' je vyloučena).", @@ -350,6 +394,8 @@ "serverStopCommandDesc": "Příkaz k odeslání programu pro jeho zastavení", "showStatus": "Zobrazit na veřejné stavové stránce", "shutdownTimeout": "Časový limit pro vypnutí", + "statsHint1": "Zde by měl být uveden port, na kterém běží váš serv.er. Tímto způsobem Crafty otevře spojení s vaším serverem pro statistiky.", + "statsHint2": "Toto nezmění tvůj port pro tvůj server. Musíš to také změnit v konfiguračním souboru tvého serveru.", "stopBeforeDeleting": "Před odstraněním serveru jej prosím zastavte", "timeoutExplain1": "Jak dlouho bude Crafty čekat na vypnutí serveru po provedení příkazu", "timeoutExplain2": "než proces ukončí.", @@ -478,8 +524,10 @@ "every": "Každý", "interval": "Interval", "name": "Název", + "newSchedule": "Nový úkol", "nextRun": "Příští spuštění", "no": "Ne", + "no-schedule": "Teď tu nejsou žádné úkoly pro tento server. Pro nový úkol klini", "scheduledTasks": "Naplánované úlohy", "yes": "Ano" }, @@ -487,6 +535,7 @@ "cpuUsage": "Využití CPU", "description": "Popis", "errorCalculatingUptime": "Chyba při výpočtu doby provozu", + "loadingMotd": "Načítaní MOTD", "memUsage": "Využití paměti", "offline": "Offline", "online": "Online", @@ -529,6 +578,7 @@ "importServerButton": "Importovat server!", "importZip": "Imporovat ze souboru Zip", "importing": "Importování serveru...", + "labelZipFile": "Vybrat zip soubor", "maxMem": "Maximální paměť", "minMem": "Minimální paměť", "myNewServer": "Nový server", @@ -549,8 +599,11 @@ "serverPort": "Port serveru", "serverSelect": "Výběr serveru", "serverType": "Typ serveru", + "serverUpload": "Nahrát zazipovány Server", "serverVersion": "Verze serveru", "sizeInGB": "Velikost v GB", + "unsupported": "Verze Minecraftu nižší než 1.8 nejsou v Crafty podporovány. Přesto si ji můžete nainstalovat. Není zaručena funkčnost", + "uploadButton": "Nahrát", "uploadZip": "Nahrání souboru Zip pro importování serveru", "zipPath": "Cesta k serveru" }, @@ -559,10 +612,20 @@ "credits": "Zásluhy", "dashboard": "Ovládací panel", "documentation": "Dokumentace", + "inApp": "V app dokumentaci", "navigation": "Navigace", "newServer": "Vytvořit nový server", "servers": "Servery" }, + "startup": { + "almost": "Dokončuji. Držte se...", + "internals": "Nastavuji a startuji Crafty interní komponenty", + "internet": "Kontroluju připojení k internetu", + "server": "Konfigurace ", + "serverInit": "Konfigurace serveru", + "starting": "Crafty se startuje...", + "tasks": "Startuji plány" + }, "userConfig": { "apiKey": "Klíče API", "auth": "Autorizovaný? ", @@ -602,5 +665,30 @@ "userSettings": "Nastavení uživatele", "userTheme": "Motiv UI", "uses": "Počet povolených použití (-1==bez omezení)" + }, + "webhooks": { + "areYouSureDel": "Seš si jistý že chceš smazat tento webhook?", + "areYouSureRun": "Seš si jistý že chceš otestovat tento webhook?", + "backup_server": "Záloha serveru je hotova.", + "bot_name": "Název bota", + "color": "Vyberte barvu ", + "crash_detected": "Server spadl", + "edit": "Upravit", + "enabled": "Aktivní", + "jar_update": "Server byl aktualizován", + "kill": "Server byl násilně ukončen", + "name": "Název", + "new": "Nový Webhook", + "newWebhook": "Nový Webhook", + "no-webhook": "Teď tu nejsou žádné webhooky pro tento server. Pro nový webhook klikni.", + "run": "Spustit test webhooku", + "send_command": "Server příkaz obdržel", + "start_server": "Server zapnut", + "stop_server": "Server vypnut", + "trigger": "Spustit", + "type": "Typ webhooku", + "url": "Webhook URL", + "webhook_body": "Webhook Body", + "webhooks": "Webhooky" } } From 39cfd40fadc1abab81de1816dc3540af1aec291d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 1 Apr 2024 19:09:26 -0400 Subject: [PATCH 106/342] Add link to go back to dashboard on error page --- app/frontend/templates/public/error.html | 4 ++++ app/translations/en_EN.json | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/frontend/templates/public/error.html b/app/frontend/templates/public/error.html index f51d1dc9..1df11458 100644 --- a/app/frontend/templates/public/error.html +++ b/app/frontend/templates/public/error.html @@ -60,6 +60,10 @@ {{ translate('error', 'hereIsTheError', data['lang']) }}: {{data['error']}}

That's all the help I can give you - Godspeed

+ {{ translate('error', 'return', + data['lang'])}} +
+
{{ translate('error', 'contact', data['lang']) }}

diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 5c48b873..da7cb02b 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -203,6 +203,7 @@ "not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?", "portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.", "privMsg": "and the ", + "return": "Return to Dashboard", "serverJars1": "Server JARs API unreachable. Please check", "serverJars2": "for the most up to date information.", "start-error": "Server {} failed to start with error code: {}", @@ -671,4 +672,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From e564208140bc8881a722d59219f83d4404dcd9a1 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Tue, 2 Apr 2024 23:56:05 +0100 Subject: [PATCH 107/342] Update changelog !733 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44750a39..c3b07600 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ TBD ### Bug fixes TBD ### Tweaks -TBD +- Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) ### Lang TBD

From 655546446501b1af6521e7253d9b133b5b6395c4 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 3 Apr 2024 00:24:54 +0100 Subject: [PATCH 108/342] Update changelog !737 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b07600..9c879e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ TBD TBD ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) +- Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) ### Lang TBD

From a137cebb69d07127eb644a50ae07c665dc29cfb3 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 3 Apr 2024 00:31:53 +0100 Subject: [PATCH 109/342] Update changelog !739 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c879e67..7f64d986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ TBD - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) ### Lang -TBD +- Update `it_IT, cs_CS` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/739) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/742))

## --- [4.3.1] - 2024/03/18 From 0fab5cd0f399b185d5eb5c6180cd01be4399794c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Fri, 5 Apr 2024 20:01:37 +0100 Subject: [PATCH 110/342] Refactor for temporary hardcoded types, use new health check response. api.serverjars.com not providing fetchTypes route currently, Will revert when available. NOTE: New api does not incl Spigot / Bukkit. Announce this. --- app/classes/minecraft/serverjars.py | 64 +++++++++++++++++++-------- app/classes/shared/main_controller.py | 2 +- app/classes/web/server_handler.py | 2 +- 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 83731b52..903ce046 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -12,13 +12,15 @@ from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) +# Temp type var, see line(s) #215 #257 #285 +SERVERJARS_TYPES = ["modded", "proxies", "servers", "vanilla"] PAPERJARS = ["paper", "folia"] class ServerJars: def __init__(self, helper): self.helper = helper - self.base_url = "https://serverjars.com" + self.base_url = "https://api.serverjars.com" self.paper_base = "https://api.papermc.io" @staticmethod @@ -168,21 +170,28 @@ class ServerJars: data = self._read_cache() return data.get("types") - def _check_api_alive(self): + def _check_sjars_api_alive(self): logger.info("Checking serverjars.com API status") - check_url = f"{self.base_url}/api/fetchTypes" + check_url = f"{self.base_url}" try: response = requests.get(check_url, timeout=2) + response_json = response.json() - if response.status_code in [200, 201]: - logger.info("Serverjars.com API is alive") + if ( + response.status_code in [200, 201] + and response_json.get("status") == "success" + and response_json.get("response", {}).get("status") == "ok" + ): + logger.info("Serverjars.com API is alive and responding as expected") return True except Exception as e: - logger.error(f"Unable to connect to serverjar.com api due to error: {e}") - return {} + logger.error(f"Unable to connect to serverjar.com API due to error: {e}") + return False - logger.error("unable to contact serverjars.com api") + logger.error( + "Serverjars.com API is not responding as expected or unable to contact" + ) return False def manual_refresh_cache(self): @@ -192,7 +201,7 @@ class ServerJars: # cache_old = True # if the API is down... we bomb out - if not self._check_api_alive(): + if not self._check_sjars_api_alive(): return False logger.info("Manual Refresh requested.") @@ -202,7 +211,14 @@ class ServerJars: "types": {}, } - jar_types = self._get_server_type_list() + # jar_types = self._get_server_type_list() + jar_types = { + type_: ( + {paperjar: [] for paperjar in PAPERJARS} if type_ == "servers" else {} + ) + for type_ in SERVERJARS_TYPES + } + data["types"].update(jar_types) for s in data["types"]: data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) @@ -228,7 +244,7 @@ class ServerJars: # cache_old = True # if the API is down... we bomb out - if not self._check_api_alive(): + if not self._check_sjars_api_alive(): return False logger.info("Checking Cache file age") @@ -242,7 +258,16 @@ class ServerJars: "types": {}, } - jar_types = self._get_server_type_list() + # jar_types = self._get_server_type_list() + jar_types = { + type_: ( + {paperjar: [] for paperjar in PAPERJARS} + if type_ == "servers" + else {} + ) + for type_ in SERVERJARS_TYPES + } + data["types"].update(jar_types) for s in data["types"]: data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) @@ -269,13 +294,14 @@ class ServerJars: time.sleep(0.5) return temp - def _get_server_type_list(self): - url = "/api/fetchTypes/" - response = self._get_api_result(url) - if "bedrock" in response.keys(): - # remove pocketmine from options - del response["bedrock"] - return response + # Disabled temporarily until api.serverjars.com resolve their fetchTypes route + # def _get_server_type_list(self): + # url = "/api/fetchTypes/" + # response = self._get_api_result(url) + # if "bedrock" in response.keys(): + # # remove pocketmine from options + # del response["bedrock"] + # return response def download_jar(self, jar, server, version, path, server_id): update_thread = threading.Thread( diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index b6e824a5..47e4f6ce 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -575,7 +575,7 @@ class Controller: ): server_obj = self.servers.get_server_obj(new_server_id) url = ( - "https://serverjars.com/api/fetchJar/" + "https://api.serverjars.com/api/fetchJar/" f"{create_data['category']}" f"/{create_data['type']}/{create_data['version']}" ) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index 545029aa..62b76f3c 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -147,7 +147,7 @@ class ServerHandler(BaseHandler): page_data["server_api"] = False if page_data["online"]: page_data["server_api"] = self.helper.check_address_status( - "https://serverjars.com/api/fetchTypes" + "https://api.serverjars.com" ) page_data["server_types"] = self.controller.server_jars.get_serverjar_data() page_data["js_server_types"] = json.dumps( From cc67ebef76cbb0af9e89c4122a342839a940ebf1 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 01:38:45 +0100 Subject: [PATCH 111/342] A further clean up and refactor of server jars cache logic Reordered code, and tidy'd based on DRY (Don't Repeat Yourself) --- app/classes/minecraft/serverjars.py | 339 ++++++++++++++-------------- 1 file changed, 169 insertions(+), 170 deletions(-) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 903ce046..4a9883a9 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -12,7 +12,7 @@ from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) -# Temp type var, see line(s) #215 #257 #285 +# Temp type var until sjars restores generic fetchTypes SERVERJARS_TYPES = ["modded", "proxies", "servers", "vanilla"] PAPERJARS = ["paper", "folia"] @@ -84,6 +84,174 @@ class ServerJars: builds = api_data.get("builds", []) return builds[-1] if builds else None + def _read_cache(self): + cache_file = self.helper.serverjar_cache + cache = {} + try: + with open(cache_file, "r", encoding="utf-8") as f: + cache = json.load(f) + + except Exception as e: + logger.error(f"Unable to read serverjars.com cache file: {e}") + + return cache + + def get_serverjar_data(self): + data = self._read_cache() + return data.get("types") + + def _check_sjars_api_alive(self): + logger.info("Checking serverjars.com API status") + + check_url = f"{self.base_url}" + try: + response = requests.get(check_url, timeout=2) + response_json = response.json() + + if ( + response.status_code in [200, 201] + and response_json.get("status") == "success" + and response_json.get("response", {}).get("status") == "ok" + ): + logger.info("Serverjars.com API is alive and responding as expected") + return True + except Exception as e: + logger.error(f"Unable to connect to serverjar.com API due to error: {e}") + return False + + logger.error( + "Serverjars.com API is not responding as expected or unable to contact" + ) + return False + + def _fetch_projects_for_type(self, server_type): + """ + Fetches projects for a given server type from the ServerJars API. + """ + try: + response = requests.get( + f"{self.base_url}/api/fetchTypes/{server_type}", timeout=5 + ) + response.raise_for_status() # Ensure HTTP errors are caught + data = response.json() + if data.get("status") == "success": + return data["response"].get("servers", []) + except requests.RequestException as e: + print(f"Error fetching projects for type {server_type}: {e}") + return [] + + def _get_server_type_list(self): + """ + Builds the type structure with projects fetched for each type. + """ + type_structure = {} + for server_type in SERVERJARS_TYPES: + projects = self._fetch_projects_for_type(server_type) + type_structure[server_type] = {project: [] for project in projects} + return type_structure + + def _get_jar_versions(self, server_type, project_name): + """ + Grabs available versions per project + """ + url = f"{self.base_url}/api/fetchAll/{server_type}/{project_name}" + try: + response = requests.get(url, timeout=5) + response.raise_for_status() # Ensure HTTP errors are caught + data = response.json() + logger.debug(f"Received data for {server_type}/{project_name}: {data}") + + if data.get("status") == "success": + versions = [ + item.get("version") + for item in data.get("response", []) + if "version" in item + ] + logger.debug(f"Versions extracted: {versions}") + return versions + except requests.RequestException as e: + logger.error( + f"Error fetching jar versions for {server_type}/{project_name}: {e}" + ) + + return [] + + def _refresh_cache(self): + """ + Contains the shared logic for refreshing the cache. + This method is called by both manual_refresh_cache and refresh_cache methods. + """ + now = datetime.now() + cache_data = { + "last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), + "types": self._get_server_type_list(), + } + + for server_type, projects in cache_data["types"].items(): + for project_name in projects: + versions = self._get_jar_versions(server_type, project_name) + cache_data["types"][server_type][project_name] = versions + + for paper_project in PAPERJARS: + cache_data["types"]["servers"][paper_project] = self.get_paper_versions( + paper_project + ) + + return cache_data + + def manual_refresh_cache(self): + """ + Manually triggers the cache refresh process. + """ + if not self._check_sjars_api_alive(): + logger.error("ServerJars API is not available.") + return False + + logger.info("Manual cache refresh requested.") + cache_data = self._refresh_cache() + + # Save the updated cache data + try: + with open(self.helper.serverjar_cache, "w", encoding="utf-8") as cache_file: + json.dump(cache_data, cache_file, indent=4) + logger.info("Cache file successfully refreshed manually.") + except Exception as e: + logger.error(f"Failed to update cache file manually: {e}") + + def refresh_cache(self): + """ + Automatically trigger cache refresh process based age. + + This method checks if the cache file is older than a specified number of days + before deciding to refresh. + """ + cache_file_path = self.helper.serverjar_cache + + # Determine if the cache is old and needs refreshing + cache_old = self.helper.is_file_older_than_x_days(cache_file_path) + + # debug override + # cache_old = True + + if not self._check_sjars_api_alive(): + logger.error("ServerJars API is not available.") + return False + + if not cache_old: + logger.info("Cache file is not old enough to require automatic refresh.") + return False + + logger.info("Automatic cache refresh initiated due to old cache.") + cache_data = self._refresh_cache() + + # Save the updated cache data + try: + with open(cache_file_path, "w", encoding="utf-8") as cache_file: + json.dump(cache_data, cache_file, indent=4) + logger.info("Cache file successfully refreshed automatically.") + except Exception as e: + logger.error(f"Failed to update cache file automatically: {e}") + def get_fetch_url(self, jar, server, version): """ Constructs the URL for downloading a server JAR file based on the server type. @@ -134,175 +302,6 @@ class ServerJars: logger.error(f"An error occurred while constructing fetch URL: {e}") return None - def _get_api_result(self, call_url: str): - full_url = f"{self.base_url}{call_url}" - - try: - response = requests.get(full_url, timeout=2) - response.raise_for_status() - api_data = json.loads(response.content) - except Exception as e: - logger.error(f"Unable to load {full_url} api due to error: {e}") - return {} - - api_result = api_data.get("status") - api_response = api_data.get("response", {}) - - if api_result != "success": - logger.error(f"Api returned a failed status: {api_result}") - return {} - - return api_response - - def _read_cache(self): - cache_file = self.helper.serverjar_cache - cache = {} - try: - with open(cache_file, "r", encoding="utf-8") as f: - cache = json.load(f) - - except Exception as e: - logger.error(f"Unable to read serverjars.com cache file: {e}") - - return cache - - def get_serverjar_data(self): - data = self._read_cache() - return data.get("types") - - def _check_sjars_api_alive(self): - logger.info("Checking serverjars.com API status") - - check_url = f"{self.base_url}" - try: - response = requests.get(check_url, timeout=2) - response_json = response.json() - - if ( - response.status_code in [200, 201] - and response_json.get("status") == "success" - and response_json.get("response", {}).get("status") == "ok" - ): - logger.info("Serverjars.com API is alive and responding as expected") - return True - except Exception as e: - logger.error(f"Unable to connect to serverjar.com API due to error: {e}") - return False - - logger.error( - "Serverjars.com API is not responding as expected or unable to contact" - ) - return False - - def manual_refresh_cache(self): - cache_file = self.helper.serverjar_cache - - # debug override - # cache_old = True - - # if the API is down... we bomb out - if not self._check_sjars_api_alive(): - return False - - logger.info("Manual Refresh requested.") - now = datetime.now() - data = { - "last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), - "types": {}, - } - - # jar_types = self._get_server_type_list() - jar_types = { - type_: ( - {paperjar: [] for paperjar in PAPERJARS} if type_ == "servers" else {} - ) - for type_ in SERVERJARS_TYPES - } - - data["types"].update(jar_types) - for s in data["types"]: - data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) - for j in data["types"].get(s): - versions = self._get_jar_details(j, s) - data["types"][s].update({j: versions}) - for item in PAPERJARS: - data["types"]["servers"][item] = self.get_paper_versions(item) - # save our cache - try: - with open(cache_file, "w", encoding="utf-8") as f: - f.write(json.dumps(data, indent=4)) - logger.info("Cache file refreshed") - - except Exception as e: - logger.error(f"Unable to update serverjars.com cache file: {e}") - - def refresh_cache(self): - cache_file = self.helper.serverjar_cache - cache_old = self.helper.is_file_older_than_x_days(cache_file) - - # debug override - # cache_old = True - - # if the API is down... we bomb out - if not self._check_sjars_api_alive(): - return False - - logger.info("Checking Cache file age") - # if file is older than 1 day - - if cache_old: - logger.info("Cache file is over 1 day old, refreshing") - now = datetime.now() - data = { - "last_refreshed": now.strftime("%m/%d/%Y, %H:%M:%S"), - "types": {}, - } - - # jar_types = self._get_server_type_list() - jar_types = { - type_: ( - {paperjar: [] for paperjar in PAPERJARS} - if type_ == "servers" - else {} - ) - for type_ in SERVERJARS_TYPES - } - - data["types"].update(jar_types) - for s in data["types"]: - data["types"].update({s: dict.fromkeys(data["types"].get(s), {})}) - for j in data["types"].get(s): - versions = self._get_jar_details(j, s) - data["types"][s].update({j: versions}) - for item in PAPERJARS: - data["types"]["servers"][item] = self.get_paper_versions(item) - # save our cache - try: - with open(cache_file, "w", encoding="utf-8") as f: - f.write(json.dumps(data, indent=4)) - logger.info("Cache file refreshed") - - except Exception as e: - logger.error(f"Unable to update serverjars.com cache file: {e}") - - def _get_jar_details(self, server_type, jar_type="servers"): - url = f"/api/fetchAll/{jar_type}/{server_type}" - response = self._get_api_result(url) - temp = [] - for v in response: - temp.append(v.get("version")) - time.sleep(0.5) - return temp - - # Disabled temporarily until api.serverjars.com resolve their fetchTypes route - # def _get_server_type_list(self): - # url = "/api/fetchTypes/" - # response = self._get_api_result(url) - # if "bedrock" in response.keys(): - # # remove pocketmine from options - # del response["bedrock"] - # return response - def download_jar(self, jar, server, version, path, server_id): update_thread = threading.Thread( name=f"server_download-{server_id}-{server}-{version}", From 46970e128309bc53d8e03e316eb68769af327329 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 6 Apr 2024 11:39:06 -0400 Subject: [PATCH 112/342] Make dash button more noticable --- app/frontend/templates/public/error.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/frontend/templates/public/error.html b/app/frontend/templates/public/error.html index 1df11458..535c1205 100644 --- a/app/frontend/templates/public/error.html +++ b/app/frontend/templates/public/error.html @@ -1,5 +1,6 @@ - + @@ -60,8 +61,9 @@ {{ translate('error', 'hereIsTheError', data['lang']) }}: {{data['error']}}

That's all the help I can give you - Godspeed

- {{ translate('error', 'return', - data['lang'])}} +

{{ translate('error', From 9bed7092d6db6d275d56614c69667db81b4c3d55 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 6 Apr 2024 13:42:41 -0400 Subject: [PATCH 113/342] Check for API permission as well as user permission --- .../controllers/server_perms_controller.py | 25 +++++ app/classes/web/base_handler.py | 6 +- .../web/routes/api/servers/server/action.py | 13 +-- .../servers/server/backups/backup/index.py | 36 ++++--- .../api/servers/server/backups/index.py | 25 ++--- .../web/routes/api/servers/server/files.py | 96 ++++++++++--------- .../web/routes/api/servers/server/index.py | 26 ++--- .../web/routes/api/servers/server/logs.py | 13 +-- .../web/routes/api/servers/server/stdin.py | 13 +-- .../routes/api/servers/server/tasks/index.py | 13 +-- .../api/servers/server/tasks/task/index.py | 37 +++---- .../api/servers/server/webhooks/index.py | 25 ++--- .../servers/server/webhooks/webhook/index.py | 50 +++++----- 13 files changed, 221 insertions(+), 157 deletions(-) diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index f6632cd7..30c69ab0 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -17,6 +17,10 @@ class ServerPermsController: def get_server_user_list(server_id): return PermissionsServers.get_server_user_list(server_id) + @staticmethod + def get_permissions(permissions_mask): + return PermissionsServers.get_permissions(permissions_mask) + @staticmethod def list_defined_permissions(): permissions_list = PermissionsServers.get_permissions_list() @@ -61,6 +65,22 @@ class ServerPermsController: def get_permissions_mask(role_id, server_id): return PermissionsServers.get_permissions_mask(role_id, server_id) + @staticmethod + def get_lowest_api_perm_mask(user_server_permissions_mask, api_key_permssions_mask): + mask = "" + # If this isn't an API key we'll know the request came from basic + # authentication and ignore the API key permissions mask. + if not api_key_permssions_mask: + return user_server_permissions_mask + for _index, (user_perm, api_perm) in enumerate( + zip(user_server_permissions_mask, api_key_permssions_mask) + ): + if user_perm == "1" and api_perm == "1": + mask += "1" + else: + mask += "0" + return mask + @staticmethod def set_permission( permission_mask, permission_tested: EnumPermissionsServer, value @@ -82,6 +102,11 @@ class ServerPermsController: def get_api_key_permissions_list(key: ApiKeys, server_id: str): return PermissionsServers.get_api_key_permissions_list(key, server_id) + @staticmethod + def get_user_permissions_mask(user_id: str, server_id: str): + user = HelperUsers.get_user_model(user_id) + return PermissionsServers.get_user_permissions_mask(user, server_id) + @staticmethod def get_authorized_servers_stats_from_roles(user_id): user_roles = HelperUsers.get_user_roles_id(user_id) diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index 6967ebe2..7cca08e8 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -182,6 +182,7 @@ class BaseHandler(tornado.web.RequestHandler): t.List[str], bool, t.Dict[str, t.Any], + str, ] ]: try: @@ -190,9 +191,10 @@ class BaseHandler(tornado.web.RequestHandler): ) superuser = user["superuser"] + server_permissions_api_mask = "" if api_key is not None: superuser = superuser and api_key.full_access - + server_permissions_api_mask = api_key.server_permissions exec_user_role = set() if superuser: authorized_servers = self.controller.servers.get_all_defined_servers() @@ -214,6 +216,7 @@ class BaseHandler(tornado.web.RequestHandler): user["user_id"] ) ) + logger.debug(user["roles"]) for r in user["roles"]: role = self.controller.roles.get_role(r) @@ -234,6 +237,7 @@ class BaseHandler(tornado.web.RequestHandler): exec_user_role, superuser, user, + server_permissions_api_mask, ) logging.debug("Auth unsuccessful") auth_log.error( diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index 01ce45c4..fde71056 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -18,13 +18,14 @@ class ApiServersServerActionHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.COMMANDS - not in self.controller.server_perms.get_user_id_permissions_list( + 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.COMMANDS not in server_permissions: # if the user doesn't have Commands permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) 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 05edd3a9..3235202a 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 @@ -26,12 +26,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( + 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"}) self.finish_json(200, self.controller.management.get_backup_config(server_id)) @@ -41,12 +43,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): backup_conf = self.controller.management.get_backup_config(server_id) if not auth_data: return - if ( - EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( + 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"}) @@ -89,12 +93,14 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( + 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"}) 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..865fe25a 100644 --- a/app/classes/web/routes/api/servers/server/backups/index.py +++ b/app/classes/web/routes/api/servers/server/backups/index.py @@ -42,12 +42,14 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( + 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"}) self.finish_json(200, self.controller.management.get_backup_config(server_id)) @@ -82,13 +84,14 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( + 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"}) diff --git a/app/classes/web/routes/api/servers/server/files.py b/app/classes/web/routes/api/servers/server/files.py index 8e70d4fe..2951ff25 100644 --- a/app/classes/web/routes/api/servers/server/files.py +++ b/app/classes/web/routes/api/servers/server/files.py @@ -80,16 +80,16 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler): 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"}) - + 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.FILES - not in self.controller.server_perms.get_user_id_permissions_list( - auth_data[4]["user_id"], server_id - ) - and EnumPermissionsServer.BACKUP - not in self.controller.server_perms.get_user_id_permissions_list( - auth_data[4]["user_id"], server_id - ) + EnumPermissionsServer.FILES not in server_permissions + and EnumPermissionsServer.BACKUP not in server_permissions ): # if the user doesn't have Files or Backup permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) @@ -197,13 +197,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -254,13 +255,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -307,13 +309,14 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -373,13 +376,14 @@ class ApiServersServerFilesCreateHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -438,13 +442,14 @@ class ApiServersServerFilesCreateHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -504,13 +509,14 @@ class ApiServersServerFilesZipHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.FILES - not in self.controller.server_perms.get_user_id_permissions_list( + 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.FILES not in server_permissions: # if the user doesn't have Files permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: diff --git a/app/classes/web/routes/api/servers/server/index.py b/app/classes/web/routes/api/servers/server/index.py index 81035bd0..9bfc3a9a 100644 --- a/app/classes/web/routes/api/servers/server/index.py +++ b/app/classes/web/routes/api/servers/server/index.py @@ -102,13 +102,14 @@ class ApiServersServerIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG not in server_permissions: # if the user doesn't have Config permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) @@ -154,13 +155,14 @@ class ApiServersServerIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG not in server_permissions: # if the user doesn't have Config permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) diff --git a/app/classes/web/routes/api/servers/server/logs.py b/app/classes/web/routes/api/servers/server/logs.py index 94a8a71b..eb6ede00 100644 --- a/app/classes/web/routes/api/servers/server/logs.py +++ b/app/classes/web/routes/api/servers/server/logs.py @@ -30,13 +30,14 @@ class ApiServersServerLogsHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.LOGS - not in self.controller.server_perms.get_user_id_permissions_list( + 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.LOGS not in server_permissions: # if the user doesn't have Logs permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) diff --git a/app/classes/web/routes/api/servers/server/stdin.py b/app/classes/web/routes/api/servers/server/stdin.py index ba8400b7..ca2cd7d9 100644 --- a/app/classes/web/routes/api/servers/server/stdin.py +++ b/app/classes/web/routes/api/servers/server/stdin.py @@ -16,13 +16,14 @@ class ApiServersServerStdinHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.COMMANDS - not in self.controller.server_perms.get_user_id_permissions_list( + 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.COMMANDS not in server_permissions: # if the user doesn't have Commands permission, return an error return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) diff --git a/app/classes/web/routes/api/servers/server/tasks/index.py b/app/classes/web/routes/api/servers/server/tasks/index.py index 8e98bbbe..0c03319c 100644 --- a/app/classes/web/routes/api/servers/server/tasks/index.py +++ b/app/classes/web/routes/api/servers/server/tasks/index.py @@ -78,13 +78,14 @@ class ApiServersServerTasksIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.SCHEDULE - not in self.controller.server_perms.get_user_id_permissions_list( + 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.SCHEDULE 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"}) data["server_id"] = server_id diff --git a/app/classes/web/routes/api/servers/server/tasks/task/index.py b/app/classes/web/routes/api/servers/server/tasks/task/index.py index 742312a6..dac60762 100644 --- a/app/classes/web/routes/api/servers/server/tasks/task/index.py +++ b/app/classes/web/routes/api/servers/server/tasks/task/index.py @@ -54,12 +54,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.SCHEDULE - not in self.controller.server_perms.get_user_id_permissions_list( + 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.SCHEDULE 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_scheduled_task(task_id)) @@ -68,12 +70,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.SCHEDULE - not in self.controller.server_perms.get_user_id_permissions_list( + 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.SCHEDULE 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"}) @@ -120,13 +124,14 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.SCHEDULE - not in self.controller.server_perms.get_user_id_permissions_list( + 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.SCHEDULE 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"}) diff --git a/app/classes/web/routes/api/servers/server/webhooks/index.py b/app/classes/web/routes/api/servers/server/webhooks/index.py index 223171c8..2557c309 100644 --- a/app/classes/web/routes/api/servers/server/webhooks/index.py +++ b/app/classes/web/routes/api/servers/server/webhooks/index.py @@ -38,12 +38,14 @@ class ApiServersServerWebhooksIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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( @@ -81,13 +83,14 @@ class ApiServersServerWebhooksIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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"}) data["server_id"] = server_id diff --git a/app/classes/web/routes/api/servers/server/webhooks/webhook/index.py b/app/classes/web/routes/api/servers/server/webhooks/webhook/index.py index 4b58011e..c94aa975 100644 --- a/app/classes/web/routes/api/servers/server/webhooks/webhook/index.py +++ b/app/classes/web/routes/api/servers/server/webhooks/webhook/index.py @@ -39,12 +39,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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"}) if ( @@ -66,12 +68,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler): auth_data = self.authenticate_user() if not auth_data: return - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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"}) @@ -117,13 +121,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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"}) @@ -159,13 +164,14 @@ class ApiServersServerWebhooksManagementIndexHandler(BaseApiHandler): 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"}) - - if ( - EnumPermissionsServer.CONFIG - not in self.controller.server_perms.get_user_id_permissions_list( + 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.CONFIG 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"}) webhook = self.controller.management.get_webhook_by_id(webhook_id) From 5f00f9d6fa1c7db1157601078ae7330f47f9b728 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 6 Apr 2024 13:53:38 -0400 Subject: [PATCH 114/342] Add full access translation --- app/frontend/templates/panel/panel_edit_user_apikeys.html | 4 ++-- app/translations/en_EN.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/frontend/templates/panel/panel_edit_user_apikeys.html b/app/frontend/templates/panel/panel_edit_user_apikeys.html index b1b179ef..76fa780b 100644 --- a/app/frontend/templates/panel/panel_edit_user_apikeys.html +++ b/app/frontend/templates/panel/panel_edit_user_apikeys.html @@ -58,7 +58,7 @@ {{ translate('apiKeys', 'name', data['lang']) }} {{ translate('apiKeys', 'created', data['lang']) }} - {{ translate('apiKeys', 'superUser', data['lang']) }} + {{ translate('apiKeys', 'fullAccess', data['lang']) }} {{ translate('apiKeys', 'perms', data['lang']) }} {{ translate('apiKeys', 'buttons', data['lang']) }} @@ -164,7 +164,7 @@ - +
diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 5c48b873..cdc4cbf2 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -28,7 +28,7 @@ "permName": "Permission Name", "perms": "Permissions", "server": "Server: ", - "superUser": "Super User", + "fullAccess": "Full Access", "yes": "Yes" }, "base": { @@ -671,4 +671,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From afb75753988ee13177a0fc46cae20ade7c2036f6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 6 Apr 2024 15:05:26 -0400 Subject: [PATCH 115/342] Fix API perms request returning stack --- app/classes/web/routes/api/users/user/permissions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/classes/web/routes/api/users/user/permissions.py b/app/classes/web/routes/api/users/user/permissions.py index b6c8703a..5981eaf4 100644 --- a/app/classes/web/routes/api/users/user/permissions.py +++ b/app/classes/web/routes/api/users/user/permissions.py @@ -52,6 +52,8 @@ class ApiUsersUserPermissionsHandler(BaseApiHandler): }, ) + counter_data = PermissionsCrafty.get_created_quantity_list(user_id) + self.finish_json( 200, { @@ -59,9 +61,9 @@ class ApiUsersUserPermissionsHandler(BaseApiHandler): "data": { "permissions": res_data.permissions, "counters": { - SERVER_CREATION: res_data.created_server, - USER_CONFIG: res_data.created_user, - ROLES_CONFIG: res_data.created_role, + SERVER_CREATION: counter_data["SERVER_CREATION"], + USER_CONFIG: counter_data["USER_CONFIG"], + ROLES_CONFIG: counter_data["ROLES_CONFIG"], }, "limits": { SERVER_CREATION: res_data.limit_server_creation, From 20d5f856020273152a7f00ae17ea515fb0935473 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 20:20:45 +0100 Subject: [PATCH 116/342] Increase max versions returned --- app/classes/minecraft/serverjars.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 4a9883a9..d0e90024 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -12,7 +12,7 @@ from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) -# Temp type var until sjars restores generic fetchTypes +# Temp type var until sjars restores generic fetchTypes0 SERVERJARS_TYPES = ["modded", "proxies", "servers", "vanilla"] PAPERJARS = ["paper", "folia"] @@ -150,11 +150,19 @@ class ServerJars: type_structure[server_type] = {project: [] for project in projects} return type_structure - def _get_jar_versions(self, server_type, project_name): + def _get_jar_versions(self, server_type, project_name, max_ver=50): """ - Grabs available versions per project + Grabs available versions for specified project + + Args: + server_type (str): Server Type Category (modded, servers, etc) + project_name (str): Target project (paper, forge, magma, etc) + max (int, optional): Max versions returned. Defaults to 50. + + Returns: + list: An array of versions """ - url = f"{self.base_url}/api/fetchAll/{server_type}/{project_name}" + url = f"{self.base_url}/api/fetchAll/{server_type}/{project_name}?max={max_ver}" try: response = requests.get(url, timeout=5) response.raise_for_status() # Ensure HTTP errors are caught From b34e0c813ee3f0c8a29cf2fd0a29b7393c89b640 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 20:38:29 +0100 Subject: [PATCH 117/342] Update changelog !734 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f64d986..924018e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### New features TBD ### Bug fixes -TBD +- Fix migrator issues when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 52a4ba4a972302525abb93b3644ca20e61466dfd Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 20:53:56 +0100 Subject: [PATCH 118/342] Update changelog !736 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 924018e8..b0c03e23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ### New features TBD ### Bug fixes -- Fix migrator issues when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) +- Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) +- Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 5d9a3b57ec458cb67a7dce904a740679a642a4b1 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 21:08:10 +0100 Subject: [PATCH 119/342] Update changelog !744 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c03e23..b8450118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## --- [4.3.2] - 2024/TBD ### New features TBD +### Refactor +- Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744)) ### Bug fixes - Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) - Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) From 4f469f3df1d08692172283bc5a1099f4e8d30e25 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 6 Apr 2024 16:24:55 -0400 Subject: [PATCH 120/342] Do not allow clone while server is running --- app/classes/web/routes/api/servers/server/action.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index 7de2697d..526899b5 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -33,6 +33,17 @@ class ApiServersServerActionHandler(BaseApiHandler): self.controller.crafty_perms.can_create_server(auth_data[4]["user_id"]) or auth_data[4]["superuser"] ): + srv_object = self.controller.servers.get_server_instance_by_id( + server_id + ) + if srv_object.check_running(): + return self.finish_json( + 409, + { + "status": "error", + "error": "Server Running!", + }, + ) self._clone_server(server_id, auth_data[4]["user_id"]) return self.finish_json(200, {"status": "ok"}) return self.finish_json( From b4a962b5a6b964ade91aed75bcfe1e2b001b205d Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 21:39:30 +0100 Subject: [PATCH 121/342] Update changelog !741 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8450118..74695a9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ TBD ### Bug fixes - Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) - Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) +- Fix backend issue causing error when cloning servers in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/741)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 057139c7e11bcfc17e0669e0ae278e4f1f6ab63c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:01:37 +0100 Subject: [PATCH 122/342] Rerverse order of returned server jars versions Logic I removed in my refactor, whoops! Newest -> oldest --- app/classes/minecraft/serverjars.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index d0e90024..944ec382 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -175,6 +175,7 @@ class ServerJars: for item in data.get("response", []) if "version" in item ] + versions.reverse() # Reverse so versions are newest -> oldest logger.debug(f"Versions extracted: {versions}") return versions except requests.RequestException as e: From 03717d453a2613c2093ca4c4390561f9ff4862f7 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:06:02 +0100 Subject: [PATCH 123/342] Update changelog !746 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74695a9c..94f732ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### New features TBD ### Refactor -- Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744)) +- Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/746)) ### Bug fixes - Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) - Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) From 8f692d885f06287d00faf713bcbf23619813bd9b Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:32:14 +0100 Subject: [PATCH 124/342] Bump orjson for CVE-2024-27454 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e3a58bad..ed0f7698 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,5 @@ termcolor==1.1 tornado==6.3.3 tzlocal==5.1 jsonschema==4.19.1 -orjson==3.9.7 +orjson==3.9.15 prometheus-client==0.17.1 From ff20705f32e77f4212fcc4783901b2226b69ccd5 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:35:10 +0100 Subject: [PATCH 125/342] Update changelog !747 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74695a9c..66b9b363 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ TBD - Fix migrator issue when jumping versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/734)) - Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) - Fix backend issue causing error when cloning servers in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/741)) +- Bump orjson for CVE-2024-27454 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 63133809226c73bad67e9d88008d65f93cdb1cb8 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:54:43 +0100 Subject: [PATCH 126/342] Fix calling of orjson .JSONDecodeError class --- app/classes/web/routes/api/crafty/config/index.py | 4 ++-- app/classes/web/routes/api/crafty/config/server_dir.py | 2 +- app/classes/web/routes/api/roles/role/index.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/classes/web/routes/api/crafty/config/index.py b/app/classes/web/routes/api/crafty/config/index.py index b95d15a0..40504d76 100644 --- a/app/classes/web/routes/api/crafty/config/index.py +++ b/app/classes/web/routes/api/crafty/config/index.py @@ -106,7 +106,7 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler): try: data = orjson.loads(self.request.body) - except orjson.decoder.JSONDecodeError as e: + except orjson.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) @@ -187,7 +187,7 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler): try: data = orjson.loads(self.request.body) - except orjson.decoder.JSONDecodeError as e: + except orjson.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) diff --git a/app/classes/web/routes/api/crafty/config/server_dir.py b/app/classes/web/routes/api/crafty/config/server_dir.py index 87835d35..07cf7c26 100644 --- a/app/classes/web/routes/api/crafty/config/server_dir.py +++ b/app/classes/web/routes/api/crafty/config/server_dir.py @@ -68,7 +68,7 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler): try: data = orjson.loads(self.request.body) - except orjson.decoder.JSONDecodeError as e: + except orjson.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index a45c297f..97362f5b 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -133,7 +133,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): try: data = orjson.loads(self.request.body) - except orjson.decoder.JSONDecodeError as e: + except orjson.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) From 21394211355191f1ba9dbc59dfecc2abe19bc76d Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 22:56:14 +0100 Subject: [PATCH 127/342] Update changelog !747 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b9b363..7c9c1d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TBD - Fix backend issue causing error when restoring backups in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/736)) - Fix backend issue causing error when cloning servers in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/741)) - Bump orjson for CVE-2024-27454 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747)) +- Fix calling of orjson JSONDecodeError class ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 2357b9fa21dacb70a61a541f323918f1413acfba Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 6 Apr 2024 23:41:24 +0100 Subject: [PATCH 128/342] Make help text in repo templates comments. People aren't removing the example text, thus MR's and issues are messy --- .gitlab/issue_templates/Bug.md | 8 ++++---- .gitlab/issue_templates/Change Request.md | 7 ++++--- .gitlab/issue_templates/Feature Request.md | 8 ++++---- .gitlab/merge_request_templates/Default.md | 18 +++++++++--------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 88b2a3b4..77136b69 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -3,16 +3,16 @@ - **Install Type:** Git Cloned(Manual) / Installer / WinPackage / Docker ## What Happened? -*A brief description of what happened when you tried to perform an action* + ## Expected result -*What should have happened when you performed the actions* + ## Steps to reproduce -*List the steps required to produce the error. These should be as few as possible* + ## Screenshots -Any relevant screenshots which show the issue* !-->* + ## Priority/Severity - [ ] High (anything that impacts the normal user flow or blocks app usage) diff --git a/.gitlab/issue_templates/Change Request.md b/.gitlab/issue_templates/Change Request.md index 995e7ad1..f767ee15 100644 --- a/.gitlab/issue_templates/Change Request.md +++ b/.gitlab/issue_templates/Change Request.md @@ -1,13 +1,14 @@ ## Summary -*Outline the issue being faced, and why this needs to change* + ## Area of the system -*This might only be one part, but may involve multiple sections, Login/Dashboad/Terminal/Config* + ## How does this currently work? + ## What is the desired way of working? -*After the change, what should the process/operation be?* + ## Priority/Severity - [ ] High (This will bring a huge increase in performance/productivity/usability) diff --git a/.gitlab/issue_templates/Feature Request.md b/.gitlab/issue_templates/Feature Request.md index 2450aa60..b26067d6 100644 --- a/.gitlab/issue_templates/Feature Request.md +++ b/.gitlab/issue_templates/Feature Request.md @@ -1,8 +1,8 @@ ## Problem Statement -*What is the issue being faced and needs addressing?* + ## Who will benefit? -*Will this fix a problem that only one user has, or will it benefit a lot of people* + ## Benefits and risks What benefits does this bring? @@ -16,10 +16,10 @@ ## Proposed solution -*How would you like to see this issue resolved?* + ## Examples -*Are there any examples of this which exist in other software?* + ## Priority/Severity - [ ] High (This will bring a huge increase in performance/productivity/usability) diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md index a82cb3f8..70bcd7db 100644 --- a/.gitlab/merge_request_templates/Default.md +++ b/.gitlab/merge_request_templates/Default.md @@ -1,22 +1,22 @@ ## What does this MR do and why? -___Describe in detail what your merge request does and why.___
-> *Please keep this description updated with any discussion that takes place so*
-*that reviewers can understand your intent. Keeping the description updated is*
-*especially important if they didn't participate in the discussion.*
+ + + + ## Screenshots or screen recordings -___These are strongly recommended to assist reviewers and reduce the time to merge your change.___
-> *Please include any relevant screenshots or screen recordings that will assist*
-*reviewers and future readers. If you need help visually verifying the change,*
-*please leave a comment and ping a GitLab reviewer, maintainer, or MR coach.*
+ + + + ## How to set up and validate locally -___Numbered steps to set up and validate the change are strongly suggested.___ + ## MR acceptance checklist From d03fa57d0b949d0aa5e4b55165f7a7a3b1fdf1de Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 00:07:28 +0100 Subject: [PATCH 129/342] Update changelog !745 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7535061..ac3d0608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ TBD - Fix backend issue causing error when cloning servers in 4.3.x ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/741)) - Bump orjson for CVE-2024-27454 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747)) - Fix calling of orjson JSONDecodeError class ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/747)) +- Fix stack on Crafty permissions route request in API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/745)) ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) From 3c7c05b4052b0126473f35f13dc5ec01de41a93f Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 00:17:16 +0100 Subject: [PATCH 130/342] Close changelog v4.3.2 --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac3d0608..aae03620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,5 @@ # Changelog -## --- [4.3.2] - 2024/TBD -### New features -TBD +## --- [4.3.2] - 2024/04/07 ### Refactor - Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/746)) ### Bug fixes From 77b0c2c9d2eac124a7504a3d3916fa22d29fa9d1 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 00:36:10 +0100 Subject: [PATCH 131/342] Add openjdk-21 to docker img --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index e8bdb102..d05cf3d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,7 @@ RUN apt-get update \ openjdk-8-jre-headless \ openjdk-11-jre-headless \ openjdk-17-jre-headless \ + openjdk-21-jre-headless \ tzdata \ && apt-get autoremove \ && apt-get clean From 56d098ef861cd3f3e1d3fcaa6a37f34bd125f3db Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 01:04:56 +0100 Subject: [PATCH 132/342] Update changelog 3c7c05b4 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aae03620..145d869b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Tweaks - Clean up remaining http handler references ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/733)) - Remove version disclosure on login page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/737)) +- Add openjdk-21 for recent versions of MC ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/77b0c2c9d2eac124a7504a3d3916fa22d29fa9d1)) ### Lang - Update `it_IT, cs_CS` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/739) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/742))

From 810696614632c2e55918a8d10739c7290c57e6ee Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 01:58:29 +0100 Subject: [PATCH 133/342] Prepare 4.3.3 release base --- CHANGELOG.md | 12 ++++++++++++ README.md | 2 +- app/config/version.json | 2 +- sonar-project.properties | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 145d869b..3c9bebac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,16 @@ # Changelog +# Changelog +## --- [4.3.3] - 2024/TBD +### New features +TBD +### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ ## --- [4.3.2] - 2024/04/07 ### Refactor - Refactor ServerJars caching and move to api.serverjars.com ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/744) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/746)) diff --git a/README.md b/README.md index ae70bd0e..64e26224 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.3.2 +# Crafty Controller 4.3.3 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/config/version.json b/app/config/version.json index 8dffa727..22de834f 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 3, - "sub": 2 + "sub": 3 } diff --git a/sonar-project.properties b/sonar-project.properties index a5f5e258..892bb292 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.3.2 +sonar.projectVersion=4.3.3 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/** From 0fbf14063cb7769d5df888d1e49f000d75325b4d Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 7 Apr 2024 02:33:34 +0100 Subject: [PATCH 134/342] Fix CL 4.3.3 Duplicate head --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9bebac..3be9e1a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,4 @@ # Changelog -# Changelog ## --- [4.3.3] - 2024/TBD ### New features TBD From 36f2256273350e5e830808721ebe773e00955b8d Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Sun, 7 Apr 2024 18:25:05 -0500 Subject: [PATCH 135/342] Update languages for merge 743 --- app/translations/cs_CS.json | 1 + app/translations/de_DE.json | 1 + app/translations/en_EN.json | 2 +- app/translations/es_ES.json | 1 + app/translations/fr_FR.json | 1 + app/translations/he_IL.json | 1 + app/translations/it_IT.json | 1 + app/translations/lol_EN.json | 1 + app/translations/lv_LV.json | 1 + app/translations/nl_BE.json | 1 + app/translations/pl_PL.json | 1 + app/translations/th_TH.json | 1 + app/translations/tr_TR.json | 1 + app/translations/uk_UA.json | 1 + app/translations/zh_CN.json | 1 + 15 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json index 2229d5cb..1d0ad986 100644 --- a/app/translations/cs_CS.json +++ b/app/translations/cs_CS.json @@ -218,6 +218,7 @@ "not-downloaded": "Zdá se, že nemůžeme najít váš spustitelný soubor. Bylo jeho stahování dokončeno? Jsou oprávnění nastavena na spustitelný soubor?", "portReminder": "Zjistili jsme, že server {} byl spuštěn poprvé. Ujistěte se, že jste přesměrovali port {} přes váš směrovač/firewall, aby byl tento port vzdáleně přístupný z internetu.", "privMsg": "a ", + "return": "vrátit se na hlavní stránku", "serverJars1": "Server JAR api je nepřístupná. Prosím zkontrolujte", "serverJars2": "pro aktualní informace.", "start-error": "Server {} se nepodařilo spustit s kódem chyby: {}", diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index c9ffea15..e4457765 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -203,6 +203,7 @@ "not-downloaded": "Crafty kann die auszuführende Datei nicht finden. Ist der Download abgeschlossen? Sind die Berechtigungen für Crafty korrekt?", "portReminder": "Wir haben festgestellt, dass dies das erste Mal ist, dass {} ausgeführt wurde. Stellen Sie sicher, dass Sie Port {} durch Ihren Router/Firewall weiterleiten, um den Fernzugriff aus dem Internet zu ermöglichen.", "privMsg": "und der/die/das ", + "return": "Zurück zum Dashboard", "serverJars1": "Server-JAR-API nicht erreichbar. Bitte überprüfen Sie ", "serverJars2": "um die aktuellsten Informationen zu erhalten.", "start-error": "Der Server {} konnte wegen dem Fehlercode: {} nicht gestartet werden", diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index da7cb02b..cce93d23 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -672,4 +672,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} \ No newline at end of file +} diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json index 5f750fc7..3c4ec4db 100644 --- a/app/translations/es_ES.json +++ b/app/translations/es_ES.json @@ -203,6 +203,7 @@ "not-downloaded": "No podemos encontrar el archivo ejecutable. ¿Ha terminado de descargarse? ¿Están los permisos puestos como ejecutable?", "portReminder": "Detectamos que es la primera vez que se inicia {}. Asegúrese de configurar el puerto {} a través de su router/firewall para hacer el servidor accesible por Internet.", "privMsg": "y el ", + "return": "Volver al panel de control", "serverJars1": "API de Servidor JAR no disponible. por favor, compruebe", "serverJars2": "para la información más actualizada.", "start-error": "Servidor {} fallo al iniciar con código de error: {}", diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index be429c83..70228914 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -203,6 +203,7 @@ "not-downloaded": "Nous ne parvenons pas à trouver le fichier exécutable. A-t-il fini de Télécharger ? Les permissions permettent elles l'exécution ?", "portReminder": "Nous avons détecté que c'est la première fois que {} est exécuté. Assurez-vous de transférer le port {} via votre routeur/pare-feu pour le rendre accessible à distance depuis Internet.", "privMsg": "et le ", + "return": "Revenir au Tableau de Bord", "serverJars1": "l'API Server JARs est inaccessible. Merci de vérifier", "serverJars2": "pour les informations les plus à jour.", "start-error": "Le serveur {} n'a pas pu démarrer avec le code d'erreur : {}", diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index 7e2b1403..480300a9 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -203,6 +203,7 @@ "not-downloaded": "לא הצלחנו למצוא את קובץ ההפעלה שלך. האם זה סיים להוריד? האם ההרשאות מוגדרות בשביל הפעלה?", "portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.", "privMsg": "וה", + "return": "חזרה לפאנל", "serverJars1": "API של צנצנות השרת אינו נגיש. אנא בדוק", "serverJars2": "למידע מעודכן ביותר.", "start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}", diff --git a/app/translations/it_IT.json b/app/translations/it_IT.json index adb59c7d..a13d9999 100644 --- a/app/translations/it_IT.json +++ b/app/translations/it_IT.json @@ -203,6 +203,7 @@ "not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?", "portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.", "privMsg": "e il ", + "return": "Torna alla pagina iniziale", "serverJars1": "API JAR del server non raggiungibile. Si prega di controllare", "serverJars2": "per informazioni più aggiornate.", "start-error": "Server {} failed to start with error code: {}", diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index aa07ff5f..607cac2e 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -203,6 +203,7 @@ "not-downloaded": "SOZ BUT I FAILDZ CAN'T SEEM TO FINDZ YOUR FISH. PLZ GIB MEZ IT. I HUNGRY.", "portReminder": "WE HAS DETECTD DIS AR TEH FURST TIEM {} IZ BEAN RUN. IF U WANTS IT ACESIBLE TO NEIGHBORHOOD CATS PLZ UNLOCK CAT_FLAP, {}, THRU UR ROUTR IF U HAS NOT DUN SO.", "privMsg": "AND THEEZ ", + "return": "Go Bak to Dashbored", "serverJars1": "CAN'T TALK TO SERVER JARS API. CHECKZ", "serverJars2": "TO SEE NEWZ STUFFZ.", "start-error": "CHAIR {} FAILD 2 START WIF OOF CODE: {}", diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index d337d38f..af282603 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -204,6 +204,7 @@ "not-downloaded": "Mēs nevaram atrast jūsu izpildāmo failu. Vai tas ir beidzis lejupielādēties? Vai tā peikļuves ir uzstādītas kā palaižamas?", "portReminder": "Mēs noteicām ka šī ir pirmā reize, kad {} ir ticis palaists. Pārliecinies izlaist portu {} cauri savam rūterim/ugunsmūrim lai padarītu šo attāli pieejamu no interneta.", "privMsg": "un ", + "return": "Atgriezties uz pārskatu", "serverJars1": "Serveru JAR API nav sasniedzams. Lūdzu pārbaudiet", "serverJars2": "priekš jaunākās informācijas.", "start-error": "Serveris {} neveiskmīgi startējās ar kļūdas kodu: {}", diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index d20e209c..c73fa409 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -203,6 +203,7 @@ "not-downloaded": "We kunnen uw uitvoerbare bestand niet vinden. Is het klaar met downloaden? Zijn de rechten ingesteld op uitvoerbaar?", "portReminder": "We hebben ontdekt dat dit de eerste keer is dat {} wordt uitgevoerd. Zorg ervoor dat u poort {} doorstuurt via uw router/firewall om deze op afstand toegankelijk te maken vanaf het internet.", "privMsg": "en de ", + "return": "Terug naar Dashboard", "serverJars1": "Server JARs API niet bereikbaar. Controleer alstublieft", "serverJars2": "voor de meest recente informatie.", "start-error": "Server {} kan niet starten met foutcode: {}", diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index ade8c5a5..506df608 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -203,6 +203,7 @@ "not-downloaded": "Nie możemy znaleść twojego pliku serwera. Czy skończył się pobierać? Czy permisje są ustawione na wykonywanle?", "portReminder": "Zauważyliśmy że to jest pierwszy raz {} kiedy był włączony. Upewnij się że otworzyłeś port {} na swoim routerze/firewallu aby korzystać z tego poza domem.", "privMsg": "i także ", + "return": "Powrót do panelu", "serverJars1": "API Server Jars jest niedostępne. Proszę sprawdź", "serverJars2": "dla najnowzsych informacji.", "start-error": "Serwer {} nie mógł się odpalić z powodu: {}", diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index bbb82dae..7114504e 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -203,6 +203,7 @@ "not-downloaded": "ดูเหมือนว่าเราจะไม่พบแฟ้มกระทำการของคุณ (.jar) ตรวจสอบให้แน่ใจว่าการดาวโหลดน์เสร็จสิ้นแล้ว, การอนุญาตถูกตั้งไปยังแฟ้มกระทำการหรือไม่?", "portReminder": "เราตรวจพบว่านี่เป็นครั้งแรกที่มีการเรียกใช้ {} ตรวจสอบให้แน่ใจว่าได้ Forward port {} ผ่านเราเตอร์/ไฟร์วอลล์ของคุณเพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตจากระยะไกล", "privMsg": "และ ", + "return": "ย้อนกลับไปยังแผงควบคุม", "serverJars1": "ไม่สามารถเข้าถึงเซิร์ฟเวอร์ JARs API กรุณาตรวจสอบ", "serverJars2": "เพื่อข้อมูลที่ทันสมัยที่สุด", "start-error": "เซิร์ฟเวอร์ {} ไม่สามารถเริ่มต้นได้เนื่องจากรหัสข้อผิดพลาด: {}", diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index cddd2a50..a6ab4df7 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -203,6 +203,7 @@ "not-downloaded": "Çalıştırılabilir dosyanızı bulamıyoruz. İndirme işlemi tamamlandı mı? İzinler çalıştırılabilir olarak ayarlandı mı?", "portReminder": "{} ilk kez çalıştırılıyor olduğunu tespit ettik. Bunu internetten uzaktan erişilebilir kılmak için {} bağlantı noktasını yönlendiriciniz/güvenlik duvarınız üzerinden ilettiğinizden emin olun.", "privMsg": "ve ", + "return": "Arayüze Geri Dön", "serverJars1": "Sunucu JARs API'ına erişilemiyor.", "serverJars2": "en güncel bilgilere sahiptir", "start-error": "{} sunucusu başlamatılamadı. Hata kodu: {}", diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index 74b683dd..5d4b9326 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -203,6 +203,7 @@ "not-downloaded": "Здається, ми не можемо знайти ваш виконуваний файл. Чи завершилось завантаження? Чи встановлено дозволи на виконуваний файл?", "portReminder": "Ми виявили це вперше {} був запущений. Обов’язково перенаправте порт {} через ваш маршрутизатор/брандмауер, щоб зробити це доступним з Інтернету.", "privMsg": "і ", + "return": "Повернутись до панелі", "serverJars1": "API сервера JAR недоступний. Будь ласка, перевірте", "serverJars2": "для найактуальнішої інформації.", "start-error": "Сервер {} не запустився через помилку: {}", diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 0b817b7e..538950e4 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -203,6 +203,7 @@ "not-downloaded": "我们似乎找不到您的可执行文件。它下载完成了吗?可执行文件的权限设置正确了吗?", "portReminder": "我们检测到这是你首次运行 {}。请确保从您的路由器/防火墙转发 {} 端口,以使程序可以从公网远程访问。", "privMsg": "以及", + "return": "返回仪表板", "serverJars1": "无法访问服务器 JAR API。请检查", "serverJars2": "以获取最新信息。", "start-error": "服务器 {} 启动失败,错误代码为:{}", From ed37387fbf4565eaf2a548b13af02e2424635358 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Wed, 10 Apr 2024 18:18:44 -0500 Subject: [PATCH 136/342] updated translations for branch refactor/api-key-su --- app/translations/{cs_CS_incomplete.json => cs_CS.json} | 1 + app/translations/de_DE.json | 2 ++ app/translations/en_EN.json | 4 ++-- app/translations/es_ES.json | 1 + app/translations/fr_FR.json | 1 + app/translations/he_IL.json | 1 + app/translations/{it_IT_incomplete.json => it_IT.json} | 1 + app/translations/lol_EN.json | 1 + app/translations/lv_LV.json | 1 + app/translations/nl_BE.json | 1 + app/translations/pl_PL.json | 1 + app/translations/th_TH.json | 1 + app/translations/tr_TR.json | 1 + app/translations/zh_CN.json | 1 + 14 files changed, 16 insertions(+), 2 deletions(-) rename app/translations/{cs_CS_incomplete.json => cs_CS.json} (99%) rename app/translations/{it_IT_incomplete.json => it_IT.json} (99%) diff --git a/app/translations/cs_CS_incomplete.json b/app/translations/cs_CS.json similarity index 99% rename from app/translations/cs_CS_incomplete.json rename to app/translations/cs_CS.json index 879b78cd..ebf1276f 100644 --- a/app/translations/cs_CS_incomplete.json +++ b/app/translations/cs_CS.json @@ -29,6 +29,7 @@ "perms": "Oprávnění", "server": "Server: ", "superUser": "Super uživatel", + "fullAccess": "všechno", "yes": "Ano" }, "base": { diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index c9ffea15..de8588ea 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -28,7 +28,9 @@ "permName": "Berechtigungs Name", "perms": "Berechtigungen", "server": "Server: ", + "fullAccess": "Vollzugriff", "superUser": "Super Benutzer", + "yes": "Ja" }, "base": { diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index cdc4cbf2..957cc29b 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -20,6 +20,7 @@ "created": "Created", "deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.", "deleteKeyConfirmationTitle": "Remove API key ${keyId}?", + "fullAccess": "Full Access", "getToken": "Get A Token", "name": "Name", "nameDesc": "What would you like to call this API token? ", @@ -28,7 +29,6 @@ "permName": "Permission Name", "perms": "Permissions", "server": "Server: ", - "fullAccess": "Full Access", "yes": "Yes" }, "base": { @@ -671,4 +671,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} \ No newline at end of file +} diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json index 5f750fc7..14c2e9ad 100644 --- a/app/translations/es_ES.json +++ b/app/translations/es_ES.json @@ -29,6 +29,7 @@ "perms": "Permisos", "server": "Servidor: ", "superUser": "Super Usuario", + "fullAccess": "Acceso completo", "yes": "Si" }, "base": { diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index be429c83..cd154439 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -29,6 +29,7 @@ "perms": "Permissions", "server": "Serveur: ", "superUser": "Super Utilisateur", + "fullAccess": "Accès Complet", "yes": "Oui" }, "base": { diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index 7e2b1403..bf17dc2d 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -27,6 +27,7 @@ "pageTitle": "של משתמש זה API עריכת מפתחות", "permName": "שם הגישה", "perms": "גישות", + "fullAccess": "גישה מלאה להכל", "server": "שרת: ", "superUser": "משתמש על", "yes": "כן" diff --git a/app/translations/it_IT_incomplete.json b/app/translations/it_IT.json similarity index 99% rename from app/translations/it_IT_incomplete.json rename to app/translations/it_IT.json index 99b29d7e..db434ffc 100644 --- a/app/translations/it_IT_incomplete.json +++ b/app/translations/it_IT.json @@ -29,6 +29,7 @@ "perms": "Permessi", "server": "Server: ", "superUser": "Super User", + "fullAccess": " Accesso completo", "yes": "Sì" }, "base": { diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index aa07ff5f..1a9037da 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -28,6 +28,7 @@ "permName": "PERMISSION NAME", "perms": "PERMISSIONS", "server": "SERVER: ", + "fullAccess": "All da Doors Open", "superUser": "SUPA DOOPA USR", "yes": "YESH!" }, diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index d337d38f..b76d5d94 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -29,6 +29,7 @@ "perms": "Atļaujas", "server": "Serveris: ", "superUser": "Super Lietotājs", + "fullAccess": "Pilna piekļuve", "yes": "Jā" }, "base": { diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index d20e209c..96edc5ff 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -29,6 +29,7 @@ "perms": "Machtigingen", "server": "Server: ", "superUser": "Supergebruiker", + "fullAccess": "Volledige toegang", "yes": "Ja" }, "base": { diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index ade8c5a5..b8159cf0 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -29,6 +29,7 @@ "perms": "Permisje", "server": "Serwer: ", "superUser": "Super użytkownik", + "fullAccess": "Pełny dostęp", "yes": "Tak" }, "base": { diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index bbb82dae..79c39e3e 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -29,6 +29,7 @@ "perms": "สิทธิ์อนุญาต", "server": "เซิร์ฟเวอร์: ", "superUser": "Super User", + "fullAccess": "เข้าถึงได้ทั้งหมด", "yes": "ใช่" }, "base": { diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index cddd2a50..b5fe4d89 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -29,6 +29,7 @@ "perms": "Yetkiler", "server": "Sunucu: ", "superUser": "Süper Kullanıcı", + "fullAccess": "Tam Erişim", "yes": "Evet" }, "base": { diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 0b817b7e..31d4da98 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -29,6 +29,7 @@ "perms": "权限", "server": "服务器:", "superUser": "超级用户", + "fullAccess": "完全访问", "yes": "是" }, "base": { From 2a601c9f31e2bc09b4401bca0eb753ab14ef4463 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Thu, 11 Apr 2024 09:16:03 -0500 Subject: [PATCH 137/342] update translations for refactor/api-key-su --- app/translations/cs_CS.json | 2 +- app/translations/de_DE.json | 3 +-- app/translations/es_ES.json | 2 +- app/translations/fr_FR.json | 2 +- app/translations/he_IL.json | 2 +- app/translations/it_IT.json | 2 +- app/translations/lol_EN.json | 2 +- app/translations/lv_LV.json | 2 +- app/translations/nl_BE.json | 2 +- app/translations/pl_PL.json | 2 +- app/translations/th_TH.json | 2 +- app/translations/tr_TR.json | 2 +- app/translations/uk_UA.json | 1 + app/translations/zh_CN.json | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json index 7a799d7b..fa5acbae 100644 --- a/app/translations/cs_CS.json +++ b/app/translations/cs_CS.json @@ -20,6 +20,7 @@ "created": "Vytvořen", "deleteKeyConfirmation": "Chcete tento API klíč odstranit? Tuto akci nelze vrátit zpět.", "deleteKeyConfirmationTitle": "Odstranit klíč API ${keyId}?", + "fullAccess": "všechno", "getToken": "Získat token", "name": "Jméno", "nameDesc": "Jak chcete nazvat tento token API? ", @@ -29,7 +30,6 @@ "perms": "Oprávnění", "server": "Server: ", "superUser": "Super uživatel", - "fullAccess": "všechno", "yes": "Ano" }, "base": { diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index de8588ea..d57292eb 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -20,6 +20,7 @@ "created": "Erstellt", "deleteKeyConfirmation": "Möchten Sie diesen API Schlüssel löschen? Diese Aktion kann nicht rückgängig gemacht werden.", "deleteKeyConfirmationTitle": "Folgenden API Schlüssel löschen: ${keyId}?", + "fullAccess": "Vollzugriff", "getToken": "Schlüssel erhalten", "name": "Name", "nameDesc": "Wie soll der API Schlüssel genannt werden? ", @@ -28,9 +29,7 @@ "permName": "Berechtigungs Name", "perms": "Berechtigungen", "server": "Server: ", - "fullAccess": "Vollzugriff", "superUser": "Super Benutzer", - "yes": "Ja" }, "base": { diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json index 14c2e9ad..1c968dd6 100644 --- a/app/translations/es_ES.json +++ b/app/translations/es_ES.json @@ -20,6 +20,7 @@ "created": "Creado", "deleteKeyConfirmation": "¿Quieres eliminar esta clave de API? Esto no se puede deshacer.", "deleteKeyConfirmationTitle": "¿Eliminar la clave API ${keyId}?", + "fullAccess": "Acceso completo", "getToken": "Conseguir un Token", "name": "Nombre", "nameDesc": "¿Como te gustaría llamar a este Token de API? ", @@ -29,7 +30,6 @@ "perms": "Permisos", "server": "Servidor: ", "superUser": "Super Usuario", - "fullAccess": "Acceso completo", "yes": "Si" }, "base": { diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index cd154439..dd428ce3 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -20,6 +20,7 @@ "created": "Crée", "deleteKeyConfirmation": "Es-tu sûr de vouloir supprimer cette clé API? Tu ne pourras plus revenir en arrière.", "deleteKeyConfirmationTitle": "Supprimer la clé API ${keyId}?", + "fullAccess": "Accès Complet", "getToken": "Obtenir un Jeton", "name": "Nom", "nameDesc": "Comment appeler ce Jeton d'API ? ", @@ -29,7 +30,6 @@ "perms": "Permissions", "server": "Serveur: ", "superUser": "Super Utilisateur", - "fullAccess": "Accès Complet", "yes": "Oui" }, "base": { diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index bf17dc2d..e773755f 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -20,6 +20,7 @@ "created": "נוצר", "deleteKeyConfirmation": "האם ברצונך למחוק מפתח API זה? אי אפשר לבטל את זה.", "deleteKeyConfirmationTitle": "? ${keyId} API-להסיר את מפתח ה", + "fullAccess": "גישה מלאה להכל", "getToken": "קבלת אסימון", "name": "שם", "nameDesc": "הזה API-איך תרצו לקרוא לאסימון ה", @@ -27,7 +28,6 @@ "pageTitle": "של משתמש זה API עריכת מפתחות", "permName": "שם הגישה", "perms": "גישות", - "fullAccess": "גישה מלאה להכל", "server": "שרת: ", "superUser": "משתמש על", "yes": "כן" diff --git a/app/translations/it_IT.json b/app/translations/it_IT.json index 66a2602f..7c9adf83 100644 --- a/app/translations/it_IT.json +++ b/app/translations/it_IT.json @@ -20,6 +20,7 @@ "created": "Creato", "deleteKeyConfirmation": "Vuoi cancellare questa chiave API? Non puoi tornare indietro.", "deleteKeyConfirmationTitle": "Rimuovere la chiave API ${keyId}?", + "fullAccess": " Accesso completo", "getToken": "Prendi un Token", "name": "Nome", "nameDesc": "Come desideri chiamare questo Token API? ", @@ -29,7 +30,6 @@ "perms": "Permessi", "server": "Server: ", "superUser": "Super User", - "fullAccess": " Accesso completo", "yes": "Sì" }, "base": { diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index 1a9037da..fa5e8575 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -20,6 +20,7 @@ "created": "CREATED", "deleteKeyConfirmation": "U SURE U WANTZ TO DELETE DIS? CAN'T UNDO!", "deleteKeyConfirmationTitle": "I CAN EATZ IT??? : ${keyId}?", + "fullAccess": "All da Doors Open", "getToken": "GIT TOKEN", "name": "NAME", "nameDesc": "WUT WUD U LIEK 2 CALL DIS API TOKEN? ", @@ -28,7 +29,6 @@ "permName": "PERMISSION NAME", "perms": "PERMISSIONS", "server": "SERVER: ", - "fullAccess": "All da Doors Open", "superUser": "SUPA DOOPA USR", "yes": "YESH!" }, diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index b76d5d94..66e24cb0 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -20,6 +20,7 @@ "created": "Izveidots", "deleteKeyConfirmation": "Vai vēlies dzēst šo API atslēgu? Šo nevar atdarīt.", "deleteKeyConfirmationTitle": "Noņemt API atslēgu ${keyId}?", + "fullAccess": "Pilna piekļuve", "getToken": "Saņemt Pilnvaru (Token)", "name": "Nosaukums", "nameDesc": "Kā jūs vēlaties nosaukt šo Pilnvaru (Token)? ", @@ -29,7 +30,6 @@ "perms": "Atļaujas", "server": "Serveris: ", "superUser": "Super Lietotājs", - "fullAccess": "Pilna piekļuve", "yes": "Jā" }, "base": { diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index 96edc5ff..1b5d9a5f 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -20,6 +20,7 @@ "created": "Gecreëerd", "deleteKeyConfirmation": "Wilt u deze API sleutel verwijderen? Dit kan niet ongedaan gemaakt worden.", "deleteKeyConfirmationTitle": "API sleutel verwijderen ${keyId}?", + "fullAccess": "Volledige toegang", "getToken": "Verkrijg een Token", "name": "Naam", "nameDesc": "Hoe wilt u dit API token noemen? ", @@ -29,7 +30,6 @@ "perms": "Machtigingen", "server": "Server: ", "superUser": "Supergebruiker", - "fullAccess": "Volledige toegang", "yes": "Ja" }, "base": { diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index b8159cf0..bcbd6ff8 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -20,6 +20,7 @@ "created": "Stworzono", "deleteKeyConfirmation": "Czy chcesz usunąć ten klucz API? Nie można tego cofnąć.", "deleteKeyConfirmationTitle": "Usunąć Klucz API ${keyId}?", + "fullAccess": "Pełny dostęp", "getToken": "Zdobądź token", "name": "Nazwa", "nameDesc": "Jak chcesz nazwać ten klucz API? ", @@ -29,7 +30,6 @@ "perms": "Permisje", "server": "Serwer: ", "superUser": "Super użytkownik", - "fullAccess": "Pełny dostęp", "yes": "Tak" }, "base": { diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index 79c39e3e..ed21b959 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -20,6 +20,7 @@ "created": "สร้างเมื่อ", "deleteKeyConfirmation": "คุณต้องการลบคีย์ API นี้หรือไม่ สิ่งนี้ไม่สามารถยกเลิกได้", "deleteKeyConfirmationTitle": "ลบคีย์ API นี้ ${keyId} หรือไม่?", + "fullAccess": "เข้าถึงได้ทั้งหมด", "getToken": "แสดงโทเค็น", "name": "ชื่อ", "nameDesc": "คุณต้องการเรียกโทเค็น API นี้ว่าอะไร ? ", @@ -29,7 +30,6 @@ "perms": "สิทธิ์อนุญาต", "server": "เซิร์ฟเวอร์: ", "superUser": "Super User", - "fullAccess": "เข้าถึงได้ทั้งหมด", "yes": "ใช่" }, "base": { diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index b5fe4d89..74f6545e 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -20,6 +20,7 @@ "created": "Oluşturuldu", "deleteKeyConfirmation": "Bu API anahtarını silmek istediğine emin misin? Bu geri alınamaz.", "deleteKeyConfirmationTitle": "${keyId} API anahtarını kaldırma işlemi.", + "fullAccess": "Tam Erişim", "getToken": "Bir Token Al", "name": "Ad", "nameDesc": "Bu API tokeninin adı ne olsun?", @@ -29,7 +30,6 @@ "perms": "Yetkiler", "server": "Sunucu: ", "superUser": "Süper Kullanıcı", - "fullAccess": "Tam Erişim", "yes": "Evet" }, "base": { diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index 74b683dd..a4791d45 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -20,6 +20,7 @@ "created": "Створений", "deleteKeyConfirmation": "Ви дійсно бажаєте видалити API ключ? Це незворотня дія.", "deleteKeyConfirmationTitle": "Видалення API ключ ${keyId}?", + "fullAccess": "Повний доступ", "getToken": "Отримати Токен", "name": "Ім'я", "nameDesc": "Як ви хочете назвати даний API токен?", diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 31d4da98..5e63590e 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -20,6 +20,7 @@ "created": "创建时间", "deleteKeyConfirmation": "您想要删除这个 API 密钥吗?此操作不能撤销。", "deleteKeyConfirmationTitle": "删除 API 密钥 ${keyId}?", + "fullAccess": "完全访问", "getToken": "获得一个令牌", "name": "名称", "nameDesc": "你想把这个 API 令牌叫做什么?", @@ -29,7 +30,6 @@ "perms": "权限", "server": "服务器:", "superUser": "超级用户", - "fullAccess": "完全访问", "yes": "是" }, "base": { From 6c28ef63278aa8fd6b9de999e8779595ae633910 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Mon, 15 Apr 2024 17:48:53 -0500 Subject: [PATCH 138/342] Update cs_CS.json --- app/translations/cs_CS.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json index 2229d5cb..f12fe284 100644 --- a/app/translations/cs_CS.json +++ b/app/translations/cs_CS.json @@ -612,7 +612,7 @@ "credits": "Zásluhy", "dashboard": "Ovládací panel", "documentation": "Dokumentace", - "inApp": "V app dokumentaci", + "inApp": "V lokalní dokumentaci", "navigation": "Navigace", "newServer": "Vytvořit nový server", "servers": "Servery" From 92bd7b80c80c5f02cf0c35b3fd5ba201f888a01e Mon Sep 17 00:00:00 2001 From: --unset Date: Tue, 16 Apr 2024 10:26:09 -0400 Subject: [PATCH 139/342] Remove console log on login --- app/frontend/templates/public/login.html | 1 - 1 file changed, 1 deletion(-) diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 275d2000..9c7d3f91 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -171,7 +171,6 @@ //Create an object from the form data entries let formDataObject = Object.fromEntries(formData.entries()); - console.log(formDataObject) let res = await fetch(`/login`, { method: 'POST', headers: { From 89b552a880b16b0bc786f46034677b9ef3f896da Mon Sep 17 00:00:00 2001 From: --unset Date: Tue, 16 Apr 2024 10:26:32 -0400 Subject: [PATCH 140/342] Redirect to /login if garbage is in query args --- app/classes/web/public_handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 21e2d495..a3d89d25 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -48,7 +48,10 @@ class PublicHandler(BaseHandler): } if self.request.query: - page_data["query"] = self.request.query_arguments.get("next")[0].decode() + request_query = self.request.query_arguments.get("next") + if not request_query: + self.redirect("/login") + page_data["query"] = request_query[0].decode() # sensible defaults template = "public/404.html" From 227d642546e78f3eb302c61801c504822a578cda Mon Sep 17 00:00:00 2001 From: --unset Date: Wed, 17 Apr 2024 18:03:10 -0400 Subject: [PATCH 141/342] Set audit logging to logfile instead of DB --- app/classes/logging/log_formatter.py | 54 ++++++++++++++++++++++++ app/classes/models/management.py | 61 +++++++++------------------- app/config/logging.json | 17 ++++++++ main.py | 6 +++ 4 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 app/classes/logging/log_formatter.py diff --git a/app/classes/logging/log_formatter.py b/app/classes/logging/log_formatter.py new file mode 100644 index 00000000..7167ab4d --- /dev/null +++ b/app/classes/logging/log_formatter.py @@ -0,0 +1,54 @@ +import logging +import logging.config +import json +from datetime import datetime + + +class JsonEncoderStrFallback(json.JSONEncoder): + def default(self, o): + try: + return super().default(o) + except TypeError as exc: + if "not JSON serializable" in str(exc): + return str(o) + raise + + +class JsonEncoderDatetime(JsonEncoderStrFallback): + def default(self, o): + if isinstance(o, datetime): + return o.strftime("%Y-%m-%dT%H:%M:%S%z") + else: + return super().default(o) + + +class JsonFormatter(logging.Formatter): + def formatTime(self, record, datefmt=None): + """ + Override formatTime to customize the time format. + """ + timestamp = datetime.fromtimestamp(record.created) + if datefmt: + # Use the specified date format + return timestamp.strftime(datefmt) + else: + # Default date format: YYYY-MM-DD HH:MM:SS,mmm + secs = int(record.msecs) + return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')},{secs:03d}" + + def format(self, record): + log_data = { + "level": record.levelname, + "time": self.formatTime(record), + "log_msg": record.getMessage(), + } + + # Filter out standard log record attributes and include only custom ones + custom_attrs = ["user_name", "user_id", "server_id", "source_ip"] + extra_attrs = { + key: value for key, value in record.__dict__.items() if key in custom_attrs + } + + # Merge extra attributes with log data + log_data.update(extra_attrs) + return json.dumps(log_data) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index ffe207c2..12eeac0b 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -20,6 +20,7 @@ from app.classes.shared.main_models import DatabaseShortcuts from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) +auth_logger = logging.getLogger("audit_log") # ********************************************************************************** @@ -166,50 +167,26 @@ class HelpersManagement: WebSocketManager().broadcast_user(user, "notification", audit_msg) except Exception as e: logger.error(f"Error broadcasting to user {user} - {e}") - - AuditLog.insert( - { - AuditLog.user_name: user_data["username"], - AuditLog.user_id: user_id, - AuditLog.server_id: server_id, - AuditLog.log_msg: audit_msg, - AuditLog.source_ip: source_ip, - } - ).execute() - # deletes records when there's more than 300 - ordered = AuditLog.select().order_by(+AuditLog.created) - for item in ordered: - if not self.helper.get_setting("max_audit_entries"): - max_entries = 300 - else: - max_entries = self.helper.get_setting("max_audit_entries") - if AuditLog.select().count() > max_entries: - AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute() - else: - return + auth_logger.info( + str(log_msg), + extra={ + "user_name": user_data["username"], + "user_id": user_id, + "server_id": server_id, + "source_ip": source_ip, + }, + ) def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip): - AuditLog.insert( - { - AuditLog.user_name: user_name, - AuditLog.user_id: user_id, - AuditLog.server_id: server_id, - AuditLog.log_msg: log_msg, - AuditLog.source_ip: source_ip, - } - ).execute() - # deletes records when there's more than 300 - ordered = AuditLog.select().order_by(+AuditLog.created) - for item in ordered: - # configurable through app/config/config.json - if not self.helper.get_setting("max_audit_entries"): - max_entries = 300 - else: - max_entries = self.helper.get_setting("max_audit_entries") - if AuditLog.select().count() > max_entries: - AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute() - else: - return + auth_logger.info( + str(log_msg), + extra={ + "user_name": user_name, + "user_id": user_id, + "server_id": server_id, + "source_ip": source_ip, + }, + ) @staticmethod def create_crafty_row(): diff --git a/app/config/logging.json b/app/config/logging.json index fd1173eb..d0a20cdf 100644 --- a/app/config/logging.json +++ b/app/config/logging.json @@ -14,6 +14,9 @@ "auth": { "format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s" }, + "audit": { + "()": "app.classes.logging.log_formatter.JsonFormatter" + }, "cmd_queue": { "format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s" } @@ -70,6 +73,14 @@ "maxBytes": 10485760, "backupCount": 20, "encoding": "utf8" + }, + "audit_log_handler": { + "class": "logging.handlers.RotatingFileHandler", + "formatter": "audit", + "filename": "logs/audit.log", + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8" } }, "loggers": { @@ -108,6 +119,12 @@ "cmd_queue_file_handler" ], "propagate": false + }, + "audit_log": { + "level": "INFO", + "handlers": [ + "audit_log_handler" + ] } } } \ No newline at end of file diff --git a/main.py b/main.py index ebaf7806..f8a4aab1 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ from app.classes.models.users import HelperUsers from app.classes.models.management import HelpersManagement from app.classes.shared.import_helper import ImportHelpers from app.classes.shared.websocket_manager import WebSocketManager +from app.classes.logging.log_formatter import JsonFormatter console = Console() helper = Helpers() @@ -284,6 +285,11 @@ def setup_logging(debug=True): logging.config.dictConfig(logging_config) + # Apply JSON formatting to the "audit" handler + for handler in logging.getLogger().handlers: + if handler.name == "audit_log_handler": + handler.setFormatter(JsonFormatter()) + else: logging.basicConfig(level=logging.DEBUG) logging.warning(f"Unable to read logging config from {logging_config_file}") From a76a2722db3003841b67fd18ba339a026e0c77b7 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 19 Apr 2024 16:05:35 -0400 Subject: [PATCH 142/342] Fix child schedule failing to load after del parent --- app/classes/web/panel_handler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 716671c8..4a3242a5 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1237,9 +1237,11 @@ class PanelHandler(BaseHandler): page_data["schedule"]["interval_type"] = schedule.interval_type if schedule.interval_type == "reaction": difficulty = "reaction" - page_data["parent"] = self.controller.management.get_scheduled_task( - schedule.parent - ) + page_data["parent"] = None + if schedule.parent: + page_data["parent"] = self.controller.management.get_scheduled_task( + schedule.parent + ) elif schedule.cron_string == "": difficulty = "basic" page_data["parent"] = None From 92dfd18dbd923cdff7eb456e70cc8bab745ceed3 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 15:47:34 -0400 Subject: [PATCH 143/342] Refactor API for new activity log format --- app/classes/web/routes/api/crafty/clogs/index.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/crafty/clogs/index.py b/app/classes/web/routes/api/crafty/clogs/index.py index 97a24a34..f42d7e2c 100644 --- a/app/classes/web/routes/api/crafty/clogs/index.py +++ b/app/classes/web/routes/api/crafty/clogs/index.py @@ -1,3 +1,5 @@ +import os +import json from app.classes.web.base_api_handler import BaseApiHandler @@ -22,9 +24,17 @@ class ApiCraftyLogIndexHandler(BaseApiHandler): raise NotImplementedError if log_type == "audit": + with open( + os.path.join(self.controller.project_root, "logs", "audit.log"), + "r", + encoding="utf-8", + ) as f: + log_lines = [json.loads(line) for line in f] + rev_log_lines = log_lines[::-1] + return self.finish_json( 200, - {"status": "ok", "data": self.controller.management.get_activity_log()}, + {"status": "ok", "data": rev_log_lines}, ) if log_type == "session": From be2e6f5e6cbf2d87f465cce17ea6f02252a6b799 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 15:47:50 -0400 Subject: [PATCH 144/342] Update acttivity log page to use new format --- .../templates/panel/activity_logs.html | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/app/frontend/templates/panel/activity_logs.html b/app/frontend/templates/panel/activity_logs.html index 389edaf5..27f0459c 100644 --- a/app/frontend/templates/panel/activity_logs.html +++ b/app/frontend/templates/panel/activity_logs.html @@ -36,25 +36,14 @@ - - - - - + + + + + - {% for row in data['audit_logs'] %} - - - - - - - - {% end %}
UsernameTimeActionServer IDIPTimeUsernameActionServer IDIP
{{ row['user_name'] }} - {{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }} - {{ row['log_msg'] }}{{ row['server_id'] }}{{ row['source_ip'] }}
@@ -79,17 +68,6 @@ {% end %} {% block js %} - {% end %} \ No newline at end of file From 37373733d905d759859d7d04cf0af828f7847bc4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 16:07:58 -0400 Subject: [PATCH 145/342] Add loading splash screen --- .../templates/panel/activity_logs.html | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/frontend/templates/panel/activity_logs.html b/app/frontend/templates/panel/activity_logs.html index 27f0459c..45ab3a79 100644 --- a/app/frontend/templates/panel/activity_logs.html +++ b/app/frontend/templates/panel/activity_logs.html @@ -44,6 +44,13 @@ + + + Crafty Logo, Crafty is loading

{{ translate('datatables', + 'loadingRecords', data['lang'])}} + + @@ -134,6 +141,26 @@ bootbox.alert(responseData.error) } } + + function rotateImage(degree) { + $('#logo-animate').animate({ transform: degree }, { + step: function (now, fx) { + $(this).css({ + '-webkit-transform': 'rotate(' + now + 'deg)', + '-moz-transform': 'rotate(' + now + 'deg)', + 'transform': 'rotate(' + now + 'deg)' + }); + } + }); + setTimeout(function () { + rotateImage(360); + }, 2000); + } + $(document).ready(function () { + setTimeout(function () { + rotateImage(360); + }, 2000); + }); {% end %} \ No newline at end of file From 038f275388c136603463e6e8ea2f0aa234184dd0 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 16:08:16 -0400 Subject: [PATCH 146/342] Restructure translation json --- app/translations/cs_CS.json | 3 ++- app/translations/de_DE.json | 3 ++- app/translations/en_EN.json | 3 ++- app/translations/es_ES.json | 3 ++- app/translations/fi_FI_incomplete.json | 3 ++- app/translations/fr_FR.json | 3 ++- app/translations/fy_NL_incomplete.json | 3 ++- app/translations/he_IL.json | 3 ++- app/translations/hr_HR_incomplete.json | 3 ++- app/translations/id_ID_incomplete.json | 3 ++- app/translations/it_IT.json | 3 ++- app/translations/lol_EN.json | 3 ++- app/translations/lv_LV.json | 3 ++- app/translations/nl_BE.json | 3 ++- app/translations/nl_NL_incomplete.json | 3 ++- app/translations/pl_PL.json | 3 ++- app/translations/pt_BR_incomplete.json | 3 ++- app/translations/th_TH.json | 3 ++- app/translations/tr_TR.json | 3 ++- app/translations/uk_UA.json | 3 ++- app/translations/zh_CN.json | 3 ++- 21 files changed, 42 insertions(+), 21 deletions(-) diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json index 2229d5cb..0e12dc11 100644 --- a/app/translations/cs_CS.json +++ b/app/translations/cs_CS.json @@ -116,6 +116,7 @@ "welcome": "Vítejte v Crafty Controlleru" }, "datatables": { + "loadingRecords": "Načítání...", "i18n": { "aria": { "sortAscending": ": aktivace řazení sloupce vzestupně", @@ -691,4 +692,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooky" } -} +} \ No newline at end of file diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index c9ffea15..2ebd902f 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -116,6 +116,7 @@ "welcome": "Willkommen bei Crafty Controller" }, "datatables": { + "loadingRecords": "Laden...", "i18n": { "aria": { "sortAscending": ": Aktivieren, um die Spalte aufsteigend zu sortieren", @@ -672,4 +673,4 @@ "webhook_body": "Webhook-Inhalt", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 5c48b873..91ff2f17 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -116,6 +116,7 @@ "welcome": "Welcome to Crafty Controller" }, "datatables": { + "loadingRecords": "Loading...", "i18n": { "aria": { "sortAscending": ": activate to sort column ascending", @@ -671,4 +672,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json index 5f750fc7..e59458e7 100644 --- a/app/translations/es_ES.json +++ b/app/translations/es_ES.json @@ -116,6 +116,7 @@ "welcome": "Bienvenido a Crafty Controller" }, "datatables": { + "loadingRecords": "Cargando...", "i18n": { "aria": { "sortAscending": ": activar para ordenar las columnas de manera ascendente", @@ -672,4 +673,4 @@ "webhook_body": "Cuerpo del Webhook", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/fi_FI_incomplete.json b/app/translations/fi_FI_incomplete.json index 55cd990c..8d6168a6 100644 --- a/app/translations/fi_FI_incomplete.json +++ b/app/translations/fi_FI_incomplete.json @@ -100,6 +100,7 @@ "welcome": "Tervetuloa Crafty Controller" }, "datatables": { + "loadingRecords": "Ladataan...", "i18n": { "aria": { "sortAscending": ": lajittele sarake nousevasti", @@ -560,4 +561,4 @@ "userSettings": "Käyttäjäasetukset", "uses": "Sallittujen käyttäkertojen määtä (-1 == Ei rajaa)" } -} +} \ No newline at end of file diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index be429c83..1014f44f 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -116,6 +116,7 @@ "welcome": "Bienvenue sur Crafty Controller" }, "datatables": { + "loadingRecords": "Chargement ...", "i18n": { "aria": { "sortAscending": ": activer pour trier les colonnes dans l'ordre croissant", @@ -672,4 +673,4 @@ "webhook_body": "Corps du Webhook", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/fy_NL_incomplete.json b/app/translations/fy_NL_incomplete.json index 98785a2b..6c5d76e8 100644 --- a/app/translations/fy_NL_incomplete.json +++ b/app/translations/fy_NL_incomplete.json @@ -99,6 +99,7 @@ "welcome": "Wolkom by Crafty Controller" }, "datatables": { + "loadingRecords": "Laden...", "i18n": { "aria": { "sortAscending": ": aktivearje om kolom oprinnend te sortearjen", @@ -529,4 +530,4 @@ "userSettings": "Brûkersynstellingen", "uses": "Oantal gebrûk tastien (-1==Gjin limyt)" } -} +} \ No newline at end of file diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index 7e2b1403..ce08bb01 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -116,6 +116,7 @@ "welcome": "ברוכים הבאים ל-פאנל קראפטי" }, "datatables": { + "loadingRecords": "...טוען", "i18n": { "aria": { "sortAscending": ": הפעילו כדי למיין עמודות בסדר עולה", @@ -672,4 +673,4 @@ "webhook_body": "גוף ה-Webhook", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/hr_HR_incomplete.json b/app/translations/hr_HR_incomplete.json index 3f63770f..392ee098 100644 --- a/app/translations/hr_HR_incomplete.json +++ b/app/translations/hr_HR_incomplete.json @@ -99,6 +99,7 @@ "welcome": "Dobrodošli u Crafty Controller" }, "datatables": { + "loadingRecords": "Učitavanje...", "i18n": { "aria": { "sortAscending": ": aktiviraj za sortiranje stupca uzlazno", @@ -529,4 +530,4 @@ "userSettings": "Korisničke postavke", "uses": "Broj dopuštenih upotreba (-1==Bez ograničenja)" } -} +} \ No newline at end of file diff --git a/app/translations/id_ID_incomplete.json b/app/translations/id_ID_incomplete.json index cfec5557..c2e8813d 100644 --- a/app/translations/id_ID_incomplete.json +++ b/app/translations/id_ID_incomplete.json @@ -100,6 +100,7 @@ "welcome": "Selamat Datang Di Crafty Controller" }, "datatables": { + "loadingRecords": "Loading...", "i18n": { "aria": { "sortAscending": ": aktifkan untuk mengurutkan kolom menaik", @@ -536,4 +537,4 @@ "userSettings": "Pengaturan Pengguna", "uses": "Jumlah penggunaan yang diizinkan (-1==No Limit)" } -} +} \ No newline at end of file diff --git a/app/translations/it_IT.json b/app/translations/it_IT.json index adb59c7d..f2613f22 100644 --- a/app/translations/it_IT.json +++ b/app/translations/it_IT.json @@ -116,6 +116,7 @@ "welcome": "Benvenuto su Crafty Controller" }, "datatables": { + "loadingRecords": "Carico...", "i18n": { "aria": { "sortAscending": ": attiva per ordinare le colonne in modo ascendente", @@ -672,4 +673,4 @@ "webhook_body": "Corpo del Webhook", "webhooks": "Webhook" } -} +} \ No newline at end of file diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index aa07ff5f..038608a5 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -116,6 +116,7 @@ "welcome": "WELCOM 2 CWAFTY CONTROLLR" }, "datatables": { + "loadingRecords": "Loading...", "i18n": { "aria": { "sortAscending": ": activate to sort column ascending", @@ -672,4 +673,4 @@ "webhook_body": "WEBHOOK FISH", "webhooks": "WEBHOOKZ" } -} +} \ No newline at end of file diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index d337d38f..02974c8c 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -117,6 +117,7 @@ "welcome": "Esiet sveicināts Crafty Controller" }, "datatables": { + "loadingRecords": "Ielādē...", "i18n": { "aria": { "sortAscending": ": aktivizēt lai kārotu kolonnu augoši", @@ -673,4 +674,4 @@ "webhook_body": "Webhook Saturs", "webhooks": "Webhooki" } -} +} \ No newline at end of file diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index d20e209c..428c0323 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -116,6 +116,7 @@ "welcome": "Welkom bij Crafty Controller " }, "datatables": { + "loadingRecords": "Bezig met laden...", "i18n": { "aria": { "sortAscending": ": activeren om kolom oplopend te sorteren", @@ -672,4 +673,4 @@ "webhook_body": "Webhook-body", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/nl_NL_incomplete.json b/app/translations/nl_NL_incomplete.json index 8fd97240..1d158594 100644 --- a/app/translations/nl_NL_incomplete.json +++ b/app/translations/nl_NL_incomplete.json @@ -99,6 +99,7 @@ "welcome": "Welkom bij Crafty Controller" }, "datatables": { + "loadingRecords": "Laden...", "i18n": { "aria": { "sortAscending": ": activeer om kolom oplopend te sorteren", @@ -529,4 +530,4 @@ "userSettings": "Gebruikersinstellingen", "uses": "Aantal keer toegestaan (-1==Geen limiet)" } -} +} \ No newline at end of file diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index ade8c5a5..2d089607 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -116,6 +116,7 @@ "welcome": "Witamy w Crafty Controller" }, "datatables": { + "loadingRecords": "Wczytywanie...", "i18n": { "aria": { "sortAscending": ": aktywuj, aby sortować kolumny w góre", @@ -671,4 +672,4 @@ "webhook_body": "Treść Webhooka", "webhooks": "Webhooki" } -} +} \ No newline at end of file diff --git a/app/translations/pt_BR_incomplete.json b/app/translations/pt_BR_incomplete.json index c8844f40..dcd375f6 100644 --- a/app/translations/pt_BR_incomplete.json +++ b/app/translations/pt_BR_incomplete.json @@ -100,6 +100,7 @@ "welcome": "Bem-vindo ao Crafty Controller" }, "datatables": { + "loadingRecords": "Carregando...", "i18n": { "aria": { "sortAscending": ": ative para ordenar a coluna de forma ascendente", @@ -537,4 +538,4 @@ "userSettings": "Configurações do Usuário", "uses": "Número de Usos Permitidos (-1==Sem Limite)" } -} +} \ No newline at end of file diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index bbb82dae..e60e86e0 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -116,6 +116,7 @@ "welcome": "ยินดีต้อนรับสู่ Crafty Controller" }, "datatables": { + "loadingRecords": "กำลังโหลด...", "i18n": { "aria": { "sortAscending": ": เปิดใช้งานเพื่อเรียงลำดับคอลัมน์จากน้อยไปมาก", @@ -671,4 +672,4 @@ "webhook_body": "ภายใน Webhook", "webhooks": "Webhooks" } -} +} \ No newline at end of file diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index cddd2a50..c3a1bf5b 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -116,6 +116,7 @@ "welcome": "Crafty Controller'a Hoşgeldiniz!" }, "datatables": { + "loadingRecords": "Yükleniyor...", "i18n": { "aria": { "sortAscending": ": artan sütun sıralamasını aktifleştir", @@ -671,4 +672,4 @@ "webhook_body": "Webhook Gövdesi", "webhooks": "Webhooklar" } -} +} \ No newline at end of file diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index 74b683dd..116792b8 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -116,6 +116,7 @@ "welcome": "Ласкаво просимо у Crafty Controller" }, "datatables": { + "loadingRecords": "Завантаження...", "i18n": { "aria": { "sortAscending": ": активуйте, щоб сортувати стовпці за зростанням", @@ -671,4 +672,4 @@ "webhook_body": "Код Вебхука", "webhooks": "Вебхуки" } -} +} \ No newline at end of file diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 0b817b7e..68067fb7 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -116,6 +116,7 @@ "welcome": "欢迎来到 Crafty Controller" }, "datatables": { + "loadingRecords": "正在加载……", "i18n": { "aria": { "sortAscending": ":激活对队列的升序排列", @@ -672,4 +673,4 @@ "webhook_body": "Webhook 消息体(Body)", "webhooks": "Webhook" } -} +} \ No newline at end of file From 3fac1a39a8095e1fd1b2e02ba730cb5801c9b579 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 16:21:06 -0400 Subject: [PATCH 147/342] Setup drop table migrations --- .../controllers/management_controller.py | 3 -- app/classes/models/management.py | 22 ------------ app/classes/web/panel_handler.py | 2 -- .../20240216_rework_servers_uuid.py | 19 ----------- .../20240217_rework_servers_uuid_part2.py | 30 ---------------- app/migrations/20240420_audit_log_drop.py | 34 +++++++++++++++++++ 6 files changed, 34 insertions(+), 76 deletions(-) create mode 100644 app/migrations/20240420_audit_log_drop.py diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 7085b503..cab755b6 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -95,9 +95,6 @@ class ManagementController: # ********************************************************************************** # Audit_Log Methods # ********************************************************************************** - @staticmethod - def get_activity_log(): - return HelpersManagement.get_activity_log() def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None): return self.management_helper.add_to_audit_log( diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 12eeac0b..38ce53b2 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -23,24 +23,6 @@ logger = logging.getLogger(__name__) auth_logger = logging.getLogger("audit_log") -# ********************************************************************************** -# Audit_Log Class -# ********************************************************************************** -class AuditLog(BaseModel): - audit_id = AutoField() - created = DateTimeField(default=datetime.datetime.now) - user_name = CharField(default="") - user_id = IntegerField(default=0, index=True) - source_ip = CharField(default="127.0.0.1") - server_id = ForeignKeyField( - Servers, backref="audit_server", null=True - ) # When auditing global events, use server ID null - log_msg = TextField(default="") - - class Meta: - table_name = "audit_log" - - # ********************************************************************************** # Crafty Settings Class # ********************************************************************************** @@ -150,10 +132,6 @@ class HelpersManagement: # ********************************************************************************** # Audit_Log Methods # ********************************************************************************** - @staticmethod - def get_activity_log(): - query = AuditLog.select() - return DatabaseShortcuts.return_db_rows(query) def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None): logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ") diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 716671c8..dc2cc313 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1503,8 +1503,6 @@ class PanelHandler(BaseHandler): template = "panel/panel_edit_role.html" elif page == "activity_logs": - page_data["audit_logs"] = self.controller.management.get_activity_log() - template = "panel/activity_logs.html" elif page == "download_file": diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py index facd0e42..7850346d 100644 --- a/app/migrations/20240216_rework_servers_uuid.py +++ b/app/migrations/20240216_rework_servers_uuid.py @@ -6,7 +6,6 @@ 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, @@ -61,17 +60,6 @@ def migrate(migrator: Migrator, database, **kwargs): 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, @@ -109,13 +97,6 @@ def rollback(migrator: Migrator, database, **kwargs): 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, diff --git a/app/migrations/20240217_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py index eab04c28..a4b01c45 100644 --- a/app/migrations/20240217_rework_servers_uuid_part2.py +++ b/app/migrations/20240217_rework_servers_uuid_part2.py @@ -6,7 +6,6 @@ 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, @@ -73,20 +72,6 @@ def migrate(migrator: Migrator, database, **kwargs): 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(): @@ -247,21 +232,6 @@ def rollback(migrator: Migrator, database, **kwargs): try: logger.info("Migrating Data from UUID to Int (Foreign Keys)") Console.info("Migrating Data from UUID to Int (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 is None: - new_server_id = 0 - else: - try: - server = Servers.get_or_none(Servers.server_uuid == old_server_id) - new_server_id = server.server_id - except: - new_server_id = old_server_id - AuditLog.update(server_id=new_server_id).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 diff --git a/app/migrations/20240420_audit_log_drop.py b/app/migrations/20240420_audit_log_drop.py new file mode 100644 index 00000000..be153f11 --- /dev/null +++ b/app/migrations/20240420_audit_log_drop.py @@ -0,0 +1,34 @@ +import peewee +import datetime +from peewee import ( + AutoField, + DateTimeField, + CharField, + IntegerField, + ForeignKeyField, + TextField, +) + +from app.classes.shared.server import Servers + + +def migrate(migrator, db): + migrator.drop_table("audit_log") + + +def rollback(migrator, db): + class AuditLog(peewee.Model): + audit_id = AutoField() + created = DateTimeField(default=datetime.datetime.now) + user_name = CharField(default="") + user_id = IntegerField(default=0, index=True) + source_ip = CharField(default="127.0.0.1") + server_id = ForeignKeyField( + Servers, backref="audit_server", null=True + ) # When auditing global events, use server ID null + log_msg = TextField(default="") + + class Meta: + table_name = "audit_log" + + migrator.create_table(AuditLog) From 2134c4226de25957c7a778dc7ada93eb1e007a83 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 16:54:33 -0400 Subject: [PATCH 148/342] Fix audit log raw --- app/classes/models/management.py | 2 ++ app/frontend/templates/panel/activity_logs.html | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 38ce53b2..dc57816d 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -156,6 +156,8 @@ class HelpersManagement: ) def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip): + if isinstance(server_id, Servers) and server_id is not None: + server_id = server_id.server_id auth_logger.info( str(log_msg), extra={ diff --git a/app/frontend/templates/panel/activity_logs.html b/app/frontend/templates/panel/activity_logs.html index 45ab3a79..e053e50e 100644 --- a/app/frontend/templates/panel/activity_logs.html +++ b/app/frontend/templates/panel/activity_logs.html @@ -111,7 +111,11 @@ $.each(data, function (index, value) { let row = $(''); row.append(`${value.time}`); - row.append(`
${value.user_name}`); + if (value.user_name != "system" && value.user_id != "-1") { + row.append(`${value.user_name}`); + } else { + row.append(`${value.user_name}`); + } row.append(`${value.log_msg}`); row.append(`${value.server_id}`); row.append(`${value.source_ip}`); From d998d82de022e907bd864fbf79a7fcb9c5bd9e66 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 17:04:30 -0400 Subject: [PATCH 149/342] Fix code quaity issues --- app/classes/logging/log_formatter.py | 11 +++++------ app/classes/models/management.py | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/classes/logging/log_formatter.py b/app/classes/logging/log_formatter.py index 7167ab4d..e3f2b4f7 100644 --- a/app/classes/logging/log_formatter.py +++ b/app/classes/logging/log_formatter.py @@ -18,8 +18,8 @@ class JsonEncoderDatetime(JsonEncoderStrFallback): def default(self, o): if isinstance(o, datetime): return o.strftime("%Y-%m-%dT%H:%M:%S%z") - else: - return super().default(o) + + return super().default(o) class JsonFormatter(logging.Formatter): @@ -31,10 +31,9 @@ class JsonFormatter(logging.Formatter): if datefmt: # Use the specified date format return timestamp.strftime(datefmt) - else: - # Default date format: YYYY-MM-DD HH:MM:SS,mmm - secs = int(record.msecs) - return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')},{secs:03d}" + # Default date format: YYYY-MM-DD HH:MM:SS,mmm + secs = int(record.msecs) + return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')},{secs:03d}" def format(self, record): log_data = { diff --git a/app/classes/models/management.py b/app/classes/models/management.py index dc57816d..e59dd39c 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -16,7 +16,6 @@ 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.main_models import DatabaseShortcuts from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) From b898595371b0bf5dad2006fb864b54e5975ade2e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 20 Apr 2024 17:30:37 -0400 Subject: [PATCH 150/342] 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 151/342] 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 152/342] 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 %}
- +
- - - - - + + + + + {% 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 153/342] 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 f845f546547636bd53a60d7f452852d8b7abac55 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 18 May 2024 20:32:27 -0400 Subject: [PATCH 188/342] Add info note to default creds file --- main.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index fd84328b..617fe5f4 100644 --- a/main.py +++ b/main.py @@ -367,7 +367,15 @@ if __name__ == "__main__": encoding="utf-8", ) as cred_file: cred_file.write( - json.dumps({"username": "admin", "password": PASSWORD}, indent=4) + json.dumps( + { + "username": "admin", + "password": PASSWORD, + "info": "This is NOT where you change your password." + " This file is only a means to give you a default password.", + }, + indent=4, + ) ) os.chmod( os.path.join(APPLICATION_PATH, "app", "config", "default-creds.txt"), 0o600 From 5d53a1fa46b73be5687152087e153200e776af82 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 23 May 2024 21:54:35 +0100 Subject: [PATCH 189/342] Pin sonar-scanner-cli to 5.0.1 Latest Broken --- .gitlab/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/lint.yml b/.gitlab/lint.yml index 37649e1a..d64cb921 100644 --- a/.gitlab/lint.yml +++ b/.gitlab/lint.yml @@ -66,7 +66,7 @@ pylint: sonarcloud-check: stage: lint image: - name: sonarsource/sonar-scanner-cli:latest + name: sonarsource/sonar-scanner-cli:5.0.1 entrypoint: [""] tags: - saas-linux-medium-amd64 From 2a6c0ca75133a72062e1afbb985663f406f2dad0 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 23 May 2024 23:40:03 +0100 Subject: [PATCH 190/342] Revert pinned sonarq version, They've moved to a rootless image and cache was retaining files with root permissions, solution is to clear cache --- .gitlab/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/lint.yml b/.gitlab/lint.yml index d64cb921..37649e1a 100644 --- a/.gitlab/lint.yml +++ b/.gitlab/lint.yml @@ -66,7 +66,7 @@ pylint: sonarcloud-check: stage: lint image: - name: sonarsource/sonar-scanner-cli:5.0.1 + name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] tags: - saas-linux-medium-amd64 From 895ba2d2f595cc228eadff38429b6098129e1096 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Mon, 20 May 2024 10:20:17 -0500 Subject: [PATCH 191/342] updates for the big bucket release --- app/translations/th_TH.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index 8c9d014e..28322a72 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -184,6 +184,8 @@ "error": { "agree": "ยอมรับ", "bedrockError": "การดาวน์โหลดเวอร์ชั่น Bedrock ไม่พร้อมใช้งาน โปรดตรวจสอบ", + "bigBucket1": "การตรวจสอบสุขภาพ Big Bucket ล้มเหลว โปรดตรวจสอบ", + "bigBucket2": "สำหรับข้อมูลล่าสุด", "cancel": "ยกเลิก", "contact": "ติดต่อฝ่ายสนับสนุน Crafty Control ผ่านดิสคอร์ด", "craftyStatus": "หน้าสถานะของ Crafty", @@ -206,6 +208,7 @@ "portReminder": "เราตรวจพบว่านี่เป็นครั้งแรกที่มีการเรียกใช้ {} ตรวจสอบให้แน่ใจว่าได้ Forward port {} ผ่านเราเตอร์/ไฟร์วอลล์ของคุณเพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตจากระยะไกล", "privMsg": "และ ", "return": "ย้อนกลับไปยังแผงควบคุม", + "selfHost": "หากคุณโฮสต์ repo นี้ด้วยตนเอง โปรดตรวจสอบที่อยู่ของคุณ หรือศึกษาคู่มือแก้ปัญหาของเรา", "serverJars1": "ไม่สามารถเข้าถึงเซิร์ฟเวอร์ JARs API กรุณาตรวจสอบ", "serverJars2": "เพื่อข้อมูลที่ทันสมัยที่สุด", "start-error": "เซิร์ฟเวอร์ {} ไม่สามารถเริ่มต้นได้เนื่องจากรหัสข้อผิดพลาด: {}", @@ -602,6 +605,7 @@ }, "startup": { "almost": "เสร็จสิ้นการทำงาน. รอซักครู่...", + "cache": "กำลังรีเฟรชไฟล์แคช Big Bucket", "internals": "กำหนดค่าและเริ่มการทำงานภายในของ Crafty", "internet": "กำลังตรวจสอบการเชื่อมต่ออินเทอร์เน็ต", "server": "กำลังเริ่มต้นการทำงาน ", From fdd1d2fca3d4559f85ec42a87197101c4a44ff91 Mon Sep 17 00:00:00 2001 From: Analicia Abernathy Date: Thu, 23 May 2024 13:21:55 -0500 Subject: [PATCH 192/342] added big bucket translations --- app/translations/he_IL.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index 865222cd..9dfe860b 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -184,6 +184,8 @@ "error": { "agree": "מסכים", "bedrockError": "הורדות Bedrock אינן זמינות. אנא בדוק", + "bigBucket1": "בדיקת הבריאות של Big Bucket נכשלה. אנא בדוק", + "bigBucket2": "כדי לקבל את המידע המעודכן ביותר.", "cancel": "בטל", "contact": "בבקשה צרו קשר עם תמיכת פאנל קראפטי באמצעות דיסקורד", "craftyStatus": "דף המצב של Crafty", @@ -206,6 +208,7 @@ "portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.", "privMsg": "וה", "return": "חזרה לפאנל", + "selfHost": "אם אתה מארח בעצמך את הריפו הזה, אנא בדוק את הכתובת שלך או התייעץ עם מדריך פתרון הבעיות שלנו.", "serverJars1": "API של צנצנות השרת אינו נגיש. אנא בדוק", "serverJars2": "למידע מעודכן ביותר.", "start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}", @@ -603,6 +606,7 @@ }, "startup": { "almost": "מסיימים. תחזיקו חזק...", + "cache": "מרענן את קובץ המטמון של Big Bucket", "internals": "הגדרה והפעלה של הרכיבים הפנימיים של Crafty", "internet": "בודק את חיבור האינטרנט", "server": "אתחול ", From f8626633cfcaf2c31304cb18371b768e09506a49 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sat, 25 May 2024 13:51:40 -0400 Subject: [PATCH 193/342] 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 194/342] 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 195/342] 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 196/342] 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 197/342] 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 198/342] 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 199/342] 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 200/342] 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 201/342] 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 @@

-
-
@@ -523,61 +523,8 @@ {% end %} {% block js%} + From a9c9598ad06ecb25f658fac9f2c08f4f18f66703 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 26 May 2024 23:38:59 -0400 Subject: [PATCH 224/342] Fix error on upload --- app/frontend/static/assets/js/shared/upload.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/frontend/static/assets/js/shared/upload.js b/app/frontend/static/assets/js/shared/upload.js index d6877570..0606e03e 100644 --- a/app/frontend/static/assets/js/shared/upload.js +++ b/app/frontend/static/assets/js/shared/upload.js @@ -55,8 +55,15 @@ async function uploadFile(type) { .then(data => { if (data.status === "completed") { $("#upload_input").html(`
🔒
`); - document.getElementById("lower_half").style.visibility = "visible"; - document.getElementById("lower_half").hidden = false; + if (type === "import") { + document.getElementById("lower_half").style.visibility = "visible"; + document.getElementById("lower_half").hidden = false; + } else if (type === "background") { + setTimeout(function () { + location.href = `/panel/custom_login` + }, 2000) + + } } else if (data.status !== "partial") { throw new Error(data.message); } @@ -71,7 +78,7 @@ async function uploadFile(type) { try { await Promise.all(uploadPromises); } catch (error) { - alert("Error uploading file: " + error.message); + bootbox.alert("Error uploading file: " + error.message); } } From 3b7a463184b42936d61b4ef4ce9c0fb1bff49804 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 26 May 2024 23:42:25 -0400 Subject: [PATCH 225/342] Remove stream_size_GB option --- app/classes/shared/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 55a588fc..1dd7be80 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -508,7 +508,6 @@ class Helpers: "max_log_lines": 700, "max_audit_entries": 300, "disabled_language_files": [], - "stream_size_GB": 1, "keywords": ["help", "chunk"], "allow_nsfw_profile_pictures": False, "enable_user_self_delete": False, From 9b7ddbfe1e3c41053f000125206c6f5c97270ab9 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 27 May 2024 15:19:23 -0400 Subject: [PATCH 226/342] Check for server dir on server_upload --- .../web/routes/api/crafty/upload/index.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/crafty/upload/index.py b/app/classes/web/routes/api/crafty/upload/index.py index 8513f719..a289befd 100644 --- a/app/classes/web/routes/api/crafty/upload/index.py +++ b/app/classes/web/routes/api/crafty/upload/index.py @@ -36,6 +36,7 @@ class ApiFilesUploadHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "NOT_AUTHORIZED"} ) + u_type = "server_upload" elif auth_data[4]["superuser"] and upload_type == "background": u_type = "admin_config" @@ -89,11 +90,29 @@ class ApiFilesUploadHandler(BaseApiHandler): self.upload_dir = self.request.headers.get("location", None) self.temp_dir = os.path.join(self.controller.project_root, "temp", self.file_id) + if u_type == "server_upload": + full_path = os.path.join(self.upload_dir, self.filename) + + if not self.helper.is_subdir( + full_path, + Helpers.get_os_understandable_path( + self.controller.servers.get_server_data_by_id(server_id)["path"] + ), + ): + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT AUTHORIZED", + "data": {"message": "Traversal detected"}, + }, + ) + _total, _used, free = shutil.disk_usage(self.upload_dir) # Check to see if we have enough space if free <= file_size: - self.finish_json( + return self.finish_json( 507, { "status": "error", From d7bee5a7b8cb402072e04dcd03dd7db6bb3a3696 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 27 May 2024 19:12:31 -0400 Subject: [PATCH 227/342] Refactor uploads to same JS file --- .../static/assets/js/shared/upload.js | 243 +++++++++++++----- .../templates/panel/server_files.html | 147 ++--------- 2 files changed, 195 insertions(+), 195 deletions(-) diff --git a/app/frontend/static/assets/js/shared/upload.js b/app/frontend/static/assets/js/shared/upload.js index 0606e03e..7ea043af 100644 --- a/app/frontend/static/assets/js/shared/upload.js +++ b/app/frontend/static/assets/js/shared/upload.js @@ -1,90 +1,207 @@ -async function uploadFile(type) { - file = $("#file")[0].files[0] +async function uploadFile(type, file = null, path = null, file_num = 0, _onProgress) { + if (file == null) { + try { + file = $("#file")[0].files[0]; + } catch { + bootbox.alert("Please select a file first.") + return; + } + + } const fileId = uuidv4(); - const token = getCookie("_xsrf") - document.getElementById("upload_input").innerHTML = '
 
' - if (!file) { - alert("Please select a file first."); - return; + const token = getCookie("_xsrf"); + if (type !== "server_upload") { + document.getElementById("upload_input").innerHTML = '
 
'; } + let url = `` + if (type === "server_upload") { + url = `/api/v2/servers/${serverId}/files/upload/`; + } else if (type === "background") { + url = `/api/v2/crafty/admin/upload/` + } else if (type === "import") { + url = `/api/v2/servers/import/upload/` + } + console.log(url) const chunkSize = 1024 * 1024; // 1MB const totalChunks = Math.ceil(file.size / chunkSize); + const file_hash = await calculateFileHash(file); const uploadPromises = []; - let res = await fetch(`/api/v2/servers/import/upload/`, { - method: 'POST', - headers: { - 'X-XSRFToken': token, - 'chunked': true, - 'fileSize': file.size, - 'type': type, - 'total_chunks': totalChunks, - 'filename': file.name, - 'fileId': fileId, - }, - body: null, - }); - - let responseData = await res.json(); - - let file_id = "" - if (responseData.status === "ok") { - file_id = responseData.data["file-id"] - } - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, file.size); - const chunk = file.slice(start, end); - - const uploadPromise = fetch(`/api/v2/servers/import/upload/`, { + let errors = []; // Array to store errors + try { + let res = await fetch(url, { method: 'POST', - body: chunk, headers: { - 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, - 'Content-Length': chunk.size, - 'fileSize': file.size, + 'X-XSRFToken': token, 'chunked': true, + 'fileHash': file_hash, + 'fileSize': file.size, 'type': type, 'total_chunks': totalChunks, 'filename': file.name, + 'location': path, 'fileId': fileId, - 'chunkId': i, }, - }).then(response => response.json()) - .then(data => { - if (data.status === "completed") { - $("#upload_input").html(`
🔒
`); - if (type === "import") { - document.getElementById("lower_half").style.visibility = "visible"; - document.getElementById("lower_half").hidden = false; - } else if (type === "background") { - setTimeout(function () { - location.href = `/panel/custom_login` - }, 2000) + body: null, + }); + if (!res.ok) { + let errorResponse = await res.json(); + throw new Error(JSON.stringify(errorResponse)); + } + + let responseData = await res.json(); + + if (responseData.status !== "ok") { + throw new Error(JSON.stringify(responseData)); + } + + for (let i = 0; i < totalChunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize, file.size); + const chunk = file.slice(start, end); + const chunk_hash = await calculateFileHash(chunk); + + const uploadPromise = fetch(url, { + method: 'POST', + body: chunk, + headers: { + 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, + 'Content-Length': chunk.size, + 'fileSize': file.size, + 'fileHash': file_hash, + 'chunkHash': chunk_hash, + 'chunked': true, + 'type': type, + 'total_chunks': totalChunks, + 'filename': file.name, + 'location': path, + 'fileId': fileId, + 'chunkId': i, + }, + }) + .then(async response => { + if (!response.ok) { + const errorData = await response.json(); + throw new Error(JSON.stringify(errorData) || 'Unknown error occurred'); } - } else if (data.status !== "partial") { - throw new Error(data.message); - } - // Update progress bar - const progress = (i + 1) / totalChunks * 100; - updateProgressBar(Math.round(progress)); - }); + return response.json(); // Return the JSON data + }) + .then(data => { + if (data.status !== "completed" && data.status !== "partial") { + throw new Error(data.message || 'Unknown error occurred'); + } + // Update progress bar + const progress = (i + 1) / totalChunks * 100; + updateProgressBar(Math.round(progress), type, file_num); + }) + .catch(error => { + errors.push(error); // Store the error + }); - uploadPromises.push(uploadPromise); - } + uploadPromises.push(uploadPromise); + } - try { await Promise.all(uploadPromises); } catch (error) { - bootbox.alert("Error uploading file: " + error.message); + errors.push(error); // Store the error + } + + if (errors.length > 0) { + const errorMessage = errors.map(error => JSON.parse(error.message).data.message || 'Unknown error occurred').join('
'); + console.log(errorMessage) + bootbox.alert({ + title: 'Error', + message: errorMessage, + callback: function () { + window.location.reload(); + }, + }); + } else { + if (type !== "server_upload") { + // All promises resolved successfully + $("#upload_input").html(`
🔒
`); + if (type === "import") { + document.getElementById("lower_half").style.visibility = "visible"; + document.getElementById("lower_half").hidden = false; + } else if (type === "background") { + setTimeout(function () { + location.href = `/panel/custom_login`; + }, 2000); + } + } else { + let caught = false; + let expanded = false; + try { + expanded = document.getElementById(path).classList.contains("clicked"); + } catch { } + + let par_el; + let items; + try { + par_el = document.getElementById(path + "ul"); + items = par_el.children; + } catch (err) { + console.log(err); + caught = true; + par_el = document.getElementById("files-tree"); + items = par_el.children; + } + + let name = file.name; + let full_path = path + '/' + name; + let flag = false; + + for (let k = 0; k < items.length; ++k) { + if ($(items[k]).attr("data-name") == name) { + flag = true; + } + } + + if (!flag) { + if (caught && !expanded) { + $(par_el).append(`
  • ${name}
  • `); + } else if (expanded) { + $(par_el).append(`
  • ${name}
  • `); + } + setTreeViewContext(); + } + + $(`#upload-progress-bar-${file_num + 1}`).removeClass("progress-bar-striped"); + $(`#upload-progress-bar-${file_num + 1}`).addClass("bg-success"); + $(`#upload-progress-bar-${file_num + 1}`).html(''); + } } } -function updateProgressBar(progress) { - $(`#upload-progress-bar`).css('width', progress + '%'); - $(`#upload-progress-bar`).html(progress + '%'); +async function calculateFileHash(file) { + const arrayBuffer = await file.arrayBuffer(); + const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return hashHex; +} + +function updateProgressBar(progress, type, i) { + if (type !== "server_upload") { + if (progress === 100) { + $(`#upload-progress-bar`).removeClass("progress-bar-striped") + + $(`#upload-progress-bar`).removeClass("progress-bar-animated") + } + $(`#upload-progress-bar`).css('width', progress + '%'); + $(`#upload-progress-bar`).html(progress + '%'); + } else { + if (progress === 100) { + $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped") + + $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-animated") + } + $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); + $(`#upload-progress-bar-${i + 1}`).html(progress + '%'); + } } function uuidv4() { diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index 7dae4934..ce7d2a9d 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -723,131 +723,7 @@ } } - async function uploadFile(file, path, file_num, onProgress) { - const fileId = uuidv4(); - const token = getCookie("_xsrf") - if (!file) { - alert("Please select a file first."); - return; - } - - const chunkSize = 1024 * 1024; // 1MB - const totalChunks = Math.ceil(file.size / chunkSize); - - const uploadPromises = []; - let res = await fetch(`/api/v2/servers/${serverId}/files/upload/`, { - method: 'POST', - headers: { - 'X-XSRFToken': token, - 'chunked': true, - 'type': "server_upload", - 'fileSize': file.size, - 'total_chunks': totalChunks, - 'location': path, - 'filename': file.name, - 'fileId': fileId, - }, - body: null, - }); - - let responseData = await res.json(); - - let file_id = "" - if (responseData.status === "ok") { - file_id = responseData.data["file-id"] - } - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, file.size); - const chunk = file.slice(start, end); - - const uploadPromise = fetch(`/api/v2/servers/${serverId}/files/upload/`, { - method: 'POST', - body: chunk, - headers: { - 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, - 'Content-Length': chunk.size, - 'chunked': true, - 'type': "server_upload", - 'fileSize': file.size, - 'total_chunks': totalChunks, - 'filename': file.name, - 'location': path, - 'fileId': fileId, - 'chunkId': i, - }, - }).then(response => response.json()) - .then(data => { - if (data.status === "completed") { - let caught = false; - try { - if (document.getElementById(path).classList.contains("clicked")) { - var expanded = true; - } - } catch { - var expanded = false; - } - - try { - var par_el = document.getElementById(path + "ul"); - var items = par_el.children; - } catch (err) { - console.log(err) - caught = true; - var par_el = document.getElementById("files-tree"); - var items = par_el.children; - } - let name = file.name; - console.log(par_el) - let full_path = path + '/' + name - let flag = false; - for (var k = 0; k < items.length; ++k) { - if ($(items[k]).attr("data-name") == name) { - flag = true; - } - } - if (!flag) { - if (caught && expanded == false) { - $(par_el).append('
  • ' + name + '
  • '); - } else if (expanded == true) { - $(par_el).append('
  • ' + name + '
  • '); - } - setTreeViewContext(); - } - $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped"); - $(`#upload-progress-bar-${i + 1}`).addClass("bg-success"); - $(`#upload-progress-bar-${i + 1}`).html('') - } else if (data.status !== "partial") { - throw new Error(data.message); - } - // Update progress bar - const progress = (i + 1) / totalChunks * 100; - updateProgressBar(Math.round(progress), file_num); - }); - - uploadPromises.push(uploadPromise); - } - - try { - await Promise.all(uploadPromises); - } catch (error) { - alert("Error uploading file: " + error.message); - } - } - function updateProgressBar(progress, i) { - $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); - $(`#upload-progress-bar-${i + 1}`).html(progress + '%'); - } - function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = Math.random() * 16 | 0, - v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - let uploadWaitDialog; - let doUpload = true; async function uploadFilesE(event) { path = event.target.parentElement.getAttribute('data-path'); @@ -907,19 +783,17 @@ $('#upload-progress-bar-parent').append(progressHtml); - const uploadPromise = uploadFile(file, path, i, (progress) => { + const uploadPromise = uploadFile("server_upload", file, path, i, (progress) => { $(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress) $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); }); uploadPromises.push(uploadPromise); } - try { - await Promise.all(uploadPromises); - hideUploadBox(); - } catch (error) { - alert("Error uploading file: " + error.message); - } + + await Promise.all(uploadPromises); + hideUploadBox(); + } } } @@ -927,6 +801,15 @@ }); } + async function calculateFileHash(file) { + const arrayBuffer = await file.arrayBuffer(); + const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return hashHex; + } + function getDirView(event) { let path = event.target.parentElement.getAttribute("data-path"); if (document.getElementById(path).classList.contains('clicked')) { @@ -1232,5 +1115,5 @@ - + {% end %} \ No newline at end of file From 3fd763eebd4ecd117501ad6dd4ea7afcfc7c9979 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 27 May 2024 19:12:39 -0400 Subject: [PATCH 228/342] Check hashes --- app/classes/shared/file_helpers.py | 21 ++++ .../web/routes/api/crafty/upload/index.py | 106 ++++++++++++++++-- 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 574fe8dc..2406c127 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -4,6 +4,8 @@ import logging import pathlib import tempfile import zipfile +import hashlib +import mimetypes from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED import urllib.request import ssl @@ -22,6 +24,7 @@ class FileHelpers: def __init__(self, helper): self.helper: Helpers = helper + self.mime_types = mimetypes.MimeTypes() @staticmethod def ssl_get_file( @@ -142,6 +145,24 @@ class FileHelpers: logger.error(f"Path specified is not a file or does not exist. {path}") return e + def check_mime_types(self, file_path): + m_type, _value = self.mime_types.guess_type(file_path) + return m_type + + @staticmethod + def calculate_file_hash(file_path): + sha256_hash = hashlib.sha256() + with open(file_path, "rb") as f: + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + return sha256_hash.hexdigest() + + @staticmethod + def calculate_buffer_hash(buffer): + sha256_hash = hashlib.sha256() + sha256_hash.update(buffer) + return sha256_hash.hexdigest() + @staticmethod def copy_dir(src_path, dest_path, dirs_exist_ok=False): # pylint: disable=unexpected-keyword-arg diff --git a/app/classes/web/routes/api/crafty/upload/index.py b/app/classes/web/routes/api/crafty/upload/index.py index a289befd..e9b95fa4 100644 --- a/app/classes/web/routes/api/crafty/upload/index.py +++ b/app/classes/web/routes/api/crafty/upload/index.py @@ -9,6 +9,32 @@ from app.classes.shared.helpers import Helpers from app.classes.shared.main_controller import WebSocketManager, Controller from app.classes.web.base_api_handler import BaseApiHandler +IMAGE_MIME_TYPES = [ + "image/bmp", + "image/cis-cod", + "image/gif", + "image/ief", + "image/jpeg", + "image/pipeg", + "image/svg+xml", + "image/tiff", + "image/x-cmu-raster", + "image/x-cmx", + "image/x-icon", + "image/x-portable-anymap", + "image/x-portable-bitmap", + "image/x-portable-graymap", + "image/x-portable-pixmap", + "image/x-rgb", + "image/x-xbitmap", + "image/x-xpixmap", + "image/x-xwindowdump", + "image/png", + "image/webp", +] + +ARCHIVE_MIME_TYPES = ["application/zip"] + class ApiFilesUploadHandler(BaseApiHandler): async def post(self, server_id=None): @@ -17,6 +43,7 @@ class ApiFilesUploadHandler(BaseApiHandler): return upload_type = self.request.headers.get("type") + accepted_types = [] if server_id: if server_id not in [str(x["server_id"]) for x in auth_data[0]]: @@ -44,6 +71,7 @@ class ApiFilesUploadHandler(BaseApiHandler): self.controller.project_root, "app/frontend/static/assets/images/auth/custom", ) + accepted_types = IMAGE_MIME_TYPES elif upload_type == "import": if ( not self.controller.crafty_perms.can_create_server( @@ -63,6 +91,7 @@ class ApiFilesUploadHandler(BaseApiHandler): self.controller.project_root, "import", "upload" ) u_type = "server_import" + accepted_types = ARCHIVE_MIME_TYPES else: return self.finish_json( 400, @@ -73,8 +102,8 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) # Get the headers from the request - fileHash = self.request.headers.get("fileHash", 0) - chunkHash = self.request.headers.get("chunk-hash", 0) + self.file_hash = self.request.headers.get("fileHash", 0) + self.chunk_hash = self.request.headers.get("chunkHash", 0) self.file_id = self.request.headers.get("fileId") self.chunked = self.request.headers.get("chunked", True) self.filename = self.request.headers.get("filename", None) @@ -108,6 +137,20 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) + if ( + self.file_helper.check_mime_types(self.filename) not in accepted_types + and u_type != "server_upload" + ): + return self.finish_json( + 422, + { + "status": "error", + "error": "INVALID FILE TYPE", + "data": { + "message": f"Invalid File Type only accepts {accepted_types}" + }, + }, + ) _total, _used, free = shutil.disk_usage(self.upload_dir) # Check to see if we have enough space @@ -134,6 +177,24 @@ class ApiFilesUploadHandler(BaseApiHandler): if not chunk: break file.write(chunk) + if ( + self.file_helper.calculate_file_hash( + os.path.join(self.upload_dir, self.filename) + ) + != self.file_hash + ): + os.remove(os.path.join(self.upload_dir, self.filename)) + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID HASH", + "data": { + "message": "Hash recieved does not" + " match reported sent hash.", + }, + }, + ) self.finish_json( 200, { @@ -171,6 +232,20 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) + calculated_hash = self.file_helper.calculate_buffer_hash(self.request.body) + if str(self.chunk_hash) != str(calculated_hash): + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID_HASH", + "data": { + "message": "Hash recieved does not match reported sent hash.", + "chunk_id": self.chunk_index, + }, + }, + ) + # File paths file_path = os.path.join(self.upload_dir, self.filename) chunk_path = os.path.join( @@ -194,6 +269,20 @@ class ApiFilesUploadHandler(BaseApiHandler): with open(chunk_file, "rb") as infile: outfile.write(infile.read()) os.remove(chunk_file) + if self.file_helper.calculate_file_hash(file_path) != self.file_hash: + os.remove(file_path) + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID HASH", + "data": { + "message": "Hash recieved does not" + " match reported sent hash.", + "chunk_id": self.file_id, + }, + }, + ) self.finish_json( 200, @@ -203,11 +292,10 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) else: - self.write( - json.dumps( - { - "status": "partial", - "message": f"Chunk {self.chunk_index} received", - } - ) + self.finish_json( + 200, + { + "status": "partial", + "data": {"message": f"Chunk {self.chunk_index} received"}, + }, ) From 407ca4c0bbb8e5f1335f9b0a64ecf09aba001f86 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 27 May 2024 19:48:35 -0400 Subject: [PATCH 229/342] Add logging --- .../web/routes/api/crafty/upload/index.py | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/app/classes/web/routes/api/crafty/upload/index.py b/app/classes/web/routes/api/crafty/upload/index.py index e9b95fa4..a1d0790e 100644 --- a/app/classes/web/routes/api/crafty/upload/index.py +++ b/app/classes/web/routes/api/crafty/upload/index.py @@ -1,14 +1,11 @@ import os import logging -import json import shutil -from jsonschema import validate -from jsonschema.exceptions import ValidationError from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.shared.helpers import Helpers -from app.classes.shared.main_controller import WebSocketManager, Controller from app.classes.web.base_api_handler import BaseApiHandler +logger = logging.getLogger(__name__) IMAGE_MIME_TYPES = [ "image/bmp", "image/cis-cod", @@ -184,6 +181,10 @@ class ApiFilesUploadHandler(BaseApiHandler): != self.file_hash ): os.remove(os.path.join(self.upload_dir, self.filename)) + logger.error( + f"File upload failed. Filename: {self.filename}" + f"Type: {u_type} Error: INVALID HASH" + ) return self.finish_json( 400, { @@ -195,7 +196,10 @@ class ApiFilesUploadHandler(BaseApiHandler): }, }, ) - self.finish_json( + logger.info( + f"File upload completed. Filename: {self.filename}" f" Type: {u_type}" + ) + return self.finish_json( 200, { "status": "completed", @@ -210,6 +214,10 @@ class ApiFilesUploadHandler(BaseApiHandler): # Read headers and query parameters content_length = int(self.request.headers.get("Content-Length")) if content_length <= 0: + logger.error( + f"File upload failed. Filename: {self.filename}" + f"Type: {u_type} Error: INVALID CONTENT LENGTH" + ) return self.finish_json( 400, { @@ -220,6 +228,10 @@ class ApiFilesUploadHandler(BaseApiHandler): ) if not self.filename or self.chunk_index is None or total_chunks is None: + logger.error( + f"File upload failed. Filename: {self.filename}" + f"Type: {u_type} Error: CHUNK INDEX NOT FOUND" + ) return self.finish_json( 400, { @@ -234,6 +246,10 @@ class ApiFilesUploadHandler(BaseApiHandler): calculated_hash = self.file_helper.calculate_buffer_hash(self.request.body) if str(self.chunk_hash) != str(calculated_hash): + logger.error( + f"File upload failed. Filename: {self.filename}" + f"Type: {u_type} Error: INVALID HASH" + ) return self.finish_json( 400, { @@ -283,7 +299,16 @@ class ApiFilesUploadHandler(BaseApiHandler): }, }, ) - + logger.info( + f"File upload completed. Filename: {self.filename}" + f" Path: {file_path} Type: {u_type}" + ) + self.controller.management.add_to_audit_log( + auth_data[4]["user_id"], + f"Uploaded file {self.filename}", + server_id, + self.request.remote_ip, + ) self.finish_json( 200, { From 0aae82448b3de419774d87ce8d4cd73a586e3514 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 28 May 2024 15:39:44 -0400 Subject: [PATCH 230/342] Add comments --- app/classes/shared/file_helpers.py | 13 +++++++-- .../web/routes/api/crafty/upload/index.py | 28 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 2406c127..23bf01dd 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -5,6 +5,7 @@ import pathlib import tempfile import zipfile import hashlib +from typing import BinaryIO import mimetypes from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED import urllib.request @@ -150,7 +151,11 @@ class FileHelpers: return m_type @staticmethod - def calculate_file_hash(file_path): + def calculate_file_hash(file_path: str) -> str: + """ + Takes one parameter of file path. + It will generate a SHA256 hash for the path and return it. + """ sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): @@ -158,7 +163,11 @@ class FileHelpers: return sha256_hash.hexdigest() @staticmethod - def calculate_buffer_hash(buffer): + def calculate_buffer_hash(buffer: BinaryIO) -> str: + """ + Takes one argument of a stream buffer. Will return a + sha256 hash of the buffer + """ sha256_hash = hashlib.sha256() sha256_hash.update(buffer) return sha256_hash.hexdigest() diff --git a/app/classes/web/routes/api/crafty/upload/index.py b/app/classes/web/routes/api/crafty/upload/index.py index a1d0790e..036df6f8 100644 --- a/app/classes/web/routes/api/crafty/upload/index.py +++ b/app/classes/web/routes/api/crafty/upload/index.py @@ -43,6 +43,7 @@ class ApiFilesUploadHandler(BaseApiHandler): accepted_types = [] if server_id: + # Check to make sure user is authorized for the server 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( @@ -54,6 +55,7 @@ class ApiFilesUploadHandler(BaseApiHandler): ), auth_data[5], ) + # Make sure user has file access for the server server_permissions = self.controller.server_perms.get_permissions(mask) if EnumPermissionsServer.FILES not in server_permissions: # if the user doesn't have Files permission, return an error @@ -62,6 +64,7 @@ class ApiFilesUploadHandler(BaseApiHandler): ) u_type = "server_upload" + # Make sure user is a super user if they're changing panel settings elif auth_data[4]["superuser"] and upload_type == "background": u_type = "admin_config" self.upload_dir = os.path.join( @@ -70,6 +73,7 @@ class ApiFilesUploadHandler(BaseApiHandler): ) accepted_types = IMAGE_MIME_TYPES elif upload_type == "import": + # Check that user can make servers if ( not self.controller.crafty_perms.can_create_server( auth_data[4]["user_id"] @@ -84,6 +88,7 @@ class ApiFilesUploadHandler(BaseApiHandler): "data": {"message": ""}, }, ) + # Set directory to upload import dir self.upload_dir = os.path.join( self.controller.project_root, "import", "upload" ) @@ -117,8 +122,11 @@ class ApiFilesUploadHandler(BaseApiHandler): self.temp_dir = os.path.join(self.controller.project_root, "temp", self.file_id) if u_type == "server_upload": + # If this is an upload from a server the path will be what + # Is requested full_path = os.path.join(self.upload_dir, self.filename) + # Check to make sure the requested path is inside the server's directory if not self.helper.is_subdir( full_path, Helpers.get_os_understandable_path( @@ -133,7 +141,7 @@ class ApiFilesUploadHandler(BaseApiHandler): "data": {"message": "Traversal detected"}, }, ) - + # Check to make sure the file type we're being sent is what we're expecting if ( self.file_helper.check_mime_types(self.filename) not in accepted_types and u_type != "server_upload" @@ -166,20 +174,29 @@ class ApiFilesUploadHandler(BaseApiHandler): return self.finish_json( 200, {"status": "ok", "data": {"file-id": self.file_id}} ) + # Create the upload and temp directories if they don't exist + os.makedirs(self.upload_dir, exist_ok=True) + # Check for chunked header. We will handle this request differently + # if it doesn't exist if not self.chunked: + # Write the file directly to the upload dir with open(os.path.join(self.upload_dir, self.filename), "wb") as file: while True: chunk = self.request.body if not chunk: break file.write(chunk) + # We'll check the file hash against the sent hash once the file is + # written. We cannot check this buffer. if ( self.file_helper.calculate_file_hash( os.path.join(self.upload_dir, self.filename) ) != self.file_hash ): + # If the hash is bad we'll delete the malformed file and send + # a warning os.remove(os.path.join(self.upload_dir, self.filename)) logger.error( f"File upload failed. Filename: {self.filename}" @@ -206,9 +223,7 @@ class ApiFilesUploadHandler(BaseApiHandler): "data": {"message": "File uploaded successfully"}, }, ) - - # Create the upload and temp directories if they don't exist - os.makedirs(self.upload_dir, exist_ok=True) + # Since this is a chunked upload we'll create the temp dir for parts. os.makedirs(self.temp_dir, exist_ok=True) # Read headers and query parameters @@ -227,6 +242,8 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) + # At this point filename, chunk index and total chunks are required + # in the request if not self.filename or self.chunk_index is None or total_chunks is None: logger.error( f"File upload failed. Filename: {self.filename}" @@ -244,6 +261,7 @@ class ApiFilesUploadHandler(BaseApiHandler): }, ) + # Calculate the hash of the buffer and compare it against the expected hash calculated_hash = self.file_helper.calculate_buffer_hash(self.request.body) if str(self.chunk_hash) != str(calculated_hash): logger.error( @@ -278,6 +296,8 @@ class ApiFilesUploadHandler(BaseApiHandler): for f in os.listdir(self.temp_dir) if f.startswith(f"{self.filename}.part") ] + # When we've reached the total chunks we'll + # Compare the hash and write the file if len(received_chunks) == total_chunks: with open(file_path, "wb") as outfile: for i in range(total_chunks): From d8ad8f5e09a999e07e90aedc5364821fb70c6da8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 28 May 2024 17:14:05 -0400 Subject: [PATCH 231/342] Fix role selection on user creation. Security improvements --- app/classes/web/routes/api/roles/index.py | 17 ++++++++++---- .../web/routes/api/roles/role/index.py | 22 ++++++++++++++----- app/classes/web/routes/api/users/index.py | 6 ++++- .../web/routes/api/users/user/index.py | 22 ++++++++++++++++--- .../templates/panel/panel_edit_role.html | 7 ++++-- .../templates/panel/panel_edit_user.html | 4 ++-- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index a8612c75..e7cda520 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -2,6 +2,7 @@ import typing as t from jsonschema import ValidationError, validate import orjson from playhouse.shortcuts import model_to_dict +from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.web.base_api_handler import BaseApiHandler create_role_schema = { @@ -71,7 +72,7 @@ class ApiRolesIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, _, @@ -81,7 +82,10 @@ class ApiRolesIndexHandler(BaseApiHandler): # GET /api/v2/roles?ids=true get_only_ids = self.get_query_argument("ids", None) == "true" - if not superuser: + if ( + not superuser + and not EnumPermissionsCrafty.ROLES_CONFIG in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) self.finish_json( @@ -104,14 +108,17 @@ class ApiRolesIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, user, _, ) = auth_data - if not superuser: + if ( + not superuser + and not EnumPermissionsCrafty.ROLES_CONFIG in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -138,6 +145,8 @@ class ApiRolesIndexHandler(BaseApiHandler): role_name = data["name"] manager = data.get("manager", None) + if not superuser and not manager: + manager = auth_data[4]["user_id"] if manager == self.controller.users.get_id_by_name("SYSTEM") or manager == 0: manager = None diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index 73fd9ff3..a4f49911 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -1,6 +1,7 @@ from jsonschema import ValidationError, validate import orjson -from peewee import DoesNotExist +from peewee import DoesNotExist, IntegrityError +from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.web.base_api_handler import BaseApiHandler modify_role_schema = { @@ -70,14 +71,17 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, _, _, ) = auth_data - if not superuser: + if ( + not superuser + and not EnumPermissionsCrafty.ROLES_CONFIG in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -100,8 +104,11 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): user, _, ) = auth_data - - if not superuser: + role = self.controller.roles.get_role(role_id) + if ( + str(role.get("manager", "no manager found")) != str(auth_data[4]["user_id"]) + and not superuser + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) self.controller.roles.remove_role(role_id) @@ -179,7 +186,10 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): ) except DoesNotExist: return self.finish_json(404, {"status": "error", "error": "ROLE_NOT_FOUND"}) - + except IntegrityError: + return self.finish_json( + 404, {"status": "error", "error": "ROLE_NAME_EXISTS"} + ) self.controller.management.add_to_audit_log( user["user_id"], f"modified role with ID {role_id}", diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index dbdb1ac0..07c85f3a 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -153,7 +153,11 @@ class ApiUsersIndexHandler(BaseApiHandler): for role in roles: role = self.controller.roles.get_role(role) - if int(role["manager"]) != int(auth_data[4]["user_id"]) and not superuser: + if ( + str(role.get("manager", "no manager found")) + != str(auth_data[4]["user_id"]) + and not superuser + ): return self.finish_json( 400, {"status": "error", "error": "INVALID_ROLES_CREATE"} ) diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index 9fa46200..b05e4ac3 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -132,7 +132,6 @@ class ApiUsersUserIndexHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) - try: validate(data, user_patch_schema) except ValidationError as e: @@ -144,10 +143,8 @@ class ApiUsersUserIndexHandler(BaseApiHandler): "error_data": str(e), }, ) - if user_id == "@me": user_id = user["user_id"] - if ( EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions and str(user["user_id"]) != str(user_id) @@ -215,6 +212,25 @@ class ApiUsersUserIndexHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} ) + user_modify = self.controller.users.get_user_roles_id(user_id) + + for role in data["roles"]: + # Check if user is not a super user and that the exec user is the role + # manager or that the role already exists in the user's list + if not superuser and ( + str( + self.controller.roles.get_role(role).get( + "manager", "no manager found" + ) + ) + != str(auth_data[4]["user_id"]) + and role not in user_modify + ): + for item in user_modify: + print(type(role), type(item)) + return self.finish_json( + 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} + ) user_obj = HelperUsers.get_user_model(user_id) if "password" in data and str(user["user_id"]) != str(user_id): diff --git a/app/frontend/templates/panel/panel_edit_role.html b/app/frontend/templates/panel/panel_edit_role.html index df065bf9..b72d3a2a 100644 --- a/app/frontend/templates/panel/panel_edit_role.html +++ b/app/frontend/templates/panel/panel_edit_role.html @@ -428,10 +428,13 @@ if (responseData.status === "ok") { window.location.href = "/panel/panel_config"; } else { - + let errordata = responseData.error; + if (responseData.error_data){ + errordata = responseData.error + } bootbox.alert({ title: responseData.error, - message: responseData.error_data + message: errordata }); } }); diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 87631219..9821ec26 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -393,6 +393,7 @@ data['lang']) }}{% end %} } function replacer(key, value) { if (typeof value == "boolean" || key === "email" || key === "permissions" || key === "roles") { + console.log(key) return value } else { console.log(key, value) @@ -433,6 +434,7 @@ data['lang']) }}{% end %} let disabled_flag = false; let roles = null; if (superuser || userId != edit_id){ + console.log("ROLES") roles = $('.role_check').map(function() { if ($(this).attr("disabled")){ disabled_flag = true; @@ -457,9 +459,7 @@ data['lang']) }}{% end %} delete formDataObject.username } if (superuser || userId != edit_id){ - if (!disabled_flag){ formDataObject.roles = roles; - } if ($("#permissions").length){ formDataObject.permissions = permissions; } From a9856a8a2c1559544fbf4933a7f5899b849e0dc0 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 28 May 2024 19:30:12 -0400 Subject: [PATCH 232/342] Fix java server zip creation --- .../static/assets/js/shared/root-dir.js | 16 ++++++++--- app/frontend/templates/server/wizard.html | 28 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/frontend/static/assets/js/shared/root-dir.js b/app/frontend/static/assets/js/shared/root-dir.js index 6882b577..b32fd2df 100644 --- a/app/frontend/static/assets/js/shared/root-dir.js +++ b/app/frontend/static/assets/js/shared/root-dir.js @@ -41,7 +41,7 @@ async function getTreeView(path, unzip = false, upload = false) { let responseData = await res.json(); if (responseData.status === "ok") { console.log(responseData); - process_tree_response(responseData); + process_tree_response(responseData, unzip); let x = document.querySelector('.bootbox'); if (x) { x.remove() @@ -61,7 +61,7 @@ async function getTreeView(path, unzip = false, upload = false) { } } -function process_tree_response(response) { +function process_tree_response(response, unzip) { const styles = window.getComputedStyle(document.getElementById("lower_half")); //If this value is still hidden we know the user is executing a zip import and not an upload if (styles.visibility === "hidden") { @@ -70,7 +70,9 @@ function process_tree_response(response) { document.getElementById('upload_submit').disabled = false; } let path = response.data.root_path.path; - $(".root-input").val(response.data.root_path.path); + if (unzip) { + $(".root-input").val(response.data.root_path.path); + } let text = `
      `; Object.entries(response.data).forEach(([key, value]) => { if (key === "root_path" || key === "db_stats") { @@ -83,7 +85,7 @@ function process_tree_response(response) { if (value.dir) { text += `
    • - + @@ -134,4 +136,10 @@ function getToggleMain(event) { document.getElementById("files-tree").classList.toggle("d-block"); document.getElementById(path + "span").classList.toggle("tree-caret-down"); document.getElementById(path + "span").classList.toggle("tree-caret"); +} + +function printFormData() { + let myForm = document.getElementById("import-upload"); + let formObj = new FormData(myForm); + console.log(Object.fromEntries(formObj)); } \ No newline at end of file diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index fd079343..2a1680b8 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -437,11 +437,6 @@
    - -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -