mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into 'master'
4.0.6 See merge request crafty-controller/crafty-4!396
This commit is contained in:
commit
d20a5fae5a
1
.gitignore
vendored
1
.gitignore
vendored
@ -20,6 +20,7 @@ venv.bak/
|
||||
.idea/
|
||||
/servers/
|
||||
/backups/
|
||||
/temp/
|
||||
/docker/servers/
|
||||
/docker/backups/
|
||||
session.lock
|
||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,5 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## --- [4.0.6] - 2022/07/06
|
||||
### New features
|
||||
None
|
||||
### Bug fixes
|
||||
- Remove redundant path check on backup restore ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/390))
|
||||
- Fix issue with stats pinging on slow starting servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/391))
|
||||
- Fix unhandled exeption when serverjars api returns 'None' ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/392))
|
||||
- Fix ajax issue with unzip on firefox ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/393))
|
||||
- Turn off verbose logging on Docker ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/394))
|
||||
- Refactor tempdir from packaging logs ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/commit/f1d11bfb0d943c737ef2c4ef77bd0bfc9bcf83ba))
|
||||
### Tweaks
|
||||
- Remove autofill on user form ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/395))
|
||||
- Confirm username does not exist on edituser ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/395))
|
||||
- Check for passwords matching on client side ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/395))
|
||||
### Lang
|
||||
- Add string "cloneConfirm" to german translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/389))
|
||||
<br><br>
|
||||
|
||||
## --- [4.0.5] - 2022/06/24
|
||||
### New features
|
||||
None
|
||||
|
@ -53,7 +53,7 @@ EXPOSE 25500-25600
|
||||
|
||||
# Start Crafty through wrapper
|
||||
ENTRYPOINT ["/crafty/docker_launcher.sh"]
|
||||
CMD ["-v", "-d", "-i"]
|
||||
CMD ["-d", "-i"]
|
||||
|
||||
# Add meta labels
|
||||
ARG BUILD_DATE
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.5--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.6--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
||||
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
||||
|
||||
# Crafty Controller 4.0.5-beta
|
||||
# Crafty Controller 4.0.6-beta
|
||||
> Python based Control Panel for your Minecraft Server
|
||||
|
||||
## What is Crafty Controller?
|
||||
|
@ -100,6 +100,10 @@ class UsersController:
|
||||
def get_all_user_ids() -> t.List[int]:
|
||||
return HelperUsers.get_all_user_ids()
|
||||
|
||||
@staticmethod
|
||||
def get_all_usernames():
|
||||
return HelperUsers.get_all_usernames()
|
||||
|
||||
@staticmethod
|
||||
def get_id_by_name(username):
|
||||
return HelperUsers.get_user_id_by_name(username)
|
||||
|
@ -266,14 +266,24 @@ class Stats:
|
||||
"Unable to read the server icon due to the following error:", exc_info=e
|
||||
)
|
||||
if ping_obj:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": ping_obj.description,
|
||||
"server_version": ping_obj.version,
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
try:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": ping_obj.description,
|
||||
"server_version": ping_obj.version,
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
except:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
"max": online_stats.get("max", 0),
|
||||
"players": online_stats.get("players", 0),
|
||||
"server_description": "",
|
||||
"server_version": "",
|
||||
"server_icon": server_icon,
|
||||
}
|
||||
else:
|
||||
ping_data = {
|
||||
"online": online_stats.get("online", 0),
|
||||
|
@ -99,6 +99,14 @@ class HelperUsers:
|
||||
query = Users.select().where(Users.username != "system")
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def get_all_usernames():
|
||||
usernames = []
|
||||
query = Users.select().where(Users.username != "system")
|
||||
for user in query:
|
||||
usernames.append(user.username)
|
||||
return usernames
|
||||
|
||||
@staticmethod
|
||||
def get_all_user_ids() -> t.List[int]:
|
||||
return [
|
||||
|
@ -101,7 +101,7 @@ class FileHelpers:
|
||||
ziproot = path_to_zip
|
||||
for file in files:
|
||||
try:
|
||||
logger.info(f"backing up: {os.path.join(root, file)}")
|
||||
logger.info(f"packaging: {os.path.join(root, file)}")
|
||||
if os.name == "nt":
|
||||
zip_file.write(
|
||||
os.path.join(root, file),
|
||||
@ -115,7 +115,7 @@ class FileHelpers:
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Error backing up: {os.path.join(root, file)}!"
|
||||
f"Error packaging: {os.path.join(root, file)}!"
|
||||
f" - Error was: {e}"
|
||||
)
|
||||
|
||||
|
@ -4,7 +4,6 @@ from pathlib import Path
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import tempfile
|
||||
from peewee import DoesNotExist
|
||||
|
||||
# TZLocal is set as a hidden import on win pipeline
|
||||
@ -85,27 +84,38 @@ class Controller:
|
||||
self.users.set_prepare(exec_user["user_id"])
|
||||
logger.info("Checking for previous support logs.")
|
||||
if exec_user["support_logs"] != "":
|
||||
logger.info(
|
||||
f"Found previous support log request at {exec_user['support_logs']}"
|
||||
)
|
||||
if self.helper.validate_traversal(
|
||||
tempfile.gettempdir(), exec_user["support_logs"]
|
||||
):
|
||||
logger.debug("No transversal detected. Going for the delete.")
|
||||
self.del_support_file(exec_user["support_logs"])
|
||||
if os.path.exists(exec_user["support_logs"]):
|
||||
logger.info(
|
||||
f"Found previous support log request at {exec_user['support_logs']}"
|
||||
)
|
||||
if self.helper.validate_traversal(
|
||||
os.path.join(self.project_root, "temp"), exec_user["support_logs"]
|
||||
):
|
||||
logger.debug("No transversal detected. Going for the delete.")
|
||||
self.del_support_file(exec_user["support_logs"])
|
||||
# pausing so on screen notifications can run for user
|
||||
time.sleep(7)
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
exec_user["user_id"], "notification", "Preparing your support logs"
|
||||
)
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
temp_zip_storage = tempfile.mkdtemp()
|
||||
full_temp = os.path.join(temp_dir, "support_logs")
|
||||
os.mkdir(full_temp)
|
||||
self.helper.ensure_dir_exists(
|
||||
os.path.join(self.project_root, "temp", str(exec_user["user_id"]))
|
||||
)
|
||||
temp_dir = os.path.join(
|
||||
self.project_root, "temp", str(exec_user["user_id"]), "support_logs"
|
||||
)
|
||||
|
||||
self.helper.ensure_dir_exists(
|
||||
os.path.join(self.project_root, "temp", str(exec_user["user_id"]), "zip")
|
||||
)
|
||||
temp_zip_storage = os.path.join(
|
||||
self.project_root, "temp", str(exec_user["user_id"]), "zip"
|
||||
)
|
||||
os.mkdir(temp_dir)
|
||||
temp_zip_storage = os.path.join(temp_zip_storage, "support_logs")
|
||||
crafty_path = os.path.join(full_temp, "crafty")
|
||||
crafty_path = os.path.join(temp_dir, "crafty")
|
||||
os.mkdir(crafty_path)
|
||||
server_path = os.path.join(full_temp, "server")
|
||||
server_path = os.path.join(temp_dir, "server")
|
||||
os.mkdir(server_path)
|
||||
if exec_user["superuser"]:
|
||||
defined_servers = self.servers.list_defined_servers()
|
||||
@ -160,15 +170,14 @@ class Controller:
|
||||
"interval",
|
||||
seconds=1,
|
||||
id="logs_" + str(exec_user["user_id"]),
|
||||
args=[full_temp, temp_zip_storage + ".zip", exec_user],
|
||||
args=[temp_dir, temp_zip_storage + ".zip", exec_user],
|
||||
)
|
||||
FileHelpers.make_archive(temp_zip_storage, temp_dir)
|
||||
|
||||
FileHelpers.make_compressed_archive(temp_zip_storage, temp_dir)
|
||||
if len(self.helper.websocket_helper.clients) > 0:
|
||||
self.helper.websocket_helper.broadcast_user(
|
||||
exec_user["user_id"],
|
||||
"support_status_update",
|
||||
Helpers.calc_percent(full_temp, temp_zip_storage + ".zip"),
|
||||
Helpers.calc_percent(temp_dir, temp_zip_storage + ".zip"),
|
||||
)
|
||||
|
||||
temp_zip_storage += ".zip"
|
||||
@ -783,26 +792,21 @@ class Controller:
|
||||
|
||||
def rename_backup_dir(self, old_server_id, new_server_id, new_uuid):
|
||||
server_data = self.servers.get_server_data_by_id(old_server_id)
|
||||
server_obj = self.servers.get_server_obj(new_server_id)
|
||||
old_bu_path = server_data["backup_path"]
|
||||
ServerPermsController.backup_role_swap(old_server_id, new_server_id)
|
||||
if not Helpers.is_os_windows():
|
||||
backup_path = Helpers.validate_traversal(
|
||||
self.helper.backup_path, old_bu_path
|
||||
)
|
||||
if Helpers.is_os_windows():
|
||||
backup_path = Helpers.validate_traversal(
|
||||
Helpers.wtol_path(self.helper.backup_path),
|
||||
Helpers.wtol_path(old_bu_path),
|
||||
)
|
||||
backup_path = Helpers.wtol_path(str(backup_path))
|
||||
backup_path.replace(" ", "^ ")
|
||||
backup_path = Path(backup_path)
|
||||
backup_path = old_bu_path
|
||||
backup_path = Path(backup_path)
|
||||
backup_path_components = list(backup_path.parts)
|
||||
backup_path_components[-1] = new_uuid
|
||||
new_bu_path = pathlib.PurePath(os.path.join(*backup_path_components))
|
||||
if os.path.isdir(new_bu_path):
|
||||
if Helpers.validate_traversal(self.helper.backup_path, new_bu_path):
|
||||
os.rmdir(new_bu_path)
|
||||
server_obj.backup_path = new_bu_path
|
||||
default_backup_dir = os.path.join(self.helper.backup_path, new_uuid)
|
||||
try:
|
||||
os.rmdir(default_backup_dir)
|
||||
except:
|
||||
logger.error("Could not delete default backup dir")
|
||||
self.servers.update_server(server_obj)
|
||||
backup_path.rename(new_bu_path)
|
||||
|
||||
def register_server(
|
||||
|
@ -15,6 +15,7 @@ from app.classes.models.management import HelpersManagement
|
||||
from app.classes.models.users import HelperUsers
|
||||
from app.classes.controllers.users_controller import UsersController
|
||||
from app.classes.shared.console import Console
|
||||
from app.classes.shared.file_helpers import FileHelpers
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.main_controller import Controller
|
||||
from app.classes.web.tornado_handler import Webserver
|
||||
@ -145,6 +146,15 @@ class TasksManager:
|
||||
self.controller.servers.stop_all_servers()
|
||||
except:
|
||||
logger.info("Caught error during shutdown", exc_info=True)
|
||||
try:
|
||||
temp_dir = os.path.join(self.controller.project_root, "temp")
|
||||
FileHelpers.del_dirs(temp_dir)
|
||||
except:
|
||||
logger.info(
|
||||
"Caught error during shutdown - "
|
||||
"unable to delete files from Crafty Temp Dir",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
logger.info("***** Crafty Shutting Down *****\n\n")
|
||||
Console.info("***** Crafty Shutting Down *****\n\n")
|
||||
|
@ -1860,6 +1860,13 @@ class PanelHandler(BaseHandler):
|
||||
)
|
||||
user_id = bleach.clean(self.get_argument("id", None))
|
||||
username = bleach.clean(self.get_argument("username", None).lower())
|
||||
if (
|
||||
username != self.controller.users.get_user_by_id(user_id)["username"]
|
||||
and username in self.controller.users.get_all_usernames()
|
||||
):
|
||||
self.redirect(
|
||||
"/panel/error?error=Duplicate User: Useranme already exists."
|
||||
)
|
||||
password0 = bleach.clean(self.get_argument("password0", None))
|
||||
password1 = bleach.clean(self.get_argument("password1", None))
|
||||
email = bleach.clean(self.get_argument("email", "default@example.com"))
|
||||
|
@ -156,6 +156,9 @@ class ServerHandler(BaseHandler):
|
||||
page_data["js_server_types"] = json.dumps(
|
||||
self.controller.server_jars.get_serverjar_data()
|
||||
)
|
||||
if page_data["server_types"] is None:
|
||||
page_data["server_types"] = []
|
||||
page_data["js_server_types"] = []
|
||||
template = "server/wizard.html"
|
||||
|
||||
if page == "bedrock_step1":
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"sub": 5,
|
||||
"sub": 6,
|
||||
"meta": "beta"
|
||||
}
|
||||
|
@ -75,29 +75,35 @@
|
||||
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="username" id="username"
|
||||
value="{{ data['user']['username'] }}" placeholder="User Name">
|
||||
<input type="text" class="form-control" name="username" id="username" autocomplete="off"
|
||||
data-lpignore="true" value="{{ data['user']['username'] }}" placeholder="User Name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }}
|
||||
</small> </label>
|
||||
<input type="password" class="form-control" name="password0" id="password0" value=""
|
||||
placeholder="Password">
|
||||
autocomplete="new-password" data-lpignore="true" placeholder="Password">
|
||||
<span class="passwords-match" ,
|
||||
data-content="{{ translate('panelConfig', 'match', data['lang']) }}" ,
|
||||
data-placement="right"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }}
|
||||
<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="password" class="form-control" name="password1" id="password1" value=""
|
||||
placeholder="Repeat Password">
|
||||
autocomplete="new-password" data-lpignore="true" placeholder="Repeat Password">
|
||||
<span class="passwords-match" ,
|
||||
data-content="{{ translate('panelConfig', 'match', data['lang']) }}" ,
|
||||
data-placement="right"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="email" class="form-control" name="email" id="email"
|
||||
value="{{ data['user']['email'] }}" placeholder="Gravatar Email">
|
||||
<input type="email" class="form-control" name="email" id="email" autocomplete="off"
|
||||
data-lpignore="true" value="{{ data['user']['email'] }}" placeholder="Gravatar Email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang'])
|
||||
@ -237,7 +243,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{
|
||||
<button class="btn btn-success mr-2" onclick="submit_user(event);"><i class="fas fa-save"></i> {{
|
||||
translate('panelConfig', 'save', data['lang']) }}</button>
|
||||
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i
|
||||
class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
|
||||
@ -290,12 +296,43 @@
|
||||
|
||||
|
||||
</div>
|
||||
<style>
|
||||
.popover-body {
|
||||
color: white !important;
|
||||
outline: 1px solid red !important;
|
||||
;
|
||||
}
|
||||
</style>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
function submit_user(event) {
|
||||
if (!validateForm()) {
|
||||
event.preventDefault();
|
||||
} else {
|
||||
$('#userForm').submit();
|
||||
}
|
||||
}
|
||||
function validateForm() {
|
||||
let password0 = document.getElementById("password0").value
|
||||
let password1 = document.getElementById("password1").value
|
||||
if (password0 != password1) {
|
||||
$('.passwords-match').popover('show');
|
||||
$('.popover-body').click(function () {
|
||||
$('.passwords-match').popover("hide");
|
||||
});
|
||||
document.body.scrollTop = 0;
|
||||
document.documentElement.scrollTop = 0;
|
||||
$("#password0").css("outline", "1px solid red");
|
||||
$("#password1").css("outline", "1px solid red");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const userId = new URLSearchParams(document.location.search).get('id')
|
||||
|
||||
$(".delete-user").click(function () {
|
||||
|
@ -691,8 +691,10 @@
|
||||
data: {
|
||||
path: path
|
||||
},
|
||||
success: function (data) {
|
||||
window.location.href = "/panel/server_detail?id=" + serverId + "&subpage=files";
|
||||
},
|
||||
});
|
||||
window.location.href = "/panel/server_detail?id=" + serverId + "&subpage=files"
|
||||
}
|
||||
|
||||
function sendFile(file, path, serverId, left, onProgress) {
|
||||
|
@ -66,6 +66,7 @@
|
||||
"cannotSeeOnMobile": "Sehen Sie nicht alles auf dem Handy?",
|
||||
"cannotSeeOnMobile2": "Versuchen Sie, die Tabelle seitlich zu verschieben.",
|
||||
"clone": "Klonen",
|
||||
"cloneConfirm": "Sind Sie sich sicher, dass Sie den Server klonen wollen? Diese Aufgabe kann eine Weile dauern.",
|
||||
"cpuCores": "CPU Kerne",
|
||||
"cpuCurFreq": "Momentaner CPU-Takt",
|
||||
"cpuMaxFreq": "Maximaler CPU-Takt",
|
||||
|
@ -220,7 +220,8 @@
|
||||
"superConfirm": "Proceed only if you want this user to have access to EVERYTHING (all user accounts, servers, panel settings, etc.). They can even revoke your superuser rights.",
|
||||
"superConfirmTitle": "Enable superuser? Are you sure?",
|
||||
"user": "User",
|
||||
"users": "Users"
|
||||
"users": "Users",
|
||||
"match": "Passwords must match"
|
||||
},
|
||||
"rolesConfig": {
|
||||
"config": "Role Config",
|
||||
|
6
main.py
6
main.py
@ -226,6 +226,12 @@ if __name__ == "__main__":
|
||||
|
||||
Console.info("Crafty has fully started and is now ready for use!")
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user