From 76e8b01335b8733fad96cc1090ee337c2c73252a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 29 Oct 2023 18:37:30 -0400 Subject: [PATCH 01/98] Fix notifications not showing up/being reset --- app/classes/shared/helpers.py | 13 ++++++++----- .../web/routes/api/crafty/announcements/index.py | 16 ++++++++++++++++ app/frontend/templates/notify.html | 6 +++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index ba9c5a28..62ce8819 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -578,16 +578,19 @@ class Helpers: return version_data def get_announcements(self): - data = [] try: + data = [] response = requests.get("https://craftycontrol.com/notify", timeout=2) data = json.loads(response.content) + if self.update_available: + data.append(self.update_available) + return data except Exception as e: logger.error(f"Failed to fetch notifications with error: {e}") - - if self.update_available: - data.append(self.update_available) - return data + if self.update_available: + data = [self.update_available] + else: + return False def get_version_string(self): version_data = self.get_version() diff --git a/app/classes/web/routes/api/crafty/announcements/index.py b/app/classes/web/routes/api/crafty/announcements/index.py index 409aceed..b5286289 100644 --- a/app/classes/web/routes/api/crafty/announcements/index.py +++ b/app/classes/web/routes/api/crafty/announcements/index.py @@ -29,6 +29,14 @@ class ApiAnnounceIndexHandler(BaseApiHandler): ) = auth_data data = self.helper.get_announcements() + if not data: + return self.finish_json( + 418, + { + "status": "error", + "data": "Failed to get announcements", + }, + ) cleared = str( self.controller.users.get_user_by_id(auth_data[4]["user_id"])[ "cleared_notifs" @@ -84,6 +92,14 @@ class ApiAnnounceIndexHandler(BaseApiHandler): }, ) announcements = self.helper.get_announcements() + if not announcements: + return self.finish_json( + 418, + { + "status": "error", + "data": "Failed to get current announcements", + }, + ) res = [d.get("id", None) for d in announcements] cleared_notifs = str( self.controller.users.get_user_by_id(auth_data[4]["user_id"])[ diff --git a/app/frontend/templates/notify.html b/app/frontend/templates/notify.html index b7dd6dd9..83e80909 100644 --- a/app/frontend/templates/notify.html +++ b/app/frontend/templates/notify.html @@ -93,7 +93,7 @@ return true; } function updateAnnouncements(data) { - console.log(data) + console.log(data); let text = ""; for (let value of data) { text += `
  • ${value.title}

    ${value.date}

    ${value.desc}

  • ` @@ -138,12 +138,12 @@ console.log(responseData); setTimeout(function() { getAnnouncements(); - }, 1800); + }, 1800000); //Wait 30 minutes in miliseconds console.log("Registered annoucement fetch event in 30 minutes.") if (responseData.status === "ok") { updateAnnouncements(responseData.data) } else { - updateAnnouncements("
  • Trouble Getting Annoucements

  • ") + updateAnnouncements([]) } } async function send_clear(uuid) { From 645542b459639d502bea34fdd2e9aa3d3b48caf6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 29 Oct 2023 18:49:31 -0400 Subject: [PATCH 02/98] Move from 418 (i am a teapot) to 424 missing dep --- app/classes/web/routes/api/crafty/announcements/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/classes/web/routes/api/crafty/announcements/index.py b/app/classes/web/routes/api/crafty/announcements/index.py index b5286289..75f00f16 100644 --- a/app/classes/web/routes/api/crafty/announcements/index.py +++ b/app/classes/web/routes/api/crafty/announcements/index.py @@ -31,7 +31,7 @@ class ApiAnnounceIndexHandler(BaseApiHandler): data = self.helper.get_announcements() if not data: return self.finish_json( - 418, + 424, { "status": "error", "data": "Failed to get announcements", @@ -94,7 +94,7 @@ class ApiAnnounceIndexHandler(BaseApiHandler): announcements = self.helper.get_announcements() if not announcements: return self.finish_json( - 418, + 424, { "status": "error", "data": "Failed to get current announcements", From bdb3e94f4178dc71d3a9cd45ab763b02e21efa9e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 30 Oct 2023 12:13:14 -0400 Subject: [PATCH 03/98] Fix userId reference const declaration --- app/frontend/templates/panel/panel_edit_user.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 85a5ea46..87631219 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -363,6 +363,7 @@ data['lang']) }}{% end %} {% block js %} +{% end %} \ No newline at end of file From 61c1621cbb3eb956db90b41ef695f112bc248db4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 3 Nov 2023 16:41:37 -0400 Subject: [PATCH 14/98] Add messages for loading screen --- main.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index 143dfb4f..35608ca0 100644 --- a/main.py +++ b/main.py @@ -109,11 +109,12 @@ if __name__ == "__main__": ) args = parser.parse_args() - helper.ensure_logging_setup() - + helper.crafty_starting = True + # Init WebSocket Manager Here + ws = WebSocketManager() + ws.broadcast("update", {"message": "Setting Up Logging"}) setup_logging(debug=args.verbose) - if args.verbose: Console.level = "debug" @@ -126,15 +127,18 @@ if __name__ == "__main__": # print our pretty start message do_intro() + ws.broadcast("update", {"message": "Securing Session For Process"}) # our session file, helps prevent multiple controller agents on the same machine. helper.create_session_file(ignore=args.ignore) + ws.broadcast("update", {"message": "Initializing Database"}) # start the database database = peewee.SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) database_proxy.initialize(database) + ws.broadcast("update", {"message": "Checking For Database Migrations"}) migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations @@ -165,8 +169,6 @@ if __name__ == "__main__": Console.info("No flag found. Secrets are staying") file_helper = FileHelpers(helper) import_helper = ImportHelpers(helper, file_helper) - # Init WebSocket Manager Here - WebSocketManager() # now the tables are created, we can load the tasks_manager and server controller controller = Controller(database, helper, file_helper, import_helper) Console.info("Checking for remote changes to config.json") @@ -174,6 +176,7 @@ if __name__ == "__main__": Console.info("Remote change complete.") import3 = Import3(helper, controller) + ws.broadcast("update", {"message": "Starting Task Scheduler"}) tasks_manager = TasksManager(helper, controller, file_helper) tasks_manager.start_webserver() @@ -225,6 +228,7 @@ if __name__ == "__main__": "the server may be limited." ) + ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") def controller_setup(): @@ -272,7 +276,7 @@ if __name__ == "__main__": time.sleep(0.01) # Wait for the daemon info message Console.info("Setting up Crafty's internal components...") - + ws.broadcast("update", {"message": "Final checks and launching"}) # Start the setup threads tasks_starter_thread.start() internet_check_thread.start() @@ -282,8 +286,9 @@ if __name__ == "__main__": tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() - + ws.broadcast("send_start_reload", "") Console.info("Crafty has fully started and is now ready for use!") + helper.crafty_starting = False # Check if new version available remote_ver = helper.check_remote_version() From 4152e2be695fedf494b05b03a0444afdb5fa7670 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Fri, 3 Nov 2023 16:55:41 -0400 Subject: [PATCH 15/98] Only show server inits to admins --- app/classes/controllers/servers_controller.py | 2 +- app/classes/shared/websocket_manager.py | 2 +- main.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 95c54bdb..92a68d81 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -173,7 +173,7 @@ class ServersController(metaclass=Singleton): self.failed_servers = [] for server in servers: - self.ws.broadcast( + self.ws.broadcast_to_admins( "update", {"message": f"Initializing {server['server_name']}."} ) server_id = server.get("server_id") diff --git a/app/classes/shared/websocket_manager.py b/app/classes/shared/websocket_manager.py index f48adef8..b840ba36 100644 --- a/app/classes/shared/websocket_manager.py +++ b/app/classes/shared/websocket_manager.py @@ -37,7 +37,7 @@ class WebSocketManager(metaclass=Singleton): def broadcast_to_admins(self, event_type: str, data): def filter_fn(client): - if client.get_user_id in HelperUsers.get_super_user_list(): + if str(client.get_user_id()) in str(HelperUsers.get_super_user_list()): return True return False diff --git a/main.py b/main.py index 35608ca0..452e89f0 100644 --- a/main.py +++ b/main.py @@ -195,6 +195,7 @@ if __name__ == "__main__": # init servers logger.info("Initializing all servers defined") Console.info("Initializing all servers defined") + ws.broadcast("update", {"message": "Initializing Servers"}) controller.servers.init_all_servers() def tasks_starter(): From ecb40ee2ec798d9be0897020e332a3a95834e86e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 11:09:57 -0500 Subject: [PATCH 16/98] Retain tab when navigating back to server_details --- app/classes/controllers/servers_controller.py | 1 + app/classes/web/panel_handler.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index c0bae7b0..99151a32 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -36,6 +36,7 @@ class ServersController(metaclass=Singleton): self.management_helper = management_helper self.servers_list = [] self.stats = Stats(self.helper, self) + self.server_subpage = {} # ********************************************************************************** # Generic Servers Methods diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index e1d21f03..e8643aa7 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -481,6 +481,12 @@ class PanelHandler(BaseHandler): subpage = nh3.clean(self.get_argument("subpage", "")) server_id = self.check_server_id() + # load page the user was on last + server_subpage = self.controller.servers.server_subpage.get(server_id, "") + if subpage == "" and server_subpage != "": + subpage = self.controller.servers.server_subpage.get(server_id, "") + else: + self.controller.servers.server_subpage[server_id] = subpage if server_id is None: return if not self.failed_server: From 547872eda6f4a4c02b4ec2e2fcf792e6c5ca2b55 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 11:23:54 -0500 Subject: [PATCH 17/98] Set starting to false before sending refresh --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 452e89f0..c5ec1297 100644 --- a/main.py +++ b/main.py @@ -287,9 +287,9 @@ if __name__ == "__main__": tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() + helper.crafty_starting = False ws.broadcast("send_start_reload", "") Console.info("Crafty has fully started and is now ready for use!") - helper.crafty_starting = False # Check if new version available remote_ver = helper.check_remote_version() From a5f4a68c49776b971004e615b6fcbeac7471f6e2 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 12:06:57 -0500 Subject: [PATCH 18/98] Add badge to server detail pages --- app/frontend/templates/base.html | 3 ++ .../templates/panel/parts/details_stats.html | 28 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 40ee757b..cca5ef3b 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -82,6 +82,9 @@ +     + + {% include notify.html %} diff --git a/app/frontend/templates/panel/parts/details_stats.html b/app/frontend/templates/panel/parts/details_stats.html index 47fa501d..ad190bc3 100644 --- a/app/frontend/templates/panel/parts/details_stats.html +++ b/app/frontend/templates/panel/parts/details_stats.html @@ -248,12 +248,38 @@ $("#player-body").html(text); } - + //used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security + function getCookie(name) { + var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); + return r ? r[1] : undefined; + } + const token = getCookie("_xsrf") $(window).ready(function () { console.log("ready!"); //if (webSocket) { webSocket.on('update_server_details', update_server_details); + add_server_name(); //} }); + async function add_server_name(){ + let res = await fetch(`/api/v2/servers/${serverId}`, { + method: 'GET', + headers: { + 'X-XSRFToken': token + }, + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + console.log(responseData) + $("#server-name-nav").html(`${responseData.data['server_name']}`) + $("#server-name-nav").show(); + } else { + + bootbox.alert({ + title: responseData.error, + message: responseData.error_data + }); + } + } \ No newline at end of file From 5546118517d51a09c2cff1c7af5c09dc3b42fd1c Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 13:25:39 -0500 Subject: [PATCH 19/98] Remove password max length --- app/classes/controllers/users_controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index ed53ad61..87cc513c 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -45,8 +45,7 @@ class UsersController: }, "password": { "type": "string", - "maxLength": 20, - "minLength": 6, + "minLength": 8, "examples": ["crafty"], "title": "Password", }, From 0dc075d14766e62e1bb3de1d380c59c06b1fe9c2 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 13:25:52 -0500 Subject: [PATCH 20/98] Setup auth_tracker file --- main.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main.py b/main.py index 143dfb4f..4cd78814 100644 --- a/main.py +++ b/main.py @@ -73,6 +73,14 @@ def do_intro(): def setup_logging(debug=True): logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") + if not helper.check_file_exists( + os.path.join(os.path.curdir, "logs", "auth_tracker.log") + ): + open( + os.path.join(os.path.curdir, "logs", "auth_tracker.log"), + "a", + encoding="utf-8", + ).close() if os.path.exists(logging_config_file): # open our logging config file From e9105ffbe43cbbfe2c4109e0a2677053511ff683 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 13:26:27 -0500 Subject: [PATCH 21/98] Log authentication attempts --- app/classes/shared/main_controller.py | 24 ++++++++++++++++++ app/classes/web/public_handler.py | 33 +++++++++++++++++++++++- app/config/logging.json | 36 +++++++++++++++++++++------ 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 23586696..41773c41 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -78,6 +78,30 @@ class Controller: self.first_login = False self.cached_login = self.management.get_login_image() self.support_scheduler.start() + try: + with open( + os.path.join(os.path.curdir, "logs", "auth_tracker.log"), + "r", + encoding="utf-8", + ) as f: + self.auth_tracker = json.load(f) + except: + self.auth_tracker = {} + + def log_attempt(self, remote_ip, username): + remote = self.auth_tracker.get(str(remote_ip), None) + if remote: + remote["names"].append(username) + remote["attempts"] += 1 + self.auth_tracker[str(remote_ip)] = remote + else: + self.auth_tracker[str(remote_ip)] = {"names": [username], "attempts": 1} + with open( + os.path.join(os.path.curdir, "logs", "auth_tracker.log"), + "w", + encoding="utf-8", + ) as f: + json.dump(self.auth_tracker, f, indent=4) @staticmethod def check_system_user(): diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index b7d1be9b..017ea554 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -6,6 +6,7 @@ from app.classes.models.users import HelperUsers from app.classes.web.base_handler import BaseHandler logger = logging.getLogger(__name__) +auth_log = logging.getLogger("auth") class PublicHandler(BaseHandler): @@ -96,6 +97,9 @@ class PublicHandler(BaseHandler): page_data["query"] = self.request.query if page == "login": + 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 @@ -108,6 +112,12 @@ class PublicHandler(BaseHandler): user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) user_data = HelperUsers.get_user_model(user_id) except: + self.controller.log_attempt(self.get_remote_ip(), entered_username) + auth_log.error( + f"User attempted to log into {entered_username}." + f" Authentication failed from remote IP {self.get_remote_ip()}" + " Users does not exist." + ) error_msg = "Incorrect username or password. Please try again." # self.clear_cookie("user") # self.clear_cookie("user_data") @@ -120,6 +130,12 @@ class PublicHandler(BaseHandler): # if we don't have a user if not user_data: + auth_log.error( + f"User attempted to log into {entered_username}. Authentication" + f" failed from remote IP {self.get_remote_ip()}" + " User does not exist." + ) + self.controller.log_attempt(self.get_remote_ip(), entered_username) error_msg = "Incorrect username or password. Please try again." # self.clear_cookie("user") # self.clear_cookie("user_data") @@ -132,6 +148,12 @@ class PublicHandler(BaseHandler): # if they are disabled if not user_data.enabled: + auth_log.error( + f"User attempted to log into {entered_username}. " + f"Authentication failed from remote IP {self.get_remote_ip()}." + " 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." @@ -159,7 +181,11 @@ class PublicHandler(BaseHandler): user_data.last_ip = self.get_remote_ip() user_data.last_login = Helpers.get_time_as_string() user_data.save() - + auth_log.info( + f"{entered_username} successfully" + " authenticated and logged" + f" into panel from remote IP {self.get_remote_ip()}" + ) # log this login self.controller.management.add_to_audit_log( user_data.user_id, "Logged in", 0, self.get_remote_ip() @@ -172,6 +198,11 @@ class PublicHandler(BaseHandler): self.redirect(next_page) 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") diff --git a/app/config/logging.json b/app/config/logging.json index 99de60e4..9dd09c81 100644 --- a/app/config/logging.json +++ b/app/config/logging.json @@ -10,16 +10,17 @@ }, "schedule": { "format": "%(asctime)s - [Schedules] - %(levelname)s - %(message)s" + }, + "auth": { + "format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s" } }, - "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "commander", "stream": "ext://sys.stdout" }, - "main_file_handler": { "class": "logging.handlers.RotatingFileHandler", "formatter": "commander", @@ -50,24 +51,45 @@ "maxBytes": 10485760, "backupCount": 20, "encoding": "utf8" + }, + "auth_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "formatter": "auth", + "filename": "logs/auth.log", + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8" } }, - "loggers": { "": { "level": "INFO", - "handlers": ["main_file_handler", "session_file_handler"], + "handlers": [ + "main_file_handler", + "session_file_handler" + ], "propagate": false }, "tornado.access": { "level": "INFO", - "handlers": ["tornado_access_file_handler"], + "handlers": [ + "tornado_access_file_handler" + ], "propagate": false }, "apscheduler": { "level": "INFO", - "handlers": ["schedule_file_handler"], + "handlers": [ + "schedule_file_handler" + ], + "propagate": false + }, + "auth": { + "level": "INFO", + "handlers": [ + "auth_file_handler" + ], "propagate": false } } -} +} \ No newline at end of file From bfbb130db9200b23fe6b62623d8be9a5d8044ed6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 13:33:30 -0500 Subject: [PATCH 22/98] Add logs to API --- app/classes/web/routes/api/auth/login.py | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/auth/login.py b/app/classes/web/routes/api/auth/login.py index 84ae2815..b91b295d 100644 --- a/app/classes/web/routes/api/auth/login.py +++ b/app/classes/web/routes/api/auth/login.py @@ -7,7 +7,7 @@ from app.classes.shared.helpers import Helpers from app.classes.web.base_api_handler import BaseApiHandler logger = logging.getLogger(__name__) - +auth_log = logging.getLogger("auth") login_schema = { "type": "object", "properties": { @@ -29,6 +29,10 @@ class ApiAuthLoginHandler(BaseApiHandler): 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)} ) @@ -36,6 +40,10 @@ class ApiAuthLoginHandler(BaseApiHandler): 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, { @@ -52,12 +60,23 @@ class ApiAuthLoginHandler(BaseApiHandler): user_data = Users.get_or_none(Users.username == username) if user_data is None: + self.controller.log_attempt(self.get_remote_ip(), username) + auth_log.error( + f"User attempted to log into {username}." + " Authentication failed from remote IP" + f" {self.get_remote_ip()}. User not found" + ) return self.finish_json( 401, {"status": "error", "error": "INCORRECT_CREDENTIALS", "token": None}, ) if not user_data.enabled: + auth_log.error( + f"User attempted to log into {username}." + " Authentication failed from remote" + f" IP {self.get_remote_ip()} account disabled" + ) self.finish_json( 403, {"status": "error", "error": "ACCOUNT_DISABLED", "token": None} ) @@ -67,6 +86,11 @@ class ApiAuthLoginHandler(BaseApiHandler): # Valid Login if login_result: + auth_log.info( + f"{username} successfully" + " authenticated and logged" + f" into panel from remote IP {self.get_remote_ip()}" + ) logger.info(f"User: {user_data} Logged in from IP: {self.get_remote_ip()}") # record this login From ca0546a5f4f2e79dde15f5c004ab454e5909cb05 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 13:46:21 -0500 Subject: [PATCH 23/98] Add auth log for sessions --- app/classes/web/base_handler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index 2504bc13..d8181b94 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -14,6 +14,7 @@ from app.classes.shared.translation import Translation from app.classes.shared.main_models import DatabaseShortcuts logger = logging.getLogger(__name__) +auth_log = logging.getLogger("auth") bearer_pattern = re.compile(r"^Bearer ", flags=re.IGNORECASE) @@ -231,9 +232,16 @@ class BaseHandler(tornado.web.RequestHandler): user, ) logging.debug("Auth unsuccessful") + auth_log.error( + f"Authentication attempted from {self.get_remote_ip()}. Invalid token" + ) self.access_denied(None, "the user provided an invalid token") return None except Exception as auth_exception: + auth_log.error( + f"Authentication attempted from {self.get_remote_ip()}." + f" Error: {auth_exception}" + ) logger.debug( "An error occured while authenticating an API user:", exc_info=auth_exception, From 7e18cc8d1b805d2456c11eae6e452b9fb2af8a38 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 14:17:50 -0500 Subject: [PATCH 24/98] Schedule json dump to logfile every 5 minutes --- app/classes/shared/main_controller.py | 9 ++++++++- app/classes/shared/tasks.py | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 41773c41..27104b62 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -93,9 +93,16 @@ class Controller: if remote: remote["names"].append(username) remote["attempts"] += 1 + remote["times"].append(datetime.now().strftime("%d/%m/%Y %H:%M:%S")) self.auth_tracker[str(remote_ip)] = remote else: - self.auth_tracker[str(remote_ip)] = {"names": [username], "attempts": 1} + self.auth_tracker[str(remote_ip)] = { + "names": [username], + "attempts": 1, + "times": [datetime.now().strftime("%d/%m/%Y %H:%M:%S")], + } + + def write_auth_tracker(self): with open( os.path.join(os.path.curdir, "logs", "auth_tracker.log"), "w", diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 0402c587..ff20e7ec 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -201,6 +201,13 @@ class TasksManager: id="update_watcher", start_date=datetime.datetime.now(), ) + self.scheduler.add_job( + self.controller.write_auth_tracker, + "interval", + minutes=5, + id="auth_tracker_write", + start_date=datetime.datetime.now(), + ) # self.scheduler.add_job( # self.scheduler.print_jobs, "interval", seconds=10, id="-1" # ) From 98efec141751a252893cdd3963925af73d462f56 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Sun, 5 Nov 2023 15:48:43 -0500 Subject: [PATCH 25/98] Remove API v1 --- app/classes/web/api_handler.py | 446 ----------------------------- app/classes/web/tornado_handler.py | 23 -- 2 files changed, 469 deletions(-) delete mode 100644 app/classes/web/api_handler.py diff --git a/app/classes/web/api_handler.py b/app/classes/web/api_handler.py deleted file mode 100644 index 34b09ee8..00000000 --- a/app/classes/web/api_handler.py +++ /dev/null @@ -1,446 +0,0 @@ -from datetime import datetime -import logging -import re - -from app.classes.controllers.crafty_perms_controller import EnumPermissionsCrafty -from app.classes.controllers.server_perms_controller import EnumPermissionsServer -from app.classes.web.base_handler import BaseHandler -from app.classes.models.management import DatabaseShortcuts - -logger = logging.getLogger(__name__) -bearer_pattern = re.compile(r"^Bearer", flags=re.IGNORECASE) - - -class ApiHandler(BaseHandler): - def return_response(self, status: int, data: dict): - # Define a standardized response - self.set_status(status) - self.write(data) - - def check_xsrf_cookie(self): - # Disable CSRF protection on API routes - pass - - def access_denied(self, user, reason=""): - if reason: - reason = " because " + reason - logger.info( - "User %s from IP %s was denied access to the API route " - + self.request.path - + reason, - user, - self.get_remote_ip(), - ) - self.finish( - self.return_response( - 403, - { - "error": "ACCESS_DENIED", - "info": "You were denied access to the requested resource", - }, - ) - ) - - def authenticate_user(self) -> bool: - self.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, - "Server_Creation": EnumPermissionsCrafty.SERVER_CREATION, - "User_Config": EnumPermissionsCrafty.USER_CONFIG, - "Roles_Config": EnumPermissionsCrafty.ROLES_CONFIG, - } - try: - logger.debug("Searching for specified token") - - api_token = self.get_argument("token", "") - self.api_token = api_token - if api_token is None and self.request.headers.get("Authorization"): - api_token = bearer_pattern.sub( - "", self.request.headers.get("Authorization") - ) - elif api_token is None: - api_token = self.get_cookie("token") - user_data = self.controller.users.get_user_by_api_token(api_token) - - logger.debug("Checking results") - if user_data: - # Login successful! Check perms - logger.info(f"User {user_data['username']} has authenticated to API") - - return True # This is to set the "authenticated" - logging.debug("Auth unsuccessful") - self.access_denied("unknown", "the user provided an invalid token") - return False - except Exception as e: - logger.warning("An error occured while authenticating an API user: %s", e) - self.finish( - self.return_response( - 403, - { - "error": "ACCESS_DENIED", - "info": "An error occured while authenticating the user", - }, - ) - ) - return False - - -class ServersStats(ApiHandler): - def get(self): - """Get details about all servers""" - authenticated = self.authenticate_user() - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - if not authenticated: - return - if user_obj["superuser"]: - raw_stats = self.controller.servers.get_all_servers_stats() - else: - raw_stats = self.controller.servers.get_authorized_servers_stats( - user_obj["user_id"] - ) - stats = [] - for rs in raw_stats: - s = {} - for k, v in rs["server_data"].items(): - if isinstance(v, datetime): - s[k] = v.timestamp() - else: - s[k] = v - stats.append(s) - - # Get server stats - # TODO Check perms - self.finish(self.write({"servers": stats})) - - -class NodeStats(ApiHandler): - def get(self): - """Get stats for particular node""" - authenticated = self.authenticate_user() - if not authenticated: - return - - # Get node stats - node_stats = self.controller.servers.stats.get_node_stats() - self.return_response(200, {"code": node_stats["node_stats"]}) - - -class SendCommand(ApiHandler): - def post(self): - user = self.authenticate_user() - - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - server_id = self.get_argument("id") - - if ( - not user_obj["user_id"] - in self.controller.server_perms.get_server_user_list(server_id) - and not user_obj["superuser"] - ): - self.access_denied("unknown") - return - - if not self.permissions[ - "Commands" - ] in self.controller.server_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token), server_id - ): - self.access_denied(user) - return - - command = self.get_argument("command", default=None, strip=True) - server_id = self.get_argument("id") - if command: - server = self.controller.servers.get_server_instance_by_id(server_id) - if server.check_running: - server.send_command(command) - self.return_response(200, {"run": True}) - else: - self.return_response(200, {"error": "SER_NOT_RUNNING"}) - else: - self.return_response(200, {"error": "NO_COMMAND"}) - - -class ServerBackup(ApiHandler): - def post(self): - user = self.authenticate_user() - - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - server_id = self.get_argument("id") - - if ( - not user_obj["user_id"] - in self.controller.server_perms.get_server_user_list(server_id) - and not user_obj["superuser"] - ): - self.access_denied("unknown") - return - - if not self.permissions[ - "Backup" - ] in self.controller.server_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token), server_id - ): - self.access_denied(user) - return - - server = self.controller.servers.get_server_instance_by_id(server_id) - - server.backup_server() - - self.return_response(200, {"code": "SER_BAK_CALLED"}) - - -class StartServer(ApiHandler): - def post(self): - user = self.authenticate_user() - remote_ip = self.get_remote_ip() - - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - server_id = self.get_argument("id") - - if ( - not user_obj["user_id"] - in self.controller.server_perms.get_server_user_list(server_id) - and not user_obj["superuser"] - ): - self.access_denied("unknown") - return - if not self.permissions[ - "Commands" - ] in self.controller.server_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token), server_id - ): - self.access_denied("unknown") - return - - server = self.controller.servers.get_server_instance_by_id(server_id) - - if not server.check_running(): - self.controller.management.send_command( - user_obj["user_id"], server_id, remote_ip, "start_server" - ) - self.return_response(200, {"code": "SER_START_CALLED"}) - else: - self.return_response(500, {"error": "SER_RUNNING"}) - - -class StopServer(ApiHandler): - def post(self): - user = self.authenticate_user() - remote_ip = self.get_remote_ip() - - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - server_id = self.get_argument("id") - - if ( - not user_obj["user_id"] - in self.controller.server_perms.get_server_user_list(server_id) - and not user_obj["superuser"] - ): - self.access_denied("unknown") - - if not self.permissions[ - "Commands" - ] in self.controller.server_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token), server_id - ): - self.access_denied(user) - return - - server = self.controller.servers.get_server_instance_by_id(server_id) - - if server.check_running(): - self.controller.management.send_command( - user, server_id, remote_ip, "stop_server" - ) - - self.return_response(200, {"code": "SER_STOP_CALLED"}) - else: - self.return_response(500, {"error": "SER_NOT_RUNNING"}) - - -class RestartServer(ApiHandler): - def post(self): - user = self.authenticate_user() - remote_ip = self.get_remote_ip() - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - server_id = self.get_argument("id") - - if not user_obj["user_id"] in self.controller.server_perms.get_server_user_list( - server_id - ): - self.access_denied("unknown") - - if not self.permissions[ - "Commands" - ] in self.controller.server_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token), server_id - ): - self.access_denied(user) - - self.controller.management.send_command( - user, server_id, remote_ip, "restart_server" - ) - self.return_response(200, {"code": "SER_RESTART_CALLED"}) - - -class CreateUser(ApiHandler): - def post(self): - user = self.authenticate_user() - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - user_perms = self.controller.crafty_perms.get_crafty_permissions_list( - user_obj["user_id"] - ) - if ( - not self.permissions["User_Config"] in user_perms - and not user_obj["superuser"] - ): - self.access_denied("unknown") - return - - if user is None: - self.access_denied("unknown") - return - - if not self.permissions[ - "User_Config" - ] in self.controller.crafty_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token) - ): - self.access_denied(user) - return - - new_username = self.get_argument("username").lower() - new_pass = self.get_argument("password") - manager = int(user_obj["user_id"]) - - if new_username: - self.controller.users.add_user( - new_username, manager, new_pass, "default@example.com", True, False - ) - - self.return_response( - 200, - { - "code": "COMPLETE", - "username": new_username, - "password": new_pass, - }, - ) - else: - self.return_response( - 500, - { - "error": "MISSING_PARAMS", - "info": "Some paramaters failed validation", - }, - ) - - -class DeleteUser(ApiHandler): - def post(self): - user = self.authenticate_user() - - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - user_perms = self.controller.crafty_perms.get_crafty_permissions_list( - user_obj["user_id"] - ) - - if ( - not self.permissions["User_Config"] in user_perms - and not user_obj["superuser"] - ): - self.access_denied("unknown") - return - - if user is None: - self.access_denied("unknown") - return - - if not self.permissions[ - "User_Config" - ] in self.controller.crafty_perms.get_api_key_permissions_list( - self.controller.users.get_api_key_by_token(self.api_token) - ): - self.access_denied(user) - return - - user_id = self.get_argument("user_id", None, True) - user_to_del = self.controller.users.get_user_by_id(user_id) - - if user_to_del["superuser"]: - self.return_response( - 500, - {"error": "NOT_ALLOWED", "info": "You cannot delete a super user"}, - ) - else: - if user_id: - self.controller.users.remove_user(user_id) - self.return_response(200, {"code": "COMPLETED"}) - - -class ListServers(ApiHandler): - def get(self): - user = self.authenticate_user() - user_obj = self.controller.users.get_user_by_api_token(self.api_token) - - if user is None: - self.access_denied("unknown") - return - - if self.api_token is None: - self.access_denied("unknown") - return - - if user_obj["superuser"]: - servers = self.controller.servers.get_all_defined_servers() - servers = [str(i) for i in servers] - else: - servers = self.controller.servers.get_authorized_servers( - user_obj["user_id"] - ) - page_servers = [] - for server in servers: - if server not in page_servers: - page_servers.append( - DatabaseShortcuts.get_data_obj(server.server_object) - ) - servers = page_servers - servers = [str(i) for i in servers] - - self.return_response( - 200, - { - "code": "COMPLETED", - "servers": servers, - }, - ) diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index 621c930a..f5501d31 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -22,18 +22,6 @@ from app.classes.web.default_handler import DefaultHandler from app.classes.web.routes.api.api_handlers import api_handlers from app.classes.web.routes.metrics.metrics_handlers import metrics_handlers from app.classes.web.server_handler import ServerHandler -from app.classes.web.api_handler import ( - ServersStats, - NodeStats, - ServerBackup, - StartServer, - StopServer, - RestartServer, - CreateUser, - DeleteUser, - ListServers, - SendCommand, -) from app.classes.web.websocket_handler import WebSocketHandler from app.classes.web.static_handler import CustomStaticHandler from app.classes.web.upload_handler import UploadHandler @@ -162,17 +150,6 @@ class Webserver: (r"/ws", WebSocketHandler, handler_args), (r"/upload", UploadHandler, handler_args), (r"/status", StatusHandler, handler_args), - # API Routes V1 - (r"/api/v1/stats/servers", ServersStats, handler_args), - (r"/api/v1/stats/node", NodeStats, handler_args), - (r"/api/v1/server/send_command", SendCommand, handler_args), - (r"/api/v1/server/backup", ServerBackup, handler_args), - (r"/api/v1/server/start", StartServer, handler_args), - (r"/api/v1/server/stop", StopServer, handler_args), - (r"/api/v1/server/restart", RestartServer, handler_args), - (r"/api/v1/list_servers", ListServers, handler_args), - (r"/api/v1/users/create_user", CreateUser, handler_args), - (r"/api/v1/users/delete_user", DeleteUser, handler_args), # API Routes V2 *api_handlers(handler_args), # API Routes OpenMetrics From 53b69fe0f69b902a1d4301082ffd70e265fcfb78 Mon Sep 17 00:00:00 2001 From: Wout Bouckaert Date: Sat, 11 Nov 2023 12:21:59 -0700 Subject: [PATCH 26/98] Remove trailing space for pardon player command. --- app/frontend/templates/panel/parts/server_players.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/templates/panel/parts/server_players.html b/app/frontend/templates/panel/parts/server_players.html index df059e95..ca81f39e 100644 --- a/app/frontend/templates/panel/parts/server_players.html +++ b/app/frontend/templates/panel/parts/server_players.html @@ -69,7 +69,7 @@ Banned on {{ player['banned_on'] }} Banned by : {{ player['source'] }}
    Reason : {{ player['reason'] }} - + {% end %} From 91e62c647ea0790545eac2cc8d526289cadc856e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 14:14:13 -0500 Subject: [PATCH 27/98] Create text file in config folder with init creds --- app/classes/shared/helpers.py | 119 ++++++++++++++++++++++++++++++ app/classes/shared/main_models.py | 4 +- main.py | 41 ++++++---- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 62ce8819..a769eb25 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -6,6 +6,8 @@ import json import tempfile import time import uuid +import random +import array import string import base64 import socket @@ -361,6 +363,123 @@ class Helpers: return result_of_check == 0 + def create_pass(self): + # maximum length of password needed + # this can be changed to suit your password length + max_len = 12 + + # declare arrays of the character that we need in out password + # Represented as chars to enable easy string concatenation + digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + locase = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + ] + + upcase = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + ] + + symbols = [ + "@", + "#", + "$", + "%", + "=", + ":", + "?", + ".", + "/", + "|", + "~", + "*", + "(", + ")", + ] + + # combines all the character arrays above to form one array + combo = digits + upcase + locase + symbols + + # randomly select at least one character from each character set above + rand_digit = random.choice(digits) + rand_upper = random.choice(upcase) + rand_lower = random.choice(locase) + rand_symbol = random.choice(symbols) + + # combine the character randomly selected above + # at this stage, the password contains only 4 characters but + # we want a 12-character password + temp_pass = rand_digit + rand_upper + rand_lower + rand_symbol + + # now that we are sure we have at least one character from each + # set of characters, we fill the rest of + # the password length by selecting randomly from the combined + # list of character above. + for x in range(max_len - 4): + temp_pass = temp_pass + random.choice(combo) + + # convert temporary password into array and shuffle to + # prevent it from having a consistent pattern + # where the beginning of the password is predictable + temp_pass_list = array.array("u", temp_pass) + random.shuffle(temp_pass_list) + + # traverse the temporary password array and append the chars + # to form the password + password = "" + for x in temp_pass_list: + password = password + x + + # print out password + return password + @staticmethod def cmdparse(cmd_in): # Parse a string into arguments diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index 4bfca52c..2bb3e3fa 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -14,13 +14,13 @@ class DatabaseBuilder: self.management_helper = management_helper self.users_helper = users_helper - def default_settings(self): + def default_settings(self, password="crafty"): logger.info("Fresh Install Detected - Creating Default Settings") Console.info("Fresh Install Detected - Creating Default Settings") default_data = self.helper.find_default_password() username = default_data.get("username", "admin") - password = default_data.get("password", "crafty") + password = default_data.get("password", password) self.users_helper.add_user( username=username, diff --git a/main.py b/main.py index 143dfb4f..32606cce 100644 --- a/main.py +++ b/main.py @@ -144,6 +144,18 @@ if __name__ == "__main__": installer = DatabaseBuilder(database, helper, user_helper, management_helper) FRESH_INSTALL = installer.is_fresh_install() + if getattr(sys, "frozen", False): + app_path = os.path.dirname(sys.executable) + RUN_MODE = "Frozen/executable" + else: + try: + app_full_path = os.path.realpath(__file__) + app_path = os.path.dirname(app_full_path) + RUN_MODE = "Non-interactive (e.g. 'python main.py')" + except NameError: + app_path = os.getcwd() + RUN_MODE = "Interactive" + if FRESH_INSTALL: Console.debug("Fresh install detected") Console.warning( @@ -152,7 +164,14 @@ if __name__ == "__main__": f"through your router/firewall if you would like to be able " f"to access Crafty remotely." ) - installer.default_settings() + password = helper.create_pass() + installer.default_settings(password) + with open( + os.path.join(app_path, "app", "config", "default-creds.txt"), + "w", + encoding="utf-8", + ) as f: + f.write(json.dumps({"username": "admin", "password": password}, indent=4)) else: Console.debug("Existing install detected") Console.info("Checking for reset secret flag") @@ -227,22 +246,10 @@ if __name__ == "__main__": internet_check_thread = Thread(target=internet_check, name="internet_check") - def controller_setup(): + def controller_setup(application_path, running_mode): if not controller.check_system_user(): controller.add_system_user() - if getattr(sys, "frozen", False): - application_path = os.path.dirname(sys.executable) - running_mode = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - application_path = os.path.dirname(app_full_path) - running_mode = "Non-interactive (e.g. 'python main.py')" - except NameError: - application_path = os.getcwd() - running_mode = "Interactive" - controller.set_project_root(application_path) master_server_dir = controller.management.get_master_server_dir() if master_server_dir == "": @@ -262,7 +269,11 @@ if __name__ == "__main__": helper, tasks_manager, migration_manager, controller, import3 ) - controller_setup_thread = Thread(target=controller_setup, name="controller_setup") + controller_setup_thread = Thread( + target=controller_setup, + name="controller_setup", + args=[RUN_MODE, app_path], + ) def setup_starter(): if not args.daemon: From 623d6f58558ccfcbe3d96a91afa7fd2feb2b59b3 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 16:02:50 -0500 Subject: [PATCH 28/98] Use string library instead of monolithic arrays up max length to 25 --- app/classes/shared/helpers.py | 78 +++-------------------------------- 1 file changed, 5 insertions(+), 73 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index a769eb25..67dd97da 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -365,84 +365,16 @@ class Helpers: def create_pass(self): # maximum length of password needed - # this can be changed to suit your password length - max_len = 12 + max_len = 25 # declare arrays of the character that we need in out password # Represented as chars to enable easy string concatenation - digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - locase = [ - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - ] + digits = string.digits + locase = string.ascii_lowercase - upcase = [ - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - ] + upcase = string.ascii_uppercase - symbols = [ - "@", - "#", - "$", - "%", - "=", - ":", - "?", - ".", - "/", - "|", - "~", - "*", - "(", - ")", - ] + symbols = string.punctuation # combines all the character arrays above to form one array combo = digits + upcase + locase + symbols From de39b041cbc0bcec11e4fbb8986739ae58ce90ad Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 16:03:53 -0500 Subject: [PATCH 29/98] Fix comment --- 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 67dd97da..77cf731e 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -367,7 +367,7 @@ class Helpers: # maximum length of password needed max_len = 25 - # declare arrays of the character that we need in out password + # declare string of the character that we need in out password # Represented as chars to enable easy string concatenation digits = string.digits locase = string.ascii_lowercase @@ -376,7 +376,7 @@ class Helpers: symbols = string.punctuation - # combines all the character arrays above to form one array + # combines all the character strings above to form one array combo = digits + upcase + locase + symbols # randomly select at least one character from each character set above From 7ef461489a6f576308283670e0b9d0f59133512c Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 17:22:54 -0500 Subject: [PATCH 30/98] Set perms to 600 --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 32606cce..a94a465e 100644 --- a/main.py +++ b/main.py @@ -172,6 +172,7 @@ if __name__ == "__main__": encoding="utf-8", ) as f: f.write(json.dumps({"username": "admin", "password": password}, indent=4)) + os.chmod(os.path.join(app_path, "app", "config", "default-creds.txt"), 0o600) else: Console.debug("Existing install detected") Console.info("Checking for reset secret flag") From 2fd66420c4a8b587c8245e4e9afe15b87efd093f Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 17:23:02 -0500 Subject: [PATCH 31/98] Add terminal message --- app/classes/shared/main_models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index 2bb3e3fa..f57b2bfd 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -18,7 +18,11 @@ 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 not password 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) From 63d84ed1bba7f62135ff1892f18435c5666cd2f4 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 17:23:15 -0500 Subject: [PATCH 32/98] Remove clean from password input --- 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 b7d1be9b..5e0c42f5 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -101,7 +101,7 @@ class PublicHandler(BaseHandler): next_page = "/login?" + self.request.query entered_username = nh3.clean(self.get_argument("username")) - entered_password = nh3.clean(self.get_argument("password")) + entered_password = self.get_argument("password") # pylint: disable=no-member try: From 131337c7d0daa5f384e52f464c9c8e4850ee8646 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 13 Nov 2023 18:16:08 -0500 Subject: [PATCH 33/98] Appease the linter --- app/classes/shared/helpers.py | 6 +++--- main.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 77cf731e..d0c1b0d4 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -394,7 +394,7 @@ class Helpers: # set of characters, we fill the rest of # the password length by selecting randomly from the combined # list of character above. - for x in range(max_len - 4): + for char in range(max_len - 4): temp_pass = temp_pass + random.choice(combo) # convert temporary password into array and shuffle to @@ -406,8 +406,8 @@ class Helpers: # traverse the temporary password array and append the chars # to form the password password = "" - for x in temp_pass_list: - password = password + x + for char in temp_pass_list: + password = password + char # print out password return password diff --git a/main.py b/main.py index a94a465e..acf6dd0f 100644 --- a/main.py +++ b/main.py @@ -170,8 +170,10 @@ if __name__ == "__main__": os.path.join(app_path, "app", "config", "default-creds.txt"), "w", encoding="utf-8", - ) as f: - f.write(json.dumps({"username": "admin", "password": password}, indent=4)) + ) as cred_file: + cred_file.write( + json.dumps({"username": "admin", "password": password}, indent=4) + ) os.chmod(os.path.join(app_path, "app", "config", "default-creds.txt"), 0o600) else: Console.debug("Existing install detected") From 13531c254346dbdf1d6d4459fadbdbe7ebb22086 Mon Sep 17 00:00:00 2001 From: Freddy 0 Date: Thu, 16 Nov 2023 18:10:15 +0000 Subject: [PATCH 34/98] adjustment for !672 --- docker/unraid.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/unraid.xml b/docker/unraid.xml index c1b106fb..8c036ba2 100644 --- a/docker/unraid.xml +++ b/docker/unraid.xml @@ -21,7 +21,7 @@ https://wiki.craftycontrol.com/uploads/en/crafty%204%20dashboard%20with%20one%20server.jpeg https://wiki.craftycontrol.com/uploads/en/crafty%204%20server%20setup%20details.png - Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. [br]Boasting a clean new look, rebuilt from the ground up. [br] [br] Crafty 4 brings a whole host of new features such as Bedrock support. [br] With SteamCMD support on the way![br] **Default login Credentrails are username: "admin" password: "crafty". ** [br]Crafty 4 is the successor of Crafty Controller. [br]For official support join the Discord server https://discord.gg/9VJPhCE [br] For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/ + Crafty 4 is the next iteration of our Minecraft Server Wrapper / Controller / Launcher. [br]Boasting a clean new look, rebuilt from the ground up. [br] [br] Crafty 4 brings a whole host of new features such as Bedrock support. [br] With SteamCMD support on the way![br] **Default login Credentrails are stored in your Crafty Configuration location in the file default-creds.txt ** [br]Crafty 4 is the successor of Crafty Controller. [br]For official support join the Discord server https://discord.gg/9VJPhCE [br] For migration from 3.x please refer to the documentation: https://wiki.craftycontrol.com/en/4/ GameServers: Other: https://[IP]:[PORT:8443]/ From 62b095c38fddf0230b58109b02f9d6d78a2e23de Mon Sep 17 00:00:00 2001 From: JonathanAlKass Date: Tue, 21 Nov 2023 06:30:45 +0000 Subject: [PATCH 35/98] removed ".decoder" from orjson in line 113 --- app/classes/web/routes/api/roles/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index 0d46e11b..b0c773a7 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -110,7 +110,7 @@ class ApiRolesIndexHandler(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 ee4066c70cd74db4dbf01b4ec0a9f812ebd81c41 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 18:55:49 +0000 Subject: [PATCH 36/98] Update changelog !664 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d81afb9f..39045400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### New features TBD ### Bug fixes -TBD +- Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) ### Tweaks TBD ### Lang From 198bca266e14a61c416d4ce5866728fb88a84cba Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 19:10:33 +0000 Subject: [PATCH 37/98] Update changelog !666 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39045400..81a5c492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ TBD ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) ### Tweaks -TBD +- Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) ### Lang TBD

    From 437bc1525cc9cbb8c5377fde9a36b4deb6600b77 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 19:17:52 +0000 Subject: [PATCH 38/98] Update changelog !667 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81a5c492..917cc3a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ TBD - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) +- Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) +- Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) ### Lang TBD

    From a6aaebba88f078a599c19d992d9c7bd9528d2a30 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 19:32:03 +0000 Subject: [PATCH 39/98] Update changelog !669 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 917cc3a3..4673ec39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ TBD - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) +- Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) ### Lang TBD

    From eded956053c78760075e45e5af2dd73c6289c9b4 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 19:39:51 +0000 Subject: [PATCH 40/98] Update changelog !669 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4673ec39..2a862277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TBD - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) +- Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) ### Lang TBD

    From 5882d2f10f9882ad462429ef0c5c355d8bdba4f9 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 19:52:51 +0000 Subject: [PATCH 41/98] Update changelog !670 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a862277..af6f52f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## --- [4.2.2] - 2023/TBD ### New features TBD +### Refactor +- Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) ### Tweaks From 75aacda120ce27ca7eb9dd8c112a4e96262a7304 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 20:01:20 +0000 Subject: [PATCH 42/98] Update changelog !671 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af6f52f4..ca5fa0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ TBD - Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) +- Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) ### Lang TBD

    From 8bad0fb0a94c6cddab339a75d0cd1e1840734ba8 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 20:33:21 +0000 Subject: [PATCH 43/98] Update changelog !674 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5fa0d6..dfd883a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ TBD - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) +- Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) ### Lang TBD

    From 7045118cd27392dbc923020bbd6bbd4810c42d06 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 20:42:18 +0000 Subject: [PATCH 44/98] Refactor password generator to be OWASP compliant --- app/classes/shared/helpers.py | 50 +++++++++++++---------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index d0c1b0d4..783ac16e 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -363,53 +363,41 @@ class Helpers: return result_of_check == 0 +class PasswordGenerator: def create_pass(self): - # maximum length of password needed + # Maximum length of password needed max_len = 25 - # declare string of the character that we need in out password - # Represented as chars to enable easy string concatenation + # Declare string of the character that we need in our password digits = string.digits locase = string.ascii_lowercase - upcase = string.ascii_uppercase - symbols = string.punctuation - # combines all the character strings above to form one array + # Combine all the character strings above to form one string combo = digits + upcase + locase + symbols - # randomly select at least one character from each character set above - rand_digit = random.choice(digits) - rand_upper = random.choice(upcase) - rand_lower = random.choice(locase) - rand_symbol = random.choice(symbols) + # Randomly select at least one character from each character set above + rand_digit = secrets.choice(digits) + rand_upper = secrets.choice(upcase) + rand_lower = secrets.choice(locase) + rand_symbol = secrets.choice(symbols) - # combine the character randomly selected above - # at this stage, the password contains only 4 characters but - # we want a 12-character password + # Combine the character randomly selected above temp_pass = rand_digit + rand_upper + rand_lower + rand_symbol - # now that we are sure we have at least one character from each - # set of characters, we fill the rest of - # the password length by selecting randomly from the combined - # list of character above. - for char in range(max_len - 4): - temp_pass = temp_pass + random.choice(combo) + # Fill the rest of the password length by selecting randomly char list + for _ in range(max_len - 4): + temp_pass += secrets.choice(combo) - # convert temporary password into array and shuffle to - # prevent it from having a consistent pattern - # where the beginning of the password is predictable - temp_pass_list = array.array("u", temp_pass) - random.shuffle(temp_pass_list) + # Shuffle the temporary password to prevent predictable patterns + temp_pass_list = list(temp_pass) + secrets.SystemRandom().shuffle(temp_pass_list) - # traverse the temporary password array and append the chars - # to form the password - password = "" - for char in temp_pass_list: - password = password + char + # Form the password by concatenating the characters + password = ''.join(temp_pass_list) - # print out password + # Return completed password return password @staticmethod From fb4adfb22e24f6b2cc1edff0dd89f6770f37c150 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 20:47:45 +0000 Subject: [PATCH 45/98] Fix black finding --- app/classes/shared/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 783ac16e..577feb8d 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -363,6 +363,7 @@ class Helpers: return result_of_check == 0 + class PasswordGenerator: def create_pass(self): # Maximum length of password needed @@ -395,7 +396,7 @@ class PasswordGenerator: secrets.SystemRandom().shuffle(temp_pass_list) # Form the password by concatenating the characters - password = ''.join(temp_pass_list) + password = "".join(temp_pass_list) # Return completed password return password From 16f8df20ac3653824e5620e10ed38e5d8e38046c Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 21:20:39 +0000 Subject: [PATCH 46/98] Update lang pl_PL with minor fixes Thanks Terrariadlc --- app/translations/pl_PL.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 828ec0b1..2c60c6df 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -326,8 +326,8 @@ "bePatientDeleteFiles": "Poczekaj, aż usuniemy twój serwer i jego pliki. Strona za chwilę się zamknie.", "bePatientUpdate": "Poczekaj kiedy my aktualizujemy twój serwer. Pobieranie zależy od prędkości twojego internetu.
    Strona się odświeży za chwile.", "cancel": "Anuluj", - "crashTime": "Crash wyszedł poza limit czasu", - "crashTimeDesc": "How long should we wait before we consider your server as crashed?", + "crashTime": "Crash serwera wyszedł poza limit czasu", + "crashTimeDesc": "Jak długo powinniśmy poczekać zanim uznać serwer za zcrashowany?", "deleteFilesQuestion": "Usuń pliki serwera z maszyny?", "deleteFilesQuestionMessage": "Czy chcesz aby Crafty usunął wszystkie pliki tego serwera?

    To zawiera backupy.", "deleteServer": "Usuń serwer", @@ -403,7 +403,7 @@ "filterList": "Filtrowane słowa", "logs": "Logi", "metrics": "Statystyki", - "playerControls": "Player Management", + "playerControls": "Zarządzanie użytkownikami", "reset": "Resetuj Scrolla", "schedule": "Harmonogram", "serverDetails": "Detale serwera", @@ -421,7 +421,7 @@ "deleteItemQuestion": "Czy jesteś pewien że chcesz usunąć \" + name + \"?", "deleteItemQuestionMessage": "Usuwasz \\\"\" + path + \"\\\"!

    Ta akcja jest nieodwracalna i zostanie usunięta na zawsze!", "download": "Pobierz", - "editingFile": "Edytuję plik", + "editingFile": "Edytuj plik", "error": "Error while getting files", "fileReadError": "Error odczytu pliku", "files": "Pliki", @@ -432,7 +432,7 @@ "rename": "Zmień nazwę", "renameItemQuestion": "Jaka ma być nowa nazwa?", "save": "Zapisz", - "size": "Włącz zmienianie rozmiaru edytora", + "size": "Włącz rozszerzanie i zmniejszanie edytora", "stayHere": "NIE WYCHODŹ Z TEJ STRONY!", "unsupportedLanguage": "Uwaga: To nie jest wspierany typ pliku", "unzip": "Rozpakuj", @@ -545,7 +545,7 @@ "buildServer": "Zbuduj serwer!", "clickRoot": "Kilknij tutaj aby zaznaczyć główną ścieżkę", "close": "Zamknij", - "defaultPort": "25565 podstawowy", + "defaultPort": "Domyślnie 25565", "downloading": "Pobieranie serwera...", "explainRoot": "Proszę, kliknij przycisk poniżej aby zaznaczyć główną ścieżkę w tym archiwum", "importServer": "Importuj egzystujący serwer", @@ -640,12 +640,12 @@ "edit": "Edytuj", "enabled": "Włączony", "jar_update": "Plik startowy zaktualizowany", - "kill": "Serwer zatrzymany", + "kill": "Serwer został zabity", "name": "Nazwa", "new": "Nowy Webhook", "newWebhook": "Nowy Webhook", "no-webhook": "Nie posiadasz aktualnie żadnych Webhooków dla tego serwera. Aby dodać webhook kliknij na", - "run": "Włącz Webhook", + "run": "Przetestuj Webhook", "send_command": "Komenda serwera otrzymana!", "start_server": "Serwer włączony", "stop_server": "Serwer wyłączony", From 7db259eb982acb9e53a77de2e1671ac3affd64c8 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sat, 25 Nov 2023 21:26:34 +0000 Subject: [PATCH 47/98] Update changelog !675 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd883a7..4784608b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ TBD - Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) - Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) ### Lang -TBD +- pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

    ## --- [4.2.1] - 2023/11/01 From e5cec247dcedf140bf7a669cd1fdcd8f6cb6806f Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 26 Nov 2023 14:48:36 -0500 Subject: [PATCH 48/98] Remove class declaration in helpers --- app/classes/shared/helpers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 577feb8d..c9b64f12 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -363,8 +363,6 @@ class Helpers: return result_of_check == 0 - -class PasswordGenerator: def create_pass(self): # Maximum length of password needed max_len = 25 From 966d359a9a67fa0d67b17339865dc16d2a25b27d Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 26 Nov 2023 21:45:48 +0000 Subject: [PATCH 49/98] Remove unused imports from pass gen refactor --- app/classes/shared/helpers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index c9b64f12..646b9f52 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -6,8 +6,6 @@ import json import tempfile import time import uuid -import random -import array import string import base64 import socket From a00f418fcf16f6937a876efa0718f0da6e89e604 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Tue, 28 Nov 2023 01:54:55 +0000 Subject: [PATCH 50/98] Fix password case lint warning --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 110d5e77..54bb4092 100644 --- a/main.py +++ b/main.py @@ -172,15 +172,15 @@ if __name__ == "__main__": f"through your router/firewall if you would like to be able " f"to access Crafty remotely." ) - password = helper.create_pass() - installer.default_settings(password) + PASSWORD = helper.create_pass() + installer.default_settings(PASSWORD) with open( os.path.join(app_path, "app", "config", "default-creds.txt"), "w", encoding="utf-8", ) as cred_file: cred_file.write( - json.dumps({"username": "admin", "password": password}, indent=4) + json.dumps({"username": "admin", "password": PASSWORD}, indent=4) ) os.chmod(os.path.join(app_path, "app", "config", "default-creds.txt"), 0o600) else: From ae41ef1ef9833e97655e564cf3f5b6c00df1e861 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Tue, 28 Nov 2023 02:21:05 +0000 Subject: [PATCH 51/98] Fix pathing issue, hoist env check and flip args to correct malformed app path --- main.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/main.py b/main.py index 54bb4092..94fa7543 100644 --- a/main.py +++ b/main.py @@ -39,6 +39,20 @@ if not (sys.version_info.major == 3 and sys.version_info.minor >= 9): time.sleep(3) Console.info("Crafty stopped. Exiting...") sys.exit(0) + +# Determine the environment we're running in and set path accordingly +if getattr(sys, "frozen", False): + app_path = os.path.dirname(sys.executable) + RUN_MODE = "Frozen/executable" +else: + try: + app_full_path = os.path.realpath(__file__) + app_path = os.path.dirname(app_full_path) + RUN_MODE = "Non-interactive (e.g. 'python main.py')" + except NameError: + app_path = os.getcwd() + RUN_MODE = "Interactive" + # pylint: disable=wrong-import-position try: from app.classes.models.base_model import database_proxy @@ -72,12 +86,12 @@ def do_intro(): def setup_logging(debug=True): - logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") + logging_config_file = os.path.join(app_path, "app", "config", "logging.json") if not helper.check_file_exists( - os.path.join(os.path.curdir, "logs", "auth_tracker.log") + os.path.join(app_path, "logs", "auth_tracker.log") ): open( - os.path.join(os.path.curdir, "logs", "auth_tracker.log"), + os.path.join(app_path, "logs", "auth_tracker.log"), "a", encoding="utf-8", ).close() @@ -152,18 +166,6 @@ if __name__ == "__main__": installer = DatabaseBuilder(database, helper, user_helper, management_helper) FRESH_INSTALL = installer.is_fresh_install() - if getattr(sys, "frozen", False): - app_path = os.path.dirname(sys.executable) - RUN_MODE = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - app_path = os.path.dirname(app_full_path) - RUN_MODE = "Non-interactive (e.g. 'python main.py')" - except NameError: - app_path = os.getcwd() - RUN_MODE = "Interactive" - if FRESH_INSTALL: Console.debug("Fresh install detected") Console.warning( @@ -271,8 +273,10 @@ if __name__ == "__main__": else: helper.servers_dir = master_server_dir - Console.debug(f"Execution Mode: {running_mode}") - Console.debug(f"Application path : '{application_path}'") + logger.info(f"Execution Mode: {running_mode}") + logger.info(f"Application path: '{application_path}'") + Console.info(f"Execution Mode: {running_mode}") + Console.info(f"Application path: '{application_path}'") controller.clear_support_status() @@ -283,7 +287,7 @@ if __name__ == "__main__": controller_setup_thread = Thread( target=controller_setup, name="controller_setup", - args=[RUN_MODE, app_path], + args=[app_path, RUN_MODE], ) def setup_starter(): From 4b9c6cfd7177d373864fdbefb5e4e859872f6df5 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Tue, 28 Nov 2023 02:22:25 +0000 Subject: [PATCH 52/98] Fix sonar finding Use the opposite operator ("not in") instead. Boolean checks should not be inverted python:S1940 --- app/classes/shared/main_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/shared/main_models.py b/app/classes/shared/main_models.py index f57b2bfd..c166b7fb 100644 --- a/app/classes/shared/main_models.py +++ b/app/classes/shared/main_models.py @@ -18,7 +18,7 @@ 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 not password 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" From 151f77589baff531dec5619855148c97c48875e5 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 18:15:11 -0500 Subject: [PATCH 53/98] Restructure main.py to be more comprehensive --- main.py | 318 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 195 insertions(+), 123 deletions(-) diff --git a/main.py b/main.py index c5ec1297..151c6364 100644 --- a/main.py +++ b/main.py @@ -51,7 +51,179 @@ except ModuleNotFoundError as err: helper.auto_installer_fix(err) +def internet_check(): + """ + This checks to see if the Crafty host is connected to the + internet. This will show a warning in the console if no interwebs. + """ + print() + logger.info("Checking Internet. This may take a minute.") + Console.info("Checking Internet. This may take a minute.") + + if not helper.check_internet(): + logger.warning( + "We have detected the machine running Crafty has no " + "connection to the internet. Client connections to " + "the server may be limited." + ) + Console.warning( + "We have detected the machine running Crafty has no " + "connection to the internet. Client connections to " + "the server may be limited." + ) + + +def controller_setup(): + """ + Method sets up the software controllers. + This also sets the application path as well as the + master server dir (if not set). + + This also clears the support logs status. + """ + if not controller.check_system_user(): + controller.add_system_user() + + if getattr(sys, "frozen", False): + application_path = os.path.dirname(sys.executable) + running_mode = "Frozen/executable" + else: + try: + app_full_path = os.path.realpath(__file__) + application_path = os.path.dirname(app_full_path) + running_mode = "Non-interactive (e.g. 'python main.py')" + except NameError: + application_path = os.getcwd() + running_mode = "Interactive" + + controller.set_project_root(application_path) + master_server_dir = controller.management.get_master_server_dir() + if master_server_dir == "": + logger.debug("Could not find master server path. Setting default") + controller.set_master_server_dir( + os.path.join(controller.project_root, "servers") + ) + else: + helper.servers_dir = master_server_dir + + Console.debug(f"Execution Mode: {running_mode}") + Console.debug(f"Application path : '{application_path}'") + + controller.clear_support_status() + + +def tasks_starter(): + """ + Method starts stats recording, app scheduler, and + serverjars/steamCMD cache refreshers + """ + # start stats logging + tasks_manager.start_stats_recording() + + # once the controller is up and stats are logging, we can kick off + # the scheduler officially + tasks_manager.start_scheduler() + + # refresh our cache and schedule for every 12 hoursour cache refresh + # for serverjars.com + tasks_manager.serverjar_cache_refresher() + + +def signal_handler(signum, _frame): + """ + Method handles sigterm and shuts the app down. + """ + if not args.daemon: + print() # for newline after prompt + signame = signal.Signals(signum).name + logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") + Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") + tasks_manager._main_graceful_exit() + crafty_prompt.universal_exit() + + +def do_cleanup(): + """ + Checks Crafty's temporary directory and clears it out on boot. + """ + try: + logger.info("Removing old temp dirs") + FileHelpers.del_dirs(os.path.join(controller.project_root, "temp")) + except: + logger.info("Did not find old temp dir.") + os.mkdir(os.path.join(controller.project_root, "temp")) + + +def do_version_check(): + """ + Checks for remote version differences. + + Prints in terminal with differences if true. + + Also sets helper variable to update available when pages + are served. + """ + + # Check if new version available + remote_ver = helper.check_remote_version() + if remote_ver: + notice = f""" + A new version of Crafty is available! + {'/' * 37} + New version available: {remote_ver} + Current version: {pkg_version.parse(helper.get_version_string())} + {'/' * 37} + """ + Console.yellow(notice) + + crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > " + + +def setup_starter(): + """ + This method starts our setup threads. + (tasks scheduler, internet checks, controller setups) + + Once our threads complete we will set our startup + variable to false and send a reload to any clients waiting. + + + """ + if not args.daemon: + time.sleep(0.01) # Wait for the prompt to start + print() # Make a newline after the prompt so logs are on an empty line + else: + time.sleep(0.01) # Wait for the daemon info message + + Console.info("Setting up Crafty's internal components...") + ws.broadcast("update", {"message": "Final checks and launching"}) + # Start the setup threads + tasks_starter_thread.start() + internet_check_thread.start() + controller_setup_thread.start() + + # Wait for the setup threads to finish + tasks_starter_thread.join() + internet_check_thread.join() + controller_setup_thread.join() + helper.crafty_starting = False + ws.broadcast("send_start_reload", "") + do_version_check() + Console.info("Crafty has fully started and is now ready for use!") + + do_cleanup() + + if not args.daemon: + # Put the prompt under the cursor + crafty_prompt.print_prompt() + + def do_intro(): + """ + Runs the Crafty Controller Terminal Intro with information about the software + This method checks for a "settings file" or config.json. If it does not find + one it will create one. + """ logger.info("***** Crafty Controller Started *****") version = helper.get_version_string() @@ -72,6 +244,11 @@ def do_intro(): def setup_logging(debug=True): + """ + This method sets up our logging for Crafty. It takes + one optional (defaulted to True) parameter which + determines whether or not the logging level is "debug" or verbose. + """ logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") if os.path.exists(logging_config_file): @@ -142,10 +319,16 @@ if __name__ == "__main__": migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations - # do our installer stuff + # init classes + # now the tables are created, we can load the tasks_manager and server controller user_helper = HelperUsers(database, helper) management_helper = HelpersManagement(database, helper) installer = DatabaseBuilder(database, helper, user_helper, management_helper) + file_helper = FileHelpers(helper) + import_helper = ImportHelpers(helper, file_helper) + controller = Controller(database, helper, file_helper, import_helper) + tasks_manager = TasksManager(helper, controller, file_helper) + import3 = Import3(helper, controller) FRESH_INSTALL = installer.is_fresh_install() if FRESH_INSTALL: @@ -167,28 +350,17 @@ if __name__ == "__main__": helper.set_setting("reset_secrets_on_next_boot", False) else: Console.info("No flag found. Secrets are staying") - file_helper = FileHelpers(helper) - import_helper = ImportHelpers(helper, file_helper) - # now the tables are created, we can load the tasks_manager and server controller - controller = Controller(database, helper, file_helper, import_helper) + + # Check to see if client config.json version is different than the + # Master config.json in helpers.py Console.info("Checking for remote changes to config.json") controller.get_config_diff() Console.info("Remote change complete.") - import3 = Import3(helper, controller) - ws.broadcast("update", {"message": "Starting Task Scheduler"}) - tasks_manager = TasksManager(helper, controller, file_helper) + # startup the web server + ws.broadcast("update", {"message": "Starting Tornado Webserver"}) tasks_manager.start_webserver() - def signal_handler(signum, _frame): - if not args.daemon: - print() # for newline after prompt - signame = signal.Signals(signum).name - logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") - Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...") - tasks_manager._main_graceful_exit() - crafty_prompt.universal_exit() - signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGINT, signal_handler) @@ -198,124 +370,24 @@ if __name__ == "__main__": ws.broadcast("update", {"message": "Initializing Servers"}) controller.servers.init_all_servers() - def tasks_starter(): - # start stats logging - tasks_manager.start_stats_recording() - - # once the controller is up and stats are logging, we can kick off - # the scheduler officially - tasks_manager.start_scheduler() - - # refresh our cache and schedule for every 12 hoursour cache refresh - # for serverjars.com - tasks_manager.serverjar_cache_refresher() - + # start up our tasks handler in tasks.py tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter") - def internet_check(): - print() - logger.info("Checking Internet. This may take a minute.") - Console.info("Checking Internet. This may take a minute.") - - if not helper.check_internet(): - logger.warning( - "We have detected the machine running Crafty has no " - "connection to the internet. Client connections to " - "the server may be limited." - ) - Console.warning( - "We have detected the machine running Crafty has no " - "connection to the internet. Client connections to " - "the server may be limited." - ) - + # check to see if instance has internet ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") - def controller_setup(): - if not controller.check_system_user(): - controller.add_system_user() - - if getattr(sys, "frozen", False): - application_path = os.path.dirname(sys.executable) - running_mode = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - application_path = os.path.dirname(app_full_path) - running_mode = "Non-interactive (e.g. 'python main.py')" - except NameError: - application_path = os.getcwd() - running_mode = "Interactive" - - controller.set_project_root(application_path) - master_server_dir = controller.management.get_master_server_dir() - if master_server_dir == "": - logger.debug("Could not find master server path. Setting default") - controller.set_master_server_dir( - os.path.join(controller.project_root, "servers") - ) - else: - helper.servers_dir = master_server_dir - - Console.debug(f"Execution Mode: {running_mode}") - Console.debug(f"Application path : '{application_path}'") - - controller.clear_support_status() - + # start the Crafty console. crafty_prompt = MainPrompt( helper, tasks_manager, migration_manager, controller, import3 ) + # set up all controllers controller_setup_thread = Thread(target=controller_setup, name="controller_setup") - def setup_starter(): - if not args.daemon: - time.sleep(0.01) # Wait for the prompt to start - print() # Make a newline after the prompt so logs are on an empty line - else: - time.sleep(0.01) # Wait for the daemon info message + setup_starter_thread = Thread(target=setup_starter, name="setup_starter") - Console.info("Setting up Crafty's internal components...") - ws.broadcast("update", {"message": "Final checks and launching"}) - # Start the setup threads - tasks_starter_thread.start() - internet_check_thread.start() - controller_setup_thread.start() - - # Wait for the setup threads to finish - tasks_starter_thread.join() - internet_check_thread.join() - controller_setup_thread.join() - helper.crafty_starting = False - ws.broadcast("send_start_reload", "") - Console.info("Crafty has fully started and is now ready for use!") - - # Check if new version available - remote_ver = helper.check_remote_version() - if remote_ver: - notice = f""" - A new version of Crafty is available! - {'/' * 37} - New version available: {remote_ver} - Current version: {pkg_version.parse(helper.get_version_string())} - {'/' * 37} - """ - Console.yellow(notice) - - crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > " - try: - logger.info("Removing old temp dirs") - FileHelpers.del_dirs(os.path.join(controller.project_root, "temp")) - except: - logger.info("Did not find old temp dir.") - os.mkdir(os.path.join(controller.project_root, "temp")) - - if not args.daemon: - # Put the prompt under the cursor - crafty_prompt.print_prompt() - - Thread(target=setup_starter, name="setup_starter").start() + setup_starter_thread.start() if not args.daemon: # Start the Crafty prompt From c13ebeaf51f306b08d8788c38f4b6dfdd655f801 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 21:04:02 -0500 Subject: [PATCH 54/98] Use constant variables for run mode and app path --- main.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index 151c6364..a1dee302 100644 --- a/main.py +++ b/main.py @@ -20,6 +20,18 @@ from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() +# Get the path our application is running on. +if getattr(sys, "frozen", False): + APPLICATION_PATH = os.path.dirname(sys.executable) + RUNNING_MODE = "Frozen/executable" +else: + try: + app_full_path = os.path.realpath(__file__) + APPLICATION_PATH = os.path.dirname(app_full_path) + RUNNING_MODE = "Non-interactive (e.g. 'python main.py')" + except NameError: + APPLICATION_PATH = os.getcwd() + RUNNING_MODE = "Interactive" if helper.check_root(): Console.critical( "Root detected. Root/Admin access denied. " @@ -84,19 +96,7 @@ def controller_setup(): if not controller.check_system_user(): controller.add_system_user() - if getattr(sys, "frozen", False): - application_path = os.path.dirname(sys.executable) - running_mode = "Frozen/executable" - else: - try: - app_full_path = os.path.realpath(__file__) - application_path = os.path.dirname(app_full_path) - running_mode = "Non-interactive (e.g. 'python main.py')" - except NameError: - application_path = os.getcwd() - running_mode = "Interactive" - - controller.set_project_root(application_path) + controller.set_project_root(APPLICATION_PATH) master_server_dir = controller.management.get_master_server_dir() if master_server_dir == "": logger.debug("Could not find master server path. Setting default") @@ -106,8 +106,8 @@ def controller_setup(): else: helper.servers_dir = master_server_dir - Console.debug(f"Execution Mode: {running_mode}") - Console.debug(f"Application path : '{application_path}'") + Console.debug(f"Execution Mode: {RUNNING_MODE}") + Console.debug(f"Application path : '{APPLICATION_PATH}'") controller.clear_support_status() From aa0eee68e3117abebce46166e1ecb030f2b85a1a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 21:21:28 -0500 Subject: [PATCH 55/98] Remove border line on loadup --- app/frontend/templates/panel/loading.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html index 1caeca5f..7445f254 100644 --- a/app/frontend/templates/panel/loading.html +++ b/app/frontend/templates/panel/loading.html @@ -7,7 +7,7 @@ {% block content %}
    -
    +
    From 74c0e8b23ac4c09b50364ca03ac23364add7e54f Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 21:30:29 -0500 Subject: [PATCH 56/98] Fix bug where su cannot edit general user password --- app/classes/web/routes/api/users/user/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index d416e800..1b7f6f91 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -215,7 +215,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler): user_obj = HelperUsers.get_user_model(user_id) if "password" in data and str(user["user_id"]) != str(user_id): - if str(user["user_id"]) != str(user_obj.manager): + if str(user["user_id"]) != str(user_obj.manager) and not user["superuser"]: # TODO: edit your own password return self.finish_json( 400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"} From 3840cc3863e478a46f929f1b8e7ec07e5eaa5145 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 29 Nov 2023 22:28:18 -0500 Subject: [PATCH 57/98] Fix bug where no file error doesn't show up --- app/classes/web/routes/api/crafty/imports/index.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/classes/web/routes/api/crafty/imports/index.py b/app/classes/web/routes/api/crafty/imports/index.py index e6c8c548..2aca2fa9 100644 --- a/app/classes/web/routes/api/crafty/imports/index.py +++ b/app/classes/web/routes/api/crafty/imports/index.py @@ -7,6 +7,7 @@ from jsonschema.exceptions import ValidationError from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.shared.helpers import Helpers from app.classes.web.base_api_handler import BaseApiHandler +from app.classes.web.websocket_handler import WebSocketManager logger = logging.getLogger(__name__) files_get_schema = { @@ -73,7 +74,7 @@ class ApiImportFilesIndexHandler(BaseApiHandler): else: if user_id: user_lang = self.controller.users.get_user_lang_by_id(user_id) - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { @@ -85,7 +86,7 @@ class ApiImportFilesIndexHandler(BaseApiHandler): else: if not self.helper.check_path_exists(folder) and user_id: user_lang = self.controller.users.get_user_lang_by_id(user_id) - self.helper.websocket_helper.broadcast_user( + WebSocketManager().broadcast_user( user_id, "send_start_error", { From 999460f95e36c317b142824b846ed92829b2fd5b Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 00:55:19 -0500 Subject: [PATCH 58/98] Del redundant statements (webserver not started) appease the linter --- main.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/main.py b/main.py index b997bba8..8851c75f 100644 --- a/main.py +++ b/main.py @@ -16,10 +16,13 @@ from app.classes.shared.helpers import Helpers 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.translation import Translation from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() +translation = Translation(helper) +HOST_LANG = helper.get_setting("language") # Get the path our application is running on. if getattr(sys, "frozen", False): APPLICATION_PATH = os.path.dirname(sys.executable) @@ -96,7 +99,6 @@ def controller_setup(): if not controller.check_system_user(): controller.add_system_user() - controller.set_project_root(APPLICATION_PATH) master_server_dir = controller.management.get_master_server_dir() if master_server_dir == "": logger.debug("Could not find master server path. Setting default") @@ -196,18 +198,34 @@ def setup_starter(): time.sleep(0.01) # Wait for the daemon info message Console.info("Setting up Crafty's internal components...") - ws.broadcast("update", {"message": "Final checks and launching"}) # Start the setup threads + web_sock.broadcast( + "update", {"message": translation.translate("startup", "tasks", HOST_LANG)} + ) + time.sleep(2) tasks_starter_thread.start() + web_sock.broadcast( + "update", {"message": translation.translate("startup", "internet", HOST_LANG)} + ) + time.sleep(2) internet_check_thread.start() + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "internals", HOST_LANG)}, + ) + time.sleep(2) controller_setup_thread.start() # Wait for the setup threads to finish + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "almost", HOST_LANG)}, + ) tasks_starter_thread.join() internet_check_thread.join() controller_setup_thread.join() helper.crafty_starting = False - ws.broadcast("send_start_reload", "") + web_sock.broadcast("send_start_reload", "") do_version_check() Console.info("Crafty has fully started and is now ready for use!") @@ -297,8 +315,7 @@ if __name__ == "__main__": helper.ensure_logging_setup() helper.crafty_starting = True # Init WebSocket Manager Here - ws = WebSocketManager() - ws.broadcast("update", {"message": "Setting Up Logging"}) + web_sock = WebSocketManager() setup_logging(debug=args.verbose) if args.verbose: Console.level = "debug" @@ -311,19 +328,13 @@ if __name__ == "__main__": # print our pretty start message do_intro() - - ws.broadcast("update", {"message": "Securing Session For Process"}) # our session file, helps prevent multiple controller agents on the same machine. helper.create_session_file(ignore=args.ignore) - - ws.broadcast("update", {"message": "Initializing Database"}) # start the database database = peewee.SqliteDatabase( helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10} ) database_proxy.initialize(database) - - ws.broadcast("update", {"message": "Checking For Database Migrations"}) migration_manager = MigrationManager(database, helper) migration_manager.up() # Automatically runs migrations @@ -335,6 +346,7 @@ if __name__ == "__main__": file_helper = FileHelpers(helper) import_helper = ImportHelpers(helper, file_helper) controller = Controller(database, helper, file_helper, import_helper) + controller.set_project_root(APPLICATION_PATH) tasks_manager = TasksManager(helper, controller, file_helper) import3 = Import3(helper, controller) FRESH_INSTALL = installer.is_fresh_install() @@ -366,7 +378,6 @@ if __name__ == "__main__": Console.info("Remote change complete.") # startup the web server - ws.broadcast("update", {"message": "Starting Tornado Webserver"}) tasks_manager.start_webserver() signal.signal(signal.SIGTERM, signal_handler) @@ -375,14 +386,16 @@ if __name__ == "__main__": # init servers logger.info("Initializing all servers defined") Console.info("Initializing all servers defined") - ws.broadcast("update", {"message": "Initializing Servers"}) + web_sock.broadcast( + "update", + {"message": translation.translate("startup", "serverInit", HOST_LANG)}, + ) controller.servers.init_all_servers() # start up our tasks handler in tasks.py tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter") # check to see if instance has internet - ws.broadcast("update", {"message": "Checking For Internet"}) internet_check_thread = Thread(target=internet_check, name="internet_check") # start the Crafty console. From 90ed32738f77a28b5b7adf071c62d87c093b5735 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 00:55:26 -0500 Subject: [PATCH 59/98] Add translations --- app/classes/controllers/servers_controller.py | 9 +++++++-- app/translations/en_EN.json | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 5c4df789..57b3348d 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -13,6 +13,7 @@ from app.classes.shared.server import ServerInstance from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.main_models import DatabaseShortcuts +from app.classes.shared.translation import Translation from app.classes.minecraft.stats import Stats @@ -39,6 +40,7 @@ class ServersController(metaclass=Singleton): self.stats = Stats(self.helper, self) self.ws = WebSocketManager() self.server_subpage = {} + self.translation = Translation(self.helper) # ********************************************************************************** # Generic Servers Methods @@ -172,10 +174,13 @@ class ServersController(metaclass=Singleton): def init_all_servers(self): servers = self.get_all_defined_servers() self.failed_servers = [] - + init_trans = self.translation.translate( + "startup", "server", self.helper.get_setting("language") + ) for server in servers: self.ws.broadcast_to_admins( - "update", {"message": f"Initializing {server['server_name']}."} + "update", + {"message": f"{init_trans}{server['server_name']}."}, ) server_id = server.get("server_id") diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 6db96dfc..c1168973 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -590,6 +590,14 @@ "newServer": "Create New Server", "servers": "Servers" }, + "startup": { + "serverInit": "Initializing Servers", + "server": "Initializing ", + "internet": "Checking for internet connection", + "tasks": "Starting Tasks Scheduler", + "internals": "Configuring and starting Crafty's internal componenets", + "almost": "Finishing up. Hang on tight..." + }, "userConfig": { "apiKey": "API Keys", "auth": "Authorized? ", @@ -655,4 +663,4 @@ "webhook_body": "Webhook Body", "webhooks": "Webhooks" } -} +} \ No newline at end of file From fb41bc244388ba75cefa2d81d356e5ee25b1b45a Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 01:05:00 -0500 Subject: [PATCH 60/98] Prevent stack for general user on boot --- app/classes/web/panel_handler.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index e285d4e5..8ac827c3 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -210,6 +210,8 @@ class PanelHandler(BaseHandler): error = self.get_argument("error", "WTF Error!") template = "panel/denied.html" + if self.helper.crafty_starting: + page = "loading" now = time.time() formatted_time = str( @@ -243,9 +245,13 @@ class PanelHandler(BaseHandler): for r in exec_user["roles"]: role = self.controller.roles.get_role(r) exec_user_role.add(role["role_name"]) - defined_servers = self.controller.servers.get_authorized_servers( - exec_user["user_id"] - ) + # get_auth_servers will throw an exception if run while Crafty is starting + if not self.helper.crafty_starting: + defined_servers = self.controller.servers.get_authorized_servers( + exec_user["user_id"] + ) + else: + defined_servers = [] user_order = self.controller.users.get_user_by_id(exec_user["user_id"]) user_order = user_order["server_order"].split(",") From 7a2647c3486465855c470611e19a8e81ef7bd6c6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 01:06:33 -0500 Subject: [PATCH 61/98] Black codebase --- main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.py b/main.py index 94fa7543..11d04c18 100644 --- a/main.py +++ b/main.py @@ -87,9 +87,7 @@ def do_intro(): def setup_logging(debug=True): logging_config_file = os.path.join(app_path, "app", "config", "logging.json") - if not helper.check_file_exists( - os.path.join(app_path, "logs", "auth_tracker.log") - ): + if not helper.check_file_exists(os.path.join(app_path, "logs", "auth_tracker.log")): open( os.path.join(app_path, "logs", "auth_tracker.log"), "a", From b60e6933dd69cd2cb19fe3c2f9a6f06f58c55c3e Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:12:16 -0500 Subject: [PATCH 62/98] Add broadcast to non-admin function --- app/classes/shared/websocket_manager.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/classes/shared/websocket_manager.py b/app/classes/shared/websocket_manager.py index b840ba36..7cda296d 100644 --- a/app/classes/shared/websocket_manager.py +++ b/app/classes/shared/websocket_manager.py @@ -43,6 +43,14 @@ class WebSocketManager(metaclass=Singleton): self.broadcast_with_fn(filter_fn, event_type, data) + def broadcast_to_non_admins(self, event_type: str, data): + def filter_fn(client): + if str(client.get_user_id()) not in str(HelperUsers.get_super_user_list()): + return True + return False + + self.broadcast_with_fn(filter_fn, event_type, data) + def broadcast_page(self, page: str, event_type: str, data): def filter_fn(client): return client.page == page From ca02c9d57590a44f3e460baf9af1080e39546a13 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:14:09 -0500 Subject: [PATCH 63/98] Add user-based translation to startup --- app/classes/controllers/servers_controller.py | 11 +++++------ app/frontend/templates/panel/loading.html | 15 ++++++++++++--- app/translations/en_EN.json | 1 + main.py | 17 +++++------------ 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 57b3348d..10ac0568 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -13,7 +13,6 @@ from app.classes.shared.server import ServerInstance from app.classes.shared.console import Console from app.classes.shared.helpers import Helpers from app.classes.shared.main_models import DatabaseShortcuts -from app.classes.shared.translation import Translation from app.classes.minecraft.stats import Stats @@ -40,7 +39,6 @@ class ServersController(metaclass=Singleton): self.stats = Stats(self.helper, self) self.ws = WebSocketManager() self.server_subpage = {} - self.translation = Translation(self.helper) # ********************************************************************************** # Generic Servers Methods @@ -174,13 +172,14 @@ class ServersController(metaclass=Singleton): def init_all_servers(self): servers = self.get_all_defined_servers() self.failed_servers = [] - init_trans = self.translation.translate( - "startup", "server", self.helper.get_setting("language") - ) for server in servers: self.ws.broadcast_to_admins( "update", - {"message": f"{init_trans}{server['server_name']}."}, + {"section": "server", "server": server["server_name"]}, + ) + self.ws.broadcast_to_non_admins( + "update", + {"section": "init"}, ) server_id = server.get("server_id") diff --git a/app/frontend/templates/panel/loading.html b/app/frontend/templates/panel/loading.html index 7445f254..b4848beb 100644 --- a/app/frontend/templates/panel/loading.html +++ b/app/frontend/templates/panel/loading.html @@ -15,7 +15,13 @@

    -

    Crafty Is Starting

    +

    + {{ translate('startup', 'starting', data['lang']) }}

    @@ -48,8 +54,11 @@ }, 2000); if (webSocket) { webSocket.on('update', function (data) { - console.log("got status") - $("#status").html(data.message); + if ("server" in data) { + $("#status").html(`${$("#status").data(data.section)} ${data.server}...`); + } else { + $("#status").html($("#status").data(data.section)); + } }); webSocket.on('send_start_reload', function () { setTimeout(function () { diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index c1168973..ab4c54ce 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -591,6 +591,7 @@ "servers": "Servers" }, "startup": { + "starting": "Crafty Is Starting...", "serverInit": "Initializing Servers", "server": "Initializing ", "internet": "Checking for internet connection", diff --git a/main.py b/main.py index 8851c75f..cac130c1 100644 --- a/main.py +++ b/main.py @@ -16,13 +16,10 @@ from app.classes.shared.helpers import Helpers 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.translation import Translation from app.classes.shared.websocket_manager import WebSocketManager console = Console() helper = Helpers() -translation = Translation(helper) -HOST_LANG = helper.get_setting("language") # Get the path our application is running on. if getattr(sys, "frozen", False): APPLICATION_PATH = os.path.dirname(sys.executable) @@ -199,19 +196,15 @@ def setup_starter(): Console.info("Setting up Crafty's internal components...") # Start the setup threads - web_sock.broadcast( - "update", {"message": translation.translate("startup", "tasks", HOST_LANG)} - ) + web_sock.broadcast("update", {"section": "tasks"}) time.sleep(2) tasks_starter_thread.start() - web_sock.broadcast( - "update", {"message": translation.translate("startup", "internet", HOST_LANG)} - ) + web_sock.broadcast("update", {"section": "internet"}) time.sleep(2) internet_check_thread.start() web_sock.broadcast( "update", - {"message": translation.translate("startup", "internals", HOST_LANG)}, + {"section": "internals"}, ) time.sleep(2) controller_setup_thread.start() @@ -219,7 +212,7 @@ def setup_starter(): # Wait for the setup threads to finish web_sock.broadcast( "update", - {"message": translation.translate("startup", "almost", HOST_LANG)}, + {"section": "almost"}, ) tasks_starter_thread.join() internet_check_thread.join() @@ -388,7 +381,7 @@ if __name__ == "__main__": Console.info("Initializing all servers defined") web_sock.broadcast( "update", - {"message": translation.translate("startup", "serverInit", HOST_LANG)}, + {"section": "serverInit"}, ) controller.servers.init_all_servers() From 09241bdc54460d3238df4664d4789933e54702a6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:22:40 -0500 Subject: [PATCH 64/98] Appease the linter --- app/classes/controllers/servers_controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 10ac0568..88923194 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -37,7 +37,7 @@ class ServersController(metaclass=Singleton): self.management_helper = management_helper self.servers_list = [] self.stats = Stats(self.helper, self) - self.ws = WebSocketManager() + self.web_sock = WebSocketManager() self.server_subpage = {} # ********************************************************************************** @@ -173,11 +173,11 @@ class ServersController(metaclass=Singleton): servers = self.get_all_defined_servers() self.failed_servers = [] for server in servers: - self.ws.broadcast_to_admins( + self.web_sock.broadcast_to_admins( "update", {"section": "server", "server": server["server_name"]}, ) - self.ws.broadcast_to_non_admins( + self.web_sock.broadcast_to_non_admins( "update", {"section": "init"}, ) From 52f90f1ee3407034dddba7906926e621404891e6 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Thu, 30 Nov 2023 12:32:48 -0500 Subject: [PATCH 65/98] Fix monitored mounts when non selected --- app/frontend/templates/panel/config_json.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/templates/panel/config_json.html b/app/frontend/templates/panel/config_json.html index 5b1f4df8..5736b907 100644 --- a/app/frontend/templates/panel/config_json.html +++ b/app/frontend/templates/panel/config_json.html @@ -170,7 +170,7 @@ {% block js %} -{% end %} \ No newline at end of file +{% end %} From e37bd47806d6841d8cf6f715274677d7c0b9945e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 11:57:56 +0000 Subject: [PATCH 75/98] Update changelog !680 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c8189a5..927b03ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) +- Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From af3ca7aec4ff69d08cb9a9680e83bdb5bc104e15 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 12:36:07 +0000 Subject: [PATCH 76/98] Update changelog !676 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15bd4c39..1c318938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) - Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) +- Fix bug where su cannot edit general user password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/676)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From 66f6786597c2d5616c0e9173af7c100e7742ecfe Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 12:56:55 +0000 Subject: [PATCH 77/98] Update changelog !677 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c318938..3d3753e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) - Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) - Fix bug where su cannot edit general user password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/676)) +- Fix bug where no file error on import root dir ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/677)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From fb7b78abb789206d520c146bbbc9c1c32af9ea5f Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 12:57:51 +0000 Subject: [PATCH 78/98] Fix incorrect changelog order --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d3753e1..427dc97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,14 +10,14 @@ - Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) - Fix bug where su cannot edit general user password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/676)) - Fix bug where no file error on import root dir ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/677)) +- Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) +- Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) -- Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) -- Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) ### Lang - pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

    From e2eb5c62f445498711549ca429b21a53c0c8d321 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 13:07:35 +0000 Subject: [PATCH 79/98] Update changelog !678 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 427dc97e..c1484006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Fix bug where no file error on import root dir ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/677)) - Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) - Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) +- Fix bug where you cannot select "do not monitor mounts" from `config.json` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/678)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From cca4f8314b61c675d9aba043bb814c98031acba3 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 13:31:50 +0000 Subject: [PATCH 80/98] Fix bootbox close function call --- app/frontend/templates/base.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index b046531b..48c6ee95 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -396,8 +396,8 @@ callback: function (result) { if (result){ location.href = "/panel/download_support_package"; - }else { - bootbox.close; + } else { + bootbox.close(); } } }); @@ -608,4 +608,4 @@ - \ No newline at end of file + From eab376420de708ade6630c73e0919b1e9145753e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 13:34:08 +0000 Subject: [PATCH 81/98] Update changelog !679 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1484006..63383b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671)) - Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) - Fix bug where you cannot select "do not monitor mounts" from `config.json` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/678)) +- Fix support log 'x' button still downloading logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/679)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From f16e95acd929678194714e05fd9459e8908b6758 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 13:47:04 +0000 Subject: [PATCH 82/98] Update changelog !681 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63383b45..ccd95863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) +- Give better feedback when backup delete fails ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/681)) ### Lang - pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

    From 56044456d9783b518be41a23eb50c69ea42ce0a1 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 13:53:30 +0000 Subject: [PATCH 83/98] Update changelog !682 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd95863..270dfa9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674)) - Fix bug where you cannot select "do not monitor mounts" from `config.json` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/678)) - Fix support log 'x' button still downloading logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/679)) +- Fix bug where servers are created without bu dir ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/682)) ### Tweaks - Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666)) - Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667)) From 130dc3db02287cf1236f33016f40d1fb1d3082bc Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 15:14:50 +0000 Subject: [PATCH 84/98] Fix logging config path Update the logging configuration path to use the correct application path. --- main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index b98416fe..b61a10fb 100644 --- a/main.py +++ b/main.py @@ -276,9 +276,11 @@ def setup_logging(debug=True): one optional (defaulted to True) parameter which determines whether or not the logging level is "debug" or verbose. """ - logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json") + logging_config_file = os.path.join( + APPLICATION_PATH, "app", "config", "logging.json" + ) if not helper.check_file_exists( - os.path.join(os.path.curdir, "logs", "auth_tracker.log") + os.path.join(APPLICATION_PATH, "logs", "auth_tracker.log") ): open( os.path.join(app_path, "logs", "auth_tracker.log"), From 1e68abac7034bba1f507a9919e394fe86244c375 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 15:32:18 +0000 Subject: [PATCH 85/98] Reduce number of symbols to avoid issues Limit the symbols used in the character string to avoid potential problems with certain characters. --- app/classes/shared/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index f6282ea2..f1f18fa1 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -370,7 +370,7 @@ class Helpers: digits = string.digits locase = string.ascii_lowercase upcase = string.ascii_uppercase - symbols = string.punctuation + symbols = "!@#$%^&*" # Reducing to avoid issues with ([]{}<>,'`) etc # Combine all the character strings above to form one string combo = digits + upcase + locase + symbols From e17af9dea504faca4e6dbe718544778f2a842ccf Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 15:36:07 +0000 Subject: [PATCH 86/98] Increase entropy of password With reduced symbols increasing length for better entropy --- app/classes/shared/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index f1f18fa1..bfe28c51 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -364,7 +364,7 @@ class Helpers: def create_pass(self): # Maximum length of password needed - max_len = 25 + max_len = 64 # Declare string of the character that we need in our password digits = string.digits From 73e2de6c9c2f7cfcb1d26d17161b4fa58036c654 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 7 Dec 2023 10:56:36 -0500 Subject: [PATCH 87/98] Fix fresh install Refactor app_path --- main.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/main.py b/main.py index b61a10fb..75997247 100644 --- a/main.py +++ b/main.py @@ -52,19 +52,6 @@ if not (sys.version_info.major == 3 and sys.version_info.minor >= 9): Console.info("Crafty stopped. Exiting...") sys.exit(0) -# Determine the environment we're running in and set path accordingly -if getattr(sys, "frozen", False): - app_path = os.path.dirname(sys.executable) - RUN_MODE = "Frozen/executable" -else: - try: - app_full_path = os.path.realpath(__file__) - app_path = os.path.dirname(app_full_path) - RUN_MODE = "Non-interactive (e.g. 'python main.py')" - except NameError: - app_path = os.getcwd() - RUN_MODE = "Interactive" - # pylint: disable=wrong-import-position try: from app.classes.models.base_model import database_proxy @@ -283,7 +270,7 @@ def setup_logging(debug=True): os.path.join(APPLICATION_PATH, "logs", "auth_tracker.log") ): open( - os.path.join(app_path, "logs", "auth_tracker.log"), + os.path.join(APPLICATION_PATH, "logs", "auth_tracker.log"), "a", encoding="utf-8", ).close() @@ -354,12 +341,6 @@ if __name__ == "__main__": user_helper = HelperUsers(database, helper) management_helper = HelpersManagement(database, helper) installer = DatabaseBuilder(database, helper, user_helper, management_helper) - file_helper = FileHelpers(helper) - import_helper = ImportHelpers(helper, file_helper) - controller = Controller(database, helper, file_helper, import_helper) - controller.set_project_root(APPLICATION_PATH) - tasks_manager = TasksManager(helper, controller, file_helper) - import3 = Import3(helper, controller) FRESH_INSTALL = installer.is_fresh_install() if FRESH_INSTALL: @@ -373,14 +354,16 @@ if __name__ == "__main__": PASSWORD = helper.create_pass() installer.default_settings(PASSWORD) with open( - os.path.join(app_path, "app", "config", "default-creds.txt"), + os.path.join(APPLICATION_PATH, "app", "config", "default-creds.txt"), "w", encoding="utf-8", ) as cred_file: cred_file.write( json.dumps({"username": "admin", "password": PASSWORD}, indent=4) ) - os.chmod(os.path.join(app_path, "app", "config", "default-creds.txt"), 0o600) + os.chmod( + os.path.join(APPLICATION_PATH, "app", "config", "default-creds.txt"), 0o600 + ) else: Console.debug("Existing install detected") Console.info("Checking for reset secret flag") @@ -392,6 +375,15 @@ if __name__ == "__main__": else: Console.info("No flag found. Secrets are staying") + # now we've initialized our database for fresh install we + # can finishing initializing our controllers/modules + file_helper = FileHelpers(helper) + import_helper = ImportHelpers(helper, file_helper) + controller = Controller(database, helper, file_helper, import_helper) + controller.set_project_root(APPLICATION_PATH) + tasks_manager = TasksManager(helper, controller, file_helper) + import3 = Import3(helper, controller) + # Check to see if client config.json version is different than the # Master config.json in helpers.py Console.info("Checking for remote changes to config.json") From 7037d857d75188ea76bff035039db3ae37b8f5b1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 7 Dec 2023 11:05:26 -0500 Subject: [PATCH 88/98] Black codebase --- app/classes/shared/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index bfe28c51..9c63a323 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -370,7 +370,7 @@ class Helpers: digits = string.digits locase = string.ascii_lowercase upcase = string.ascii_uppercase - symbols = "!@#$%^&*" # Reducing to avoid issues with ([]{}<>,'`) etc + symbols = "!@#$%^&*" # Reducing to avoid issues with ([]{}<>,'`) etc # Combine all the character strings above to form one string combo = digits + upcase + locase + symbols From e247b5c71e5110037f17a50dce129b980e7f3c3f Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 16:43:21 +0000 Subject: [PATCH 89/98] Update changelog !672 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 270dfa9a..3e6cb45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Refactor - Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670)) -- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) +- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) +- Force random password on first run. Stop using common default password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/672)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) - Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) From e3f8531edabaa7fe26056bf24830c438228eddf7 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Thu, 7 Dec 2023 16:44:56 +0000 Subject: [PATCH 90/98] Update changelog !673 Premptive --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e6cb45e..21aadee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Refactor - Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670)) - Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) -- Force random password on first run. Stop using common default password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/672)) +- Force random password on first run. Stop using common default password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/672) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/673)) ### Bug fixes - Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664)) - Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680)) From d2b738f8576c107e804fe10725a7c42986846dca Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 7 Dec 2023 18:02:54 -0500 Subject: [PATCH 91/98] Add queue logging --- app/classes/shared/tasks.py | 9 +++++++++ app/config/logging.json | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index ff20e7ec..f5134661 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -23,6 +23,7 @@ from app.classes.web.tornado_handler import Webserver from app.classes.shared.websocket_manager import WebSocketManager logger = logging.getLogger("apscheduler") +command_log = logging.getLogger("cmd_queue") scheduler_intervals = { "seconds", "minutes", @@ -94,7 +95,15 @@ class TasksManager: def command_watcher(self): while True: # select any commands waiting to be processed + command_log.info( + "Queue currently has " + f"{self.controller.management.command_queue.qsize()} queued commands." + ) if not self.controller.management.command_queue.empty(): + command_log.info( + "Current queued commands: " + f"{list(self.controller.management.command_queue.queue)}" + ) cmd = self.controller.management.command_queue.get() try: svr = self.controller.servers.get_server_instance_by_id( diff --git a/app/config/logging.json b/app/config/logging.json index 9dd09c81..fd1173eb 100644 --- a/app/config/logging.json +++ b/app/config/logging.json @@ -13,6 +13,9 @@ }, "auth": { "format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s" + }, + "cmd_queue": { + "format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s" } }, "handlers": { @@ -59,6 +62,14 @@ "maxBytes": 10485760, "backupCount": 20, "encoding": "utf8" + }, + "cmd_queue_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "formatter": "auth", + "filename": "logs/cmd_queue.log", + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8" } }, "loggers": { @@ -90,6 +101,13 @@ "auth_file_handler" ], "propagate": false + }, + "cmd_queue": { + "level": "INFO", + "handlers": [ + "cmd_queue_file_handler" + ], + "propagate": false } } } \ No newline at end of file From 2cab7de6af554e26eacad1a4eee5897aeae600bd Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 7 Dec 2023 18:03:31 -0500 Subject: [PATCH 92/98] Make watcher log debug --- app/classes/shared/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index f5134661..a5ea32ac 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -95,7 +95,7 @@ class TasksManager: def command_watcher(self): while True: # select any commands waiting to be processed - command_log.info( + command_log.debug( "Queue currently has " f"{self.controller.management.command_queue.qsize()} queued commands." ) From 69fcdfddf04ba03fd0a61a2e9178785032124d18 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Dec 2023 15:15:26 -0500 Subject: [PATCH 93/98] Add json sort script --- .gitignore | 1 + .gitlab/scripts/sort.py | 72 +++++++++++++++++++++++++++++++++++++ app/translations/en_EN.json | 9 +++++ 3 files changed, 82 insertions(+) create mode 100644 .gitlab/scripts/sort.py diff --git a/.gitignore b/.gitignore index 132a2a26..d3b153ad 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ app/config/ docker/* !docker/docker-compose.yml lang_sort_log.txt +lang_sort.txt diff --git a/.gitlab/scripts/sort.py b/.gitlab/scripts/sort.py new file mode 100644 index 00000000..c78885a6 --- /dev/null +++ b/.gitlab/scripts/sort.py @@ -0,0 +1,72 @@ +import json +import os + + +def get_missing_keys_and_values(obj1, obj2, path=None): + if path is None: + path = [] + + missing_keys_and_values = {} + + if isinstance(obj1, dict) and isinstance(obj2, dict): + for key in obj1: + if key not in obj2: + missing_keys_and_values[key] = obj1[key] + elif isinstance(obj1[key], (dict, list)) and isinstance( + obj2[key], (dict, list) + ): + sub_missing = get_missing_keys_and_values( + obj1[key], obj2[key], path + [key] + ) + if sub_missing: + missing_keys_and_values[key] = sub_missing + + return missing_keys_and_values + + +def main(): + project_dir = os.getcwd() + os.chdir("../../app/translations") # Change the working directory + dir_path = os.getcwd() # Get the current working directory + + en_en_path = os.path.join(dir_path, "en_EN.json") + + if not os.path.isfile(en_en_path): + print( + f"The file en_EN.json does not exist in {dir_path}. Ensure you have the right directory, Exiting." + ) + return + + result = {} # JSON object to store missing keys and values + + for root, _, files in os.walk(dir_path): + for file in files: + if ( + "_incomplete" not in file + and file != "en_EN.json" + and file.endswith(".json") + ): + file_path = os.path.join(root, file) + + with open(file_path, "r", encoding="utf-8") as current_file: + current_data = json.load(current_file) + + with open(en_en_path, "r", encoding="utf-8") as en_en_file: + en_en_data = json.load(en_en_file) + + missing_keys_and_values = get_missing_keys_and_values( + en_en_data, current_data + ) + if missing_keys_and_values: + result[file] = missing_keys_and_values + + # Write the JSON object to lang_sort.txt + with open( + os.path.join(project_dir, "lang_sort.txt"), + "w", + encoding="utf-8", + ) as output_file: + json.dump(result, output_file, indent=4) + + +main() diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 7c23f1ab..e1928d4e 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -588,6 +588,15 @@ "newServer": "Create New Server", "servers": "Servers" }, + "startup": { + "starting": "Crafty Is Starting...", + "serverInit": "Initializing Servers", + "server": "Initializing ", + "internet": "Checking for internet connection", + "tasks": "Starting Tasks Scheduler", + "internals": "Configuring and starting Crafty's internal components", + "almost": "Finishing up. Hang on tight..." + }, "userConfig": { "apiKey": "API Keys", "auth": "Authorized? ", From fb66e4fcda0601258ac5cf322b4ac70d9a52be2d Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 11 Dec 2023 15:44:48 -0500 Subject: [PATCH 94/98] Update translations --- app/translations/de_DE.json | 10 ++ app/translations/en_EN.json | 14 +- .../{es_ES.json => es_ES_incomplete.json} | 0 app/translations/fr_FR.json | 10 ++ .../{he_IL_incomplete.json => he_IL.json} | 130 +++++++++++++++++- app/translations/lol_EN.json | 10 ++ app/translations/lv_LV.json | 10 ++ app/translations/nl_BE.json | 10 ++ app/translations/pl_PL.json | 10 ++ app/translations/zh_CN.json | 10 ++ 10 files changed, 205 insertions(+), 9 deletions(-) rename app/translations/{es_ES.json => es_ES_incomplete.json} (100%) rename app/translations/{he_IL_incomplete.json => he_IL.json} (81%) diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index ac18990d..11311bb0 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -111,6 +111,7 @@ "starting": "Verzögerter Start", "status": "Status", "stop": "Stoppen", + "storage": "Speicher", "version": "Version", "welcome": "Willkommen bei Crafty Controller" }, @@ -591,6 +592,15 @@ "newServer": "Neuen Server erstellen", "servers": "Server" }, + "startup": { + "almost": "Nur noch einen Moment, fast geschafft", + "internals": "Crafty's interne Komponneten initialisieren und starten", + "internet": "Verbindung zum Internet überprüfen", + "server": "initialisieren ", + "serverInit": "Server initialisieren", + "starting": "Crafty startet...", + "tasks": "Zeitplan-Aufgaben werden geladen" + }, "userConfig": { "apiKey": "API Schlüssel", "auth": "Autorisiert? ", diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index fc4689f0..4db8be71 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -592,13 +592,13 @@ "servers": "Servers" }, "startup": { - "starting": "Crafty Is Starting...", - "serverInit": "Initializing Servers", - "server": "Initializing ", - "internet": "Checking for internet connection", - "tasks": "Starting Tasks Scheduler", + "almost": "Finishing up. Hang on tight...", "internals": "Configuring and starting Crafty's internal components", - "almost": "Finishing up. Hang on tight..." + "internet": "Checking for internet connection", + "server": "Initializing ", + "serverInit": "Initializing Servers", + "starting": "Crafty Is Starting...", + "tasks": "Starting Tasks Scheduler" }, "userConfig": { "apiKey": "API Keys", @@ -665,4 +665,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_incomplete.json similarity index 100% rename from app/translations/es_ES.json rename to app/translations/es_ES_incomplete.json diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index 33ea5225..112de289 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -111,6 +111,7 @@ "starting": "Démarrage retardé", "status": "Statut", "stop": "Arrêter", + "storage": "Stockage", "version": "Version", "welcome": "Bienvenue sur Crafty Controller" }, @@ -591,6 +592,15 @@ "newServer": "Créer un Nouveau Serveur", "servers": "Serveurs" }, + "startup": { + "almost": "Finalisation. Patienter ...", + "internals": "Configuration et Démarrage des composants internes de Crafty", + "internet": "Vérification de la connexion à Internet", + "server": "Initialisation ", + "serverInit": "Initialisation des Serveurs", + "starting": "Crafty Démarre ...", + "tasks": "Démarrage du planificateur de tâches" + }, "userConfig": { "apiKey": "Clés API", "auth": "Authorisé ? ", diff --git a/app/translations/he_IL_incomplete.json b/app/translations/he_IL.json similarity index 81% rename from app/translations/he_IL_incomplete.json rename to app/translations/he_IL.json index 2c8e0139..73e7032a 100644 --- a/app/translations/he_IL_incomplete.json +++ b/app/translations/he_IL.json @@ -53,6 +53,20 @@ "translationTitle": "שפת התרגום", "translator": "מתרגמים" }, + "customLogin": { + "apply": "החל", + "backgroundUpload": "העלאת רקע", + "customLoginPage": "התאמת דף הכניסה", + "delete": "מחק", + "labelLoginImage": "בחר את רקע כניסתך", + "loginBackground": "תמונת רקע לכניסה", + "loginImage": "העלה תמונת רקע למסך הכניסה.", + "loginOpacity": "בחר את שקיפות חלון הכניסה", + "pageTitle": "דף כניסה מותאם אישית", + "preview": "תצוגה מקדימה", + "select": "בחר", + "selectImage": "בחר תמונה" + }, "dashboard": { "actions": "פעולות", "allServers": "כל השרתים", @@ -75,6 +89,7 @@ "dashboard": "פאנל", "delay-explained": "השירות/סוכן התחיל לאחרונה והוא מעכב את הדלקת שרת המיינקראפט", "host": "אחסון", + "installing": "מתקין...", "kill": "כיבוי מידי", "killing": "...מכבה מידית", "lastBackup": "אחרון:", @@ -96,6 +111,7 @@ "starting": "התחלה בעיכוב", "status": "סטאטוס", "stop": "עצור", + "storage": "שטח אחסון", "version": "גרסה", "welcome": "ברוכים הבאים ל-פאנל קראפטי" }, @@ -164,20 +180,33 @@ } }, "error": { + "agree": "מסכים", + "bedrockError": "הורדות Bedrock אינן זמינות. אנא בדוק", + "cancel": "בטל", "contact": "בבקשה צרו קשר עם תמיכת פאנל קראפטי באמצעות דיסקורד", + "craftyStatus": "דף המצב של Crafty", + "cronFormat": "זוהה פורמט Cron לא תקין", "embarassing": "אוי, טוב, זה מביך.", "error": "שגיאה!", "eulaAgree": "אתם מסכימים?", "eulaMsg": "עליכם להסכים להסכם הרישיון למשתמש הקצה. עותק של הסכם הרישיון למשתמש הקצה של מוג'אנג מקושר תחת הודעה זו.", "eulaTitle": "להסכים להסכם רישיון משתמש קצה של מוג'אנג", + "fileError": "סוג הקובץ חייב להיות תמונה.", "fileTooLarge": "העלאה נכשלה. העלאת הקובץ גדולה מדי. פנה למנהל המערכת לקבלת סיוע.", "hereIsTheError": "הנה השגיאה", + "installerJava": "נכשל בהתקנת {} : התקנות של שרת Forge דורשות Java. זוהה ש-Java אינו מותקן. אנא התקן את Java ואז התקן את השרת.", "internet": "גילינו שלמכונה(מחשב) שמריצה את קראפטי אין חיבור לאינטרנט. חיבורי לקוחות לשרת עשויים להיות מוגבלים.", + "migration": "אחסון השרת הראשי של Crafty מועבר למיקום חדש. כל הפעלות השרתים נעצרו במהלך זמן זה. אנא המתן בזמן שאנו מסיימים את המעבר הזה", "no-file": "נראה שאיננו מצליחים לאתר את הקובץ המבוקש. בדוק שוב את הנתיב. האם ל-קראפטי יש הרשאות מתאימות?", + "noInternet": "Crafty נתקל בבעיות גישה לאינטרנט. יצירת שרתים הושבתה. אנא בדוק את חיבור האינטרנט שלך ורענן את הדף הזה.", "noJava": "השרת {} לא הצליח להתחיל עם קוד השגיאה: גילינו ש-Java אינו מותקן. אנא התקינו את Java ואז הפעילו את השרת.", "not-downloaded": "לא הצלחנו למצוא את קובץ ההפעלה שלך. האם זה סיים להוריד? האם ההרשאות מוגדרות בשביל הפעלה?", "portReminder": "זיהינו שזו הפעם הראשונה ש-{} מופעל. הקפידו להעביר את היציאה {} דרך הנתב/חומת האש שלכם כדי להפוך אותה לנגישה מרחוק מהאינטרנט.", + "privMsg": "וה", + "serverJars1": "API של צנצנות השרת אינו נגיש. אנא בדוק", + "serverJars2": "למידע מעודכן ביותר.", "start-error": "השרת {} לא הצליח להתחיל עם קוד שגיאה: {}", + "superError": "חובה להיות משתמש על כדי לבצע פעולה זו.", "terribleFailure": "איזה כישלון נורא!" }, "footer": { @@ -189,7 +218,8 @@ "forgotPassword": "שכחתי סיסמה", "login": "התחברות", "password": "סיסמה", - "username": "שם משתמש" + "username": "שם משתמש", + "viewStatus": "צפה בדף המצב הציבורי" }, "notify": { "activityLog": "יומני פעילות", @@ -201,24 +231,38 @@ "preparingLogs": "אנא המתינו בזמן שאנו מכינים את היומנים שלכם... נשלח הודעה כשהם יהיו מוכנים. זה עשוי להימשך זמן מה לפריסות שרתים גדולות.", "supportLogs": "יומני תמיכה" }, + "offline": { + "offline": "מנותק", + "pleaseConnect": "אנא חבר לאינטרנט כדי להשתמש ב-Crafty." + }, "panelConfig": { "adminControls": "בקרות מנהל", "allowedServers": "שרתים מורשים", + "apply": "החל", "assignedRoles": "תפקידים שהוקצו", "cancel": "ביטול", "clearComms": "ניקוי פקודות שלא בוצעו", + "custom": "התאמת Crafty", "delete": "מחיקה", "edit": "עריכה", + "enableLang": "אפשר כל השפות", "enabled": "מופעל", + "globalExplain": "היכן Crafty שומר את כל קבצי השרת שלך. (נוסיף את הנתיב עם /servers/[uuid של השרת])", + "globalServer": "תיקיית שרתים גלובלית", + "json": "Config.json", + "match": "הסיסמאות חייבות להתאים", "newRole": "הוספת תפקיד חדש", "newUser": "הוספת משתמש חדש", + "noMounts": "אל תציג נקודות עגינה בלוח המחוונים", "pageTitle": "הגדרת פאנל", "role": "תפקיד", "roleUsers": "תפקידי משתמשים", "roles": "תפקידים", "save": "שמירה", + "select": "בחר", "superConfirm": "המשיכו רק אם אתם רוצים שלמשתמש זה תהיה גישה להכל (כל חשבונות המשתמש, השרתים, הגדרות הפאנל וכו'). הם יכולים אפילו למחוק את זכויות משתמש העל שלך.", "superConfirmTitle": "להפעיל משתמש-על? האם אתם בטוחים?", + "title": "תצורת Crafty", "user": "משתמש", "users": "משתמשים" }, @@ -242,14 +286,17 @@ "roleTitle": "הגדרות תפקידים", "roleUserName": "שם משתמש", "roleUsers": "תפקידי המשתמשים: ", + "selectManager": "בחר מנהל לתפקיד זה", "serverAccess": "?גישה", "serverName": "שם שרת", "serversDesc": "לשרתים מותר לגשת לתפקיד זה" }, "serverBackups": { + "after": "הרץ פקודה לאחר הגיבוי", "backupAtMidnight": "גיבוי אוטומטי בחצות?", "backupNow": "!גיבוי עכשיו", "backupTask": "החלה משימת גיבוי.", + "before": "הרץ פקודה לפני הגיבוי", "cancel": "לבטל", "clickExclude": "לחצו כדי לבחור מה לא יהיה בגיבוי", "compress": "דחוס גיבוי", @@ -289,6 +336,8 @@ "deleteServerQuestionMessage": "האם אתם בטוחים שברצונכם למחוק את השרת הזה? אחרי זה אין דרך חזרה...", "exeUpdateURL": "כתובת ה-URL של עדכון השרת הניתן להפעלה", "exeUpdateURLDesc": "כתובת אתר להורדה ישירה לקבלת עדכונים.", + "ignoredExits": "קודי יציאה של קריסה שלא ישמעו", + "ignoredExitsExplain": "קודי יציאה שגילוי הקריסות של Crafty צריך להתעלם מהם כמו 'עצירה' רגילה (מופרדים בפסיקים)", "javaNoChange": "לא למחוק", "javaVersion": "כן למחוק את גרסאת ה-Java המותקנת כרגע", "javaVersionDesc": "אם אתה מתכוון לעקוף את Java, ודא שנתיב ה-Java הנוכחי שלך ב'פקודה לביצוע' עטוף במרכאות (משתנה ברירת המחדל 'java' לא נכלל)", @@ -319,7 +368,13 @@ "serverPortDesc": "קראפטי צריך פורט בשביל להתחבר לנתונים סטטיסטיים", "serverStopCommand": "פקודת עצירת שרת", "serverStopCommandDesc": "פקודה לשלוח את התוכנית כדי לעצור אותה", + "showStatus": "הצג בדף המצב הציבורי", + "shutdownTimeout": "זמן קצוב לכיבוי", + "statsHint1": "הפורט שבו השרת שלך פועל צריך להיות כאן. זה רק איך Crafty פותח חיבור לשרת שלך לצורך סטטיסטיקות.", + "statsHint2": "זה לא משנה את פורט השרת שלך. עדיין צריך לשנות את הפורט בקובץ התצורה של השרת שלך.", "stopBeforeDeleting": "בבקשה לעצור את השרת לפני מחיקתו", + "timeoutExplain1": "כמה זמן Crafty יחכה לשרת שלך להיכבות לאחר ביצוע ה", + "timeoutExplain2": "פקודה לפני שהוא יכריח את התהליך לרדת.", "update": "עדכנו את קובץ ההפעלה", "yesDelete": "כן, למחוק", "yesDeleteFiles": "כן, מחק קבצים" @@ -345,8 +400,12 @@ "backup": "גיבוי", "config": "הגדרות", "files": "קבצים", + "filter": "סינון יומנים", + "filterList": "מילים מסוננות", "logs": "לוג", + "metrics": "מדדים", "playerControls": "ניהול שחקנים", + "reset": "אפס גלילה", "schedule": "לוח זמנים", "serverDetails": "פרטי שרת", "terminal": "מסוף פקודות" @@ -383,6 +442,11 @@ "waitUpload": "אנא המתן בזמן שאנו מעלים את הקבצים שלך... זה עשוי לקחת זמן מה.", "yesDelete": "כן, אני מבין.ה את ההשלכות" }, + "serverMetrics": { + "resetZoom": "אפס זום", + "zoomHint1": "כדי להגדיל על הגרף החזק את מקש ה-shift והשתמש בגלגלת הגלילה שלך.", + "zoomHint2": "חלופית, החזק את מקש ה-shift ולחץ וגרור את האזור שברצונך להגדיל עליו." + }, "serverPlayerManagement": { "bannedPlayers": "שחקנים בבאן", "loadingBannedPlayers": "טוען שחקנים שהם בבאן", @@ -410,18 +474,36 @@ "parent-explain": "איזה לוח זמנים צריך להפעיל את זה?", "reaction": "תגובה", "restart": "הפעלה מחדש", + "select": "בחר בסיסי / Cron / תגובת שרשרת", "start": "הדלקת שרת", "stop": "כיבוי שרת", "time": "זמן", "time-explain": "באיזו שעה אתה רוצה שהלוח שלך יתבצע?" }, "serverSchedules": { + "action": "פעולה", "areYouSure": "למחוק משימה מתוזמנת?", "cancel": "לבטל", "cannotSee": "לא רואים הכל?", "cannotSeeOnMobile": "נסה ללחוץ על משימה מתוזמנת לפרטים מלאים.", + "child": "ילד של לוח זמנים עם מזהה ", + "close": "סגור", + "command": "פקודה", "confirm": "אישור", - "confirmDelete": "האם ברצונך למחוק את המשימה המתוזמנת הזו? אי אפשר לבטל את זה." + "confirmDelete": "האם ברצונך למחוק את המשימה המתוזמנת הזו? אי אפשר לבטל את זה.", + "create": "צור לוח זמנים חדש", + "cron": "מחרוזת Cron", + "delete": "מחק", + "details": "פרטי לוח זמנים", + "edit": "ערוך", + "enabled": "מופעל", + "every": "כל", + "interval": "מרווח", + "name": "שם", + "nextRun": "הריצה הבאה", + "no": "לא", + "scheduledTasks": "משימות מתוזמנות", + "yes": "כן" }, "serverStats": { "cpuUsage": "שימוש במעבד", @@ -444,6 +526,8 @@ "commandInput": "הקלידו את הפקודה שלכם", "delay-explained": "השירות/סוכן התחיל לאחרונה והוא מעכב את הדלקת שרת המיינקראפט", "downloading": "...מוריד", + "importing": "מייבא...", + "installing": "מתקין...", "restart": "הפעלה מחדש", "sendCommand": "שליחת פקודה", "start": "התחלה", @@ -468,6 +552,7 @@ "importServerButton": "ייבוא שרת!", "importZip": "ייבוא מקובץ Zip", "importing": "מייבא שרת...", + "labelZipFile": "בחר את קובץ ה-Zip שלך", "maxMem": "מקסימום זיכרון", "minMem": "מינימום זיכרון", "myNewServer": "השרת החדש שלי", @@ -478,6 +563,7 @@ "save": "שמור", "selectRole": "בחר תפקידים", "selectRoot": "בחר ארכיון שורש Dir", + "selectServer": "בחר שרת", "selectType": "בחר סוג", "selectVersion": "בחר גרסה", "selectZipDir": "בחר את הספרייה בארכיון שממנו אתה רוצה שנפתח קבצים", @@ -485,9 +571,13 @@ "serverName": "שם השרת", "serverPath": "נתיב שרת", "serverPort": "פורט שרת", + "serverSelect": "בחירת שרת", "serverType": "סוג השרת", + "serverUpload": "העלה שרת מכווץ", "serverVersion": "גרסת השרת", "sizeInGB": "גודל ב-GB", + "uploadButton": "העלה", + "uploadZip": "העלה קובץ Zip לייבוא שרת", "zipPath": "נתיב שרת" }, "sidebar": { @@ -495,10 +585,20 @@ "credits": "קרדיט", "dashboard": "פאנל", "documentation": "ויקיפדייה", + "inApp": "מסמכים באפליקציה", "navigation": "ניווט", "newServer": "צור שרת חדש", "servers": "שרתים" }, + "startup": { + "almost": "מסיימים. תחזיקו חזק...", + "internals": "הגדרה והפעלה של הרכיבים הפנימיים של Crafty", + "internet": "בודק את חיבור האינטרנט", + "server": "אתחול ", + "serverInit": "מפעיל שרתים", + "starting": "מתחילים בהפעלת מערכת Crafty...", + "tasks": "מתזמן את מחולל המשימות" + }, "userConfig": { "apiKey": "API מפתחות", "auth": "מורשה? ", @@ -519,6 +619,7 @@ "lastLogin": "כניסה אחרונה: ", "lastUpdate": "עדכון אחרון: ", "leaveBlank": "כדי לערוך משתמש מבלי לשנות סיסמה השאר אותו ריק.", + "manager": "מנהל", "member": "חבר?", "notExist": "אתה לא יכול למחוק משהו שלא קיים!", "pageTitle": "ערוך משתמש", @@ -527,6 +628,7 @@ "permName": "שם הרשאה", "repeat": "חזור על הסיסמה", "roleName": "שם התפקיד", + "selectManager": "בחר מנהל למשתמש", "super": "משתמש על", "userLang": "שפת משתמש", "userName": "שם משתמש", @@ -534,6 +636,30 @@ "userRoles": "תפקידי משתמש", "userRolesDesc": "תפקידים שמשתמש זה חבר בהם", "userSettings": "הגדרות משתמש", + "userTheme": "ערכת נושא UI", "uses": "מספר השימושים המותרים (-1==ללא הגבלה)" + }, + "webhooks": { + "areYouSureDel": "האם אתה בטוח שברצונך למחוק את ה-Webhook הזה?", + "areYouSureRun": "האם אתה בטוח שברצונך לבדוק את ה-Webhook הזה?", + "backup_server": "גיבוי השרת הושלם", + "bot_name": "שם הבוט", + "color": "בחר גוון צבע", + "crash_detected": "השרת קרס", + "edit": "ערוך", + "enabled": "מופעל", + "jar_update": "השרת הביצועי עודכן", + "kill": "השרת נסגר", + "name": "שם", + "new": "Webhook חדש", + "run": "הרץ Webhook לבדיקה", + "send_command": "פקודת שרת התקבלה", + "start_server": "השרת הופעל", + "stop_server": "השרת נעצר", + "trigger": "גרם", + "type": "סוג ה-Webhook", + "url": "URL של ה-Webhook", + "webhook_body": "גוף ה-Webhook", + "webhooks": "Webhooks" } } diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index 8c2436cb..a2368a84 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -111,6 +111,7 @@ "starting": "I WAITZ B4 I START", "status": "STATUZ", "stop": "STAHP PLZ", + "storage": "STASH BOX", "version": "VERZHUN", "welcome": "WELCOM 2 CWAFTY CONTROLLR" }, @@ -591,6 +592,15 @@ "newServer": "CONSTWUCT A SERVR", "servers": "SERVRS" }, + "startup": { + "almost": "ALMOST DUN. HOLD ON TO YER WHISKERS...", + "internals": "SETTIN' UP AN' STARTIN' CWAFTY'S INSIDE BITZ", + "internet": "LOOKIN' FOR OUTER SPACE TALKY", + "server": "WAKIN' UPZ ", + "serverInit": "MAKIN' SERVERZ READY", + "starting": "CWAFTY WAKING UP...", + "tasks": "STARTIN' TASK SCHEDULERZ" + }, "userConfig": { "apiKey": "API KEYS", "auth": "PERMISHUN TO ACESS? ", diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index 15ecf869..95d2ffae 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -112,6 +112,7 @@ "starting": "Aizkavēts-Starts", "status": "Statuss", "stop": "Apturēt", + "storage": "Glabātuve", "version": "Versija", "welcome": "Esiet sveicināts Crafty Controller" }, @@ -592,6 +593,15 @@ "newServer": "Izveidot Jaunu Serveri", "servers": "Serveri" }, + "startup": { + "almost": "Pabeidz. Vēl tik nedaudz...", + "internals": "Konfigurē un Startē Crafty iekšējās komponenetes", + "internet": "Pārbauda interneta savienojumu", + "server": "Inicializē ", + "serverInit": "Inicializē Serverus", + "starting": "Crafty Startējas...", + "tasks": "Sāknē Notikumu Plānotāju" + }, "userConfig": { "apiKey": "API Atslēgas", "auth": "Authorizēts? ", diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index 81d455ed..bed0f894 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -111,6 +111,7 @@ "starting": "Vertraagde start", "status": "Toestand", "stop": "Stoppen", + "storage": "Opslagruimte", "version": "Versie", "welcome": "Welkom bij Crafty Controller " }, @@ -591,6 +592,15 @@ "newServer": "Nieuwe server maken", "servers": "Servers" }, + "startup": { + "almost": "De laatste hand leggen. Houd je vast...", + "internals": "Crafty's interne componenten configureren en starten", + "internet": "Controleren op internetverbinding", + "server": "Initialiseren ", + "serverInit": "Servers initialiseren", + "starting": "Crafty wordt gestart...", + "tasks": "Start takenplanner" + }, "userConfig": { "apiKey": "API Sleutels", "auth": "Bevoegd? ", diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 2c60c6df..2b9ce56d 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -111,6 +111,7 @@ "starting": "Opóźniony-Start", "status": "Status", "stop": "Zatrzymaj", + "storage": "Przestrzeń dyskowa", "version": "Wersja", "welcome": "Witamy w Crafty Controller" }, @@ -590,6 +591,15 @@ "newServer": "Stwórz nowy serwer", "servers": "Serwery" }, + "startup": { + "almost": "Prawie gotowe! Jeszcze tylko chwilka...", + "internals": "Konfigurowanie i włączanie backendu...", + "internet": "Sprawdzam połączenie z internetem", + "server": "Włączanie ", + "serverInit": "Ładuje serwery...", + "starting": "Włączam Craftiego...", + "tasks": "Włączanie harmonogramu..." + }, "userConfig": { "apiKey": "Klucze API", "auth": "Autoryzacja? ", diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index e905a406..b8689b2a 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -111,6 +111,7 @@ "starting": "延迟启动", "status": "状态", "stop": "停止", + "storage": "存储", "version": "版本", "welcome": "欢迎来到 Crafty Controller" }, @@ -591,6 +592,15 @@ "newServer": "创建新服务器", "servers": "服务器" }, + "startup": { + "almost": "即将完成。请稍候……", + "internals": "正在配置并启动 Crafty 的内部组件", + "internet": "正在检查网络连接", + "server": "正在初始化 ", + "serverInit": "正在初始化服务器", + "starting": "Crafty 正在启动……", + "tasks": "正在启动任务计划器" + }, "userConfig": { "apiKey": "API 密钥", "auth": "已授权?", From 717f121f1fa7fde06ef37e685263b145b709e932 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 13 Dec 2023 01:32:28 +0000 Subject: [PATCH 95/98] Update changelog !683 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21aadee9..b30fdc84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669)) - Give better feedback when backup delete fails ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/681)) +- Add user queue debug logging ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/683)) ### Lang - pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

    From 1c65cf593791034508c17790c1e634a4ec1c3f3e Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 13 Dec 2023 01:49:49 +0000 Subject: [PATCH 96/98] Update changelog !684 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b30fdc84..e9f8875f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ - Give better feedback when backup delete fails ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/681)) - Add user queue debug logging ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/683)) ### Lang +- Update `de_DE, en_EN, fr_FR, lol_EN, lv_LV, nl_BE, pl_PL, zh_CN` translations for `4.2.2` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/684)) +- Mark `es_ES` as incomplete ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/684)) +- Mark `he_IL` as active ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/684)) - pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))

    From 148b349e2ac36c411c586ee65d4d3d428d5f7819 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 13 Dec 2023 02:01:04 +0000 Subject: [PATCH 97/98] Close changelog v4.2.2 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9f8875f..1c0dab4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -## --- [4.2.2] - 2023/TBD +## --- [4.2.2] - 2023/12/13 ### New features - Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) ### Refactor From 1d32afa09d7f1a509456e4f4e08fac9beb9f4731 Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 13 Dec 2023 02:17:54 +0000 Subject: [PATCH 98/98] Prepare 4.2.3 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 1c0dab4f..71b8b4b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog +## --- [4.2.3] - 2023/TBD +### New features +TBD +### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

    + ## --- [4.2.2] - 2023/12/13 ### New features - Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668)) diff --git a/README.md b/README.md index 0a94b6cb..7e9ab498 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.2 +# Crafty Controller 4.2.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 e629f1ba..bb776b5b 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 2, - "sub": 2 + "sub": 3 } diff --git a/sonar-project.properties b/sonar-project.properties index 938b710f..68eafe21 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.2 +sonar.projectVersion=4.2.3 sonar.python.version=3.9, 3.10, 3.11 sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**