From cd93b468b35e5293c330f5e7b60afde4e09b5ace Mon Sep 17 00:00:00 2001 From: Zedifus Date: Sun, 23 Oct 2022 21:24:10 +0100 Subject: [PATCH 01/22] Prepare 4.0.17 release base Also remove badges from README, as gitlab already handles this in a different section --- CHANGELOG.md | 11 +++++++++++ README.md | 9 +-------- app/config/version.json | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cab7514..3f81a9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog +## --- [4.0.17] - 2022/TBD +### New features +TBD +### Bug fixes +TBD +### Tweaks +TBD +### Lang +TBD +

+ ## --- [4.0.16] - 2022/10/23 ### New features - Automatically set update url for (new) server creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/487)) diff --git a/README.md b/README.md index babfc564..5ae83d5f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,5 @@ [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) - -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org) -[![Version](https://img.shields.io/badge/release-v4.0.16-blue)](https://gitlab.com/crafty-controller/crafty-4/-/releases) -[![Code Quality](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4) -[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) -[![Licence](https://img.shields.io/gitlab/license/20430749)](https://gitlab.com/crafty-controller/crafty-4/-/blob/master/LICENSE) -# Crafty Controller 4.0.16 +# Crafty Controller 4.0.17 > 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 2dece081..f97d9f2b 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 0, - "sub": 16 + "sub": 17 } From a66b95e6079afe2ffa0ad83e51037032ed82ddfa Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 28 Oct 2022 22:16:10 -0400 Subject: [PATCH 02/22] Fix no port on bedrock server creation --- app/classes/web/server_handler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index d60ce2a2..08162365 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -457,6 +457,9 @@ class ServerHandler(BaseHandler): server = bleach.clean(self.get_argument("server", "")) server_name = bleach.clean(self.get_argument("server_name", "")) port = bleach.clean(self.get_argument("port", "")) + + if not port: + port = 19132 if int(port) < 1 or int(port) > 65535: self.redirect( "/panel/error?error=Constraint Error: " From bf0255a26b79fb78ed300365fcefeef0d7e3f324 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Wed, 9 Nov 2022 14:13:35 -0500 Subject: [PATCH 03/22] Add custom login image feature --- .gitignore | 1 + .../controllers/management_controller.py | 8 ++ app/classes/models/management.py | 14 +++ app/classes/shared/main_controller.py | 1 + app/classes/web/ajax_handler.py | 32 +++++ app/classes/web/panel_handler.py | 19 +++ app/classes/web/public_handler.py | 1 + app/classes/web/upload_handler.py | 79 +++++------- app/frontend/static/assets/css/dark/style.css | 5 - .../static/assets/css/shared/style.css | 5 - .../templates/panel/panel_config.html | 114 ++++++++++++++++++ .../templates/panel/panel_edit_user.html | 2 +- app/frontend/templates/public/login.html | 5 + app/migrations/20221109_login_photo.py | 18 +++ app/translations/en_EN.json | 7 +- 15 files changed, 249 insertions(+), 62 deletions(-) create mode 100644 app/migrations/20221109_login_photo.py diff --git a/.gitignore b/.gitignore index b7f2d2b3..7f3ad858 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ venv.bak/ .idea/ /imports/ /servers/ +/app/frontend/static/assets/images/auth/custom/ /backups/ /temp/ /docker/servers/ diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 547a8ebd..47860fe1 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -94,6 +94,14 @@ class ManagementController: def delete_scheduled_task(schedule_id): return HelpersManagement.delete_scheduled_task(schedule_id) + @staticmethod + def set_login_image(path): + HelpersManagement.set_login_image(path) + + @staticmethod + def get_login_image(): + return HelpersManagement.get_login_image() + @staticmethod def update_scheduled_task(schedule_id, updates): return HelpersManagement.update_scheduled_task(schedule_id, updates) diff --git a/app/classes/models/management.py b/app/classes/models/management.py index c961f002..55c86bb7 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -43,6 +43,7 @@ class AuditLog(BaseModel): # ********************************************************************************** class CraftySettings(BaseModel): secret_api_key = CharField(default="") + login_photo = CharField(default="login_1.jpg") class Meta: table_name = "crafty_settings" @@ -254,6 +255,19 @@ class HelpersManagement: ) return settings[0].secret_api_key + @staticmethod + def get_login_image(): + settings = CraftySettings.select(CraftySettings.login_photo).where( + CraftySettings.id == 1 + ) + return settings[0].login_photo + + @staticmethod + def set_login_image(photo): + CraftySettings.update({CraftySettings.login_photo: photo}).where( + CraftySettings.id == 1 + ).execute() + # ********************************************************************************** # Schedules Methods # ********************************************************************************** diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index eb8c3219..b6af8ed0 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -73,6 +73,7 @@ class Controller: timezone=str(tz) ) self.first_login = False + self.cached_login = self.management.get_login_image() self.support_scheduler.start() @staticmethod diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index a90b4141..f4ed1dac 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -352,6 +352,38 @@ class AjaxHandler(BaseHandler): self.controller.clear_unexecuted_commands() return + elif page == "select_photo": + if exec_user["superuser"]: + photo = self.get_argument("photo", None) + if photo == "login_1.jpg": + self.controller.management.set_login_image("login_1.jpg") + self.controller.cached_login = f"{photo}" + else: + self.controller.management.set_login_image(f"custom/{photo}") + self.controller.cached_login = f"custom/{photo}" + return + + elif page == "delete_photo": + if exec_user["superuser"]: + photo = self.get_argument("photo", None) + if photo and photo != "login_1.jpg": + os.remove( + os.path.join( + self.controller.project_root, + f"app/frontend/static/assets/images/auth/custom/{photo}", + ) + ) + current = self.controller.cached_login + split = current.split("/") + if split == 1: + current_photo = current + else: + current_photo = split[1] + if current_photo == photo: + self.controller.management.set_login_image("login_1.jpg") + self.controller.cached_login = "login_1.jpg" + return + elif page == "kill": if not permissions["Commands"] in user_perms: if not superuser: diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 9111d358..85e3d083 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -849,6 +849,25 @@ class PanelHandler(BaseHandler): page_data["roles"] = self.controller.roles.get_all_roles() page_data["auth-servers"][user.user_id] = super_auth_servers page_data["managed_users"] = [] + page_data["backgrounds"] = [] + cached_split = self.controller.cached_login.split("/") + + if len(cached_split) == 1: + page_data["backgrounds"].append( + self.controller.cached_login + ) + else: + page_data["backgrounds"].append(cached_split[1]) + if "login_1.jpg" not in page_data["backgrounds"]: + page_data["backgrounds"].append("login_1.jpg") + for item in os.listdir( + os.path.join( + self.controller.project_root, + "app/frontend/static/assets/images/auth/custom", + ) + ): + if item not in page_data["backgrounds"]: + page_data["backgrounds"].append(item) else: page_data["managed_users"] = self.controller.users.get_managed_users( exec_user["user_id"] diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index cf50d535..bb31248f 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -48,6 +48,7 @@ class PublicHandler(BaseHandler): template = "public/404.html" if page == "login": + page_data["background"] = self.controller.cached_login template = "public/login.html" elif page == 404: diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 077128e4..2de4fe1f 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -152,65 +152,46 @@ class UploadHandler(BaseHandler): return self.do_upload = True - if superuser: - exec_user_server_permissions = ( - self.controller.server_perms.list_defined_permissions() + if not superuser: + self.helper.websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": self.helper.translation.translate( + "error", + "superError", + self.controller.users.get_user_lang_by_id(user_id), + ), + }, ) - elif api_key is not None: - exec_user_server_permissions = ( - self.controller.server_perms.get_api_key_permissions_list( - api_key, server_id - ) + return + if not self.request.headers.get("X-Content-Type", None).startswith( + "image/" + ): + self.helper.websocket_helper.broadcast_user( + user_id, + "send_start_error", + { + "error": self.helper.translation.translate( + "error", + "fileError", + self.controller.users.get_user_lang_by_id(user_id), + ), + }, ) - else: - exec_user_server_permissions = ( - self.controller.server_perms.get_user_id_permissions_list( - exec_user["user_id"], server_id - ) - ) - - server_id = self.request.headers.get("X-ServerId", None) - if server_id is None: - logger.warning("Server ID not found in upload handler call") - Console.warning("Server ID not found in upload handler call") - self.do_upload = False - + return if user_id is None: logger.warning("User ID not found in upload handler call") Console.warning("User ID not found in upload handler call") self.do_upload = False - if EnumPermissionsServer.FILES not in exec_user_server_permissions: - logger.warning( - f"User {user_id} tried to upload a file to " - f"{server_id} without permissions!" - ) - Console.warning( - f"User {user_id} tried to upload a file to " - f"{server_id} without permissions!" - ) - self.do_upload = False - - path = self.request.headers.get("X-Path", None) + path = os.path.join( + self.controller.project_root, + "app/frontend/static/assets/images/auth/custom", + ) filename = self.request.headers.get("X-FileName", None) full_path = os.path.join(path, filename) - if not Helpers.in_path( - Helpers.get_os_understandable_path( - self.controller.servers.get_server_data_by_id(server_id)["path"] - ), - full_path, - ): - logger.warning( - f"User {user_id} tried to upload a file to {server_id} " - f"but the path is not inside of the server!" - ) - Console.warning( - f"User {user_id} tried to upload a file to {server_id} " - f"but the path is not inside of the server!" - ) - self.do_upload = False - if self.do_upload: try: self.f = open(full_path, "wb") diff --git a/app/frontend/static/assets/css/dark/style.css b/app/frontend/static/assets/css/dark/style.css index d4b32181..dbd6ea8b 100755 --- a/app/frontend/static/assets/css/dark/style.css +++ b/app/frontend/static/assets/css/dark/style.css @@ -28362,11 +28362,6 @@ div.tagsinput span.tag a { min-height: 100vh; } -.auth.auth-bg-1 { - background: url("../../images/auth/login_1.jpg"); - background-size: cover; -} - .auth.register-bg-1 { background: url("../../images/auth/register.jpg") center center no-repeat; background-size: cover; diff --git a/app/frontend/static/assets/css/shared/style.css b/app/frontend/static/assets/css/shared/style.css index 5b223d6d..7352d637 100755 --- a/app/frontend/static/assets/css/shared/style.css +++ b/app/frontend/static/assets/css/shared/style.css @@ -26992,11 +26992,6 @@ div.tagsinput span.tag a { min-height: 100vh; } -.auth.auth-bg-1 { - background: url("../../images/auth/login_1.jpg"); - background-size: cover; -} - .auth.register-bg-1 { background: url("../../images/auth/register.jpg") center center no-repeat; background-size: cover; diff --git a/app/frontend/templates/panel/panel_config.html b/app/frontend/templates/panel/panel_config.html index b344f1c9..26f34a12 100644 --- a/app/frontend/templates/panel/panel_config.html +++ b/app/frontend/templates/panel/panel_config.html @@ -229,6 +229,59 @@ +
+
+
+
+ +

{{ translate('panelConfig', 'loginImage', data['lang']) }}

+
+

+ +

+ {% raw xsrf_form_html() %} + + +
+
+ +
+
+
+ + + + +
+
+
+
+
+ +

+
+
+
+
+
+

Login Background Image



+
+ +
+
+ + +
+
+
+
+
{% end %} @@ -307,6 +360,67 @@ }, }); }) + + $('.delete-photo').click(function () { + var token = getCookie("_xsrf") + let photo = $('#photo').find(":selected").val(); + $.ajax({ + type: "POST", + headers: { 'X-XSRFToken': token }, + url: '/ajax/delete_photo?photo=' + photo, + success: function (data) { + location.reload(); + }, + }); + }) + + $('.select-photo').click(function () { + var token = getCookie("_xsrf") + let photo = $('#photo').find(":selected").val(); + $.ajax({ + type: "POST", + headers: { 'X-XSRFToken': token }, + url: '/ajax/select_photo?photo=' + photo, + success: function (data) { + }, + }); + }) + + var file; + function sendFile() { + file = $("#file")[0].files[0] + document.getElementById("upload_input").innerHTML = '
 
' + let xmlHttpRequest = new XMLHttpRequest(); + let token = getCookie("_xsrf") + let fileName = file.name + let target = '/upload' + let mimeType = file.type + let size = file.size + let type = 'background' + + xmlHttpRequest.open('POST', target, true); + xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); + xmlHttpRequest.setRequestHeader('X-XSRFToken', token); + xmlHttpRequest.setRequestHeader('X-Content-Length', size); + xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"'); + xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', type); + xmlHttpRequest.setRequestHeader('X-FileName', fileName); + xmlHttpRequest.addEventListener('load', (event) => { + if (event.target.responseText == 'success') { + console.log('Upload for file', file.name, 'was successful!') + document.getElementById("upload_input").innerHTML = '
' + fileName + ' 🔒
'; + document.getElementById("lower_half").style.visibility = "visible"; + } + else { + alert('Upload failed with response: ' + event.target.responseText); + doUpload = false; + } + }, false); + xmlHttpRequest.addEventListener('error', (e) => { + console.error('Error while uploading file', file.name + '.', 'Event:', e) + }, false); + xmlHttpRequest.send(file); + } {% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index de60388e..a40d2903 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -124,7 +124,7 @@ data['lang']) }}{% end %}
- - +
@@ -265,7 +267,7 @@
-

Login Background Image



+

{{ translate('panelConfig', 'loginBackground', data['lang']) }}




+
+
+
{{ translate('panelConfig', 'preview', data['lang']) }}:
+ +


- - + +
@@ -332,6 +342,13 @@ $('.too_small2').popover("hide"); } // New width }); + $('#file').change(function () { + console.log("File changed"); + if ($('#file').val()) { + $('#upload-button').prop("disabled", false); + console.log("File changed good"); + } + });