diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1a8dd9e..3a736732 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,12 @@
## --- [4.2.4] - 2023/TBD
### New features
TBD
+### Refactor
+- Refactor remote file downloads ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719))
### Bug fixes
-TBD
+- Fix Bedrock cert issues ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/719))
### Tweaks
-TBD
+- Bump pyOpenSSL & cryptography for CVE-2024-0727, CVE-2023-50782 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/716))
### Lang
TBD
diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py
index 29675e8d..56cbc43e 100644
--- a/app/classes/minecraft/serverjars.py
+++ b/app/classes/minecraft/serverjars.py
@@ -1,13 +1,14 @@
+import os
import json
import threading
import time
-import shutil
import logging
from datetime import datetime
import requests
from app.classes.controllers.servers_controller import ServersController
from app.classes.models.server_permissions import PermissionsServers
+from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.websocket_manager import WebSocketManager
logger = logging.getLogger(__name__)
@@ -24,6 +25,111 @@ class ServerJars:
def get_paper_jars():
return PAPERJARS
+ def get_paper_versions(self, project):
+ """
+ Retrieves a list of versions for a specified project from the PaperMC API.
+
+ Parameters:
+ project (str): The project name to query for available versions.
+
+ Returns:
+ list: A list of version strings available for the project. Returns an empty
+ list if the API call fails or if no versions are found.
+
+ This function makes a GET request to the PaperMC API to fetch available project
+ versions, The versions are returned in reverse order, with the most recent
+ version first.
+ """
+ try:
+ response = requests.get(
+ f"{self.paper_base}/v2/projects/{project}/", timeout=2
+ )
+ response.raise_for_status()
+ api_data = response.json()
+ except Exception as e:
+ logger.error(f"Error loading project versions for {project}: {e}")
+ return []
+
+ versions = api_data.get("versions", [])
+ versions.reverse() # Ensure the most recent version comes first
+ return versions
+
+ def get_paper_build(self, project, version):
+ """
+ Fetches the latest build for a specified project and version from PaperMC API.
+
+ Parameters:
+ project (str): Project name, typically a server software like 'paper'.
+ version (str): Project version to fetch the build number for.
+
+ Returns:
+ int or None: Latest build number if successful, None if not or on error.
+
+ This method attempts to query the PaperMC API for the latest build and
+ handles exceptions by logging errors and returning None.
+ """
+ try:
+ response = requests.get(
+ f"{self.paper_base}/v2/projects/{project}/versions/{version}/builds/",
+ timeout=2,
+ )
+ response.raise_for_status()
+ api_data = response.json()
+ except Exception as e:
+ logger.error(f"Error fetching build for {project} {version}: {e}")
+ return None
+
+ builds = api_data.get("builds", [])
+ return builds[-1] if builds else None
+
+ def get_fetch_url(self, jar, server, version):
+ """
+ Constructs the URL for downloading a server JAR file based on the server type.
+
+ Supports two main types of server JAR sources:
+ - ServerJars API for servers not in PAPERJARS.
+ - Paper API for servers available through the Paper project.
+
+ Parameters:
+ jar (str): Name of the JAR file.
+ server (str): Server software name (e.g., "paper").
+ version (str): Server version.
+
+ Returns:
+ str or None: URL for downloading the JAR file, or None if URL cannot be
+ constructed or an error occurs.
+ """
+ try:
+ # Check if the server type is not specifically handled by Paper.
+ if server not in PAPERJARS:
+ return f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}"
+
+ # For Paper servers, attempt to get the build number for the specified version.
+ paper_build_info = self.get_paper_build(server, version)
+ if paper_build_info is None:
+ # Log an error or handle the case where paper_build_info is None
+ logger.error(
+ f"Error: Unable to get build information for server: {server}, version: {version}"
+ )
+ return None
+
+ build = paper_build_info.get("build")
+ if not build:
+ # Log an error or handle the case where build is None or not found
+ logger.error(
+ f"Error: Build number not found for server: {server}, version: {version}"
+ )
+ return None
+
+ # Construct and return the URL for downloading the Paper server JAR.
+ return (
+ f"{self.paper_base}/v2/projects/{server}/versions/{version}/"
+ f"builds/{build}/downloads/{server}-{version}-{build}.jar"
+ )
+ except Exception as e:
+ logger.error(f"An error occurred while constructing fetch URL: {e}")
+ return None
+
def _get_api_result(self, call_url: str):
full_url = f"{self.base_url}{call_url}"
@@ -44,40 +150,6 @@ class ServerJars:
return api_response
- def get_paper_versions(self, project):
- try:
- response = requests.get(
- f"{self.paper_base}/v2/projects/{project}/", timeout=2
- )
- response.raise_for_status()
- api_data = json.loads(response.content)
- except Exception as e:
- logger.error(
- f"Unable to load https://api.papermc.io/v2/projects/{project}/"
- f"api due to error: {e}"
- )
- return {}
- versions = api_data.get("versions", [])
- versions.reverse()
- return versions
-
- def get_paper_build(self, project, version):
- try:
- response = requests.get(
- f"{self.paper_base}/v2/projects/{project}/versions/{version}/builds/",
- timeout=2,
- )
- response.raise_for_status()
- api_data = json.loads(response.content)
- except Exception as e:
- logger.error(
- f"Unable to load https://api.papermc.io/v2/projects/{project}/"
- f"api due to error: {e}"
- )
- return {}
- build = api_data.get("builds", [])[-1]
- return build
-
def _read_cache(self):
cache_file = self.helper.serverjar_cache
cache = {}
@@ -213,55 +285,75 @@ class ServerJars:
update_thread.start()
def a_download_jar(self, jar, server, version, path, server_id):
+ """
+ Downloads a server JAR file and performs post-download actions including
+ notifying users and setting import status.
+
+ This method waits for the server registration to complete, retrieves the
+ download URL for the specified server JAR file.
+
+ Upon successful download, it either runs the installer for
+ Forge servers or simply finishes the import process for other types. It
+ notifies server users about the completion of the download.
+
+ Parameters:
+ - jar (str): The name of the JAR file to download.
+ - server (str): The type of server software (e.g., 'forge', 'paper').
+ - version (str): The version of the server software.
+ - path (str): The local filesystem path where the JAR file will be saved.
+ - server_id (str): The unique identifier for the server being updated or
+ imported, used for notifying users and setting the import status.
+
+ Returns:
+ - bool: True if the JAR file was successfully downloaded and saved;
+ False otherwise.
+
+ The method ensures that the server is properly registered before proceeding
+ with the download and handles exceptions by logging errors and reverting
+ the import status if necessary.
+ """
# delaying download for server register to finish
time.sleep(3)
- if server not in PAPERJARS:
- fetch_url = f"{self.base_url}/api/fetchJar/{jar}/{server}/{version}"
- else:
- build = self.get_paper_build(server, version).get("build", None)
- if not build:
- return
- fetch_url = (
- f"{self.paper_base}/v2/projects"
- f"/{server}/versions/{version}/builds/{build}/downloads/"
- f"{server}-{version}-{build}.jar"
- )
+
+ fetch_url = self.get_fetch_url(jar, server, version)
+ if not fetch_url:
+ return False
+
server_users = PermissionsServers.get_server_user_list(server_id)
- # We need to make sure the server is registered before
- # we submit a db update for it's stats.
+ # Make sure the server is registered before updating its stats
while True:
try:
ServersController.set_import(server_id)
for user in server_users:
WebSocketManager().broadcast_user(user, "send_start_reload", {})
-
break
except Exception as ex:
- logger.debug(f"server not registered yet. Delaying download - {ex}")
+ logger.debug(f"Server not registered yet. Delaying download - {ex}")
- # open a file stream
- with requests.get(fetch_url, timeout=2, stream=True) as r:
- success = False
- try:
- 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":
- ServersController.finish_import(server_id, True)
- else:
- ServersController.finish_import(server_id)
+ # Initiate Download
+ jar_dir = os.path.dirname(path)
+ jar_name = os.path.basename(path)
+ logger.info(fetch_url)
+ success = FileHelpers.ssl_get_file(fetch_url, jar_dir, jar_name)
- success = True
- except Exception as e:
- logger.error(f"Unable to save jar to {path} due to error:{e}")
+ # Post-download actions
+ if success:
+ if server == "forge":
+ # If this is the newer Forge version, run the installer
+ ServersController.finish_import(server_id, True)
+ else:
ServersController.finish_import(server_id)
- server_users = PermissionsServers.get_server_user_list(server_id)
+ # Notify users
for user in server_users:
WebSocketManager().broadcast_user(
user, "notification", "Executable download finished"
)
- time.sleep(3)
+ time.sleep(3) # Delay for user notification
WebSocketManager().broadcast_user(user, "send_start_reload", {})
- return success
+ else:
+ logger.error(f"Unable to save jar to {path} due to download failure.")
+ ServersController.finish_import(server_id)
+
+ return success
diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py
index eb5e3f35..56f9d8ac 100644
--- a/app/classes/models/server_permissions.py
+++ b/app/classes/models/server_permissions.py
@@ -172,9 +172,9 @@ class PermissionsServers:
RoleServers.server_id, RoleServers.permissions
).where(RoleServers.role_id == role_id)
for role_server in role_servers:
- permissions_dict[
- role_server.server_id_id
- ] = PermissionsServers.get_permissions(role_server.permissions)
+ permissions_dict[role_server.server_id_id] = (
+ PermissionsServers.get_permissions(role_server.permissions)
+ )
return permissions_dict
@staticmethod
diff --git a/app/classes/shared/file_helpers.py b/app/classes/shared/file_helpers.py
index 5e06c708..90d8e65c 100644
--- a/app/classes/shared/file_helpers.py
+++ b/app/classes/shared/file_helpers.py
@@ -5,6 +5,10 @@ import pathlib
import tempfile
import zipfile
from zipfile import ZipFile, ZIP_DEFLATED
+import urllib.request
+import ssl
+import time
+import certifi
from app.classes.shared.helpers import Helpers
from app.classes.shared.console import Console
@@ -19,6 +23,92 @@ class FileHelpers:
def __init__(self, helper):
self.helper: Helpers = helper
+ @staticmethod
+ def ssl_get_file(
+ url, out_path, out_file, max_retries=3, backoff_factor=2, headers=None
+ ):
+ """
+ Downloads a file from a given URL using HTTPS with SSL context verification,
+ retries with exponential backoff and providing download progress feedback.
+
+ Parameters:
+ - url (str): The URL of the file to download. Must start with "https".
+ - out_path (str): The local path where the file will be saved.
+ - out_file (str): The name of the file to save the downloaded content as.
+ - max_retries (int, optional): The maximum number of retry attempts
+ in case of download failure. Defaults to 3.
+ - backoff_factor (int, optional): The factor by which the wait time
+ increases after each failed attempt. Defaults to 2.
+ - headers (dict, optional):
+ A dictionary of HTTP headers to send with the request.
+
+ Returns:
+ - bool: True if the download was successful, False otherwise.
+
+ Raises:
+ - urllib.error.URLError: If a URL error occurs during the download.
+ - ssl.SSLError: If an SSL error occurs during the download.
+ Exception: If an unexpected error occurs during the download.
+
+ Note:
+ This method logs critical errors and download progress information.
+ Ensure that the logger is properly configured to capture this information.
+ """
+ if not url.lower().startswith("https"):
+ logger.error("SSL File Get - Error: URL must start with https.")
+ return False
+
+ ssl_context = ssl.create_default_context(cafile=certifi.where())
+
+ if not headers:
+ headers = {
+ "User-Agent": (
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/58.0.3029.110 Safari/537.3"
+ )
+ }
+ req = urllib.request.Request(url, headers=headers)
+
+ write_path = os.path.join(out_path, out_file)
+ attempt = 0
+
+ logger.info(f"SSL File Get - Requesting remote: {url}")
+ file_path_full = os.path.join(out_path, out_file)
+ logger.info(f"SSL File Get - Download Destination: {file_path_full}")
+
+ while attempt < max_retries:
+ try:
+ with urllib.request.urlopen(req, context=ssl_context) as response:
+ total_size = response.getheader("Content-Length")
+ if total_size:
+ total_size = int(total_size)
+ downloaded = 0
+ with open(write_path, "wb") as file:
+ while True:
+ chunk = response.read(1024 * 1024) # 1 MB
+ if not chunk:
+ break
+ file.write(chunk)
+ downloaded += len(chunk)
+ if total_size:
+ progress = (downloaded / total_size) * 100
+ logger.info(
+ f"SSL File Get - Download progress: {progress:.2f}%"
+ )
+ return True
+ except (urllib.error.URLError, ssl.SSLError) as e:
+ logger.warning(f"SSL File Get - Attempt {attempt+1} failed: {e}")
+ time.sleep(backoff_factor**attempt)
+ except Exception as e:
+ logger.critical(f"SSL File Get - Unexpected error: {e}")
+ return False
+ finally:
+ attempt += 1
+
+ logger.error("SSL File Get - Maximum retries reached. Download failed.")
+ return False
+
@staticmethod
def del_dirs(path):
path = pathlib.Path(path)
diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py
index 0e091a56..1ed3d71f 100644
--- a/app/classes/shared/helpers.py
+++ b/app/classes/shared/helpers.py
@@ -1180,25 +1180,6 @@ class Helpers:
return temp_dir
return False
- @staticmethod
- def download_file(executable_url, jar_path):
- try:
- response = requests.get(executable_url, timeout=5)
- except Exception as ex:
- logger.error("Could not download executable: %s", ex)
- return False
- if response.status_code != 200:
- logger.error("Unable to download file from %s", executable_url)
- return False
-
- try:
- with open(jar_path, "wb") as jar_file:
- jar_file.write(response.content)
- except Exception as e:
- logger.error("Unable to finish executable download. Error: %s", e)
- return False
- return True
-
@staticmethod
def remove_prefix(text, prefix):
if text.startswith(prefix):
diff --git a/app/classes/shared/import_helper.py b/app/classes/shared/import_helper.py
index 1acf7a03..030feb56 100644
--- a/app/classes/shared/import_helper.py
+++ b/app/classes/shared/import_helper.py
@@ -3,7 +3,6 @@ import time
import shutil
import logging
import threading
-import urllib
from app.classes.controllers.server_perms_controller import PermissionsServers
from app.classes.controllers.servers_controller import ServersController
@@ -227,25 +226,39 @@ class ImportHelpers:
download_thread.start()
def download_threaded_bedrock_server(self, path, new_id):
- # downloads zip from remote url
+ """
+ Downloads the latest Bedrock server, unzips it, sets necessary permissions.
+
+ Parameters:
+ path (str): The directory path to download and unzip the Bedrock server.
+ new_id (str): The identifier for the new server import operation.
+
+ This method handles exceptions and logs errors for each step of the process.
+ """
try:
bedrock_url = Helpers.get_latest_bedrock_url()
- if bedrock_url.lower().startswith("https"):
- urllib.request.urlretrieve(
- bedrock_url,
- os.path.join(path, "bedrock_server.zip"),
+ if bedrock_url:
+ file_path = os.path.join(path, "bedrock_server.zip")
+
+ success = FileHelpers.ssl_get_file(
+ bedrock_url, path, "bedrock_server.zip"
)
+ if not success:
+ logger.error("Failed to download the Bedrock server zip.")
+ return
- unzip_path = os.path.join(path, "bedrock_server.zip")
- unzip_path = self.helper.wtol_path(unzip_path)
- # unzips archive that was downloaded.
- FileHelpers.unzip_file(unzip_path)
- # adjusts permissions for execution if os is not windows
- if not self.helper.is_os_windows():
- os.chmod(os.path.join(path, "bedrock_server"), 0o0744)
+ unzip_path = self.helper.wtol_path(file_path)
+ # unzips archive that was downloaded.
+ FileHelpers.unzip_file(unzip_path)
+ # adjusts permissions for execution if os is not windows
- # we'll delete the zip we downloaded now
- os.remove(os.path.join(path, "bedrock_server.zip"))
+ if not self.helper.is_os_windows():
+ os.chmod(os.path.join(path, "bedrock_server"), 0o0744)
+
+ # we'll delete the zip we downloaded now
+ os.remove(file_path)
+ else:
+ logger.error("Bedrock download URL issue!")
except Exception as e:
logger.critical(
f"Failed to download bedrock executable during server creation! \n{e}"
diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py
index e47fe19c..812cedce 100644
--- a/app/classes/shared/server.py
+++ b/app/classes/shared/server.py
@@ -10,7 +10,6 @@ import threading
import logging.config
import subprocess
import html
-import urllib.request
import glob
import json
@@ -1450,33 +1449,45 @@ class ServerInstance:
# lets download the files
if HelperServers.get_server_type_by_id(self.server_id) != "minecraft-bedrock":
- # boolean returns true for false for success
- downloaded = Helpers.download_file(
- self.settings["executable_update_url"], current_executable
+
+ jar_dir = os.path.dirname(current_executable)
+ jar_file_name = os.path.basename(current_executable)
+
+ downloaded = FileHelpers.ssl_get_file(
+ self.settings["executable_update_url"], jar_dir, jar_file_name
)
else:
# downloads zip from remote url
try:
bedrock_url = Helpers.get_latest_bedrock_url()
- if bedrock_url.lower().startswith("https"):
- urllib.request.urlretrieve(
- bedrock_url,
- os.path.join(self.settings["path"], "bedrock_server.zip"),
+ if bedrock_url:
+ # Use the new method for secure download
+ download_path = os.path.join(
+ self.settings["path"], "bedrock_server.zip"
+ )
+ downloaded = FileHelpers.ssl_get_file(
+ bedrock_url, self.settings["path"], "bedrock_server.zip"
)
- unzip_path = os.path.join(self.settings["path"], "bedrock_server.zip")
- unzip_path = self.helper.wtol_path(unzip_path)
- # unzips archive that was downloaded.
- FileHelpers.unzip_file(unzip_path, server_update=True)
- # adjusts permissions for execution if os is not windows
- if not self.helper.is_os_windows():
- os.chmod(
- os.path.join(self.settings["path"], "bedrock_server"), 0o0744
- )
+ if downloaded:
+ unzip_path = download_path
+ unzip_path = self.helper.wtol_path(unzip_path)
- # we'll delete the zip we downloaded now
- os.remove(os.path.join(self.settings["path"], "bedrock_server.zip"))
- downloaded = True
+ # unzips archive that was downloaded.
+ FileHelpers.unzip_file(unzip_path, server_update=True)
+
+ # adjusts permissions for execution if os is not windows
+ if not self.helper.is_os_windows():
+ os.chmod(
+ os.path.join(self.settings["path"], "bedrock_server"),
+ 0o0744,
+ )
+
+ # we'll delete the zip we downloaded now
+ os.remove(download_path)
+ else:
+ logger.error("Failed to download the Bedrock server zip.")
+ downloaded = False
except Exception as e:
logger.critical(
f"Failed to download bedrock executable for update \n{e}"
diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py
index a7e54974..e8c93c68 100644
--- a/app/classes/web/panel_handler.py
+++ b/app/classes/web/panel_handler.py
@@ -345,15 +345,17 @@ class PanelHandler(BaseHandler):
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
"super_user": superuser,
- "api_key": {
- "name": api_key.name,
- "created": api_key.created,
- "server_permissions": api_key.server_permissions,
- "crafty_permissions": api_key.crafty_permissions,
- "superuser": api_key.superuser,
- }
- if api_key is not None
- else None,
+ "api_key": (
+ {
+ "name": api_key.name,
+ "created": api_key.created,
+ "server_permissions": api_key.server_permissions,
+ "crafty_permissions": api_key.crafty_permissions,
+ "superuser": api_key.superuser,
+ }
+ if api_key is not None
+ else None
+ ),
"superuser": superuser,
}
try:
@@ -417,14 +419,14 @@ class PanelHandler(BaseHandler):
self.controller.first_login = False
if superuser: # TODO: Figure out a better solution
try:
- page_data[
- "servers"
- ] = self.controller.servers.get_all_servers_stats()
+ page_data["servers"] = (
+ self.controller.servers.get_all_servers_stats()
+ )
except IndexError:
self.controller.servers.stats.record_stats()
- page_data[
- "servers"
- ] = self.controller.servers.get_all_servers_stats()
+ page_data["servers"] = (
+ self.controller.servers.get_all_servers_stats()
+ )
else:
try:
user_auth = self.controller.servers.get_authorized_servers_stats(
@@ -454,19 +456,19 @@ class PanelHandler(BaseHandler):
for server_id in user_order[:]:
for server in un_used_servers[:]:
if flag == 0:
- server["stats"][
- "importing"
- ] = self.controller.servers.get_import_status(
- str(server["stats"]["server_id"]["server_id"])
+ server["stats"]["importing"] = (
+ self.controller.servers.get_import_status(
+ str(server["stats"]["server_id"]["server_id"])
+ )
)
server["stats"]["crashed"] = self.controller.servers.is_crashed(
str(server["stats"]["server_id"]["server_id"])
)
try:
- server["stats"][
- "waiting_start"
- ] = self.controller.servers.get_waiting_start(
- str(server["stats"]["server_id"]["server_id"])
+ server["stats"]["waiting_start"] = (
+ self.controller.servers.get_waiting_start(
+ str(server["stats"]["server_id"]["server_id"])
+ )
)
except Exception as e:
logger.error(f"Failed to get server waiting to start: {e}")
@@ -543,9 +545,9 @@ class PanelHandler(BaseHandler):
server_id
)
if not self.failed_server:
- page_data[
- "server_stats"
- ] = self.controller.servers.get_server_stats_by_id(server_id)
+ page_data["server_stats"] = (
+ self.controller.servers.get_server_stats_by_id(server_id)
+ )
else:
server_temp_obj = self.controller.servers.get_server_data_by_id(
server_id
@@ -611,19 +613,19 @@ class PanelHandler(BaseHandler):
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
- page_data[
- "user_permissions"
- ] = self.controller.server_perms.get_user_id_permissions_list(
- exec_user["user_id"], server_id
+ page_data["user_permissions"] = (
+ self.controller.server_perms.get_user_id_permissions_list(
+ exec_user["user_id"], server_id
+ )
)
if not self.failed_server:
- page_data["server_stats"][
- "crashed"
- ] = self.controller.servers.is_crashed(server_id)
+ page_data["server_stats"]["crashed"] = (
+ self.controller.servers.is_crashed(server_id)
+ )
if not self.failed_server:
- page_data["server_stats"][
- "server_type"
- ] = self.controller.servers.get_server_type_by_id(server_id)
+ page_data["server_stats"]["server_type"] = (
+ self.controller.servers.get_server_type_by_id(server_id)
+ )
if not subpage:
for spage, perm in SUBPAGE_PERMS.items():
@@ -674,23 +676,23 @@ class PanelHandler(BaseHandler):
page_data["java_versions"] = page_java
if subpage == "backup":
server_info = self.controller.servers.get_server_data_by_id(server_id)
- page_data[
- "backup_config"
- ] = self.controller.management.get_backup_config(server_id)
+ page_data["backup_config"] = (
+ self.controller.management.get_backup_config(server_id)
+ )
exclusions = []
- page_data[
- "exclusions"
- ] = self.controller.management.get_excluded_backup_dirs(server_id)
- page_data[
- "backing_up"
- ] = self.controller.servers.get_server_instance_by_id(
- server_id
- ).is_backingup
- page_data[
- "backup_stats"
- ] = self.controller.servers.get_server_instance_by_id(
- server_id
- ).send_backup_status()
+ page_data["exclusions"] = (
+ self.controller.management.get_excluded_backup_dirs(server_id)
+ )
+ page_data["backing_up"] = (
+ self.controller.servers.get_server_instance_by_id(
+ server_id
+ ).is_backingup
+ )
+ page_data["backup_stats"] = (
+ self.controller.servers.get_server_instance_by_id(
+ server_id
+ ).send_backup_status()
+ )
# makes it so relative path is the only thing shown
for file in page_data["exclusions"]:
if Helpers.is_os_windows():
@@ -723,10 +725,10 @@ class PanelHandler(BaseHandler):
server_id, hours=(days * 24)
)
if subpage == "webhooks":
- page_data[
- "webhooks"
- ] = self.controller.management.get_webhooks_by_server(
- server_id, model=True
+ page_data["webhooks"] = (
+ self.controller.management.get_webhooks_by_server(
+ server_id, model=True
+ )
)
page_data["triggers"] = WebhookFactory.get_monitored_events()
@@ -758,9 +760,9 @@ class PanelHandler(BaseHandler):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access")
page_data["banned_players_html"] = get_banned_players_html()
- page_data[
- "banned_players"
- ] = self.controller.servers.get_banned_players(server_id)
+ page_data["banned_players"] = (
+ self.controller.servers.get_banned_players(server_id)
+ )
server_instance = self.controller.servers.get_server_instance_by_id(
server_id
)
@@ -925,9 +927,9 @@ class PanelHandler(BaseHandler):
if item not in page_data["backgrounds"]:
page_data["backgrounds"].append(item)
page_data["background"] = self.controller.cached_login
- page_data[
- "login_opacity"
- ] = self.controller.management.get_login_opacity()
+ page_data["login_opacity"] = (
+ self.controller.management.get_login_opacity()
+ )
page_data["active_link"] = "custom_login"
template = "panel/custom_login.html"
@@ -959,13 +961,11 @@ class PanelHandler(BaseHandler):
page_data["servers"] = []
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["role-servers"] = []
- page_data[
- "permissions_all"
- ] = self.controller.crafty_perms.list_defined_crafty_permissions()
+ page_data["permissions_all"] = (
+ self.controller.crafty_perms.list_defined_crafty_permissions()
+ )
page_data["permissions_list"] = set()
- page_data[
- "quantity_server"
- ] = (
+ page_data["quantity_server"] = (
self.controller.crafty_perms.list_all_crafty_permissions_quantity_limits() # pylint: disable=line-too-long
)
page_data["languages"] = []
@@ -1007,10 +1007,10 @@ class PanelHandler(BaseHandler):
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
server_id
)
- page_data[
- "user_permissions"
- ] = self.controller.server_perms.get_user_id_permissions_list(
- exec_user["user_id"], server_id
+ page_data["user_permissions"] = (
+ self.controller.server_perms.get_user_id_permissions_list(
+ exec_user["user_id"], server_id
+ )
)
page_data["permissions"] = {
"Commands": EnumPermissionsServer.COMMANDS,
@@ -1025,9 +1025,9 @@ class PanelHandler(BaseHandler):
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
server_id
)
- page_data["server_stats"][
- "server_type"
- ] = self.controller.servers.get_server_type_by_id(server_id)
+ page_data["server_stats"]["server_type"] = (
+ self.controller.servers.get_server_type_by_id(server_id)
+ )
page_data["new_webhook"] = True
page_data["webhook"] = {}
page_data["webhook"]["webhook_type"] = "Custom"
@@ -1061,10 +1061,10 @@ class PanelHandler(BaseHandler):
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
server_id
)
- page_data[
- "user_permissions"
- ] = self.controller.server_perms.get_user_id_permissions_list(
- exec_user["user_id"], server_id
+ page_data["user_permissions"] = (
+ self.controller.server_perms.get_user_id_permissions_list(
+ exec_user["user_id"], server_id
+ )
)
page_data["permissions"] = {
"Commands": EnumPermissionsServer.COMMANDS,
@@ -1079,9 +1079,9 @@ class PanelHandler(BaseHandler):
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
server_id
)
- page_data["server_stats"][
- "server_type"
- ] = self.controller.servers.get_server_type_by_id(server_id)
+ page_data["server_stats"]["server_type"] = (
+ self.controller.servers.get_server_type_by_id(server_id)
+ )
page_data["new_webhook"] = False
page_data["webhook"] = self.controller.management.get_webhook_by_id(
webhook_id
@@ -1121,10 +1121,10 @@ class PanelHandler(BaseHandler):
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
- page_data[
- "user_permissions"
- ] = self.controller.server_perms.get_user_id_permissions_list(
- exec_user["user_id"], server_id
+ page_data["user_permissions"] = (
+ self.controller.server_perms.get_user_id_permissions_list(
+ exec_user["user_id"], server_id
+ )
)
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
server_id
@@ -1132,9 +1132,9 @@ class PanelHandler(BaseHandler):
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
server_id
)
- page_data["server_stats"][
- "server_type"
- ] = self.controller.servers.get_server_type_by_id(server_id)
+ page_data["server_stats"]["server_type"] = (
+ self.controller.servers.get_server_type_by_id(server_id)
+ )
page_data["new_schedule"] = True
page_data["schedule"] = {}
page_data["schedule"]["children"] = []
@@ -1189,10 +1189,10 @@ class PanelHandler(BaseHandler):
"Config": EnumPermissionsServer.CONFIG,
"Players": EnumPermissionsServer.PLAYERS,
}
- page_data[
- "user_permissions"
- ] = self.controller.server_perms.get_user_id_permissions_list(
- exec_user["user_id"], server_id
+ page_data["user_permissions"] = (
+ self.controller.server_perms.get_user_id_permissions_list(
+ exec_user["user_id"], server_id
+ )
)
page_data["server_data"] = self.controller.servers.get_server_data_by_id(
server_id
@@ -1200,9 +1200,9 @@ class PanelHandler(BaseHandler):
page_data["server_stats"] = self.controller.servers.get_server_stats_by_id(
server_id
)
- page_data["server_stats"][
- "server_type"
- ] = self.controller.servers.get_server_type_by_id(server_id)
+ page_data["server_stats"]["server_type"] = (
+ self.controller.servers.get_server_type_by_id(server_id)
+ )
page_data["new_schedule"] = False
page_data["schedule"] = {}
page_data["schedule"]["server_id"] = server_id
@@ -1212,9 +1212,9 @@ class PanelHandler(BaseHandler):
page_data["schedule"]["name"] = schedule.name
else:
page_data["schedule"]["name"] = ""
- page_data["schedule"][
- "children"
- ] = self.controller.management.get_child_schedules(sch_id)
+ page_data["schedule"]["children"] = (
+ self.controller.management.get_child_schedules(sch_id)
+ )
# We check here to see if the command is any of the default ones.
# We do not want a user changing to a custom command
# and seeing our command there.
@@ -1280,16 +1280,16 @@ class PanelHandler(BaseHandler):
}
if exec_user["superuser"]:
page_data["users"] = self.controller.users.get_all_users()
- page_data[
- "permissions_all"
- ] = self.controller.crafty_perms.list_defined_crafty_permissions()
- page_data[
- "permissions_list"
- ] = self.controller.crafty_perms.get_crafty_permissions_list(user_id)
- page_data[
- "quantity_server"
- ] = self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
- user_id
+ page_data["permissions_all"] = (
+ self.controller.crafty_perms.list_defined_crafty_permissions()
+ )
+ page_data["permissions_list"] = (
+ self.controller.crafty_perms.get_crafty_permissions_list(user_id)
+ )
+ page_data["quantity_server"] = (
+ self.controller.crafty_perms.list_crafty_permissions_quantity_limits(
+ user_id
+ )
)
page_data["languages"] = []
page_data["languages"].append(
@@ -1349,12 +1349,12 @@ class PanelHandler(BaseHandler):
page_data["user"] = self.controller.users.get_user_by_id(user_id)
page_data["api_keys"] = self.controller.users.get_user_api_keys(user_id)
# self.controller.crafty_perms.list_defined_crafty_permissions()
- page_data[
- "server_permissions_all"
- ] = self.controller.server_perms.list_defined_permissions()
- page_data[
- "crafty_permissions_all"
- ] = self.controller.crafty_perms.list_defined_crafty_permissions()
+ page_data["server_permissions_all"] = (
+ self.controller.server_perms.list_defined_permissions()
+ )
+ page_data["crafty_permissions_all"] = (
+ self.controller.crafty_perms.list_defined_crafty_permissions()
+ )
if user_id is None:
self.redirect("/panel/error?error=Invalid User ID")
@@ -1442,9 +1442,9 @@ class PanelHandler(BaseHandler):
DatabaseShortcuts.get_data_obj(server.server_object)
)
page_data["servers_all"] = page_servers
- page_data[
- "permissions_all"
- ] = self.controller.server_perms.list_defined_permissions()
+ page_data["permissions_all"] = (
+ self.controller.server_perms.list_defined_permissions()
+ )
page_data["permissions_dict"] = {}
template = "panel/panel_edit_role.html"
@@ -1467,12 +1467,12 @@ class PanelHandler(BaseHandler):
DatabaseShortcuts.get_data_obj(server.server_object)
)
page_data["servers_all"] = page_servers
- page_data[
- "permissions_all"
- ] = self.controller.server_perms.list_defined_permissions()
- page_data[
- "permissions_dict"
- ] = self.controller.server_perms.get_role_permissions_dict(role_id)
+ page_data["permissions_all"] = (
+ self.controller.server_perms.list_defined_permissions()
+ )
+ page_data["permissions_dict"] = (
+ self.controller.server_perms.get_role_permissions_dict(role_id)
+ )
page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users()
diff --git a/app/classes/web/routes/api/crafty/config/index.py b/app/classes/web/routes/api/crafty/config/index.py
index a2bff723..c901732c 100644
--- a/app/classes/web/routes/api/crafty/config/index.py
+++ b/app/classes/web/routes/api/crafty/config/index.py
@@ -80,9 +80,13 @@ class ApiCraftyConfigIndexHandler(BaseApiHandler):
200,
{
"status": "ok",
- "data": self.controller.roles.get_all_role_ids()
- if get_only_ids
- else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
+ "data": (
+ self.controller.roles.get_all_role_ids()
+ if get_only_ids
+ else [
+ model_to_dict(r) for r in self.controller.roles.get_all_roles()
+ ]
+ ),
},
)
@@ -158,9 +162,13 @@ class ApiCraftyCustomizeIndexHandler(BaseApiHandler):
200,
{
"status": "ok",
- "data": self.controller.roles.get_all_role_ids()
- if get_only_ids
- else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
+ "data": (
+ self.controller.roles.get_all_role_ids()
+ if get_only_ids
+ else [
+ model_to_dict(r) for r in self.controller.roles.get_all_roles()
+ ]
+ ),
},
)
diff --git a/app/classes/web/routes/api/crafty/config/server_dir.py b/app/classes/web/routes/api/crafty/config/server_dir.py
index 4e41be14..91c4cc89 100644
--- a/app/classes/web/routes/api/crafty/config/server_dir.py
+++ b/app/classes/web/routes/api/crafty/config/server_dir.py
@@ -36,9 +36,13 @@ class ApiCraftyConfigServerDirHandler(BaseApiHandler):
200,
{
"status": "ok",
- "data": self.controller.roles.get_all_role_ids()
- if get_only_ids
- else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
+ "data": (
+ self.controller.roles.get_all_role_ids()
+ if get_only_ids
+ else [
+ model_to_dict(r) for r in self.controller.roles.get_all_roles()
+ ]
+ ),
},
)
diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py
index b0c773a7..dce6f453 100644
--- a/app/classes/web/routes/api/roles/index.py
+++ b/app/classes/web/routes/api/roles/index.py
@@ -87,9 +87,13 @@ class ApiRolesIndexHandler(BaseApiHandler):
200,
{
"status": "ok",
- "data": self.controller.roles.get_all_role_ids()
- if get_only_ids
- else [model_to_dict(r) for r in self.controller.roles.get_all_roles()],
+ "data": (
+ self.controller.roles.get_all_role_ids()
+ if get_only_ids
+ else [
+ model_to_dict(r) for r in self.controller.roles.get_all_roles()
+ ]
+ ),
},
)
diff --git a/app/classes/web/routes/api/roles/role/servers.py b/app/classes/web/routes/api/roles/role/servers.py
index b9b920ca..0a0eff6f 100644
--- a/app/classes/web/routes/api/roles/role/servers.py
+++ b/app/classes/web/routes/api/roles/role/servers.py
@@ -25,8 +25,12 @@ class ApiRolesRoleServersHandler(BaseApiHandler):
200,
{
"status": "ok",
- "data": PermissionsServers.get_server_ids_from_role(role_id)
- if get_only_ids
- else self.controller.roles.get_server_ids_and_perms_from_role(role_id),
+ "data": (
+ PermissionsServers.get_server_ids_from_role(role_id)
+ if get_only_ids
+ else self.controller.roles.get_server_ids_and_perms_from_role(
+ role_id
+ )
+ ),
},
)
diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py
index e940352e..545029aa 100644
--- a/app/classes/web/server_handler.py
+++ b/app/classes/web/server_handler.py
@@ -118,15 +118,17 @@ class ServerHandler(BaseHandler):
"lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"])
),
- "api_key": {
- "name": api_key.name,
- "created": api_key.created,
- "server_permissions": api_key.server_permissions,
- "crafty_permissions": api_key.crafty_permissions,
- "superuser": api_key.superuser,
- }
- if api_key is not None
- else None,
+ "api_key": (
+ {
+ "name": api_key.name,
+ "created": api_key.created,
+ "server_permissions": api_key.server_permissions,
+ "crafty_permissions": api_key.crafty_permissions,
+ "superuser": api_key.superuser,
+ }
+ if api_key is not None
+ else None
+ ),
"superuser": superuser,
}
diff --git a/requirements.txt b/requirements.txt
index 7b1adcfc..30414b35 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,13 +4,13 @@ argon2-cffi==23.1.0
cached_property==1.5.2
colorama==0.4.6
croniter==1.4.1
-cryptography==41.0.7
+cryptography==42.0.2
libgravatar==1.0.4
nh3==0.2.14
packaging==23.2
peewee==3.13
psutil==5.9.5
-pyOpenSSL==23.3.0
+pyOpenSSL==24.0.0
pyjwt==2.8.0
PyYAML==6.0.1
requests==2.31.0