diff --git a/.gitlab/docker-build.yml b/.gitlab/docker-build.yml index 297c2b20..4c906e4e 100644 --- a/.gitlab/docker-build.yml +++ b/.gitlab/docker-build.yml @@ -29,6 +29,7 @@ docker-build-dev: - docker run --rm --privileged aptman/qus -- -r - docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64 - echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY + - echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY script: - | tag=":$CI_COMMIT_REF_SLUG" @@ -45,6 +46,7 @@ docker-build-dev: --build-arg "BUILD_REF=${CI_COMMIT_SHA}" --build-arg "CRAFTY_VER=${VERSION}" --tag "$CI_REGISTRY_IMAGE${tag}" + --tag "arcadiatechnology/crafty-4${tag}" --platform linux/arm64/v8,linux/amd64 --push . after_script: @@ -83,6 +85,7 @@ docker-build-prod: - docker run --rm --privileged aptman/qus -- -r - docker run --rm --privileged aptman/qus -s -- -p aarch64 x86_64 - echo $CI_BUILD_TOKEN | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY + - echo $DOCKERHUB_TOKEN | docker login -u "$DOCKERHUB_USER" --password-stdin $DOCKERHUB_REGISTRY script: - | VERSION="${MAJOR}.${MINOR}.${SUB}" @@ -99,6 +102,8 @@ docker-build-prod: --build-arg "CRAFTY_VER=${VERSION}" --tag "$CI_REGISTRY_IMAGE:$VERSION" --tag "$CI_REGISTRY_IMAGE:latest" + --tag "arcadiatechnology/crafty-4:$VERSION" + --tag "arcadiatechnology/crafty-4:latest" --platform linux/arm64/v8,linux/amd64 --push . after_script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d211d2..56b9730d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,29 @@ # Changelog +## --- [4.0.19] - 2022/01/07 +### Bug fixes +- Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503)) +- Fix '+' char in path causing any file operation to fail. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/502)) +- Fix colours on public pages. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/504)) +- Fix bug where public background was not sent to public pages...like the error page resulting in an error...ironic...I know. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/505)) +- Be sure a user cannot server import crafty dir. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/506)) +- Remove Pathlib from sub path check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/507)) +- Fix root dir selection in Upload Zip Import ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/508)) +- Fix stats error on mac M1 chips ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/512)) +- Fix window path escape on java override ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/513)) +- Fix Forge import stalling on 1.17 Forge servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/515)) +- Fix issue with server config for SU Accounts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/516)) +- Fix Nested reaction tasks ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/521)) +- Remove legacy unzip code causing issues with single file zip files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/522)) +### Tweaks +- Make server directories non-configurable ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/511)) +- Add popover to server port to detail it's purpose ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/514)) +- Add server start timeout w/ WS Warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/518)) +- Replace google ping for ntp for internet checks in locked-down countries ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/524)) +- Add pushing to DockerHub registry (`arcadiatechnology/crafty-4`) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/526)) +### Lang +- Added Czech translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/519)) +

+ ## --- [4.0.17/4.0.18] - 2022/11/30 ### New features - Automate forge install process through Crafty server creation for Forge server version 1.16 and greater. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/495)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab58ba50..80d148d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ *Don't Panic!*

First off, thank you for choosing Crafty Controller!
-We hope you've been enjoying the beta so far and are absolutely thrilled that you are looking to contribute! +We hope you've been enjoying Crafty so far and are absolutely thrilled that you are looking to contribute! The following guide will show you how to easily and safely contribute to our current workflow. There are a few components that need to be taken into account and processes that need followed before we can merge your code into our repository.

diff --git a/README.md b/README.md index a3426311..f9a19ae1 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.0.18 +# Crafty Controller 4.0.19 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? @@ -48,7 +48,7 @@ As the Dockerfile uses the permission structure of `crafty:root` **internally** ### - Using the registry image 🌎 The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`. -The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest` +The image is located at: `registry.gitlab.com/crafty-controller/crafty-4:latest` or `arcadiatechnology/crafty-4` | Branch | Status | | ----------------- | ------------------------------------------------------------------ | | :latest | [![pipeline status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master) | diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index a656944b..3ecfdb8f 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -192,7 +192,7 @@ class ServerJars: with open(path, "wb") as output: shutil.copyfileobj(r.raw, output) # If this is the newer forge version we will run the installer - if server == "forge" and int(version.split(".")[1]) > 15: + if server == "forge": ServersController.finish_import(server_id, True) else: ServersController.finish_import(server_id) diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py index 5ef9550d..e3611509 100644 --- a/app/classes/minecraft/stats.py +++ b/app/classes/minecraft/stats.py @@ -86,7 +86,7 @@ class Stats: def get_node_stats(self) -> NodeStatsReturnDict: try: cpu_freq = psutil.cpu_freq() - except NotImplementedError: + except (NotImplementedError, FileNotFoundError): cpu_freq = None if cpu_freq is None: cpu_freq = psutil._common.scpufreq(current=-1, min=-1, max=-1) diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py index 82b5a560..e26220bb 100644 --- a/app/classes/shared/file_helpers.py +++ b/app/classes/shared/file_helpers.py @@ -298,14 +298,7 @@ class FileHelpers: try: with zipfile.ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall(temp_dir) - for i in enumerate(zip_ref.filelist): - if len(zip_ref.filelist) > 1 or not zip_ref.filelist[ - i - ].filename.endswith("/"): - break - - full_root_path = temp_dir - + full_root_path = temp_dir for item in os.listdir(full_root_path): if os.path.isdir(os.path.join(full_root_path, item)): try: diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index c4d9ad10..c5238ae8 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -272,7 +272,7 @@ class Helpers: @staticmethod def check_internet(): try: - requests.get("https://google.com", timeout=1) + requests.get("https://ntp.org", timeout=1) return True except Exception: return False @@ -376,6 +376,17 @@ class Helpers: return default_return + @staticmethod + def is_subdir(server_path, root_dir): + server_path = os.path.realpath(server_path) + root_dir = os.path.realpath(root_dir) + + relative = os.path.relpath(server_path, root_dir) + + if relative.startswith(os.pardir): + return False + return True + def set_setting(self, key, new_value): try: with open(self.settings_file, "r", encoding="utf-8") as f: diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index e6263cdf..3a229d20 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -815,6 +815,7 @@ class Controller: user_id, server_type="minecraft-bedrock", ) + ServersController.set_import(new_id) self.import_helper.import_bedrock_zip_server( temp_dir, new_server_dir, full_jar_path, port, new_id ) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index ccc50f70..745d840d 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -10,6 +10,7 @@ import logging.config import subprocess import html import urllib.request +import glob # TZLocal is set as a hidden import on win pipeline from tzlocal import get_localzone @@ -580,54 +581,105 @@ class ServerInstance: # Process has exited. Lets do some work to setup the new # run command. # Let's grab the server object we're going to update. - server_obj = HelperServers.get_server_obj(self.server_id) + server_obj: Servers = HelperServers.get_server_obj(self.server_id) # The forge install is done so we can delete that install file. os.remove(os.path.join(server_obj.path, server_obj.executable)) # We need to grab the exact forge version number. # We know we can find it here in the run.sh/bat script. - run_file_path = "" - if self.helper.is_os_windows(): - run_file_path = os.path.join(server_obj.path, "run.bat") - else: - run_file_path = os.path.join(server_obj.path, "run.sh") + try: - if Helpers.check_file_perms(run_file_path) and os.path.isfile( - run_file_path - ): - run_file = open(run_file_path, "r", encoding="utf-8") - run_file_text = run_file.read() - else: - Console.error( - "ERROR ! Forge install can't read the scripts files." - " Aborting ..." + # Getting the forge version from the executable command + version = re.findall( + r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar", + server_obj.execution_command, ) - return + version_param = version[0][0].split(".") + version_major = int(version_param[0]) + version_minor = int(version_param[1]) - # We get the server command parameters from forge script - server_command = re.findall( - r"java @([a-zA-Z0-9_\.]+)" - r" @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?", - run_file_text, - )[0] + # Checking which version we are with + if version_major <= 1 and version_minor < 17: + # OLD VERSION < 1.17 - version = server_command[2] - executable_path = f"{server_command[1]}{server_command[2]}/" + # Retrieving the executable jar filename + file_path = glob.glob( + f"{server_obj.path}/forge-{version[0][0]}*.jar" + )[0] + file_name = re.findall( + r"(forge[-0-9.]+.jar)", + file_path, + )[0] - # Let's set the proper server executable - server_obj.executable = os.path.join( - f"{executable_path}forge-{version}-server.jar" - ) - # Now lets set up the new run command. - # This is based off the run.sh/bat that - # Forge uses in 1.16 and < - execution_command = ( - f"java @{server_command[0]}" - f" @{executable_path}{server_command[3]} nogui {server_command[4]}" - ) - server_obj.execution_command = execution_command - Console.debug("SUCCESS! Forge install completed") + # Let's set the proper server executable + server_obj.executable = os.path.join(file_name) + + # Get memory values + memory_values = re.findall( + r"-Xms([A-Z0-9\.]+) -Xmx([A-Z0-9\.]+)", + server_obj.execution_command, + ) + + # Now lets set up the new run command. + # This is based off the run.sh/bat that + # Forge uses in 1.17 and < + execution_command = ( + f"java -Xms{memory_values[0][0]} -Xmx{memory_values[0][1]}" + f' -jar "{file_name}" nogui' + ) + server_obj.execution_command = execution_command + Console.debug("SUCCESS! Forge install completed") + + else: + # NEW VERSION >= 1.17 + + run_file_path = "" + if self.helper.is_os_windows(): + run_file_path = os.path.join(server_obj.path, "run.bat") + else: + run_file_path = os.path.join(server_obj.path, "run.sh") + + if Helpers.check_file_perms(run_file_path) and os.path.isfile( + run_file_path + ): + run_file = open(run_file_path, "r", encoding="utf-8") + run_file_text = run_file.read() + else: + Console.error( + "ERROR ! Forge install can't read the scripts files." + " Aborting ..." + ) + return + + # We get the server command parameters from forge script + server_command = re.findall( + r"java @([a-zA-Z0-9_\.]+)" + r" @([a-z.\/\-]+)([0-9.\-]+)" + r"\/\b([a-z_0-9]+\.txt)\b( .{2,4})?", + run_file_text, + )[0] + + version = server_command[2] + executable_path = f"{server_command[1]}{server_command[2]}/" + + # Let's set the proper server executable + server_obj.executable = os.path.join( + f"{executable_path}forge-{version}-server.jar" + ) + # Now lets set up the new run command. + # This is based off the run.sh/bat that + # Forge uses in 1.17 and < + execution_command = ( + f"java @{server_command[0]}" + f" @{executable_path}{server_command[3]} nogui" + " {server_command[4]}" + ) + server_obj.execution_command = execution_command + Console.debug("SUCCESS! Forge install completed") + except: + logger.debug("Could not find run file.") + # TODO Use regex to get version and rebuild simple execution # We'll update the server with the new information now. HelperServers.update_server(server_obj) diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index e7266db7..17940181 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -556,7 +556,7 @@ class TasksManager: if task.one_time: self.remove_job(task.schedule_id) logger.info("one time task detected. Deleting...") - else: + elif task.interval_type != "reaction": self.controller.management.update_scheduled_task( task.schedule_id, { diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index fe3fb14f..cdd67146 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -4,6 +4,7 @@ import pathlib import re import logging import time +import urllib.parse import bleach import tornado.web import tornado.escape @@ -507,12 +508,12 @@ class AjaxHandler(BaseHandler): self.redirect("/panel/dashboard") elif page == "unzip_server": - path = self.get_argument("path", None) + path = urllib.parse.unquote(self.get_argument("path", "")) if not path: path = os.path.join( self.controller.project_root, "imports", - self.get_argument("file", ""), + urllib.parse.unquote(self.get_argument("file", "")), ) if Helpers.check_file_exists(path): self.helper.unzip_server(path, exec_user["user_id"]) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index d64774bd..582517db 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -7,6 +7,7 @@ import json import logging import threading import shlex +import urllib.parse import bleach import requests import tornado.web @@ -289,6 +290,7 @@ class PanelHandler(BaseHandler): page_data: t.Dict[str, t.Any] = { # todo: make this actually pull and compare version data "update_available": self.helper.update_available, + "background": self.controller.cached_login, "serverTZ": tz, "version_data": self.helper.get_version_string(), "failed_servers": self.controller.servers.failed_servers, @@ -806,9 +808,15 @@ class PanelHandler(BaseHandler): user_roles_list = self.controller.users.get_user_roles_names( user.user_id ) - user_servers = self.controller.servers.get_authorized_servers( - user.user_id - ) + try: + user_servers = self.controller.servers.get_authorized_servers( + user.user_id + ) + except: + return self.redirect( + "/panel/error?error=Cannot load panel config" + " while servers are unloaded" + ) servers = [] for server in user_servers: if server.name not in servers: @@ -1386,9 +1394,10 @@ class PanelHandler(BaseHandler): template = "panel/activity_logs.html" elif page == "download_file": - file = Helpers.get_os_understandable_path(self.get_argument("path", "")) - name = self.get_argument("name", "") - + file = Helpers.get_os_understandable_path( + urllib.parse.unquote(self.get_argument("path", "")) + ) + name = urllib.parse.unquote(self.get_argument("name", "")) server_id = self.check_server_id() if server_id is None: return @@ -1512,10 +1521,6 @@ class PanelHandler(BaseHandler): server_obj = self.controller.servers.get_server_obj(server_id) shutdown_timeout = self.get_argument("shutdown_timeout", 60) if superuser: - server_path = self.get_argument("server_path", None) - if Helpers.is_os_windows(): - server_path.replace(" ", "^ ") - server_path = Helpers.wtol_path(server_path) log_path = self.get_argument("log_path", "") if log_path: if Helpers.is_os_windows(): @@ -1551,7 +1556,10 @@ class PanelHandler(BaseHandler): return if java_selection: try: - execution_list = shlex.split(execution_command) + if self.helper.is_os_windows(): + execution_list = shlex.split(execution_command, posix=False) + else: + execution_list = shlex.split(execution_command, posix=True) except ValueError: self.redirect( "/panel/error?error=Invalid execution command. Java path" @@ -1601,9 +1609,8 @@ class PanelHandler(BaseHandler): server_obj.shutdown_timeout = shutdown_timeout if superuser: if Helpers.validate_traversal( - self.helper.get_servers_root_dir(), server_path + self.helper.get_servers_root_dir(), server_obj.path ): - server_obj.path = server_path server_obj.log_path = log_path if Helpers.validate_traversal( self.helper.get_servers_root_dir(), executable @@ -1615,7 +1622,6 @@ class PanelHandler(BaseHandler): server_obj.executable_update_url = executable_update_url server_obj.show_status = show_status else: - server_obj.path = server_obj.path server_obj.log_path = server_obj.log_path server_obj.executable = server_obj.executable server_obj.execution_command = execution_command diff --git a/app/classes/web/routes/api/servers/server/index.py b/app/classes/web/routes/api/servers/server/index.py index 11f8620b..3d5e3e2f 100644 --- a/app/classes/web/routes/api/servers/server/index.py +++ b/app/classes/web/routes/api/servers/server/index.py @@ -90,7 +90,8 @@ class ApiServersServerIndexHandler(BaseApiHandler): server_obj = self.controller.servers.get_server_obj(server_id) for key in data: # If we don't validate the input there could be security issues - setattr(server_obj, key, data[key]) + if key != "path": + setattr(server_obj, key, data[key]) self.controller.servers.update_server(server_obj) self.controller.management.add_to_audit_log( diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index 08162365..62c549e5 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -331,6 +331,15 @@ class ServerHandler(BaseHandler): return if import_type == "import_jar": + if not self.helper.is_subdir( + import_server_path, self.controller.project_root + ): + self.redirect( + "/panel/error?error=Loop Error: The selected path will cause" + " an infinite copy loop. Make sure Crafty's directory is not" + " in your server path." + ) + return good_path = self.controller.verify_jar_server( import_server_path, import_server_jar ) @@ -480,6 +489,15 @@ class ServerHandler(BaseHandler): return if import_type == "import_jar": + if self.helper.is_subdir( + import_server_path, self.controller.project_root + ): + self.redirect( + "/panel/error?error=Loop Error: The selected path will cause" + " an infinite copy loop. Make sure Crafty's directory is not" + " in your server path." + ) + return good_path = self.controller.verify_jar_server( import_server_path, import_server_exe ) diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 2de4fe1f..785d5783 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -1,6 +1,7 @@ import logging import os import time +import urllib.parse import tornado.web import tornado.options import tornado.httpserver @@ -108,7 +109,9 @@ class UploadHandler(BaseHandler): logger.debug("Could not delete file on user server upload") self.helper.ensure_dir_exists(path) - filename = self.request.headers.get("X-FileName", None) + filename = urllib.parse.unquote( + self.request.headers.get("X-FileName", None) + ) if not str(filename).endswith(".zip"): self.helper.websocket_helper.broadcast("close_upload_box", "error") self.finish("error") diff --git a/app/config/version.json b/app/config/version.json index 4f94cf56..02ec3ae1 100644 --- a/app/config/version.json +++ b/app/config/version.json @@ -1,5 +1,5 @@ { "major": 4, "minor": 0, - "sub": 18 + "sub": 19 } diff --git a/app/frontend/static/assets/css/dark/style.css b/app/frontend/static/assets/css/dark/style.css index dbd6ea8b..c8ac5eb9 100755 --- a/app/frontend/static/assets/css/dark/style.css +++ b/app/frontend/static/assets/css/dark/style.css @@ -22155,7 +22155,7 @@ ul li { } .popover .popover-body { - color: #000; + color: var(--base-text); background: var(--card-banner-bg); } diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 1fac58cf..adfe7645 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -278,186 +278,188 @@ {% end %} - + - {% if server['stats']['running'] %} - {{ translate('dashboard', 'online', - data['lang']) }} - {% elif server['stats']['crashed'] %} - {{ translate('dashboard', - 'crashed', - data['lang']) }} - {% else %} - {{ translate('dashboard', 'offline', - data['lang']) }} - {% end %} -
-
+
+ {% if server['stats']['running'] %} + {{ translate('dashboard', 'online', + data['lang']) }} + {% elif server['stats']['crashed'] %} + {{ translate('dashboard', + 'crashed', + data['lang']) }} + {% else %} + {{ translate('dashboard', 'offline', + data['lang']) }} + {% end %} +
+
{% end %} - - {% for server in data['failed_servers'] %} - -  {{server['server_name']}} - - - - - - -  Unloaded - - {% end %} - -
+
+ {% for server in data['failed_servers'] %} + +  {{server['server_name']}} + + + + + + +  Unloaded + {% end %} - {% if len(data['servers']) > 0 %} - -
-
- {% for server in data['servers'] %} -
-
-

-
-
- {% if server['alert'] %} - - {{ server['server_data']['server_name'] }}  - + + +
+ {% end %} + {% if len(data['servers']) > 0 %} + +
+
+ {% for server in data['servers'] %} +
+
+

+
+ +
+ +
+ +
+
+ {% if server['user_command_permission'] %} + {% if server['stats']['running'] %} +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ {% elif server['stats']['updating']%} + + + {% elif server['stats']['waiting_start']%} + + + {% elif server['stats']['importing']%} + + {% else %} +
+
+ + + +
+
+ + + +
+
+ + +
+
+ {% end %} {% end %}
-
- -
- -
-
- {% if server['user_command_permission'] %} - {% if server['stats']['running'] %} -
-
- - - -
-
- - - -
-
- - - -
-
- {% elif server['stats']['updating']%} - - - {% elif server['stats']['waiting_start']%} - - - {% elif server['stats']['importing']%} - - {% else %} -
-
- - - -
-
- - - -
-
- - -
-
- {% end %} - {% end %} -
-
-

-
+
+

+
-
-
-
-
-
{{ translate('dashboard', 'cpuUsage', data['lang']) }}
-
-
-
+
+
+
+
{{ translate('dashboard', 'cpuUsage', data['lang']) }}
+
+
+
-
- {{server['stats']['cpu']}}% + aria-valuemax="100">
+ {{server['stats']['cpu']}}%
-
-
{{ translate('dashboard', 'memUsage', data['lang']) }}
-
-
-
+
{{ translate('dashboard', 'memUsage', data['lang']) }}
+
+
+
-
- {{server['stats']['mem_percent']}}% - - - {% if server['stats']['mem'] == 0 %} - 0 MB - {% else %} - {{server['stats']['mem']}} - {% end %} + aria-valuemin="0" aria-valuemax="100">
+ {{server['stats']['mem_percent']}}% - + + {% if server['stats']['mem'] == 0 %} + 0 MB + {% else %} + {{server['stats']['mem']}} + {% end %}
-
-
-
-
{{ translate('dashboard', 'size', data['lang']) }}
-
- {{ server['stats']['world_size'] }} -
+
+
+
+
+
{{ translate('dashboard', 'size', data['lang']) }}
+
+ {{ server['stats']['world_size'] }}
-
-
{{ translate('dashboard', 'players', data['lang']) }}
-
- {% if server['stats']['int_ping_results'] %} - {{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', - 'max', - data['lang']) }}
+
+
+
{{ translate('dashboard', 'players', data['lang']) }}
+
+ {% if server['stats']['int_ping_results'] %} + {{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', + 'max', + data['lang']) }}
- {% if server['stats']['desc'] != 'False' %} -
- {{ server['stats']['desc'] }}

- {% end %} + {% if server['stats']['desc'] != 'False' %} +
+ {{ server['stats']['desc'] }}

+ {% end %} - {% if server['stats']['version'] != 'False' %} - {{ server['stats']['version'] }} - {% end %} - {% end %} -
+ {% if server['stats']['version'] != 'False' %} + {{ server['stats']['version'] }} + {% end %} + {% end %}
- {% end %}
+ {% end %}
- {% end %}
+ {% end %}
+
@@ -604,7 +606,6 @@ function send_command(server_id, command) { /* this getCookie function is in base.html */ const token = getCookie("_xsrf"); - $.ajax({ type: "POST", headers: { 'X-XSRFToken': token }, @@ -621,6 +622,51 @@ }); } + + function warn(message, link = null, className = null) { + var closeEl = document.createElement('span'); + var strongEL = document.createElement('strong'); + var msgEl = document.createElement('div'); + + closeEl.innerHTML = '×'; + strongEL.textContent = 'Warning: '; + msgEl.append(strongEL, message); + + + closeEl.style.marginLeft = '15px'; + closeEl.style.fontWeight = 'bold'; + closeEl.style.float = 'right'; + closeEl.style.fontSize = '22px'; + closeEl.style.lineHeight = '20px'; + closeEl.style.cursor = 'pointer'; + + closeEl.addEventListener('click', function () { this.parentElement.style.display = 'none'; }); + + var parentEl = document.createElement('div'); + + parentEl.style.padding = '20px'; + parentEl.style.backgroundColor = '#f7970f'; + + parentEl.appendChild(closeEl); + parentEl.appendChild(msgEl); + if (link) { + let linkEl = document.createElement('a') + linkEl.href = link; + linkEl.innerHTML = "See our documentation for details."; + linkEl.style.color = 'white'; + linkEl.style.textDecoration = 'underline'; + linkEl.target = "_blank"; + + parentEl.appendChild(linkEl); + } + + if (className) { + parentEl.classList.add(className); + } + + document.querySelector('.dynamicMsg').appendChild(parentEl); + } + function send_kill(server_id) { /* this getCookie function is in base.html */ const token = getCookie("_xsrf"); @@ -772,11 +818,15 @@ send_command(server_id, 'start_server'); bootbox.alert({ backdrop: true, - title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', + title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', message: '
  {% raw translate("dashboard", "bePatientStart", data["lang"]) %}
' }); + setTimeout(finishTimeout, 60000); }); - + function finishTimeout() { + warn("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.", + null, 'wssError'); + } $(".stop_button").click(function () { console.log("stopping server"); server_id = $(this).attr("data-id"); diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index d0c0d42f..fb19ec05 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -62,9 +62,10 @@ - +
+ {{ data['server_stats']['server_id']['path'] }} + 🔒 +
{% if data['server_stats']['server_type'] != "minecraft-bedrock" %} @@ -157,7 +158,6 @@
-
{% end %} @@ -522,6 +527,20 @@ }); } + $("#server_port").focus(function () { + + $('[data-toggle="popover"]').popover(); + if ($(window).width() < 1000) { + $('.port-hint').attr("data-placement", "top") + } else { + $('.port-hint').attr("data-placement", "right") + } + $('.port-hint').popover("show"); + }); + $("#server_port").focusout(function () { + $('.port-hint').popover("hide"); + }); + $(document).ready(function () { webSocket.on('remove_spinner', function () { document.getElementById("update-spinner").style.visibility = "hidden"; diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index ebcf0d3b..af287b43 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -1027,7 +1027,9 @@ function downloadFileE(event) { path = event.target.parentElement.getAttribute('data-path'); name = event.target.parentElement.getAttribute('data-name'); - window.location.href = `/panel/download_file?id=${serverId}&path=${path}&name=${name}`; + encoded_path = encodeURIComponent(path) + encoded_name = encodeURIComponent(name) + window.location.href = `/panel/download_file?id=${serverId}&path=${encoded_path}&name=${encoded_name}`; } function renameItemE(event) { diff --git a/app/frontend/templates/public_base.html b/app/frontend/templates/public_base.html index 5a6d9bdb..33972b79 100644 --- a/app/frontend/templates/public_base.html +++ b/app/frontend/templates/public_base.html @@ -1,5 +1,5 @@ - + @@ -24,7 +24,7 @@ - +
diff --git a/app/frontend/templates/server/bedrock_wizard.html b/app/frontend/templates/server/bedrock_wizard.html index 8b9839e1..38d0b021 100644 --- a/app/frontend/templates/server/bedrock_wizard.html +++ b/app/frontend/templates/server/bedrock_wizard.html @@ -424,8 +424,8 @@
-