mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into bugfix/config-empty-mounts
This commit is contained in:
commit
1383accefd
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,21 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## --- [4.2.2] - 2023/TBD
|
## --- [4.2.2] - 2023/TBD
|
||||||
### New features
|
### New features
|
||||||
TBD
|
- Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668))
|
||||||
### Refactor
|
### Refactor
|
||||||
- Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670))
|
- Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670))
|
||||||
|
- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668))
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664))
|
- Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664))
|
||||||
|
- Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680))
|
||||||
|
- Fix bug where su cannot edit general user password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/676))
|
||||||
|
- Fix bug where no file error on import root dir ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/677))
|
||||||
|
- Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671))
|
||||||
|
- Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674))
|
||||||
### Tweaks
|
### Tweaks
|
||||||
- Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666))
|
- Homogenize Panel logos/branding ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/666))
|
||||||
- Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667))
|
- Retain previous tab when revisiting server details page (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667))
|
||||||
- Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667))
|
- Add server name tag in panel header (#272)([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/667))
|
||||||
- Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669))
|
- Setup logging for panel authentication attempts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669))
|
||||||
- Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669))
|
- Update minimum password length from 6 to 8, and unrestrict maximum password length ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/669))
|
||||||
- Fix Unban button failing to pardon users ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/671))
|
|
||||||
- Fix stack in API error handling ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/674))
|
|
||||||
### Lang
|
### Lang
|
||||||
TBD
|
- pl_PL Minor fixes ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/675))
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
## --- [4.2.1] - 2023/11/01
|
## --- [4.2.1] - 2023/11/01
|
||||||
|
@ -22,6 +22,7 @@ from app.classes.models.server_permissions import (
|
|||||||
PermissionsServers,
|
PermissionsServers,
|
||||||
EnumPermissionsServer,
|
EnumPermissionsServer,
|
||||||
)
|
)
|
||||||
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ class ServersController(metaclass=Singleton):
|
|||||||
self.management_helper = management_helper
|
self.management_helper = management_helper
|
||||||
self.servers_list = []
|
self.servers_list = []
|
||||||
self.stats = Stats(self.helper, self)
|
self.stats = Stats(self.helper, self)
|
||||||
|
self.web_sock = WebSocketManager()
|
||||||
self.server_subpage = {}
|
self.server_subpage = {}
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -170,8 +172,15 @@ class ServersController(metaclass=Singleton):
|
|||||||
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 = []
|
||||||
|
|
||||||
for server in servers:
|
for server in servers:
|
||||||
|
self.web_sock.broadcast_to_admins(
|
||||||
|
"update",
|
||||||
|
{"section": "server", "server": server["server_name"]},
|
||||||
|
)
|
||||||
|
self.web_sock.broadcast_to_non_admins(
|
||||||
|
"update",
|
||||||
|
{"section": "init"},
|
||||||
|
)
|
||||||
server_id = server.get("server_id")
|
server_id = server.get("server_id")
|
||||||
|
|
||||||
# if we have already initialized this server, let's skip it.
|
# if we have already initialized this server, let's skip it.
|
||||||
|
@ -80,6 +80,7 @@ class Helpers:
|
|||||||
self.translation = Translation(self)
|
self.translation = Translation(self)
|
||||||
self.update_available = False
|
self.update_available = False
|
||||||
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
self.ignored_names = ["crafty_managed.txt", "db_stats"]
|
||||||
|
self.crafty_starting = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auto_installer_fix(ex):
|
def auto_installer_fix(ex):
|
||||||
|
@ -85,7 +85,7 @@ class Controller:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
) as f:
|
||||||
self.auth_tracker = json.load(f)
|
self.auth_tracker = json.load(f)
|
||||||
except:
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
self.auth_tracker = {}
|
self.auth_tracker = {}
|
||||||
|
|
||||||
def log_attempt(self, remote_ip, username):
|
def log_attempt(self, remote_ip, username):
|
||||||
|
@ -37,7 +37,15 @@ class WebSocketManager(metaclass=Singleton):
|
|||||||
|
|
||||||
def broadcast_to_admins(self, event_type: str, data):
|
def broadcast_to_admins(self, event_type: str, data):
|
||||||
def filter_fn(client):
|
def filter_fn(client):
|
||||||
if client.get_user_id in HelperUsers.get_super_user_list():
|
if str(client.get_user_id()) in str(HelperUsers.get_super_user_list()):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.broadcast_with_fn(filter_fn, event_type, data)
|
||||||
|
|
||||||
|
def broadcast_to_non_admins(self, event_type: str, data):
|
||||||
|
def filter_fn(client):
|
||||||
|
if str(client.get_user_id()) not in str(HelperUsers.get_super_user_list()):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -210,6 +210,8 @@ class PanelHandler(BaseHandler):
|
|||||||
error = self.get_argument("error", "WTF Error!")
|
error = self.get_argument("error", "WTF Error!")
|
||||||
|
|
||||||
template = "panel/denied.html"
|
template = "panel/denied.html"
|
||||||
|
if self.helper.crafty_starting:
|
||||||
|
page = "loading"
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
formatted_time = str(
|
formatted_time = str(
|
||||||
@ -243,9 +245,13 @@ class PanelHandler(BaseHandler):
|
|||||||
for r in exec_user["roles"]:
|
for r in exec_user["roles"]:
|
||||||
role = self.controller.roles.get_role(r)
|
role = self.controller.roles.get_role(r)
|
||||||
exec_user_role.add(role["role_name"])
|
exec_user_role.add(role["role_name"])
|
||||||
defined_servers = self.controller.servers.get_authorized_servers(
|
# get_auth_servers will throw an exception if run while Crafty is starting
|
||||||
exec_user["user_id"]
|
if not self.helper.crafty_starting:
|
||||||
)
|
defined_servers = self.controller.servers.get_authorized_servers(
|
||||||
|
exec_user["user_id"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
defined_servers = []
|
||||||
|
|
||||||
user_order = self.controller.users.get_user_by_id(exec_user["user_id"])
|
user_order = self.controller.users.get_user_by_id(exec_user["user_id"])
|
||||||
user_order = user_order["server_order"].split(",")
|
user_order = user_order["server_order"].split(",")
|
||||||
@ -1615,7 +1621,8 @@ class PanelHandler(BaseHandler):
|
|||||||
logs_thread.start()
|
logs_thread.start()
|
||||||
self.redirect("/panel/dashboard")
|
self.redirect("/panel/dashboard")
|
||||||
return
|
return
|
||||||
|
if self.helper.crafty_starting:
|
||||||
|
template = "panel/loading.html"
|
||||||
self.render(
|
self.render(
|
||||||
template,
|
template,
|
||||||
data=page_data,
|
data=page_data,
|
||||||
|
@ -7,6 +7,7 @@ from jsonschema.exceptions import ValidationError
|
|||||||
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||||
from app.classes.shared.helpers import Helpers
|
from app.classes.shared.helpers import Helpers
|
||||||
from app.classes.web.base_api_handler import BaseApiHandler
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
from app.classes.web.websocket_handler import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
files_get_schema = {
|
files_get_schema = {
|
||||||
@ -73,7 +74,7 @@ class ApiImportFilesIndexHandler(BaseApiHandler):
|
|||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
||||||
self.helper.websocket_helper.broadcast_user(
|
WebSocketManager().broadcast_user(
|
||||||
user_id,
|
user_id,
|
||||||
"send_start_error",
|
"send_start_error",
|
||||||
{
|
{
|
||||||
@ -85,7 +86,7 @@ class ApiImportFilesIndexHandler(BaseApiHandler):
|
|||||||
else:
|
else:
|
||||||
if not self.helper.check_path_exists(folder) and user_id:
|
if not self.helper.check_path_exists(folder) and user_id:
|
||||||
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
user_lang = self.controller.users.get_user_lang_by_id(user_id)
|
||||||
self.helper.websocket_helper.broadcast_user(
|
WebSocketManager().broadcast_user(
|
||||||
user_id,
|
user_id,
|
||||||
"send_start_error",
|
"send_start_error",
|
||||||
{
|
{
|
||||||
|
@ -215,7 +215,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
|||||||
|
|
||||||
user_obj = HelperUsers.get_user_model(user_id)
|
user_obj = HelperUsers.get_user_model(user_id)
|
||||||
if "password" in data and str(user["user_id"]) != str(user_id):
|
if "password" in data and str(user["user_id"]) != str(user_id):
|
||||||
if str(user["user_id"]) != str(user_obj.manager):
|
if str(user["user_id"]) != str(user_obj.manager) and not user["superuser"]:
|
||||||
# TODO: edit your own password
|
# TODO: edit your own password
|
||||||
return self.finish_json(
|
return self.finish_json(
|
||||||
400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"}
|
400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"}
|
||||||
|
73
app/frontend/templates/panel/loading.html
Normal file
73
app/frontend/templates/panel/loading.html
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{% extends ../base.html %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block title %}Crafty Controller Starting{% end %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div class="card-header justify-content-between align-items-center" style="border: none;">
|
||||||
|
<div id="image-div" style="width: 100%;">
|
||||||
|
<img class="img-center" id="logo-animate" src="../static/assets/images/crafty-logo-square-1024.png"
|
||||||
|
alt="Crafty Logo, Crafty is loading" width="20%" style="clear: both;">
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</br>
|
||||||
|
<div id="text-div" style="width: 100%; text-align: center;">
|
||||||
|
<h2 id="status" style="display: block;" data-init="{{ translate('startup', 'serverInit', data['lang']) }}"
|
||||||
|
data-server="{{ translate('startup', 'server', data['lang']) }}"
|
||||||
|
data-internet="{{ translate('startup', 'internet', data['lang']) }}"
|
||||||
|
data-tasks="{{ translate('startup', 'tasks', data['lang']) }}"
|
||||||
|
data-internals="{{ translate('startup', 'internals', data['lang']) }}"
|
||||||
|
data-almost="{{ translate('startup', 'almost', data['lang']) }}">
|
||||||
|
{{ translate('startup', 'starting', data['lang']) }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.img-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function rotateImage(degree) {
|
||||||
|
$('#logo-animate').animate({ transform: degree }, {
|
||||||
|
step: function (now, fx) {
|
||||||
|
$(this).css({
|
||||||
|
'-webkit-transform': 'rotate(' + now + 'deg)',
|
||||||
|
'-moz-transform': 'rotate(' + now + 'deg)',
|
||||||
|
'transform': 'rotate(' + now + 'deg)'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
rotateImage(360);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
rotateImage(360);
|
||||||
|
}, 2000);
|
||||||
|
if (webSocket) {
|
||||||
|
webSocket.on('update', function (data) {
|
||||||
|
if ("server" in data) {
|
||||||
|
$("#status").html(`${$("#status").data(data.section)} ${data.server}...`);
|
||||||
|
} else {
|
||||||
|
$("#status").html($("#status").data(data.section));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
webSocket.on('send_start_reload', function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
location.href = '/panel/dashboard'
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% end %}
|
@ -591,6 +591,15 @@
|
|||||||
"newServer": "Create New Server",
|
"newServer": "Create New Server",
|
||||||
"servers": "Servers"
|
"servers": "Servers"
|
||||||
},
|
},
|
||||||
|
"startup": {
|
||||||
|
"starting": "Crafty Is Starting...",
|
||||||
|
"serverInit": "Initializing Servers",
|
||||||
|
"server": "Initializing ",
|
||||||
|
"internet": "Checking for internet connection",
|
||||||
|
"tasks": "Starting Tasks Scheduler",
|
||||||
|
"internals": "Configuring and starting Crafty's internal componenets",
|
||||||
|
"almost": "Finishing up. Hang on tight..."
|
||||||
|
},
|
||||||
"userConfig": {
|
"userConfig": {
|
||||||
"apiKey": "API Keys",
|
"apiKey": "API Keys",
|
||||||
"auth": "Authorized? ",
|
"auth": "Authorized? ",
|
||||||
|
@ -326,8 +326,8 @@
|
|||||||
"bePatientDeleteFiles": "Poczekaj, aż usuniemy twój serwer i jego pliki. Strona za chwilę się zamknie.",
|
"bePatientDeleteFiles": "Poczekaj, aż usuniemy twój serwer i jego pliki. Strona za chwilę się zamknie.",
|
||||||
"bePatientUpdate": "Poczekaj kiedy my aktualizujemy twój serwer. Pobieranie zależy od prędkości twojego internetu.<br /> Strona się odświeży za chwile.",
|
"bePatientUpdate": "Poczekaj kiedy my aktualizujemy twój serwer. Pobieranie zależy od prędkości twojego internetu.<br /> Strona się odświeży za chwile.",
|
||||||
"cancel": "Anuluj",
|
"cancel": "Anuluj",
|
||||||
"crashTime": "Crash wyszedł poza limit czasu",
|
"crashTime": "Crash serwera wyszedł poza limit czasu",
|
||||||
"crashTimeDesc": "How long should we wait before we consider your server as crashed?",
|
"crashTimeDesc": "Jak długo powinniśmy poczekać zanim uznać serwer za zcrashowany?",
|
||||||
"deleteFilesQuestion": "Usuń pliki serwera z maszyny?",
|
"deleteFilesQuestion": "Usuń pliki serwera z maszyny?",
|
||||||
"deleteFilesQuestionMessage": "Czy chcesz aby Crafty usunął wszystkie pliki tego serwera? <br><br><strong>To zawiera backupy.</strong>",
|
"deleteFilesQuestionMessage": "Czy chcesz aby Crafty usunął wszystkie pliki tego serwera? <br><br><strong>To zawiera backupy.</strong>",
|
||||||
"deleteServer": "Usuń serwer",
|
"deleteServer": "Usuń serwer",
|
||||||
@ -403,7 +403,7 @@
|
|||||||
"filterList": "Filtrowane słowa",
|
"filterList": "Filtrowane słowa",
|
||||||
"logs": "Logi",
|
"logs": "Logi",
|
||||||
"metrics": "Statystyki",
|
"metrics": "Statystyki",
|
||||||
"playerControls": "Player Management",
|
"playerControls": "Zarządzanie użytkownikami",
|
||||||
"reset": "Resetuj Scrolla",
|
"reset": "Resetuj Scrolla",
|
||||||
"schedule": "Harmonogram",
|
"schedule": "Harmonogram",
|
||||||
"serverDetails": "Detale serwera",
|
"serverDetails": "Detale serwera",
|
||||||
@ -421,7 +421,7 @@
|
|||||||
"deleteItemQuestion": "Czy jesteś pewien że chcesz usunąć \" + name + \"?",
|
"deleteItemQuestion": "Czy jesteś pewien że chcesz usunąć \" + name + \"?",
|
||||||
"deleteItemQuestionMessage": "Usuwasz \\\"\" + path + \"\\\"!<br/><br/>Ta akcja jest nieodwracalna i zostanie usunięta na zawsze!",
|
"deleteItemQuestionMessage": "Usuwasz \\\"\" + path + \"\\\"!<br/><br/>Ta akcja jest nieodwracalna i zostanie usunięta na zawsze!",
|
||||||
"download": "Pobierz",
|
"download": "Pobierz",
|
||||||
"editingFile": "Edytuję plik",
|
"editingFile": "Edytuj plik",
|
||||||
"error": "Error while getting files",
|
"error": "Error while getting files",
|
||||||
"fileReadError": "Error odczytu pliku",
|
"fileReadError": "Error odczytu pliku",
|
||||||
"files": "Pliki",
|
"files": "Pliki",
|
||||||
@ -432,7 +432,7 @@
|
|||||||
"rename": "Zmień nazwę",
|
"rename": "Zmień nazwę",
|
||||||
"renameItemQuestion": "Jaka ma być nowa nazwa?",
|
"renameItemQuestion": "Jaka ma być nowa nazwa?",
|
||||||
"save": "Zapisz",
|
"save": "Zapisz",
|
||||||
"size": "Włącz zmienianie rozmiaru edytora",
|
"size": "Włącz rozszerzanie i zmniejszanie edytora",
|
||||||
"stayHere": "NIE WYCHODŹ Z TEJ STRONY!",
|
"stayHere": "NIE WYCHODŹ Z TEJ STRONY!",
|
||||||
"unsupportedLanguage": "Uwaga: To nie jest wspierany typ pliku",
|
"unsupportedLanguage": "Uwaga: To nie jest wspierany typ pliku",
|
||||||
"unzip": "Rozpakuj",
|
"unzip": "Rozpakuj",
|
||||||
@ -545,7 +545,7 @@
|
|||||||
"buildServer": "Zbuduj serwer!",
|
"buildServer": "Zbuduj serwer!",
|
||||||
"clickRoot": "Kilknij tutaj aby zaznaczyć główną ścieżkę",
|
"clickRoot": "Kilknij tutaj aby zaznaczyć główną ścieżkę",
|
||||||
"close": "Zamknij",
|
"close": "Zamknij",
|
||||||
"defaultPort": "25565 podstawowy",
|
"defaultPort": "Domyślnie 25565",
|
||||||
"downloading": "Pobieranie serwera...",
|
"downloading": "Pobieranie serwera...",
|
||||||
"explainRoot": "Proszę, kliknij przycisk poniżej aby zaznaczyć główną ścieżkę w tym archiwum",
|
"explainRoot": "Proszę, kliknij przycisk poniżej aby zaznaczyć główną ścieżkę w tym archiwum",
|
||||||
"importServer": "Importuj egzystujący serwer",
|
"importServer": "Importuj egzystujący serwer",
|
||||||
@ -640,12 +640,12 @@
|
|||||||
"edit": "Edytuj",
|
"edit": "Edytuj",
|
||||||
"enabled": "Włączony",
|
"enabled": "Włączony",
|
||||||
"jar_update": "Plik startowy zaktualizowany",
|
"jar_update": "Plik startowy zaktualizowany",
|
||||||
"kill": "Serwer zatrzymany",
|
"kill": "Serwer został zabity",
|
||||||
"name": "Nazwa",
|
"name": "Nazwa",
|
||||||
"new": "Nowy Webhook",
|
"new": "Nowy Webhook",
|
||||||
"newWebhook": "Nowy Webhook",
|
"newWebhook": "Nowy Webhook",
|
||||||
"no-webhook": "Nie posiadasz aktualnie żadnych Webhooków dla tego serwera. Aby dodać webhook kliknij na",
|
"no-webhook": "Nie posiadasz aktualnie żadnych Webhooków dla tego serwera. Aby dodać webhook kliknij na",
|
||||||
"run": "Włącz Webhook",
|
"run": "Przetestuj Webhook",
|
||||||
"send_command": "Komenda serwera otrzymana!",
|
"send_command": "Komenda serwera otrzymana!",
|
||||||
"start_server": "Serwer włączony",
|
"start_server": "Serwer włączony",
|
||||||
"stop_server": "Serwer wyłączony",
|
"stop_server": "Serwer wyłączony",
|
||||||
|
342
main.py
342
main.py
@ -20,6 +20,18 @@ from app.classes.shared.websocket_manager import WebSocketManager
|
|||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
helper = Helpers()
|
helper = Helpers()
|
||||||
|
# Get the path our application is running on.
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
APPLICATION_PATH = os.path.dirname(sys.executable)
|
||||||
|
RUNNING_MODE = "Frozen/executable"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
app_full_path = os.path.realpath(__file__)
|
||||||
|
APPLICATION_PATH = os.path.dirname(app_full_path)
|
||||||
|
RUNNING_MODE = "Non-interactive (e.g. 'python main.py')"
|
||||||
|
except NameError:
|
||||||
|
APPLICATION_PATH = os.getcwd()
|
||||||
|
RUNNING_MODE = "Interactive"
|
||||||
if helper.check_root():
|
if helper.check_root():
|
||||||
Console.critical(
|
Console.critical(
|
||||||
"Root detected. Root/Admin access denied. "
|
"Root detected. Root/Admin access denied. "
|
||||||
@ -51,7 +63,178 @@ except ModuleNotFoundError as err:
|
|||||||
helper.auto_installer_fix(err)
|
helper.auto_installer_fix(err)
|
||||||
|
|
||||||
|
|
||||||
|
def internet_check():
|
||||||
|
"""
|
||||||
|
This checks to see if the Crafty host is connected to the
|
||||||
|
internet. This will show a warning in the console if no interwebs.
|
||||||
|
"""
|
||||||
|
print()
|
||||||
|
logger.info("Checking Internet. This may take a minute.")
|
||||||
|
Console.info("Checking Internet. This may take a minute.")
|
||||||
|
|
||||||
|
if not helper.check_internet():
|
||||||
|
logger.warning(
|
||||||
|
"We have detected the machine running Crafty has no "
|
||||||
|
"connection to the internet. Client connections to "
|
||||||
|
"the server may be limited."
|
||||||
|
)
|
||||||
|
Console.warning(
|
||||||
|
"We have detected the machine running Crafty has no "
|
||||||
|
"connection to the internet. Client connections to "
|
||||||
|
"the server may be limited."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def controller_setup():
|
||||||
|
"""
|
||||||
|
Method sets up the software controllers.
|
||||||
|
This also sets the application path as well as the
|
||||||
|
master server dir (if not set).
|
||||||
|
|
||||||
|
This also clears the support logs status.
|
||||||
|
"""
|
||||||
|
if not controller.check_system_user():
|
||||||
|
controller.add_system_user()
|
||||||
|
|
||||||
|
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"Application path : '{APPLICATION_PATH}'")
|
||||||
|
|
||||||
|
controller.clear_support_status()
|
||||||
|
|
||||||
|
|
||||||
|
def tasks_starter():
|
||||||
|
"""
|
||||||
|
Method starts stats recording, app scheduler, and
|
||||||
|
serverjars/steamCMD cache refreshers
|
||||||
|
"""
|
||||||
|
# start stats logging
|
||||||
|
tasks_manager.start_stats_recording()
|
||||||
|
|
||||||
|
# once the controller is up and stats are logging, we can kick off
|
||||||
|
# the scheduler officially
|
||||||
|
tasks_manager.start_scheduler()
|
||||||
|
|
||||||
|
# refresh our cache and schedule for every 12 hoursour cache refresh
|
||||||
|
# for serverjars.com
|
||||||
|
tasks_manager.serverjar_cache_refresher()
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(signum, _frame):
|
||||||
|
"""
|
||||||
|
Method handles sigterm and shuts the app down.
|
||||||
|
"""
|
||||||
|
if not args.daemon:
|
||||||
|
print() # for newline after prompt
|
||||||
|
signame = signal.Signals(signum).name
|
||||||
|
logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...")
|
||||||
|
Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...")
|
||||||
|
tasks_manager._main_graceful_exit()
|
||||||
|
crafty_prompt.universal_exit()
|
||||||
|
|
||||||
|
|
||||||
|
def do_cleanup():
|
||||||
|
"""
|
||||||
|
Checks Crafty's temporary directory and clears it out on boot.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info("Removing old temp dirs")
|
||||||
|
FileHelpers.del_dirs(os.path.join(controller.project_root, "temp"))
|
||||||
|
except:
|
||||||
|
logger.info("Did not find old temp dir.")
|
||||||
|
os.mkdir(os.path.join(controller.project_root, "temp"))
|
||||||
|
|
||||||
|
|
||||||
|
def do_version_check():
|
||||||
|
"""
|
||||||
|
Checks for remote version differences.
|
||||||
|
|
||||||
|
Prints in terminal with differences if true.
|
||||||
|
|
||||||
|
Also sets helper variable to update available when pages
|
||||||
|
are served.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check if new version available
|
||||||
|
remote_ver = helper.check_remote_version()
|
||||||
|
if remote_ver:
|
||||||
|
notice = f"""
|
||||||
|
A new version of Crafty is available!
|
||||||
|
{'/' * 37}
|
||||||
|
New version available: {remote_ver}
|
||||||
|
Current version: {pkg_version.parse(helper.get_version_string())}
|
||||||
|
{'/' * 37}
|
||||||
|
"""
|
||||||
|
Console.yellow(notice)
|
||||||
|
|
||||||
|
crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > "
|
||||||
|
|
||||||
|
|
||||||
|
def setup_starter():
|
||||||
|
"""
|
||||||
|
This method starts our setup threads.
|
||||||
|
(tasks scheduler, internet checks, controller setups)
|
||||||
|
|
||||||
|
Once our threads complete we will set our startup
|
||||||
|
variable to false and send a reload to any clients waiting.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not args.daemon:
|
||||||
|
time.sleep(0.01) # Wait for the prompt to start
|
||||||
|
print() # Make a newline after the prompt so logs are on an empty line
|
||||||
|
else:
|
||||||
|
time.sleep(0.01) # Wait for the daemon info message
|
||||||
|
|
||||||
|
Console.info("Setting up Crafty's internal components...")
|
||||||
|
# Start the setup threads
|
||||||
|
web_sock.broadcast("update", {"section": "tasks"})
|
||||||
|
time.sleep(2)
|
||||||
|
tasks_starter_thread.start()
|
||||||
|
web_sock.broadcast("update", {"section": "internet"})
|
||||||
|
time.sleep(2)
|
||||||
|
internet_check_thread.start()
|
||||||
|
web_sock.broadcast(
|
||||||
|
"update",
|
||||||
|
{"section": "internals"},
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
controller_setup_thread.start()
|
||||||
|
|
||||||
|
# Wait for the setup threads to finish
|
||||||
|
web_sock.broadcast(
|
||||||
|
"update",
|
||||||
|
{"section": "almost"},
|
||||||
|
)
|
||||||
|
tasks_starter_thread.join()
|
||||||
|
internet_check_thread.join()
|
||||||
|
controller_setup_thread.join()
|
||||||
|
helper.crafty_starting = False
|
||||||
|
web_sock.broadcast("send_start_reload", "")
|
||||||
|
do_version_check()
|
||||||
|
Console.info("Crafty has fully started and is now ready for use!")
|
||||||
|
|
||||||
|
do_cleanup()
|
||||||
|
|
||||||
|
if not args.daemon:
|
||||||
|
# Put the prompt under the cursor
|
||||||
|
crafty_prompt.print_prompt()
|
||||||
|
|
||||||
|
|
||||||
def do_intro():
|
def do_intro():
|
||||||
|
"""
|
||||||
|
Runs the Crafty Controller Terminal Intro with information about the software
|
||||||
|
This method checks for a "settings file" or config.json. If it does not find
|
||||||
|
one it will create one.
|
||||||
|
"""
|
||||||
logger.info("***** Crafty Controller Started *****")
|
logger.info("***** Crafty Controller Started *****")
|
||||||
|
|
||||||
version = helper.get_version_string()
|
version = helper.get_version_string()
|
||||||
@ -72,6 +255,11 @@ def do_intro():
|
|||||||
|
|
||||||
|
|
||||||
def setup_logging(debug=True):
|
def setup_logging(debug=True):
|
||||||
|
"""
|
||||||
|
This method sets up our logging for Crafty. It takes
|
||||||
|
one optional (defaulted to True) parameter which
|
||||||
|
determines whether or not the logging level is "debug" or verbose.
|
||||||
|
"""
|
||||||
logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json")
|
logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json")
|
||||||
if not helper.check_file_exists(
|
if not helper.check_file_exists(
|
||||||
os.path.join(os.path.curdir, "logs", "auth_tracker.log")
|
os.path.join(os.path.curdir, "logs", "auth_tracker.log")
|
||||||
@ -117,11 +305,11 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
helper.ensure_logging_setup()
|
helper.ensure_logging_setup()
|
||||||
|
helper.crafty_starting = True
|
||||||
|
# Init WebSocket Manager Here
|
||||||
|
web_sock = WebSocketManager()
|
||||||
setup_logging(debug=args.verbose)
|
setup_logging(debug=args.verbose)
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
Console.level = "debug"
|
Console.level = "debug"
|
||||||
|
|
||||||
@ -133,23 +321,27 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# print our pretty start message
|
# print our pretty start message
|
||||||
do_intro()
|
do_intro()
|
||||||
|
|
||||||
# our session file, helps prevent multiple controller agents on the same machine.
|
# our session file, helps prevent multiple controller agents on the same machine.
|
||||||
helper.create_session_file(ignore=args.ignore)
|
helper.create_session_file(ignore=args.ignore)
|
||||||
|
|
||||||
# start the database
|
# start the database
|
||||||
database = peewee.SqliteDatabase(
|
database = peewee.SqliteDatabase(
|
||||||
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
helper.db_path, pragmas={"journal_mode": "wal", "cache_size": -1024 * 10}
|
||||||
)
|
)
|
||||||
database_proxy.initialize(database)
|
database_proxy.initialize(database)
|
||||||
|
|
||||||
migration_manager = MigrationManager(database, helper)
|
migration_manager = MigrationManager(database, helper)
|
||||||
migration_manager.up() # Automatically runs migrations
|
migration_manager.up() # Automatically runs migrations
|
||||||
|
|
||||||
# do our installer stuff
|
# init classes
|
||||||
|
# now the tables are created, we can load the tasks_manager and server controller
|
||||||
user_helper = HelperUsers(database, helper)
|
user_helper = HelperUsers(database, helper)
|
||||||
management_helper = HelpersManagement(database, helper)
|
management_helper = HelpersManagement(database, helper)
|
||||||
installer = DatabaseBuilder(database, helper, user_helper, management_helper)
|
installer = DatabaseBuilder(database, helper, user_helper, management_helper)
|
||||||
|
file_helper = FileHelpers(helper)
|
||||||
|
import_helper = ImportHelpers(helper, file_helper)
|
||||||
|
controller = Controller(database, helper, file_helper, import_helper)
|
||||||
|
controller.set_project_root(APPLICATION_PATH)
|
||||||
|
tasks_manager = TasksManager(helper, controller, file_helper)
|
||||||
|
import3 = Import3(helper, controller)
|
||||||
FRESH_INSTALL = installer.is_fresh_install()
|
FRESH_INSTALL = installer.is_fresh_install()
|
||||||
|
|
||||||
if FRESH_INSTALL:
|
if FRESH_INSTALL:
|
||||||
@ -171,153 +363,45 @@ if __name__ == "__main__":
|
|||||||
helper.set_setting("reset_secrets_on_next_boot", False)
|
helper.set_setting("reset_secrets_on_next_boot", False)
|
||||||
else:
|
else:
|
||||||
Console.info("No flag found. Secrets are staying")
|
Console.info("No flag found. Secrets are staying")
|
||||||
file_helper = FileHelpers(helper)
|
|
||||||
import_helper = ImportHelpers(helper, file_helper)
|
# Check to see if client config.json version is different than the
|
||||||
# Init WebSocket Manager Here
|
# Master config.json in helpers.py
|
||||||
WebSocketManager()
|
|
||||||
# now the tables are created, we can load the tasks_manager and server controller
|
|
||||||
controller = Controller(database, helper, file_helper, import_helper)
|
|
||||||
Console.info("Checking for remote changes to config.json")
|
Console.info("Checking for remote changes to config.json")
|
||||||
controller.get_config_diff()
|
controller.get_config_diff()
|
||||||
Console.info("Remote change complete.")
|
Console.info("Remote change complete.")
|
||||||
|
|
||||||
import3 = Import3(helper, controller)
|
# startup the web server
|
||||||
tasks_manager = TasksManager(helper, controller, file_helper)
|
|
||||||
tasks_manager.start_webserver()
|
tasks_manager.start_webserver()
|
||||||
|
|
||||||
def signal_handler(signum, _frame):
|
|
||||||
if not args.daemon:
|
|
||||||
print() # for newline after prompt
|
|
||||||
signame = signal.Signals(signum).name
|
|
||||||
logger.info(f"Recieved signal {signame} [{signum}], stopping Crafty...")
|
|
||||||
Console.info(f"Recieved signal {signame} [{signum}], stopping Crafty...")
|
|
||||||
tasks_manager._main_graceful_exit()
|
|
||||||
crafty_prompt.universal_exit()
|
|
||||||
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
# init servers
|
# init servers
|
||||||
logger.info("Initializing all servers defined")
|
logger.info("Initializing all servers defined")
|
||||||
Console.info("Initializing all servers defined")
|
Console.info("Initializing all servers defined")
|
||||||
|
web_sock.broadcast(
|
||||||
|
"update",
|
||||||
|
{"section": "serverInit"},
|
||||||
|
)
|
||||||
controller.servers.init_all_servers()
|
controller.servers.init_all_servers()
|
||||||
|
|
||||||
def tasks_starter():
|
# start up our tasks handler in tasks.py
|
||||||
# start stats logging
|
|
||||||
tasks_manager.start_stats_recording()
|
|
||||||
|
|
||||||
# once the controller is up and stats are logging, we can kick off
|
|
||||||
# the scheduler officially
|
|
||||||
tasks_manager.start_scheduler()
|
|
||||||
|
|
||||||
# refresh our cache and schedule for every 12 hoursour cache refresh
|
|
||||||
# for serverjars.com
|
|
||||||
tasks_manager.serverjar_cache_refresher()
|
|
||||||
|
|
||||||
tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter")
|
tasks_starter_thread = Thread(target=tasks_starter, name="tasks_starter")
|
||||||
|
|
||||||
def internet_check():
|
# check to see if instance has internet
|
||||||
print()
|
|
||||||
logger.info("Checking Internet. This may take a minute.")
|
|
||||||
Console.info("Checking Internet. This may take a minute.")
|
|
||||||
|
|
||||||
if not helper.check_internet():
|
|
||||||
logger.warning(
|
|
||||||
"We have detected the machine running Crafty has no "
|
|
||||||
"connection to the internet. Client connections to "
|
|
||||||
"the server may be limited."
|
|
||||||
)
|
|
||||||
Console.warning(
|
|
||||||
"We have detected the machine running Crafty has no "
|
|
||||||
"connection to the internet. Client connections to "
|
|
||||||
"the server may be limited."
|
|
||||||
)
|
|
||||||
|
|
||||||
internet_check_thread = Thread(target=internet_check, name="internet_check")
|
internet_check_thread = Thread(target=internet_check, name="internet_check")
|
||||||
|
|
||||||
def controller_setup():
|
# start the Crafty console.
|
||||||
if not controller.check_system_user():
|
|
||||||
controller.add_system_user()
|
|
||||||
|
|
||||||
if getattr(sys, "frozen", False):
|
|
||||||
application_path = os.path.dirname(sys.executable)
|
|
||||||
running_mode = "Frozen/executable"
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
app_full_path = os.path.realpath(__file__)
|
|
||||||
application_path = os.path.dirname(app_full_path)
|
|
||||||
running_mode = "Non-interactive (e.g. 'python main.py')"
|
|
||||||
except NameError:
|
|
||||||
application_path = os.getcwd()
|
|
||||||
running_mode = "Interactive"
|
|
||||||
|
|
||||||
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"Application path : '{application_path}'")
|
|
||||||
|
|
||||||
controller.clear_support_status()
|
|
||||||
|
|
||||||
crafty_prompt = MainPrompt(
|
crafty_prompt = MainPrompt(
|
||||||
helper, tasks_manager, migration_manager, controller, import3
|
helper, tasks_manager, migration_manager, controller, import3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# set up all controllers
|
||||||
controller_setup_thread = Thread(target=controller_setup, name="controller_setup")
|
controller_setup_thread = Thread(target=controller_setup, name="controller_setup")
|
||||||
|
|
||||||
def setup_starter():
|
setup_starter_thread = Thread(target=setup_starter, name="setup_starter")
|
||||||
if not args.daemon:
|
|
||||||
time.sleep(0.01) # Wait for the prompt to start
|
|
||||||
print() # Make a newline after the prompt so logs are on an empty line
|
|
||||||
else:
|
|
||||||
time.sleep(0.01) # Wait for the daemon info message
|
|
||||||
|
|
||||||
Console.info("Setting up Crafty's internal components...")
|
setup_starter_thread.start()
|
||||||
|
|
||||||
# Start the setup threads
|
|
||||||
tasks_starter_thread.start()
|
|
||||||
internet_check_thread.start()
|
|
||||||
controller_setup_thread.start()
|
|
||||||
|
|
||||||
# Wait for the setup threads to finish
|
|
||||||
tasks_starter_thread.join()
|
|
||||||
internet_check_thread.join()
|
|
||||||
controller_setup_thread.join()
|
|
||||||
|
|
||||||
Console.info("Crafty has fully started and is now ready for use!")
|
|
||||||
|
|
||||||
# Check if new version available
|
|
||||||
remote_ver = helper.check_remote_version()
|
|
||||||
if remote_ver:
|
|
||||||
notice = f"""
|
|
||||||
A new version of Crafty is available!
|
|
||||||
{'/' * 37}
|
|
||||||
New version available: {remote_ver}
|
|
||||||
Current version: {pkg_version.parse(helper.get_version_string())}
|
|
||||||
{'/' * 37}
|
|
||||||
"""
|
|
||||||
Console.yellow(notice)
|
|
||||||
|
|
||||||
crafty_prompt.prompt = f"Crafty Controller v{helper.get_version_string()} > "
|
|
||||||
try:
|
|
||||||
logger.info("Removing old temp dirs")
|
|
||||||
FileHelpers.del_dirs(os.path.join(controller.project_root, "temp"))
|
|
||||||
except:
|
|
||||||
logger.info("Did not find old temp dir.")
|
|
||||||
os.mkdir(os.path.join(controller.project_root, "temp"))
|
|
||||||
|
|
||||||
if not args.daemon:
|
|
||||||
# Put the prompt under the cursor
|
|
||||||
crafty_prompt.print_prompt()
|
|
||||||
|
|
||||||
Thread(target=setup_starter, name="setup_starter").start()
|
|
||||||
|
|
||||||
if not args.daemon:
|
if not args.daemon:
|
||||||
# Start the Crafty prompt
|
# Start the Crafty prompt
|
||||||
|
@ -4,13 +4,13 @@ argon2-cffi==23.1.0
|
|||||||
cached_property==1.5.2
|
cached_property==1.5.2
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
croniter==1.4.1
|
croniter==1.4.1
|
||||||
cryptography==41.0.4
|
cryptography==41.0.7
|
||||||
libgravatar==1.0.4
|
libgravatar==1.0.4
|
||||||
nh3==0.2.14
|
nh3==0.2.14
|
||||||
packaging==23.2
|
packaging==23.2
|
||||||
peewee==3.13
|
peewee==3.13
|
||||||
psutil==5.9.5
|
psutil==5.9.5
|
||||||
pyOpenSSL==23.2.0
|
pyOpenSSL==23.3.0
|
||||||
pyjwt==2.8.0
|
pyjwt==2.8.0
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
|
Loading…
Reference in New Issue
Block a user