mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'tweak/master-server-dir-config' into 'dev'
Allow users to change the directory where Crafty Stores Servers See merge request crafty-controller/crafty-4!539
This commit is contained in:
commit
b53e7d6f50
@ -3,6 +3,8 @@
|
|||||||
### 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)*
|
||||||
### 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))
|
||||||
|
@ -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):
|
||||||
|
@ -47,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"
|
||||||
@ -272,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
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
|
@ -435,12 +435,20 @@ class Helpers:
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
def is_subdir(self, server_path, root_dir):
|
||||||
def is_subdir(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
|
||||||
@ -883,12 +891,14 @@ 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:
|
||||||
@ -972,6 +982,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("\\", "/")
|
||||||
|
@ -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
|
||||||
@ -1002,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)
|
||||||
|
@ -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
|
||||||
|
@ -290,6 +290,7 @@ 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,
|
||||||
@ -841,6 +842,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(
|
||||||
|
@ -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) {
|
||||||
|
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.
|
||||||
|
"""
|
@ -232,7 +232,9 @@
|
|||||||
"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",
|
||||||
|
"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",
|
||||||
|
9
main.py
9
main.py
@ -227,6 +227,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