diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4ec9ed84..03144b48 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ stages: - test - prod-deployment - dev-deployment + - release variables: DOCKER_HOST: tcp://docker:2376 @@ -16,6 +17,7 @@ include: - local: .gitlab/lint.yml - local: .gitlab/docker-build.yml - local: .gitlab/windows-build.yml + - local: .gitlab/release.yml - template: Security/Dependency-Scanning.gitlab-ci.yml - template: Security/SAST.gitlab-ci.yml - template: Security/Secret-Detection.gitlab-ci.yml diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md index a82cb3f8..2acf3b1e 100644 --- a/.gitlab/merge_request_templates/Default.md +++ b/.gitlab/merge_request_templates/Default.md @@ -27,3 +27,4 @@ This checklist encourages us to confirm any changes have been analyzed to reduce * [ ] Have you resolved any lint issues? * [ ] Have you assigned a reviewer? * [ ] Have you applied correct labels? +* [ ] Have you updated CHANGELOG.md? diff --git a/.gitlab/release.yml b/.gitlab/release.yml new file mode 100644 index 00000000..10f543d4 --- /dev/null +++ b/.gitlab/release.yml @@ -0,0 +1,18 @@ +# yamllint disable rule:line-length +--- +release: + stage: release + image: registry.gitlab.com/gitlab-org/release-cli:latest + rules: + - if: $CI_COMMIT_TAG + script: + - echo "Running release job for tag $CI_COMMIT_TAG" + release: + tag_name: $CI_COMMIT_TAG + name: Crafty $CI_COMMIT_TAG + description: ./CHANGELOG.md + assets: + links: + - name: Windows Package + url: "https://gitlab.com/some/repo/-/jobs/$(cat CI_JOB_ID.txt)/artifacts/download" + link_type: package diff --git a/.gitlab/windows-build.yml b/.gitlab/windows-build.yml index 1487155f..277710f0 100644 --- a/.gitlab/windows-build.yml +++ b/.gitlab/windows-build.yml @@ -50,9 +50,11 @@ win-prod-build: - .venv/ rules: - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" + - if: $CI_COMMIT_TAG environment: name: production script: + - echo "${CI_JOB_ID}" > CI_JOB_ID.txt - | $ErrorActionPreference = "Stop" py -m venv .venv @@ -79,5 +81,7 @@ win-prod-build: paths: - app\ - .\crafty_commander.exe + - CI_JOB_ID.txt + expire_in: never exclude: - app\classes\**\* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..831e44e8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## [4.0.2] - 2022/06/16 + +### New features + None + +### Bug fixes +- Fix winreg import pass on non-NT systems ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/344)) +- Make the WebSocket automatically reconnect. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/345)) +- Fix an error when there are no servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/346)) +- Use relative paths for the jarfile and logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/347)) +- Flatten all instances of username creation or editing, usernames should be lower case. +- - ([Merge Request 1](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342)) +- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/351)) +- Add version inheretence & config check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/353)) +- Fix support log temp file deletion issue/hang ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/354)) + +## [4.0.1] - 2022/06/15 + +### New features + None + +### Bug fixes + +- Remove session.lock warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/338)) +- Correct Dutch Spacing Issue ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/340)) +- Remove no-else-* pylint exemptions and tidy code. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342)) +- Make unRAID more readable, and flatten path to lower, to fit standard practice. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/337)) +- Fix Java Pathing issues on windows ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/343/diffs?commit_id=cda2120579083d447db5dbeb5489822880f4cae7)) + diff --git a/README.md b/README.md index 2b1e2b3d..7334e0e1 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![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(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.1--beta-orange)](https://gitlab.com/crafty-controller/crafty-4) +[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.2--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases) [![Code Quality(temp-hardcoded)](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) -# Crafty Controller 4.0.1-beta +# Crafty Controller 4.0.2-beta > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index f1515031..39db11cd 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -82,8 +82,16 @@ class Controller: if exec_user["preparing"]: return self.users.set_prepare(exec_user["user_id"]) - # Delete previous instace of logs - self.del_support_file(exec_user["support_logs"]) + logger.info("Checking for previous support logs.") + if exec_user["support_logs"] != "": + logger.info( + f"Found previous support log request at {exec_user['support_logs']}" + ) + if self.helper.validate_traversal( + tempfile.gettempdir(), exec_user["support_logs"] + ): + logger.debug("No transversal detected. Going for the delete.") + self.del_support_file(exec_user["support_logs"]) # pausing so on screen notifications can run for user time.sleep(7) self.helper.websocket_helper.broadcast_user( @@ -177,6 +185,9 @@ class Controller: def del_support_file(self, temp_zip_storage): try: FileHelpers.del_file(temp_zip_storage) + logger.info( + f"Old support logs successfully deleted from {temp_zip_storage}" + ) except FileNotFoundError: logger.info("No temp file found. Assuming it's already been cleaned up") except PermissionError: diff --git a/app/classes/web/api_handler.py b/app/classes/web/api_handler.py index ae525dbb..43af4ae8 100644 --- a/app/classes/web/api_handler.py +++ b/app/classes/web/api_handler.py @@ -338,7 +338,7 @@ class CreateUser(ApiHandler): self.access_denied(user) return - new_username = self.get_argument("username") + new_username = self.get_argument("username").lower() new_pass = self.get_argument("password") if new_username: diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index ed8f3a73..295fbded 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -1223,8 +1223,6 @@ class PanelHandler(BaseHandler): elif page == "download_support_package": temp_zip_storage = exec_user["support_logs"] - # We'll reset the support path for this user now. - self.controller.users.set_support_path(exec_user["user_id"], "") self.set_header("Content-Type", "application/octet-stream") self.set_header( @@ -1774,7 +1772,7 @@ class PanelHandler(BaseHandler): "system user is not editable" ) user_id = bleach.clean(self.get_argument("id", None)) - username = bleach.clean(self.get_argument("username", None)) + username = bleach.clean(self.get_argument("username", None).lower()) password0 = bleach.clean(self.get_argument("password0", None)) password1 = bleach.clean(self.get_argument("password1", None)) email = bleach.clean(self.get_argument("email", "default@example.com")) @@ -1943,7 +1941,7 @@ class PanelHandler(BaseHandler): self.finish() elif page == "add_user": - username = bleach.clean(self.get_argument("username", None)) + username = bleach.clean(self.get_argument("username", None).lower()) if username.lower() == "system": self.redirect( "/panel/error?error=Unauthorized access: " diff --git a/app/classes/web/routes/api/servers/server/action.py b/app/classes/web/routes/api/servers/server/action.py index 2360b172..cf9163b9 100644 --- a/app/classes/web/routes/api/servers/server/action.py +++ b/app/classes/web/routes/api/servers/server/action.py @@ -46,7 +46,6 @@ class ApiServersServerActionHandler(BaseApiHandler): return Servers.select().where(Servers.server_name == name).exists() server_data = self.controller.servers.get_server_data_by_id(server_id) - server_uuid = server_data.get("server_uuid") new_server_name = server_data.get("server_name") + " (Copy)" name_counter = 1 diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index 4c5a85a2..3e4cfdab 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -95,6 +95,7 @@ class ApiUsersIndexHandler(BaseApiHandler): ) username = data["username"] + username = str(username).lower() password = data["password"] email = data.get("email", "default@example.com") enabled = data.get("enabled", True) diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index e2913f5f..bf4e930d 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -201,7 +201,6 @@ class ServerHandler(BaseHandler): server_data = self.controller.servers.get_server_data_by_id( server_id ) - server_uuid = server_data.get("server_uuid") new_server_name = server_data.get("server_name") + " (Copy)" name_counter = 1 diff --git a/app/config/version.json b/app/config/version.json index 1b3a7c55..4b9d8e69 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,6 +1,6 @@ { "major": 4, "minor": 0, - "sub": 1, + "sub": 2, "meta": "beta" } diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 4b28120a..eba6c2e8 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -163,8 +163,29 @@ -{% end %} \ No newline at end of file +{% end %} diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index 66ed9c86..45e02ced 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -211,29 +211,40 @@ .tree-nested { display: none; } + + html, body, body > .container-scroller { + overflow: initial; + } + + .editorManager { + top: 63px; + position: sticky; + }
-

-
- {{ translate('serverFiles', 'editingFile', data['lang']) }} -
file_contents
-
+
+

+
+ {{ translate('serverFiles', 'editingFile', data['lang']) }} +
file_contents
+
+
+ {{ translate('serverFiles', 'keybindings', data['lang']) }}: +
+ + + + +
+

+ +
- {{ translate('serverFiles', 'keybindings', data['lang']) }}: -
- - - - -
-

- -
@@ -267,6 +278,17 @@ let editor = ace.edit('editor'); editor.setTheme('ace/theme/dracula'); editor.session.setUseSoftTabs(true); + editor.commands.addCommand({ + name: 'saveFile', + bindKey: { + win: 'Ctrl-S', + mac: 'Command-S', + sender: 'editor|cli' + }, + exec: function(env, args, request) { + save() + } + }); // mouseup = css resize end document.addEventListener("mouseup", function (e) { @@ -364,8 +386,7 @@ }, ]; - var filePath = ''; - let file_loaded = false; + let filePath = '', serverFileContent = ''; function clickOnFile(event) { filePath = event.target.getAttribute('data-path'); @@ -386,7 +407,7 @@ $('#fileError').toggle(false) // hide setFileName(event.target.innerText); editor.session.setValue(json.content); - file_loaded = false; + serverFileContent = json.content; } }, }); @@ -501,36 +522,14 @@ timer = null; }; } - editor.on('change', function (event) { - if (!event.ctrlKey && !event.shiftKey) { - if (file_loaded) { - document.getElementById('save_status').innerHTML = ''; - document.getElementById('save_status').style.color = 'gray'; - } else { - document.getElementById('save_status').innerHTML = ''; - document.getElementById('save_status').style.color = '#2fb689'; - } - } - }); - editor.on('undo', function (event) { - document.getElementById('save_status').innerHTML = ''; - document.getElementById('save_status').style.color = 'gray'; - - }); - editor.on('redo', function (event) { - document.getElementById('save_status').innerHTML = ''; - document.getElementById('save_status').style.color = 'gray'; - - }); - - $('#editor').bind('keydown', function (event) { - file_loaded = true; - if (event.ctrlKey && event.key === 's') { - event.preventDefault(); - save(); - } - //console.log(event.keyCode); - }); + /** + * @param {boolean} saved + */ + const setSaveStatus = (saved) => { + document.getElementById('save_status').innerHTML = ``; + document.getElementById('save_status').style.color = saved ? '#2fb689' : 'gray'; + } + ['change', 'undo', 'redo'].forEach(event => editor.on(event, (event) => setSaveStatus(serverFileContent === editor.session.getValue()))) setFileName(); @@ -576,11 +575,10 @@ file_contents: text, file_path: filePath }, - success: function (data) { - console.log("got response:"); - document.getElementById("save_status").innerHTML = ''; - document.getElementById('save_status').style.color = '#2fb689'; - }, + success: (data) => { + serverFileContent = text; + setSaveStatus(true) + } }); } @@ -1097,7 +1095,13 @@ function setKeyboard(target) { var handlerName = target.getAttribute('data-handler-name'); if (handlerName == 'null') handlerName = null; - editor.setKeyboardHandler(handlerName); + editor.setKeyboardHandler(handlerName, () => { + if (handlerName == 'ace/keyboard/vim') { + require("ace/keyboard/vim").Vim.defineEx('write', 'w', function() { + save(); + }); + } + }); var nodes = target.parentNode.querySelectorAll("[data-handler-name]"); nodes.forEach(node => { @@ -1113,4 +1117,4 @@ -{% end %} \ No newline at end of file +{% end %} diff --git a/docker_launcher.sh b/docker_launcher.sh index ec842f4c..3e2cfd6c 100644 --- a/docker_launcher.sh +++ b/docker_launcher.sh @@ -2,7 +2,7 @@ # Check if config exists taking one from image if needed. if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then - echo "Wrapper | 🏗️ Config not found, pulling defaults..." + echo "\033[36mWrapper | \033[33m🏗️ Config not found, pulling defaults..." mkdir ./app/config/ 2> /dev/null cp -r ./app/config_original/* ./app/config/ @@ -10,11 +10,24 @@ if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then # We're running as root; # Look for files & dirs that require group permissions to be fixed # This will do the full /crafty dir, so will take a miniute. - echo "Wrapper | 📋 Looking for problem bind mount permissions globally..." + echo "\033[36mWrapper | \033[35m📋 Looking for problem bind mount permissions globally..." find . ! -group root -exec chgrp root {} \; find . ! -perm g+rw -exec chmod g+rw {} \; find . -type d ! -perm g+s -exec chmod g+s {} \; fi +else + # Keep version file up to date with image + cp -f ./app/config_original/version.json ./app/config/version.json + + # Compare if user's config is different from image, and show differences. + echo "\033[36mWrapper | \033[35m🏗️ Checking for config.json changes..." + cp -f ./app/config_original/config.json ./app/config/config_image_template + if [ "$(diff -q ./app/config/config.json ./app/config/config_image_template)" ]; then + echo "\033[36mWrapper | \033[33m👷 We've found differences in your local config, please review!," + echo "\033[36m | \033[33m (This could be an outdated config.json)" + else + echo "\033[36mWrapper | \033[32m✅ Config good! Proceeding..." + fi fi @@ -24,21 +37,21 @@ if [ $(id -u) -eq 0 ]; then # If we find files in import directory, we need to ensure all dirs are owned by the root group, # This fixes bind mounts that may have incorrect perms. if [ "$(ls -A --ignore=.gitkeep ./import)" ]; then - echo "Wrapper | 📋 Files present in import, checking/fixing permissions..." - echo "Wrapper | ⏳ Please be paitent for larger servers..." + echo "\033[36mWrapper | \033[35m📋 Files present in import directory, checking/fixing permissions..." + echo "\033[36mWrapper | \033[33m⏳ Please be paitent for larger servers..." find . ! -group root -exec chgrp root {} \; find . ! -perm g+rw -exec chmod g+rw {} \; find . -type d ! -perm g+s -exec chmod g+s {} \; - echo "Wrapper | ✅ Permissions Fixed! (This will happen every boot until /import is empty!)" + echo "\033[36mWrapper | \033[32m✅ Permissions Fixed! (This will happen every boot until /import is empty!)" fi # Switch user, activate our prepared venv and lauch crafty args="$@" - echo "Wrapper | 🚀 Launching crafty with [$args]" + echo "\033[36mWrapper | \033[32m🚀 Launching crafty with [\033[34m$args\033[32m]" exec sudo -u crafty bash -c "source ./.venv/bin/activate && exec python3 main.py $args" else # Activate our prepared venv - echo "Wrapper | 🚀 Non-root host detected, using normal exec" + echo "\033[36mWrapper | \033[32m🚀 Non-root host detected, using normal exec" . ./.venv/bin/activate # Use exec as our perms are already correct # This is likely if using Kubernetes/OpenShift etc