diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed216490..c96af740 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,16 +3,21 @@
### New features
- Add better feedback for uploads with a progress bar ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
- Add ignored exit codes for crash detection ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/553))
+- Allow users to change the directory where Crafty Stores Servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/539))
+ *(Only for non-docker, docker users should change host volume mount)*
+- Add host storage display option to the dashboard ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/551))
### Bug fixes
- Fix exception related to page data on server start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/544))
- Fix logical issue with uploading dynamic files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/555))
- Fix backups failing by correctly using tz objects ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/556))
- Bump Cryptography/pyOpenSSL for CVE-2023-23931 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/554))
+- Fix debug logging to only display with the -v (verbose) flag ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/560))
### Tweaks
- Cleanup authentication helpers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/545))
- Optimize file upload progress WS ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
- Truncate sidebar servers to a max of 10 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/552))
-- Upgrade to FA 6. Add Translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))
+- Upgrade to FA 6. Add Translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/558))
+- Forge installer and Java Detection improvements ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/559))
### Lang
- Add additional translations to backups page strings ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/543))
- Add additional missing translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))
diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py
index 2811dce4..7c423da9 100644
--- a/app/classes/controllers/management_controller.py
+++ b/app/classes/controllers/management_controller.py
@@ -195,3 +195,14 @@ class ManagementController:
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
self.management_helper.del_excluded_backup_dir(server_id, dir_to_del)
+
+ # **********************************************************************************
+ # Crafty Methods
+ # **********************************************************************************
+ @staticmethod
+ def get_master_server_dir():
+ return HelpersManagement.get_master_server_dir()
+
+ @staticmethod
+ def set_master_server_dir(server_dir):
+ HelpersManagement.set_master_server_dir(server_dir)
diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py
index 4c97a6c7..608a1ced 100644
--- a/app/classes/controllers/servers_controller.py
+++ b/app/classes/controllers/servers_controller.py
@@ -102,6 +102,7 @@ class ServersController(metaclass=Singleton):
server_obj.server_id
)
server_instance.update_server_instance()
+
return ret
def get_history_stats(self, server_id, days):
@@ -163,10 +164,9 @@ class ServersController(metaclass=Singleton):
return server["server_obj"]
logger.warning(f"Unable to find server object for server id {server_id}")
- raise Exception(f"Unable to find server object for server id {server_id}")
+ raise ValueError(f"Unable to find server object for server id {server_id}")
def init_all_servers(self):
-
servers = self.get_all_defined_servers()
self.failed_servers = []
@@ -227,7 +227,6 @@ class ServersController(metaclass=Singleton):
)
def check_server_loaded(self, server_id_to_check: int):
-
logger.info(f"Checking to see if we already registered {server_id_to_check}")
for server in self.servers_list:
diff --git a/app/classes/minecraft/mc_ping.py b/app/classes/minecraft/mc_ping.py
index 1c52ab98..f760a8f9 100644
--- a/app/classes/minecraft/mc_ping.py
+++ b/app/classes/minecraft/mc_ping.py
@@ -19,7 +19,6 @@ class Server:
self.description = data.get("description")
# print(self.description)
if isinstance(self.description, dict):
-
# cat server
if "translate" in self.description:
self.description = self.description["translate"]
@@ -124,7 +123,7 @@ def ping(ip, port):
try:
k = sock.recv(1)
if not k:
- raise Exception()
+ raise ValueError()
except:
return 0
k = k[0]
diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py
index 3ecfdb8f..b70a8c40 100644
--- a/app/classes/minecraft/serverjars.py
+++ b/app/classes/minecraft/serverjars.py
@@ -104,7 +104,6 @@ class ServerJars:
logger.error(f"Unable to update serverjars.com cache file: {e}")
def refresh_cache(self):
-
cache_file = self.helper.serverjar_cache
cache_old = self.helper.is_file_older_than_x_days(cache_file)
diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py
index 5d4269c5..3d4cf483 100644
--- a/app/classes/minecraft/stats.py
+++ b/app/classes/minecraft/stats.py
@@ -211,7 +211,6 @@ class Stats:
@staticmethod
def get_server_dir_size(server_path):
-
total_size = 0
total_size = Helpers.get_dir_size(server_path)
@@ -221,7 +220,6 @@ class Stats:
return level_total_size
def get_server_players(self, server_id):
-
server = HelperServers.get_server_data_by_id(server_id)
logger.info(f"Getting players for server {server}")
@@ -295,7 +293,6 @@ class Stats:
@staticmethod
def parse_server_raknet_ping(ping_obj: object):
-
try:
server_icon = base64.encodebytes(ping_obj["icon"])
except Exception as e:
diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py
index 22383408..7430f332 100644
--- a/app/classes/models/crafty_permissions.py
+++ b/app/classes/models/crafty_permissions.py
@@ -15,6 +15,7 @@ from app.classes.shared.permission_helper import PermissionHelper
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# User_Crafty Class
# **********************************************************************************
diff --git a/app/classes/models/management.py b/app/classes/models/management.py
index c2b5afde..73a0d9f4 100644
--- a/app/classes/models/management.py
+++ b/app/classes/models/management.py
@@ -20,6 +20,7 @@ from app.classes.shared.main_models import DatabaseShortcuts
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# Audit_Log Class
# **********************************************************************************
@@ -46,6 +47,7 @@ class CraftySettings(BaseModel):
cookie_secret = CharField(default="")
login_photo = CharField(default="login_1.jpg")
login_opacity = IntegerField(default=100)
+ master_server_dir = CharField(default="")
class Meta:
table_name = "crafty_settings"
@@ -271,6 +273,19 @@ class HelpersManagement:
CraftySettings.id == 1
).execute()
+ @staticmethod
+ def get_master_server_dir():
+ settings = CraftySettings.select(CraftySettings.master_server_dir).where(
+ CraftySettings.id == 1
+ )
+ return settings[0].master_server_dir
+
+ @staticmethod
+ def set_master_server_dir(server_dir):
+ CraftySettings.update({CraftySettings.master_server_dir: server_dir}).where(
+ CraftySettings.id == 1
+ ).execute()
+
# **********************************************************************************
# Schedules Methods
# **********************************************************************************
diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py
index 541f67e8..abc98735 100644
--- a/app/classes/models/roles.py
+++ b/app/classes/models/roles.py
@@ -15,6 +15,7 @@ from app.classes.shared.helpers import Helpers
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# Roles Class
# **********************************************************************************
diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py
index 8844b3df..eb5e3f35 100644
--- a/app/classes/models/server_permissions.py
+++ b/app/classes/models/server_permissions.py
@@ -16,6 +16,7 @@ from app.classes.shared.permission_helper import PermissionHelper
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# Role Servers Class
# **********************************************************************************
diff --git a/app/classes/models/server_stats.py b/app/classes/models/server_stats.py
index ccb21879..14f85ad3 100644
--- a/app/classes/models/server_stats.py
+++ b/app/classes/models/server_stats.py
@@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
+
# **********************************************************************************
# Servers Stats Class
# **********************************************************************************
diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py
index 96f606a9..a83fd0a2 100644
--- a/app/classes/models/servers.py
+++ b/app/classes/models/servers.py
@@ -15,6 +15,7 @@ from app.classes.models.base_model import BaseModel
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# Servers Model
# **********************************************************************************
diff --git a/app/classes/models/users.py b/app/classes/models/users.py
index 9b4805a3..496e8d2c 100644
--- a/app/classes/models/users.py
+++ b/app/classes/models/users.py
@@ -21,6 +21,7 @@ from app.classes.models.roles import Roles, HelperRoles
logger = logging.getLogger(__name__)
+
# **********************************************************************************
# Users Class
# **********************************************************************************
@@ -58,6 +59,7 @@ PUBLIC_USER_ATTRS: t.Final = [
"lang", # maybe remove?
]
+
# **********************************************************************************
# API Keys Class
# **********************************************************************************
diff --git a/app/classes/shared/authentication.py b/app/classes/shared/authentication.py
index 1a809bfc..fad8b730 100644
--- a/app/classes/shared/authentication.py
+++ b/app/classes/shared/authentication.py
@@ -76,7 +76,7 @@ class Authentication:
output = self.check(token)
if output is None:
- raise Exception("Invalid token")
+ raise ValueError("Invalid token")
return output
def check_bool(self, token) -> bool:
diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py
index 7e1e9456..26fdd2f0 100644
--- a/app/classes/shared/command.py
+++ b/app/classes/shared/command.py
@@ -58,7 +58,6 @@ class MainPrompt(cmd.Cmd):
Console.info("Unknown migration command")
def do_set_passwd(self, line):
-
try:
username = str(line).lower()
# If no user is found it returns None
diff --git a/app/classes/shared/console.py b/app/classes/shared/console.py
index 84362032..2c463e5a 100644
--- a/app/classes/shared/console.py
+++ b/app/classes/shared/console.py
@@ -19,6 +19,8 @@ except ModuleNotFoundError as ex:
class Console:
+ level = ""
+
def __init__(self):
if "colorama" in sys.modules:
init()
@@ -61,8 +63,9 @@ class Console:
@staticmethod
def debug(message):
- date_time = Console.get_fmt_date_time()
- Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
+ if Console.level == "debug":
+ date_time = Console.get_fmt_date_time()
+ Console.magenta(f"[+] Crafty: {date_time} - DEBUG:\t{message}")
@staticmethod
def info(message):
diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py
index e47590c5..33fd81df 100644
--- a/app/classes/shared/helpers.py
+++ b/app/classes/shared/helpers.py
@@ -15,6 +15,7 @@ import html
import zipfile
import pathlib
import ctypes
+import shutil
import subprocess
import itertools
from datetime import datetime
@@ -94,7 +95,7 @@ class Helpers:
try:
# Get tags from Gitlab, select the latest and parse the semver
response = get(
- "https://gitlab.com/api/v4/projects/20430749/repository/tags"
+ "https://gitlab.com/api/v4/projects/20430749/repository/tags", timeout=1
)
if response.status_code == 200:
remote_version = pkg_version.parse(json.loads(response.text)[0]["name"])
@@ -131,7 +132,7 @@ class Helpers:
try:
# Get minecraft server download page
# (hopefully the don't change the structure)
- download_page = get(url, headers=headers)
+ download_page = get(url, headers=headers, timeout=1)
# Search for our string targets
win_download_url = re.search(target_win, download_page.text).group(0)
@@ -145,6 +146,22 @@ class Helpers:
logger.error(f"Unable to resolve remote bedrock download url! \n{e}")
return False
+ def detect_java(self):
+ if len(self.find_java_installs()) > 0:
+ return True
+
+ # We'll use this as a fallback for systems
+ # That do not properly setup reg keys or
+ # Update alternatives
+ if self.is_os_windows():
+ if shutil.which("java.exe"):
+ return True
+ else:
+ if shutil.which("java"):
+ return True
+
+ return False
+
@staticmethod
def find_java_installs():
# If we're windows return oracle java versions,
@@ -281,7 +298,7 @@ class Helpers:
@staticmethod
def check_port(server_port):
try:
- ip = get("https://api.ipify.org").content.decode("utf8")
+ ip = get("https://api.ipify.org", timeout=1).content.decode("utf8")
except:
ip = "google.com"
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -417,6 +434,7 @@ class Helpers:
"allow_nsfw_profile_pictures": False,
"enable_user_self_delete": False,
"reset_secrets_on_next_boot": False,
+ "monitored_mounts": Helpers.get_all_mounts(),
"dir_size_poll_freq_minutes": 5,
}
@@ -437,11 +455,27 @@ class Helpers:
return data
@staticmethod
- def is_subdir(server_path, root_dir):
+ def get_all_mounts():
+ mounts = []
+ for item in psutil.disk_partitions(all=False):
+ mounts.append(item.mountpoint)
+
+ return mounts
+
+ def is_subdir(self, 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 self.is_os_windows():
+ try:
+ relative = os.path.relpath(server_path, root_dir)
+ except:
+ # Windows will crash out if two paths are on different
+ # Drives We can happily return false if this is the case.
+ # Since two different drives will not be relative to eachother.
+ return False
+ else:
+ relative = os.path.relpath(server_path, root_dir)
if relative.startswith(os.pardir):
return False
@@ -597,7 +631,6 @@ class Helpers:
# open our file
with open(file_name, "r", encoding="utf-8") as f:
-
# seek
f.seek(0, 2)
@@ -753,7 +786,7 @@ class Helpers:
use_ssl=True,
) # + "?d=404"
try:
- if requests.head(url).status_code != 404:
+ if requests.head(url, timeout=1).status_code != 404:
profile_url = url
except Exception as e:
logger.debug(f"Could not pull resource from Gravatar with error {e}")
@@ -762,7 +795,6 @@ class Helpers:
@staticmethod
def get_file_contents(path: str, lines=100):
-
contents = ""
if os.path.exists(path) and os.path.isfile(path):
@@ -783,12 +815,10 @@ class Helpers:
return False
def create_session_file(self, ignore=False):
-
if ignore and os.path.exists(self.session_file):
os.remove(self.session_file)
if os.path.exists(self.session_file):
-
file_data = self.get_file_contents(self.session_file)
try:
data = json.loads(file_data)
@@ -888,15 +918,16 @@ class Helpers:
try:
os.makedirs(path)
logger.debug(f"Created Directory : {path}")
+ return True
# directory already exists - non-blocking error
except FileExistsError:
- pass
+ return True
except PermissionError as e:
logger.critical(f"Check generated exception due to permssion error: {e}")
+ return False
def create_self_signed_cert(self, cert_dir=None):
-
if cert_dir is None:
cert_dir = os.path.join(self.config_dir, "web", "certs")
@@ -978,6 +1009,15 @@ class Helpers:
def is_os_windows():
return os.name == "nt"
+ @staticmethod
+ def is_env_docker():
+ path = "/proc/self/cgroup"
+ return (
+ os.path.exists("/.dockerenv")
+ or os.path.isfile(path)
+ and any("docker" in line for line in open(path, encoding="utf-8"))
+ )
+
@staticmethod
def wtol_path(w_path):
l_path = w_path.replace("\\", "/")
@@ -1027,9 +1067,9 @@ class Helpers:
if filename not in self.ignored_names:
output += f"""