mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into bug/verbose-console
This commit is contained in:
commit
69480c8ff7
@ -3,6 +3,9 @@
|
|||||||
### New features
|
### New features
|
||||||
- Add better feedback for uploads with a progress bar ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
|
- 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))
|
- 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)) <br>
|
||||||
|
*(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
|
### Bug fixes
|
||||||
- Fix exception related to page data on server start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/544))
|
- 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 logical issue with uploading dynamic files ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/555))
|
||||||
@ -12,7 +15,8 @@
|
|||||||
- Cleanup authentication helpers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/545))
|
- 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))
|
- 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))
|
- 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
|
### Lang
|
||||||
- Add additional translations to backups page strings ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/543))
|
- 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))
|
- Add additional missing translations ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/549))
|
||||||
|
@ -195,3 +195,14 @@ class ManagementController:
|
|||||||
|
|
||||||
def del_excluded_backup_dir(self, server_id: int, dir_to_del: str):
|
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)
|
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)
|
||||||
|
@ -102,6 +102,7 @@ class ServersController(metaclass=Singleton):
|
|||||||
server_obj.server_id
|
server_obj.server_id
|
||||||
)
|
)
|
||||||
server_instance.update_server_instance()
|
server_instance.update_server_instance()
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_history_stats(self, server_id, days):
|
def get_history_stats(self, server_id, days):
|
||||||
@ -163,10 +164,9 @@ class ServersController(metaclass=Singleton):
|
|||||||
return server["server_obj"]
|
return server["server_obj"]
|
||||||
|
|
||||||
logger.warning(f"Unable to find server object for server id {server_id}")
|
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):
|
def init_all_servers(self):
|
||||||
|
|
||||||
servers = self.get_all_defined_servers()
|
servers = self.get_all_defined_servers()
|
||||||
self.failed_servers = []
|
self.failed_servers = []
|
||||||
|
|
||||||
@ -227,7 +227,6 @@ class ServersController(metaclass=Singleton):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def check_server_loaded(self, server_id_to_check: int):
|
def check_server_loaded(self, server_id_to_check: int):
|
||||||
|
|
||||||
logger.info(f"Checking to see if we already registered {server_id_to_check}")
|
logger.info(f"Checking to see if we already registered {server_id_to_check}")
|
||||||
|
|
||||||
for server in self.servers_list:
|
for server in self.servers_list:
|
||||||
|
@ -19,7 +19,6 @@ class Server:
|
|||||||
self.description = data.get("description")
|
self.description = data.get("description")
|
||||||
# print(self.description)
|
# print(self.description)
|
||||||
if isinstance(self.description, dict):
|
if isinstance(self.description, dict):
|
||||||
|
|
||||||
# cat server
|
# cat server
|
||||||
if "translate" in self.description:
|
if "translate" in self.description:
|
||||||
self.description = self.description["translate"]
|
self.description = self.description["translate"]
|
||||||
@ -124,7 +123,7 @@ def ping(ip, port):
|
|||||||
try:
|
try:
|
||||||
k = sock.recv(1)
|
k = sock.recv(1)
|
||||||
if not k:
|
if not k:
|
||||||
raise Exception()
|
raise ValueError()
|
||||||
except:
|
except:
|
||||||
return 0
|
return 0
|
||||||
k = k[0]
|
k = k[0]
|
||||||
|
@ -104,7 +104,6 @@ class ServerJars:
|
|||||||
logger.error(f"Unable to update serverjars.com cache file: {e}")
|
logger.error(f"Unable to update serverjars.com cache file: {e}")
|
||||||
|
|
||||||
def refresh_cache(self):
|
def refresh_cache(self):
|
||||||
|
|
||||||
cache_file = self.helper.serverjar_cache
|
cache_file = self.helper.serverjar_cache
|
||||||
cache_old = self.helper.is_file_older_than_x_days(cache_file)
|
cache_old = self.helper.is_file_older_than_x_days(cache_file)
|
||||||
|
|
||||||
|
@ -211,7 +211,6 @@ class Stats:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_world_size(server_path):
|
def get_world_size(server_path):
|
||||||
|
|
||||||
total_size = 0
|
total_size = 0
|
||||||
|
|
||||||
total_size = Helpers.get_dir_size(server_path)
|
total_size = Helpers.get_dir_size(server_path)
|
||||||
@ -221,7 +220,6 @@ class Stats:
|
|||||||
return level_total_size
|
return level_total_size
|
||||||
|
|
||||||
def get_server_players(self, server_id):
|
def get_server_players(self, server_id):
|
||||||
|
|
||||||
server = HelperServers.get_server_data_by_id(server_id)
|
server = HelperServers.get_server_data_by_id(server_id)
|
||||||
|
|
||||||
logger.info(f"Getting players for server {server}")
|
logger.info(f"Getting players for server {server}")
|
||||||
@ -295,7 +293,6 @@ class Stats:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_server_raknet_ping(ping_obj: object):
|
def parse_server_raknet_ping(ping_obj: object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_icon = base64.encodebytes(ping_obj["icon"])
|
server_icon = base64.encodebytes(ping_obj["icon"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -15,6 +15,7 @@ from app.classes.shared.permission_helper import PermissionHelper
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# User_Crafty Class
|
# User_Crafty Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -20,6 +20,7 @@ from app.classes.shared.main_models import DatabaseShortcuts
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Class
|
# Audit_Log Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -46,6 +47,7 @@ class CraftySettings(BaseModel):
|
|||||||
cookie_secret = CharField(default="")
|
cookie_secret = CharField(default="")
|
||||||
login_photo = CharField(default="login_1.jpg")
|
login_photo = CharField(default="login_1.jpg")
|
||||||
login_opacity = IntegerField(default=100)
|
login_opacity = IntegerField(default=100)
|
||||||
|
master_server_dir = CharField(default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "crafty_settings"
|
table_name = "crafty_settings"
|
||||||
@ -271,6 +273,19 @@ class HelpersManagement:
|
|||||||
CraftySettings.id == 1
|
CraftySettings.id == 1
|
||||||
).execute()
|
).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
|
# Schedules Methods
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -15,6 +15,7 @@ from app.classes.shared.helpers import Helpers
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Roles Class
|
# Roles Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -16,6 +16,7 @@ from app.classes.shared.permission_helper import PermissionHelper
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Role Servers Class
|
# Role Servers Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
|
|||||||
peewee_logger = logging.getLogger("peewee")
|
peewee_logger = logging.getLogger("peewee")
|
||||||
peewee_logger.setLevel(logging.INFO)
|
peewee_logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Stats Class
|
# Servers Stats Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -15,6 +15,7 @@ from app.classes.models.base_model import BaseModel
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Servers Model
|
# Servers Model
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -21,6 +21,7 @@ from app.classes.models.roles import Roles, HelperRoles
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Users Class
|
# Users Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -58,6 +59,7 @@ PUBLIC_USER_ATTRS: t.Final = [
|
|||||||
"lang", # maybe remove?
|
"lang", # maybe remove?
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# API Keys Class
|
# API Keys Class
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -76,7 +76,7 @@ class Authentication:
|
|||||||
|
|
||||||
output = self.check(token)
|
output = self.check(token)
|
||||||
if output is None:
|
if output is None:
|
||||||
raise Exception("Invalid token")
|
raise ValueError("Invalid token")
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def check_bool(self, token) -> bool:
|
def check_bool(self, token) -> bool:
|
||||||
|
@ -58,7 +58,6 @@ class MainPrompt(cmd.Cmd):
|
|||||||
Console.info("Unknown migration command")
|
Console.info("Unknown migration command")
|
||||||
|
|
||||||
def do_set_passwd(self, line):
|
def do_set_passwd(self, line):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
username = str(line).lower()
|
username = str(line).lower()
|
||||||
# If no user is found it returns None
|
# If no user is found it returns None
|
||||||
|
@ -15,6 +15,7 @@ import html
|
|||||||
import zipfile
|
import zipfile
|
||||||
import pathlib
|
import pathlib
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import itertools
|
import itertools
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -94,7 +95,7 @@ class Helpers:
|
|||||||
try:
|
try:
|
||||||
# Get tags from Gitlab, select the latest and parse the semver
|
# Get tags from Gitlab, select the latest and parse the semver
|
||||||
response = get(
|
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:
|
if response.status_code == 200:
|
||||||
remote_version = pkg_version.parse(json.loads(response.text)[0]["name"])
|
remote_version = pkg_version.parse(json.loads(response.text)[0]["name"])
|
||||||
@ -131,7 +132,7 @@ class Helpers:
|
|||||||
try:
|
try:
|
||||||
# Get minecraft server download page
|
# Get minecraft server download page
|
||||||
# (hopefully the don't change the structure)
|
# (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
|
# Search for our string targets
|
||||||
win_download_url = re.search(target_win, download_page.text).group(0)
|
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}")
|
logger.error(f"Unable to resolve remote bedrock download url! \n{e}")
|
||||||
return False
|
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
|
@staticmethod
|
||||||
def find_java_installs():
|
def find_java_installs():
|
||||||
# If we're windows return oracle java versions,
|
# If we're windows return oracle java versions,
|
||||||
@ -281,7 +298,7 @@ class Helpers:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def check_port(server_port):
|
def check_port(server_port):
|
||||||
try:
|
try:
|
||||||
ip = get("https://api.ipify.org").content.decode("utf8")
|
ip = get("https://api.ipify.org", timeout=1).content.decode("utf8")
|
||||||
except:
|
except:
|
||||||
ip = "google.com"
|
ip = "google.com"
|
||||||
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
@ -417,6 +434,7 @@ class Helpers:
|
|||||||
"allow_nsfw_profile_pictures": False,
|
"allow_nsfw_profile_pictures": False,
|
||||||
"enable_user_self_delete": False,
|
"enable_user_self_delete": False,
|
||||||
"reset_secrets_on_next_boot": False,
|
"reset_secrets_on_next_boot": False,
|
||||||
|
"monitored_mounts": Helpers.get_all_mounts(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_all_settings(self):
|
def get_all_settings(self):
|
||||||
@ -436,11 +454,27 @@ class Helpers:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
@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)
|
server_path = os.path.realpath(server_path)
|
||||||
root_dir = os.path.realpath(root_dir)
|
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):
|
if relative.startswith(os.pardir):
|
||||||
return False
|
return False
|
||||||
@ -596,7 +630,6 @@ class Helpers:
|
|||||||
|
|
||||||
# open our file
|
# open our file
|
||||||
with open(file_name, "r", encoding="utf-8") as f:
|
with open(file_name, "r", encoding="utf-8") as f:
|
||||||
|
|
||||||
# seek
|
# seek
|
||||||
f.seek(0, 2)
|
f.seek(0, 2)
|
||||||
|
|
||||||
@ -752,7 +785,7 @@ class Helpers:
|
|||||||
use_ssl=True,
|
use_ssl=True,
|
||||||
) # + "?d=404"
|
) # + "?d=404"
|
||||||
try:
|
try:
|
||||||
if requests.head(url).status_code != 404:
|
if requests.head(url, timeout=1).status_code != 404:
|
||||||
profile_url = url
|
profile_url = url
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Could not pull resource from Gravatar with error {e}")
|
logger.debug(f"Could not pull resource from Gravatar with error {e}")
|
||||||
@ -761,7 +794,6 @@ class Helpers:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_file_contents(path: str, lines=100):
|
def get_file_contents(path: str, lines=100):
|
||||||
|
|
||||||
contents = ""
|
contents = ""
|
||||||
|
|
||||||
if os.path.exists(path) and os.path.isfile(path):
|
if os.path.exists(path) and os.path.isfile(path):
|
||||||
@ -782,12 +814,10 @@ class Helpers:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def create_session_file(self, ignore=False):
|
def create_session_file(self, ignore=False):
|
||||||
|
|
||||||
if ignore and os.path.exists(self.session_file):
|
if ignore and os.path.exists(self.session_file):
|
||||||
os.remove(self.session_file)
|
os.remove(self.session_file)
|
||||||
|
|
||||||
if os.path.exists(self.session_file):
|
if os.path.exists(self.session_file):
|
||||||
|
|
||||||
file_data = self.get_file_contents(self.session_file)
|
file_data = self.get_file_contents(self.session_file)
|
||||||
try:
|
try:
|
||||||
data = json.loads(file_data)
|
data = json.loads(file_data)
|
||||||
@ -887,15 +917,16 @@ class Helpers:
|
|||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
logger.debug(f"Created Directory : {path}")
|
logger.debug(f"Created Directory : {path}")
|
||||||
|
return True
|
||||||
|
|
||||||
# directory already exists - non-blocking error
|
# directory already exists - non-blocking error
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
pass
|
return True
|
||||||
except PermissionError as e:
|
except PermissionError as e:
|
||||||
logger.critical(f"Check generated exception due to permssion error: {e}")
|
logger.critical(f"Check generated exception due to permssion error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def create_self_signed_cert(self, cert_dir=None):
|
def create_self_signed_cert(self, cert_dir=None):
|
||||||
|
|
||||||
if cert_dir is None:
|
if cert_dir is None:
|
||||||
cert_dir = os.path.join(self.config_dir, "web", "certs")
|
cert_dir = os.path.join(self.config_dir, "web", "certs")
|
||||||
|
|
||||||
@ -977,6 +1008,15 @@ class Helpers:
|
|||||||
def is_os_windows():
|
def is_os_windows():
|
||||||
return os.name == "nt"
|
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
|
@staticmethod
|
||||||
def wtol_path(w_path):
|
def wtol_path(w_path):
|
||||||
l_path = w_path.replace("\\", "/")
|
l_path = w_path.replace("\\", "/")
|
||||||
@ -1026,9 +1066,9 @@ class Helpers:
|
|||||||
if filename not in self.ignored_names:
|
if filename not in self.ignored_names:
|
||||||
output += f"""<li id="{dpath}li" class="tree-item"
|
output += f"""<li id="{dpath}li" class="tree-item"
|
||||||
data-path="{dpath}">
|
data-path="{dpath}">
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||||
class="tree-caret tree-ctx-item tree-folder">
|
class="tree-caret tree-ctx-item tree-folder">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||||
data-name="{filename}" onclick="getDirView(event)">
|
data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||||
@ -1047,7 +1087,6 @@ class Helpers:
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
def generate_dir(self, folder, output=""):
|
def generate_dir(self, folder, output=""):
|
||||||
|
|
||||||
dir_list = []
|
dir_list = []
|
||||||
unsorted_files = []
|
unsorted_files = []
|
||||||
file_list = os.listdir(folder)
|
file_list = os.listdir(folder)
|
||||||
@ -1068,9 +1107,9 @@ class Helpers:
|
|||||||
if filename not in self.ignored_names:
|
if filename not in self.ignored_names:
|
||||||
output += f"""<li id="{dpath}li" class="tree-item"
|
output += f"""<li id="{dpath}li" class="tree-item"
|
||||||
data-path="{dpath}">
|
data-path="{dpath}">
|
||||||
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
\n<div id="{dpath}" data-path="{dpath}" data-name="{filename}"
|
||||||
class="tree-caret tree-ctx-item tree-folder">
|
class="tree-caret tree-ctx-item tree-folder">
|
||||||
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
<span id="{dpath}span" class="files-tree-title" data-path="{dpath}"
|
||||||
data-name="{filename}" onclick="getDirView(event)">
|
data-name="{filename}" onclick="getDirView(event)">
|
||||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||||
|
@ -226,7 +226,6 @@ class ImportHelpers:
|
|||||||
download_thread.start()
|
download_thread.start()
|
||||||
|
|
||||||
def download_threaded_bedrock_server(self, path, new_id):
|
def download_threaded_bedrock_server(self, path, new_id):
|
||||||
|
|
||||||
# downloads zip from remote url
|
# downloads zip from remote url
|
||||||
try:
|
try:
|
||||||
bedrock_url = Helpers.get_latest_bedrock_url()
|
bedrock_url = Helpers.get_latest_bedrock_url()
|
||||||
|
@ -10,7 +10,6 @@ class Install:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def do_install(self):
|
def do_install(self):
|
||||||
|
|
||||||
# are we in a venv?
|
# are we in a venv?
|
||||||
if not self.is_venv():
|
if not self.is_venv():
|
||||||
print("Crafty Requires a venv to install")
|
print("Crafty Requires a venv to install")
|
||||||
|
@ -6,6 +6,7 @@ import platform
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
from peewee import DoesNotExist
|
from peewee import DoesNotExist
|
||||||
|
|
||||||
# TZLocal is set as a hidden import on win pipeline
|
# TZLocal is set as a hidden import on win pipeline
|
||||||
@ -347,7 +348,7 @@ class Controller:
|
|||||||
elif root_create_data["create_type"] == "import_zip":
|
elif root_create_data["create_type"] == "import_zip":
|
||||||
# TODO: Copy files from the zip file to the new server directory
|
# TODO: Copy files from the zip file to the new server directory
|
||||||
server_file = create_data["jarfile"]
|
server_file = create_data["jarfile"]
|
||||||
raise Exception("Not yet implemented")
|
raise NotImplementedError("Not yet implemented")
|
||||||
_create_server_properties_if_needed(
|
_create_server_properties_if_needed(
|
||||||
create_data["server_properties_port"],
|
create_data["server_properties_port"],
|
||||||
)
|
)
|
||||||
@ -379,7 +380,7 @@ class Controller:
|
|||||||
logger.error(f"Server import failed with error: {ex}")
|
logger.error(f"Server import failed with error: {ex}")
|
||||||
elif root_create_data["create_type"] == "import_zip":
|
elif root_create_data["create_type"] == "import_zip":
|
||||||
# TODO: Copy files from the zip file to the new server directory
|
# TODO: Copy files from the zip file to the new server directory
|
||||||
raise Exception("Not yet implemented")
|
raise NotImplementedError("Not yet implemented")
|
||||||
|
|
||||||
_create_server_properties_if_needed(0, True)
|
_create_server_properties_if_needed(0, True)
|
||||||
|
|
||||||
@ -401,7 +402,7 @@ class Controller:
|
|||||||
logger.error(f"Server import failed with error: {ex}")
|
logger.error(f"Server import failed with error: {ex}")
|
||||||
elif root_create_data["create_type"] == "import_zip":
|
elif root_create_data["create_type"] == "import_zip":
|
||||||
# TODO: Copy files from the zip file to the new server directory
|
# TODO: Copy files from the zip file to the new server directory
|
||||||
raise Exception("Not yet implemented")
|
raise NotImplementedError("Not yet implemented")
|
||||||
|
|
||||||
_create_server_properties_if_needed(0, True)
|
_create_server_properties_if_needed(0, True)
|
||||||
|
|
||||||
@ -941,7 +942,6 @@ class Controller:
|
|||||||
def remove_server(self, server_id, files):
|
def remove_server(self, server_id, files):
|
||||||
counter = 0
|
counter = 0
|
||||||
for server in self.servers.servers_list:
|
for server in self.servers.servers_list:
|
||||||
|
|
||||||
# if this is the droid... im mean server we are looking for...
|
# if this is the droid... im mean server we are looking for...
|
||||||
if str(server["server_id"]) == str(server_id):
|
if str(server["server_id"]) == str(server_id):
|
||||||
server_data = self.servers.get_server_data(server_id)
|
server_data = self.servers.get_server_data(server_id)
|
||||||
@ -1003,3 +1003,122 @@ class Controller:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def clear_support_status():
|
def clear_support_status():
|
||||||
HelperUsers.clear_support_status()
|
HelperUsers.clear_support_status()
|
||||||
|
|
||||||
|
def set_master_server_dir(self, server_dir):
|
||||||
|
# This method should only be used on a first run basis if the server dir is ""
|
||||||
|
self.helper.servers_dir = server_dir
|
||||||
|
HelpersManagement.set_master_server_dir(server_dir)
|
||||||
|
|
||||||
|
def update_master_server_dir(self, server_dir, user_id):
|
||||||
|
move_thread = threading.Thread(
|
||||||
|
name="dir_move",
|
||||||
|
target=self.t_update_master_server_dir,
|
||||||
|
daemon=True,
|
||||||
|
args=(
|
||||||
|
server_dir,
|
||||||
|
user_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
move_thread.start()
|
||||||
|
|
||||||
|
def t_update_master_server_dir(self, server_dir, user_id):
|
||||||
|
server_dir = self.helper.wtol_path(server_dir)
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config", "move_status", "Checking dir"
|
||||||
|
)
|
||||||
|
current_master = self.helper.wtol_path(
|
||||||
|
HelpersManagement.get_master_server_dir()
|
||||||
|
)
|
||||||
|
if current_master == server_dir:
|
||||||
|
logger.info(
|
||||||
|
"Admin tried to change server dir to current server dir. Canceling..."
|
||||||
|
)
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config",
|
||||||
|
"move_status",
|
||||||
|
"done",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if self.helper.is_subdir(server_dir, current_master):
|
||||||
|
logger.info(
|
||||||
|
"Admin tried to change server dir to be inside a sub directory of the"
|
||||||
|
" current server dir. This will result in a copy loop."
|
||||||
|
)
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config",
|
||||||
|
"move_status",
|
||||||
|
"done",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config", "move_status", "Checking permissions"
|
||||||
|
)
|
||||||
|
if not self.helper.ensure_dir_exists(os.path.join(server_dir, "servers")):
|
||||||
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
user_id,
|
||||||
|
"send_start_error",
|
||||||
|
{
|
||||||
|
"error": "Crafty failed to move server dir. "
|
||||||
|
"It seems Crafty lacks permission to write to "
|
||||||
|
"the new directory."
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
# set the cached serve dir
|
||||||
|
self.helper.servers_dir = server_dir
|
||||||
|
# set DB server dir
|
||||||
|
HelpersManagement.set_master_server_dir(server_dir)
|
||||||
|
servers = self.servers.get_all_defined_servers()
|
||||||
|
# move the servers
|
||||||
|
for server in servers:
|
||||||
|
server_path = server.get("path")
|
||||||
|
new_server_path = os.path.join(
|
||||||
|
server_dir, "servers", server.get("server_uuid")
|
||||||
|
)
|
||||||
|
if os.path.isdir(server_path):
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config",
|
||||||
|
"move_status",
|
||||||
|
f"Moving {server.get('server_name')}",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.file_helper.move_dir(
|
||||||
|
server_path,
|
||||||
|
new_server_path,
|
||||||
|
)
|
||||||
|
except FileExistsError as e:
|
||||||
|
logger.error(f"Failed to move server with error: {e}")
|
||||||
|
|
||||||
|
server_obj = self.servers.get_server_obj(server.get("server_id"))
|
||||||
|
|
||||||
|
# reset executable path
|
||||||
|
if current_master in server["executable"]:
|
||||||
|
server_obj.executable = str(server["executable"]).replace(
|
||||||
|
current_master, server_dir
|
||||||
|
)
|
||||||
|
# reset run command path
|
||||||
|
if current_master in server["execution_command"]:
|
||||||
|
server_obj.execution_command = str(server["execution_command"]).replace(
|
||||||
|
current_master, server_dir
|
||||||
|
)
|
||||||
|
# reset log path
|
||||||
|
if current_master in server["log_path"]:
|
||||||
|
server_obj.log_path = str(server["log_path"]).replace(
|
||||||
|
current_master, server_dir
|
||||||
|
)
|
||||||
|
server_obj.path = new_server_path
|
||||||
|
failed = False
|
||||||
|
for s in self.servers.failed_servers:
|
||||||
|
if int(s["server_id"]) == int(server.get("server_id")):
|
||||||
|
failed = True
|
||||||
|
if not failed:
|
||||||
|
self.servers.update_server(server_obj)
|
||||||
|
else:
|
||||||
|
self.servers.update_unloaded_server(server_obj)
|
||||||
|
self.servers.init_all_servers()
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/panel_config",
|
||||||
|
"move_status",
|
||||||
|
"done",
|
||||||
|
)
|
||||||
|
@ -158,6 +158,8 @@ class ServerInstance:
|
|||||||
self.jar_update_url = server_data.executable_update_url
|
self.jar_update_url = server_data.executable_update_url
|
||||||
self.name = server_data.server_name
|
self.name = server_data.server_name
|
||||||
self.server_object = server_data
|
self.server_object = server_data
|
||||||
|
self.stats_helper.select_database()
|
||||||
|
self.reload_server_settings()
|
||||||
|
|
||||||
def reload_server_settings(self):
|
def reload_server_settings(self):
|
||||||
server_data = HelperServers.get_server_data_by_id(self.server_id)
|
server_data = HelperServers.get_server_data_by_id(self.server_id)
|
||||||
@ -445,7 +447,7 @@ class ServerInstance:
|
|||||||
)
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
# Checks for java on initial fail
|
# Checks for java on initial fail
|
||||||
if os.system("java -version") == 32512:
|
if not self.helper.detect_java():
|
||||||
if user_id:
|
if user_id:
|
||||||
self.helper.websocket_helper.broadcast_user(
|
self.helper.websocket_helper.broadcast_user(
|
||||||
user_id,
|
user_id,
|
||||||
@ -590,7 +592,6 @@ class ServerInstance:
|
|||||||
# We need to grab the exact forge version number.
|
# We need to grab the exact forge version number.
|
||||||
# We know we can find it here in the run.sh/bat script.
|
# We know we can find it here in the run.sh/bat script.
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Getting the forge version from the executable command
|
# Getting the forge version from the executable command
|
||||||
version = re.findall(
|
version = re.findall(
|
||||||
r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar",
|
r"forge-([0-9\.]+)((?:)|(?:-([0-9\.]+)-[a-zA-Z]+)).jar",
|
||||||
@ -850,7 +851,6 @@ class ServerInstance:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def crash_detected(self, name):
|
def crash_detected(self, name):
|
||||||
|
|
||||||
# clear the old scheduled watcher task
|
# clear the old scheduled watcher task
|
||||||
self.server_scheduler.remove_job(f"c_{self.server_id}")
|
self.server_scheduler.remove_job(f"c_{self.server_id}")
|
||||||
# remove the stats polling job since server is stopped
|
# remove the stats polling job since server is stopped
|
||||||
@ -912,7 +912,6 @@ class ServerInstance:
|
|||||||
return self.process.pid if self.process is not None else None
|
return self.process.pid if self.process is not None else None
|
||||||
|
|
||||||
def detect_crash(self):
|
def detect_crash(self):
|
||||||
|
|
||||||
logger.info(f"Detecting possible crash for server: {self.name} ")
|
logger.info(f"Detecting possible crash for server: {self.name} ")
|
||||||
|
|
||||||
running = self.check_running()
|
running = self.check_running()
|
||||||
@ -935,7 +934,6 @@ class ServerInstance:
|
|||||||
self.stats_helper.sever_crashed()
|
self.stats_helper.sever_crashed()
|
||||||
# if we haven't tried to restart more 3 or more times
|
# if we haven't tried to restart more 3 or more times
|
||||||
if self.restart_count <= 3:
|
if self.restart_count <= 3:
|
||||||
|
|
||||||
# start the server if needed
|
# start the server if needed
|
||||||
server_restarted = self.crash_detected(self.name)
|
server_restarted = self.crash_detected(self.name)
|
||||||
|
|
||||||
@ -1461,7 +1459,6 @@ class ServerInstance:
|
|||||||
Console.critical("Can't broadcast server status to websocket")
|
Console.critical("Can't broadcast server status to websocket")
|
||||||
|
|
||||||
def get_servers_stats(self):
|
def get_servers_stats(self):
|
||||||
|
|
||||||
server_stats = {}
|
server_stats = {}
|
||||||
|
|
||||||
logger.info("Getting Stats for Server " + self.name + " ...")
|
logger.info("Getting Stats for Server " + self.name + " ...")
|
||||||
@ -1548,7 +1545,6 @@ class ServerInstance:
|
|||||||
return server_stats
|
return server_stats
|
||||||
|
|
||||||
def get_server_players(self):
|
def get_server_players(self):
|
||||||
|
|
||||||
server = HelperServers.get_server_data_by_id(self.server_id)
|
server = HelperServers.get_server_data_by_id(self.server_id)
|
||||||
|
|
||||||
logger.info(f"Getting players for server {server}")
|
logger.info(f"Getting players for server {server}")
|
||||||
@ -1569,7 +1565,6 @@ class ServerInstance:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def get_raw_server_stats(self, server_id):
|
def get_raw_server_stats(self, server_id):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = HelperServers.get_server_obj(server_id)
|
server = HelperServers.get_server_obj(server_id)
|
||||||
except:
|
except:
|
||||||
@ -1718,7 +1713,6 @@ class ServerInstance:
|
|||||||
return server_stats
|
return server_stats
|
||||||
|
|
||||||
def record_server_stats(self):
|
def record_server_stats(self):
|
||||||
|
|
||||||
server_stats = self.get_servers_stats()
|
server_stats = self.get_servers_stats()
|
||||||
self.stats_helper.insert_server_stats(server_stats)
|
self.stats_helper.insert_server_stats(server_stats)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
from tzlocal import get_localzone
|
from tzlocal import get_localzone
|
||||||
from tzlocal.utils import ZoneInfoNotFoundError
|
from tzlocal.utils import ZoneInfoNotFoundError
|
||||||
@ -672,7 +673,6 @@ class TasksManager:
|
|||||||
host_stats = HelpersManagement.get_latest_hosts_stats()
|
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
if host_stats.get(
|
if host_stats.get(
|
||||||
"cpu_usage"
|
"cpu_usage"
|
||||||
) != HelpersManagement.get_latest_hosts_stats().get(
|
) != HelpersManagement.get_latest_hosts_stats().get(
|
||||||
@ -687,18 +687,37 @@ class TasksManager:
|
|||||||
host_stats = HelpersManagement.get_latest_hosts_stats()
|
host_stats = HelpersManagement.get_latest_hosts_stats()
|
||||||
if len(self.helper.websocket_helper.clients) > 0:
|
if len(self.helper.websocket_helper.clients) > 0:
|
||||||
# There are clients
|
# There are clients
|
||||||
self.helper.websocket_helper.broadcast_page(
|
try:
|
||||||
"/panel/dashboard",
|
self.helper.websocket_helper.broadcast_page(
|
||||||
"update_host_stats",
|
"/panel/dashboard",
|
||||||
{
|
"update_host_stats",
|
||||||
"cpu_usage": host_stats.get("cpu_usage"),
|
{
|
||||||
"cpu_cores": host_stats.get("cpu_cores"),
|
"cpu_usage": host_stats.get("cpu_usage"),
|
||||||
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
"cpu_cores": host_stats.get("cpu_cores"),
|
||||||
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||||
"mem_percent": host_stats.get("mem_percent"),
|
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||||
"mem_usage": host_stats.get("mem_usage"),
|
"mem_percent": host_stats.get("mem_percent"),
|
||||||
},
|
"mem_usage": host_stats.get("mem_usage"),
|
||||||
)
|
"disk_usage": json.loads(
|
||||||
|
host_stats.get("disk_json").replace("'", '"')
|
||||||
|
),
|
||||||
|
"mounts": self.helper.get_setting("monitored_mounts"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
self.helper.websocket_helper.broadcast_page(
|
||||||
|
"/panel/dashboard",
|
||||||
|
"update_host_stats",
|
||||||
|
{
|
||||||
|
"cpu_usage": host_stats.get("cpu_usage"),
|
||||||
|
"cpu_cores": host_stats.get("cpu_cores"),
|
||||||
|
"cpu_cur_freq": host_stats.get("cpu_cur_freq"),
|
||||||
|
"cpu_max_freq": host_stats.get("cpu_max_freq"),
|
||||||
|
"mem_percent": host_stats.get("mem_percent"),
|
||||||
|
"mem_usage": host_stats.get("mem_usage"),
|
||||||
|
"disk_usage": {},
|
||||||
|
},
|
||||||
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def check_for_updates(self):
|
def check_for_updates(self):
|
||||||
|
@ -575,6 +575,31 @@ class AjaxHandler(BaseHandler):
|
|||||||
self.controller.server_jars.manual_refresh_cache()
|
self.controller.server_jars.manual_refresh_cache()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
elif page == "update_server_dir":
|
||||||
|
for server in self.controller.servers.get_all_servers_stats():
|
||||||
|
if server["stats"]["running"]:
|
||||||
|
self.helper.websocket_helper.broadcast_user(
|
||||||
|
exec_user["user_id"],
|
||||||
|
"send_start_error",
|
||||||
|
{
|
||||||
|
"error": "You must stop all servers before "
|
||||||
|
"starting a storage migration."
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not superuser:
|
||||||
|
self.redirect("/panel/error?error=Not a super user")
|
||||||
|
return
|
||||||
|
if self.helper.is_env_docker():
|
||||||
|
self.redirect(
|
||||||
|
"/panel/error?error=This feature is not"
|
||||||
|
" supported on docker environments"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
new_dir = urllib.parse.unquote(self.get_argument("server_dir"))
|
||||||
|
self.controller.update_master_server_dir(new_dir, exec_user["user_id"])
|
||||||
|
return
|
||||||
|
|
||||||
@tornado.web.authenticated
|
@tornado.web.authenticated
|
||||||
def delete(self, page):
|
def delete(self, page):
|
||||||
api_key, _, exec_user = self.current_user
|
api_key, _, exec_user = self.current_user
|
||||||
|
@ -6,7 +6,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class DefaultHandler(BaseHandler):
|
class DefaultHandler(BaseHandler):
|
||||||
|
|
||||||
# Override prepare() instead of get() to cover all possible HTTP methods.
|
# Override prepare() instead of get() to cover all possible HTTP methods.
|
||||||
def prepare(self, page=None): # pylint: disable=arguments-differ
|
def prepare(self, page=None): # pylint: disable=arguments-differ
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
@ -290,9 +290,11 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data: t.Dict[str, t.Any] = {
|
page_data: t.Dict[str, t.Any] = {
|
||||||
# todo: make this actually pull and compare version data
|
# todo: make this actually pull and compare version data
|
||||||
"update_available": self.helper.update_available,
|
"update_available": self.helper.update_available,
|
||||||
|
"docker": self.helper.is_env_docker(),
|
||||||
"background": self.controller.cached_login,
|
"background": self.controller.cached_login,
|
||||||
"login_opacity": self.controller.management.get_login_opacity(),
|
"login_opacity": self.controller.management.get_login_opacity(),
|
||||||
"serverTZ": tz,
|
"serverTZ": tz,
|
||||||
|
"monitored": self.helper.get_setting("monitored_mounts"),
|
||||||
"version_data": self.helper.get_version_string(),
|
"version_data": self.helper.get_version_string(),
|
||||||
"failed_servers": self.controller.servers.failed_servers,
|
"failed_servers": self.controller.servers.failed_servers,
|
||||||
"user_data": exec_user,
|
"user_data": exec_user,
|
||||||
@ -332,7 +334,12 @@ class PanelHandler(BaseHandler):
|
|||||||
else None,
|
else None,
|
||||||
"superuser": superuser,
|
"superuser": superuser,
|
||||||
}
|
}
|
||||||
|
try:
|
||||||
|
page_data["hosts_data"]["disk_json"] = json.loads(
|
||||||
|
page_data["hosts_data"]["disk_json"].replace("'", '"')
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
page_data["hosts_data"]["disk_json"] = {}
|
||||||
if page == "unauthorized":
|
if page == "unauthorized":
|
||||||
template = "panel/denied.html"
|
template = "panel/denied.html"
|
||||||
|
|
||||||
@ -841,6 +848,9 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["auth-servers"] = auth_servers
|
page_data["auth-servers"] = auth_servers
|
||||||
page_data["role-servers"] = auth_role_servers
|
page_data["role-servers"] = auth_role_servers
|
||||||
page_data["user-roles"] = user_roles
|
page_data["user-roles"] = user_roles
|
||||||
|
page_data[
|
||||||
|
"servers_dir"
|
||||||
|
] = self.controller.management.get_master_server_dir()
|
||||||
|
|
||||||
page_data["users"] = self.controller.users.user_query(exec_user["user_id"])
|
page_data["users"] = self.controller.users.user_query(exec_user["user_id"])
|
||||||
page_data["roles"] = self.controller.users.user_role_query(
|
page_data["roles"] = self.controller.users.user_role_query(
|
||||||
@ -880,6 +890,7 @@ class PanelHandler(BaseHandler):
|
|||||||
page_data["config-json"] = data
|
page_data["config-json"] = data
|
||||||
page_data["availables_languages"] = []
|
page_data["availables_languages"] = []
|
||||||
page_data["all_languages"] = []
|
page_data["all_languages"] = []
|
||||||
|
page_data["all_partitions"] = self.helper.get_all_mounts()
|
||||||
|
|
||||||
for file in sorted(
|
for file in sorted(
|
||||||
os.listdir(
|
os.listdir(
|
||||||
|
@ -10,7 +10,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class PublicHandler(BaseHandler):
|
class PublicHandler(BaseHandler):
|
||||||
def set_current_user(self, user_id: str = None):
|
def set_current_user(self, user_id: str = None):
|
||||||
|
|
||||||
expire_days = self.helper.get_setting("cookie_expire")
|
expire_days = self.helper.get_setting("cookie_expire")
|
||||||
|
|
||||||
# if helper comes back with false
|
# if helper comes back with false
|
||||||
@ -29,7 +28,6 @@ class PublicHandler(BaseHandler):
|
|||||||
# self.clear_cookie("user_data")
|
# self.clear_cookie("user_data")
|
||||||
|
|
||||||
def get(self, page=None):
|
def get(self, page=None):
|
||||||
|
|
||||||
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||||
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||||
|
|
||||||
@ -81,7 +79,6 @@ class PublicHandler(BaseHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def post(self, page=None):
|
def post(self, page=None):
|
||||||
|
|
||||||
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
error = bleach.clean(self.get_argument("error", "Invalid Login!"))
|
||||||
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
error_msg = bleach.clean(self.get_argument("error_msg", ""))
|
||||||
|
|
||||||
@ -96,7 +93,6 @@ class PublicHandler(BaseHandler):
|
|||||||
page_data["query"] = self.request.query
|
page_data["query"] = self.request.query
|
||||||
|
|
||||||
if page == "login":
|
if page == "login":
|
||||||
|
|
||||||
next_page = "/login"
|
next_page = "/login"
|
||||||
if self.request.query:
|
if self.request.query:
|
||||||
next_page = "/login?" + self.request.query
|
next_page = "/login?" + self.request.query
|
||||||
|
@ -26,7 +26,6 @@ login_schema = {
|
|||||||
|
|
||||||
class ApiAuthLoginHandler(BaseApiHandler):
|
class ApiAuthLoginHandler(BaseApiHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(self.request.body)
|
data = json.loads(self.request.body)
|
||||||
except json.decoder.JSONDecodeError as e:
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
@ -631,7 +631,6 @@ class ApiServersIndexHandler(BaseApiHandler):
|
|||||||
self.finish_json(200, {"status": "ok", "data": auth_data[0]})
|
self.finish_json(200, {"status": "ok", "data": auth_data[0]})
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
|
|
||||||
auth_data = self.authenticate_user()
|
auth_data = self.authenticate_user()
|
||||||
if not auth_data:
|
if not auth_data:
|
||||||
return
|
return
|
||||||
|
@ -404,6 +404,14 @@ class ServerHandler(BaseHandler):
|
|||||||
jar_type, server_type, server_version = server_parts
|
jar_type, server_type, server_version = server_parts
|
||||||
# TODO: add server type check here and call the correct server
|
# TODO: add server type check here and call the correct server
|
||||||
# add functions if not a jar
|
# add functions if not a jar
|
||||||
|
if server_type == "forge" and not self.helper.detect_java():
|
||||||
|
translation = self.helper.translation.translate(
|
||||||
|
"error",
|
||||||
|
"installerJava",
|
||||||
|
self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
|
||||||
|
).format(server_name)
|
||||||
|
self.redirect(f"/panel/error?error={translation}")
|
||||||
|
return
|
||||||
new_server_id = self.controller.create_jar_server(
|
new_server_id = self.controller.create_jar_server(
|
||||||
jar_type,
|
jar_type,
|
||||||
server_type,
|
server_type,
|
||||||
@ -552,7 +560,6 @@ class ServerHandler(BaseHandler):
|
|||||||
self.get_remote_ip(),
|
self.get_remote_ip(),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
new_server_id = self.controller.create_bedrock_server(
|
new_server_id = self.controller.create_bedrock_server(
|
||||||
server_name,
|
server_name,
|
||||||
exec_user["user_id"],
|
exec_user["user_id"],
|
||||||
|
@ -59,7 +59,6 @@ class Webserver:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log_function(handler):
|
def log_function(handler):
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
"Status_Code": handler.get_status(),
|
"Status_Code": handler.get_status(),
|
||||||
"Method": handler.request.method,
|
"Method": handler.request.method,
|
||||||
@ -103,7 +102,6 @@ class Webserver:
|
|||||||
logger.debug("Applied asyncio patch")
|
logger.debug("Applied asyncio patch")
|
||||||
|
|
||||||
def run_tornado(self):
|
def run_tornado(self):
|
||||||
|
|
||||||
# let's verify we have an SSL cert
|
# let's verify we have an SSL cert
|
||||||
self.helper.create_self_signed_cert()
|
self.helper.create_self_signed_cert()
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@tornado.web.stream_request_body
|
@tornado.web.stream_request_body
|
||||||
class UploadHandler(BaseHandler):
|
class UploadHandler(BaseHandler):
|
||||||
|
|
||||||
# noinspection PyAttributeOutsideInit
|
# noinspection PyAttributeOutsideInit
|
||||||
def initialize(
|
def initialize(
|
||||||
self,
|
self,
|
||||||
@ -173,7 +172,6 @@ class UploadHandler(BaseHandler):
|
|||||||
if not self.request.headers.get("X-Content-Type", None).startswith(
|
if not self.request.headers.get("X-Content-Type", None).startswith(
|
||||||
"image/"
|
"image/"
|
||||||
):
|
):
|
||||||
|
|
||||||
return self.finish_json(
|
return self.finish_json(
|
||||||
415,
|
415,
|
||||||
{
|
{
|
||||||
|
@ -79,7 +79,6 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
|||||||
# pylint: disable=arguments-renamed
|
# pylint: disable=arguments-renamed
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def on_message(raw_message):
|
def on_message(raw_message):
|
||||||
|
|
||||||
logger.debug(f"Got message from WebSocket connection {raw_message}")
|
logger.debug(f"Got message from WebSocket connection {raw_message}")
|
||||||
message = json.loads(raw_message)
|
message = json.loads(raw_message)
|
||||||
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
|
logger.debug(f"Event Type: {message['event']}, Data: {message['data']}")
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<button type="button" class="btn btn-outline-default custom-picker" onclick="$('option', $('#lang_select')).each(function(element) {
|
<button type="button" class="btn btn-outline-default custom-picker" onclick="$('option', $('#lang_select')).each(function(element) {
|
||||||
$(this).removeAttr('selected').prop('selected', false); $('.selectpicker').selectpicker('refresh')
|
$(this).removeAttr('selected').prop('selected', false); $('.selectpicker').selectpicker('refresh')
|
||||||
});">Enable all Languages</button>
|
});">{{ translate('panelConfig', 'enableLang', data['lang']) }}</button>
|
||||||
<select id="lang_select" class="form-control selectpicker show-tick" data-icon-base="fas"
|
<select id="lang_select" class="form-control selectpicker show-tick" data-icon-base="fas"
|
||||||
data-tick-icon="fa-check" multiple data-style="custom-picker">
|
data-tick-icon="fa-check" multiple data-style="custom-picker">
|
||||||
{% for lang in data['all_languages'] %}
|
{% for lang in data['all_languages'] %}
|
||||||
@ -86,6 +86,25 @@
|
|||||||
rows="{{ len(data['all_languages']) }}" value="{{','.join(item[1])}}"
|
rows="{{ len(data['all_languages']) }}" value="{{','.join(item[1])}}"
|
||||||
hidden>{{','.join(item[1])}}</textarea>
|
hidden>{{','.join(item[1])}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
{% elif item[0] == 'monitored_mounts'%}
|
||||||
|
<div class="input-group">
|
||||||
|
<button type="button" class="btn btn-outline-default custom-picker" onclick="$('option', $('#mount_select')).each(function(element) {
|
||||||
|
$(this).removeAttr('selected').prop('selected', false); $('.selectpicker').selectpicker('refresh')
|
||||||
|
});">{{ translate('panelConfig', 'noMounts', data['lang']) }}</button>
|
||||||
|
<select id="mount_select" class="form-control selectpicker show-tick" data-icon-base="fas"
|
||||||
|
data-tick-icon="fa-check" multiple data-style="custom-picker">
|
||||||
|
{% for mount in data['all_partitions'] %}
|
||||||
|
{% if mount in item[1] %}
|
||||||
|
<option selected>{{mount}}</option>
|
||||||
|
{% else %}
|
||||||
|
<option>{{mount}}</option>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</select>
|
||||||
|
<textarea id="monitored_mounts" name="{{item[0]}}" class="form-control list hidden"
|
||||||
|
rows="{{ len(data['all_partitions']) }}" value="{{','.join(item[1])}}"
|
||||||
|
hidden>{{','.join(item[1])}}</textarea>
|
||||||
|
</div>
|
||||||
{% elif isinstance(item[1], list) %}
|
{% elif isinstance(item[1], list) %}
|
||||||
<textarea value="{{','.join(item[1])}}" type="text" name="{{item[0]}}"
|
<textarea value="{{','.join(item[1])}}" type="text" name="{{item[0]}}"
|
||||||
class="form-control list">{{','.join(item[1])}}</textarea>
|
class="form-control list">{{','.join(item[1])}}</textarea>
|
||||||
@ -160,6 +179,9 @@
|
|||||||
let selected_Lang = $('#lang_select').val();
|
let selected_Lang = $('#lang_select').val();
|
||||||
$('#disabled_lang').val(selected_Lang);
|
$('#disabled_lang').val(selected_Lang);
|
||||||
|
|
||||||
|
let mounts = $('#mount_select').val();
|
||||||
|
$('#monitored_mounts').val(mounts);
|
||||||
|
|
||||||
let class_list = document.getElementsByClassName("list");
|
let class_list = document.getElementsByClassName("list");
|
||||||
let form_json = convertFormToJSON($("#config-form"));
|
let form_json = convertFormToJSON($("#config-form"));
|
||||||
for (let i = 0; i < class_list.length; i++) {
|
for (let i = 0; i < class_list.length; i++) {
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% end %}
|
{% end %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -50,7 +49,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4 col-md-6">
|
<div class="col-xl-4 col-md-5">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host', data['lang']) }}
|
<h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host', data['lang']) }}
|
||||||
@ -72,7 +71,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-md-6 mt-md-0 mt-4">
|
<div class="col-xl-4 col-md-4 mt-md-0 mt-4">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers', data['lang']) }}
|
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers', data['lang']) }}
|
||||||
@ -88,7 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-md-6 mt-md-0 mt-4">
|
<div class="col-xl-4 col-md-3 mt-md-0 mt-4">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players', data['lang']) }}
|
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players', data['lang']) }}
|
||||||
@ -101,6 +100,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 mt-4">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="wrapper" style="width: 100%;">
|
||||||
|
<h5 class="mb-1 font-weight-medium text-primary">Storage
|
||||||
|
</h5>
|
||||||
|
<div id="storage_data">
|
||||||
|
<div class="row">
|
||||||
|
{% for item in data.get('hosts_data').get('disk_json') %}
|
||||||
|
{% if item["mount"] in data["monitored"] %}
|
||||||
|
<div id="{{item['device']}}" class="col-xl-3 col-lg-3 col-md-4 col-12">
|
||||||
|
<h4 class="mb-0 font-weight-semibold d-inline-block text-truncate storage-heading"
|
||||||
|
id="title_{{item['device']}}" data-toggle="tooltip" data-placement="bottom"
|
||||||
|
title="{{item['mount']}}" style="max-width: 100%;"><i class="fas fa-hdd"></i>
|
||||||
|
{{item["mount"]}}</h4>
|
||||||
|
<div class="progress d-inline-block"
|
||||||
|
style="height: 20px; width: 100%; background-color: rgb(139, 139, 139) !important;">
|
||||||
|
<div class="progress-bar
|
||||||
|
{% if item['percent_used'] <= 58 %}
|
||||||
|
bg-success
|
||||||
|
{% elif 59 <= item['percent_used'] <= 75 %}
|
||||||
|
bg-warning
|
||||||
|
{% else %}
|
||||||
|
bg-danger
|
||||||
|
{% end %}
|
||||||
|
" role="progressbar" style="color: black; height: 100%; width: {{item['percent_used']}}%;"
|
||||||
|
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">{{item["used"]}} /
|
||||||
|
{{item["total"]}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -891,6 +927,32 @@
|
|||||||
cpu_usage.textContent = hostStats.cpu_usage;
|
cpu_usage.textContent = hostStats.cpu_usage;
|
||||||
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`);
|
||||||
mem_percent.textContent = hostStats.mem_percent + '%';
|
mem_percent.textContent = hostStats.mem_percent + '%';
|
||||||
|
|
||||||
|
var storage_html = '<div class="row">';
|
||||||
|
for (i = 0; i < hostStats.disk_usage.length; i++) {
|
||||||
|
if (hostStats.mounts.includes(hostStats.disk_usage[i].mount)) {
|
||||||
|
storage_html += `<div id="{{item['device']}}" class="col-xl-3 col-lg-3 col-md-4 col-12">
|
||||||
|
<h4 class="mb-0 font-weight-semibold d-inline-block text-truncate storage-heading" id="title_{{item['device']}}" data-toggle="tooltip" data-placement="bottom" title="${hostStats.disk_usage[i].mount}" style="max-width: 100%;"><i class="fas fa-hdd"></i> ${hostStats.disk_usage[i].mount}</h4>
|
||||||
|
<div class="progress" style="display: inline-block; height: 20px; width: 100%; background-color: rgb(139, 139, 139) !important;">
|
||||||
|
<div class="progress-bar`;
|
||||||
|
if (hostStats.disk_usage[i].percent_used <= 58) {
|
||||||
|
storage_html += ` bg-success`;
|
||||||
|
} else if (hostStats.disk_usage[i].percent_used <= 75 && hostStats.disk_usage[i].percent_used >= 59) {
|
||||||
|
storage_html += ` bg-warning`;
|
||||||
|
} else {
|
||||||
|
storage_html += ` bg-danger`;
|
||||||
|
}
|
||||||
|
storage_html += `" role="progressbar" style="color: black; height: 100%; width: ${hostStats.disk_usage[i].percent_used}%;"
|
||||||
|
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">${hostStats.disk_usage[i].used} / ${hostStats.disk_usage[i].total}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storage_html += `</div>`;
|
||||||
|
$(".storage-heading").tooltip('hide');
|
||||||
|
$("#storage_data").html(storage_html);
|
||||||
|
$("#storage_data").tooltip({ selector: '.storage-heading' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,9 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
|
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang'])
|
||||||
}}</h4>
|
}}</h4>
|
||||||
{% if data['user_data']['hints'] %}
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" , data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , data-placement="top"></span>
|
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
||||||
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
|
data-placement="top"></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
<!-- TODO: Translate the following -->
|
<!-- TODO: Translate the following -->
|
||||||
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> {{
|
||||||
@ -148,7 +150,9 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles',
|
||||||
data['lang']) }}</h4>
|
data['lang']) }}</h4>
|
||||||
{% if data['user_data']['hints'] %}
|
{% if data['user_data']['hints'] %}
|
||||||
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" , data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , data-placement="top"></span>
|
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}" ,
|
||||||
|
data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" ,
|
||||||
|
data-placement="top"></span>
|
||||||
{% end %}
|
{% end %}
|
||||||
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{
|
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> {{
|
||||||
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
translate('panelConfig', 'newRole', data['lang']) }}</a></div>
|
||||||
@ -226,7 +230,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if data['superuser'] %}
|
{% if data['superuser'] and not data["docker"] %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -234,6 +238,24 @@
|
|||||||
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
|
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'adminControls',
|
||||||
data['lang']) }}</h4>
|
data['lang']) }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<br>
|
||||||
|
<form id="server-path">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="global_server_path">{{ translate('panelConfig', 'globalServer',
|
||||||
|
data['lang']) }}<small class="text-muted ml-1"> - {{ translate('panelConfig', 'globalExplain',
|
||||||
|
data['lang']) }}</small></label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="global_server_path" class="form-control" name="global_server_path"
|
||||||
|
placeholder="/var/opt/servers" value="{{data['servers_dir']}}" directory>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span type="button" class="btn btn-outline-default custom-picker">/servers/</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success" type="submit">Submit</button> <span id="submit-status"></span>
|
||||||
|
<br>
|
||||||
|
<span id="submit-list"></span>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -247,10 +269,37 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.custom-picker {
|
||||||
|
border: 1px solid var(--outline);
|
||||||
|
}
|
||||||
|
|
||||||
.popover-body {
|
.popover-body {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading:after {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
-webkit-animation: ellipsis steps(4, end) 900ms infinite;
|
||||||
|
animation: ellipsis steps(4, end) 900ms infinite;
|
||||||
|
content: "\2026";
|
||||||
|
/* ascii code for the ellipsis character */
|
||||||
|
width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ellipsis {
|
||||||
|
to {
|
||||||
|
width: 1.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes ellipsis {
|
||||||
|
to {
|
||||||
|
width: 1.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- content-wrapper ends -->
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
@ -258,6 +307,40 @@
|
|||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
|
if (webSocket) {
|
||||||
|
webSocket.on('move_status', function (message) {
|
||||||
|
if (message === "done") {
|
||||||
|
$("#submit-list").removeClass("loading");
|
||||||
|
$("#submit-list").html("");
|
||||||
|
$("#submit-status").html('<i class="fa fa-check"></i>');
|
||||||
|
} else {
|
||||||
|
$("#submit-status").html('<i class="fa fa-spinner fa-spin"></i>');
|
||||||
|
$("#submit-list").html(message);
|
||||||
|
$("#submit-list").addClass("loading");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#server-path").submit(function (e) {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$("#submit-status").html('<i class="fa fa-spinner fa-spin"></i>');
|
||||||
|
|
||||||
|
let path = $("#global_server_path").val();
|
||||||
|
let encoded = encodeURIComponent(path);
|
||||||
|
console.log(path)
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: { 'X-XSRFToken': token },
|
||||||
|
dataType: "text",
|
||||||
|
url: '/ajax/update_server_dir',
|
||||||
|
data: {
|
||||||
|
"server_dir": encoded,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('[data-toggle="popover"]').popover();
|
$('[data-toggle="popover"]').popover();
|
||||||
if ($(window).width() < 1000) {
|
if ($(window).width() < 1000) {
|
||||||
|
@ -539,7 +539,7 @@
|
|||||||
* @param {boolean} saved
|
* @param {boolean} saved
|
||||||
*/
|
*/
|
||||||
const setSaveStatus = (saved) => {
|
const setSaveStatus = (saved) => {
|
||||||
document.getElementById('save_status').innerHTML = `<i class="fal ${saved ? "fa-file-check" : "fa-file"}"></i>`;
|
document.getElementById('save_status').innerHTML = `<i class="${saved ? "fa-solid fa-file-circle-check" : "fa-regular fa-file"}"></i>`;
|
||||||
document.getElementById('save_status').style.color = saved ? '#2fb689' : 'gray';
|
document.getElementById('save_status').style.color = saved ? '#2fb689' : 'gray';
|
||||||
}
|
}
|
||||||
['change', 'undo', 'redo'].forEach(event => editor.on(event, (event) => setSaveStatus(serverFileContent === editor.session.getValue())))
|
['change', 'undo', 'redo'].forEach(event => editor.on(event, (event) => setSaveStatus(serverFileContent === editor.session.getValue())))
|
||||||
|
18
app/migrations/20230126_master_server.py
Normal file
18
app/migrations/20230126_master_server.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by database migrator
|
||||||
|
import peewee
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(migrator, database, **kwargs):
|
||||||
|
migrator.add_columns(
|
||||||
|
"crafty_settings", master_server_dir=peewee.CharField(default="")
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
Write your migrations here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def rollback(migrator, database, **kwargs):
|
||||||
|
migrator.drop_columns("crafty_settings", ["master_server_dir"])
|
||||||
|
"""
|
||||||
|
Write your rollback migrations here.
|
||||||
|
"""
|
@ -179,6 +179,7 @@
|
|||||||
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",
|
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",
|
||||||
"no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?",
|
"no-file": "We can't seem to locate the requested file. Double check the path. Does Crafty have proper permissions?",
|
||||||
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.",
|
"noJava": "Server {} failed to start with error code: We have detected Java is not installed. Please install java then start the server.",
|
||||||
|
"installerJava": "Failed to install {} : Forge Server Installs require Java. We have detected Java is not installed. Please install java then install the server.",
|
||||||
"not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?",
|
"not-downloaded": "We can't seem to find your executable file. Has it finished downloading? Are the permissions set to executable?",
|
||||||
"portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.",
|
"portReminder": "We have detected this is the first time {} has been run. Make sure to forward port {} through your router/firewall to make this remotely accessible from the internet.",
|
||||||
"start-error": "Server {} failed to start with error code: {}",
|
"start-error": "Server {} failed to start with error code: {}",
|
||||||
@ -232,7 +233,11 @@
|
|||||||
"superConfirmTitle": "Enable superuser? Are you sure?",
|
"superConfirmTitle": "Enable superuser? Are you sure?",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
"title": "Crafty Configuration"
|
"title": "Crafty Configuration",
|
||||||
|
"enableLang": "Enable All Languages",
|
||||||
|
"noMounts": "Show no Mounts on Dash",
|
||||||
|
"globalServer": "Global Servers Directory",
|
||||||
|
"globalExplain": "Where Crafty stores all your server files. (We will append the path with /servers/[uuid of server])"
|
||||||
},
|
},
|
||||||
"customLogin": {
|
"customLogin": {
|
||||||
"customLoginPage": "Customise the Login Page",
|
"customLoginPage": "Customise the Login Page",
|
||||||
@ -608,4 +613,4 @@
|
|||||||
"manager": "Manager",
|
"manager": "Manager",
|
||||||
"selectManager": "Select Manager for User"
|
"selectManager": "Select Manager for User"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
main.py
9
main.py
@ -230,6 +230,15 @@ if __name__ == "__main__":
|
|||||||
running_mode = "Interactive"
|
running_mode = "Interactive"
|
||||||
|
|
||||||
controller.set_project_root(application_path)
|
controller.set_project_root(application_path)
|
||||||
|
master_server_dir = controller.management.get_master_server_dir()
|
||||||
|
if master_server_dir == "":
|
||||||
|
logger.debug("Could not find master server path. Setting default")
|
||||||
|
controller.set_master_server_dir(
|
||||||
|
os.path.join(controller.project_root, "servers")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
helper.servers_dir = master_server_dir
|
||||||
|
|
||||||
Console.debug(f"Execution Mode: {running_mode}")
|
Console.debug(f"Execution Mode: {running_mode}")
|
||||||
Console.debug(f"Application path : '{application_path}'")
|
Console.debug(f"Application path : '{application_path}'")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user