Merge branch 'dev' into enhancement/ignored-exits

This commit is contained in:
Zedifus 2023-02-11 18:33:48 +00:00
commit fa6f17e347
15 changed files with 221 additions and 164 deletions

View File

@ -1,13 +1,15 @@
# Changelog # Changelog
## --- [4.0.21] - 2023/TBD ## --- [4.0.21] - 2023/TBD
### New features ### New features
TBD - Add better feedback for uploads with a progress bar ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
### Bug fixes ### Bug fixes
TBD - Fix exception related to page data on server start ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/544))
### Tweaks ### Tweaks
TBD - Cleanup authentication helpers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/545))
- Optimize file upload progress WS ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/546))
- Truncate sidebar servers to a max of 10 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/552))
### Lang ### Lang
TBD - Add additional translations to backups page strings ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/543))
<br><br> <br><br>
## --- [4.0.20] - 2023/01/29 ## --- [4.0.20] - 2023/01/29

View File

@ -13,7 +13,6 @@ logger = logging.getLogger(__name__)
class Authentication: class Authentication:
def __init__(self, helper): def __init__(self, helper):
self.helper = helper self.helper = helper
self.secret = "my secret"
try: try:
self.secret = ManagementController.get_crafty_api_key() self.secret = ManagementController.get_crafty_api_key()
if self.secret == "": if self.secret == "":

View File

@ -1013,7 +1013,7 @@ class Helpers:
for item in file_list: for item in file_list:
if os.path.isdir(os.path.join(folder, item)): if os.path.isdir(os.path.join(folder, item)):
dir_list.append(item) dir_list.append(item)
elif str(item) != "crafty.sqlite": elif str(item) != self.ignored_names:
unsorted_files.append(item) unsorted_files.append(item)
file_list = sorted(dir_list, key=str.casefold) + sorted( file_list = sorted(dir_list, key=str.casefold) + sorted(
unsorted_files, key=str.casefold unsorted_files, key=str.casefold
@ -1054,7 +1054,7 @@ class Helpers:
for item in file_list: for item in file_list:
if os.path.isdir(os.path.join(folder, item)): if os.path.isdir(os.path.join(folder, item)):
dir_list.append(item) dir_list.append(item)
elif str(item) != "crafty.sqlite": elif str(item) != self.ignored_names:
unsorted_files.append(item) unsorted_files.append(item)
file_list = sorted(dir_list, key=str.casefold) + sorted( file_list = sorted(dir_list, key=str.casefold) + sorted(
unsorted_files, key=str.casefold unsorted_files, key=str.casefold

View File

@ -183,6 +183,7 @@ class ServerHandler(BaseHandler):
"version_data": "version_data_here", # TODO "version_data": "version_data_here", # TODO
"user_data": exec_user, "user_data": exec_user,
"show_contribute": self.helper.get_setting("show_contribute_link", True), "show_contribute": self.helper.get_setting("show_contribute_link", True),
"background": self.controller.cached_login,
"lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]), "lang": self.controller.users.get_user_lang_by_id(exec_user["user_id"]),
"lang_page": Helpers.get_lang_page( "lang_page": Helpers.get_lang_page(
self.controller.users.get_user_lang_by_id(exec_user["user_id"]) self.controller.users.get_user_lang_by_id(exec_user["user_id"])

View File

@ -7,12 +7,12 @@ logger = logging.getLogger(__name__)
class StatusHandler(BaseHandler): class StatusHandler(BaseHandler):
def get(self): def get(self):
page_data = {"background": self.controller.cached_login} page_data = {
page_data["lang"] = self.helper.get_setting("language") "background": self.controller.cached_login,
page_data["lang_page"] = self.helper.get_lang_page( "lang": self.helper.get_setting("language"),
self.helper.get_setting("language") "lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
) "servers": self.controller.servers.get_all_servers_stats(),
page_data["servers"] = self.controller.servers.get_all_servers_stats() }
running = 0 running = 0
for srv in page_data["servers"]: for srv in page_data["servers"]:
if srv["stats"]["running"]: if srv["stats"]["running"]:

View File

@ -52,18 +52,19 @@ class UploadHandler(BaseHandler):
f"User with ID {user_id} attempted to upload a file that" f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size." f" exceeded the max body size."
) )
self.helper.websocket_helper.broadcast_user(
user_id, return self.finish_json(
"send_start_error", 413,
{ {
"error": self.helper.translation.translate( "status": "error",
"error": "TOO LARGE",
"info": self.helper.translation.translate(
"error", "error",
"fileTooLarge", "fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id), self.controller.users.get_user_lang_by_id(user_id),
), ),
}, },
) )
return
self.do_upload = True self.do_upload = True
if superuser: if superuser:
@ -141,48 +142,50 @@ class UploadHandler(BaseHandler):
f"User with ID {user_id} attempted to upload a file that" f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size." f" exceeded the max body size."
) )
self.helper.websocket_helper.broadcast_user(
user_id, return self.finish_json(
"send_start_error", 413,
{ {
"error": self.helper.translation.translate( "status": "error",
"error": "TOO LARGE",
"info": self.helper.translation.translate(
"error", "error",
"fileTooLarge", "fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id), self.controller.users.get_user_lang_by_id(user_id),
), ),
}, },
) )
return
self.do_upload = True self.do_upload = True
if not superuser: if not superuser:
self.helper.websocket_helper.broadcast_user( return self.finish_json(
user_id, 401,
"send_start_error",
{ {
"error": self.helper.translation.translate( "status": "error",
"error": "UNAUTHORIZED ACCESS",
"info": self.helper.translation.translate(
"error", "error",
"superError", "superError",
self.controller.users.get_user_lang_by_id(user_id), self.controller.users.get_user_lang_by_id(user_id),
), ),
}, },
) )
return
if not self.request.headers.get("X-Content-Type", None).startswith( if not self.request.headers.get("X-Content-Type", None).startswith(
"image/" "image/"
): ):
self.helper.websocket_helper.broadcast_user(
user_id, return self.finish_json(
"send_start_error", 415,
{ {
"error": self.helper.translation.translate( "status": "error",
"error": "TYPE ERROR",
"info": self.helper.translation.translate(
"error", "error",
"fileError", "fileError",
self.controller.users.get_user_lang_by_id(user_id), self.controller.users.get_user_lang_by_id(user_id),
), ),
}, },
) )
return
if user_id is None: if user_id is None:
logger.warning("User ID not found in upload handler call") logger.warning("User ID not found in upload handler call")
Console.warning("User ID not found in upload handler call") Console.warning("User ID not found in upload handler call")
@ -219,18 +222,19 @@ class UploadHandler(BaseHandler):
f"User with ID {user_id} attempted to upload a file that" f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size." f" exceeded the max body size."
) )
self.helper.websocket_helper.broadcast_user(
user_id, return self.finish_json(
"send_start_error", 413,
{ {
"error": self.helper.translation.translate( "status": "error",
"error": "TOO LARGE",
"info": self.helper.translation.translate(
"error", "error",
"fileTooLarge", "fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id), self.controller.users.get_user_lang_by_id(user_id),
), ),
}, },
) )
return
self.do_upload = True self.do_upload = True
if superuser: if superuser:

View File

@ -15,7 +15,7 @@
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css"> <link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
<link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css"> <link rel="stylesheet" href="/static/assets/vendors/fontawesome5/css/all.css">
<link rel="stylesheet" type="text/css" <link rel="stylesheet" type="text/css"
href="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.css" /> href="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.css" />
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css"> <link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
<link rel="stylesheet" href="/static/assets/css/crafty.css"> <link rel="stylesheet" href="/static/assets/css/crafty.css">
@ -41,14 +41,14 @@
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet"> <link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<script defer src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script> <script defer src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"
integrity="sha512-ElRFoEQdI5Ht6kZvyzXhYG9NqjtkmlkfYk0wr6wHxU9JEHakS7UJZNeml5ALk+8IKlU6jDgMabC3vkumRokgJA==" integrity="sha512-ElRFoEQdI5Ht6kZvyzXhYG9NqjtkmlkfYk0wr6wHxU9JEHakS7UJZNeml5ALk+8IKlU6jDgMabC3vkumRokgJA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"
integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.min.js"
integrity="sha512-klQv6lz2YR+MecyFYMFRuU2eAl8IPRo6zHnsc9n142TJuJHS8CG0ix4Oq9na9ceeg1u5EkBfZsFcV3U7J51iew==" integrity="sha512-klQv6lz2YR+MecyFYMFRuU2eAl8IPRo6zHnsc9n142TJuJHS8CG0ix4Oq9na9ceeg1u5EkBfZsFcV3U7J51iew=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- End Bootstrap Toggle --> <!-- End Bootstrap Toggle -->
@ -82,7 +82,7 @@
{% include notify.html %} {% include notify.html %}
<button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button" <button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button"
data-toggle="offcanvas"> data-toggle="offcanvas">
<span class="mdi mdi-menu"></span> <span class="mdi mdi-menu"></span>
</button> </button>
</div> </div>
@ -174,7 +174,7 @@
<script src="/static/assets/js/shared/hoverable-collapse.js"></script> <script src="/static/assets/js/shared/hoverable-collapse.js"></script>
<script src="/static/assets/js/shared/misc.js"></script> <script src="/static/assets/js/shared/misc.js"></script>
<script type="text/javascript" <script type="text/javascript"
src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script> src="https://cdn.datatables.net/v/bs4/dt-1.10.22/fh-3.1.7/r-2.2.6/sc-2.0.3/sp-1.2.2/datatables.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
<script type="text/javascript" src="/static/assets/js/motd.js"></script> <script type="text/javascript" src="/static/assets/js/motd.js"></script>

View File

@ -75,13 +75,13 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" <a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false"
aria-controls="page-layouts"> aria-controls="page-layouts">
<i class="fas fa-server"></i> &nbsp; <i class="fas fa-server"></i> &nbsp;
<span class="menu-title">{{ translate('sidebar', 'servers', data['lang']) }}</span> <span class="menu-title">{{ translate('sidebar', 'servers', data['lang']) }}</span>
<i class="menu-arrow"></i> <i class="menu-arrow"></i>
</a> </a>
<div class="collapse" id="page-layouts"> <div class="collapse" id="page-layouts">
<ul class="nav flex-column sub-menu"> <ul class="nav flex-column sub-menu" id="sidebar-servers">
{% if data['crafty_permissions']['Server_Creation'] in data['user_crafty_permissions'] %} {% if data['crafty_permissions']['Server_Creation'] in data['user_crafty_permissions'] %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('sidebar', <a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('sidebar',
@ -138,4 +138,9 @@
</ul> </ul>
</nav> </nav>
<script>
$(document).ready(function () {
$('#sidebar-servers li:gt(10)').remove();
})
</script>
<!-- partial --> <!-- partial -->

View File

@ -6,7 +6,8 @@
{% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %} {% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %}
{% block content %} {% block content %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/css/bootstrap-select.min.css"> <link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/css/bootstrap-select.min.css">
<div class="content-wrapper"> <div class="content-wrapper">
@ -50,62 +51,68 @@
{% for item in data['config-json'].items() %} {% for item in data['config-json'].items() %}
{% if item[0] == "reset_secrets_on_next_boot" %} {% if item[0] == "reset_secrets_on_next_boot" %}
<div class="form-group" style="background: rgba(243, 21, 6, 0.3); outline: 1px solid red; padding: 10px;"> <div class="form-group" style="background: rgba(243, 21, 6, 0.3); outline: 1px solid red; padding: 10px;">
{% else %} {% else %}
<div class="form-group"> <div class="form-group">
{% end %}
<label class="form" for="{{item[0]}}">{{item[0]}}
<small class="text-muted ml-1">
</small> </label><br />
{% if item[0] == 'language' %}
<select name="{{item[0]}}" class="form-control">
{% for lang in data['availables_languages'] %}
{% if lang == item[1] %}
<option selected>{{lang}}</option>
{% else %}
<option>{{lang}}</option>
{% end %} {% end %}
{% end %} <label class="form" for="{{item[0]}}">{{item[0]}}
</select> <small class="text-muted ml-1">
{% elif item[0] == 'disabled_language_files' %} </small> </label><br />
<div class="input-group"> {% if item[0] == 'language' %}
<button type="button" class="btn btn-outline-default custom-picker" onclick="$('option', $('#lang_select')).each(function(element) { <select name="{{item[0]}}" class="form-control">
$(this).removeAttr('selected').prop('selected', false); $('.selectpicker').selectpicker('refresh') {% for lang in data['availables_languages'] %}
});">Enable all Languages</button> {% if lang == item[1] %}
<select id="lang_select" class="form-control selectpicker show-tick" data-icon-base="fas" data-tick-icon="fa-check" multiple data-style="custom-picker">
{% for lang in data['all_languages'] %}
{% if lang in item[1] %}
<option selected>{{lang}}</option> <option selected>{{lang}}</option>
{% else %} {% else %}
<option>{{lang}}</option> <option>{{lang}}</option>
{% end %} {% end %}
{% end %} {% end %}
</select> </select>
<textarea id="disabled_lang" name="{{item[0]}}" class="form-control list hidden" rows="{{ len(data['all_languages']) }}" value="{{','.join(item[1])}}" hidden>{{','.join(item[1])}}</textarea> {% elif item[0] == 'disabled_language_files' %}
<div class="input-group">
<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')
});">Enable all Languages</button>
<select id="lang_select" class="form-control selectpicker show-tick" data-icon-base="fas"
data-tick-icon="fa-check" multiple data-style="custom-picker">
{% for lang in data['all_languages'] %}
{% if lang in item[1] %}
<option selected>{{lang}}</option>
{% else %}
<option>{{lang}}</option>
{% end %}
{% end %}
</select>
<textarea id="disabled_lang" name="{{item[0]}}" class="form-control list hidden"
rows="{{ len(data['all_languages']) }}" value="{{','.join(item[1])}}"
hidden>{{','.join(item[1])}}</textarea>
</div>
{% elif isinstance(item[1], list) %}
<textarea value="{{','.join(item[1])}}" type="text" name="{{item[0]}}"
class="form-control list">{{','.join(item[1])}}</textarea>
{% elif isinstance(item[1], bool) %}
<div style="margin-left: 30px;">
{% if item[1] == True %}
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True" checked>
 <label for="True">True</label><br>
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False">
 <label for="False">False</label>
{% else %}
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True">
 <label for="True">True</label><br>
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False" checked>
 <label for="False">False</label>
{% end %}
</div>
{% elif isinstance(item[1], int) %}
<input type="number" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}"
step="1" min="0" required>
{% else %}
<input type="text" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}"
step="2" min="0" required>
{% end %}
</div> </div>
{% elif isinstance(item[1], list) %}
<textarea value="{{','.join(item[1])}}" type="text" name="{{item[0]}}" class="form-control list">{{','.join(item[1])}}</textarea>
{% elif isinstance(item[1], bool) %}
<div style="margin-left: 30px;">
{% if item[1] == True %}
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True" checked>
 <label for="True">True</label><br>
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False">
 <label for="False">False</label>
{% else %}
<input type="radio" class="form-check-input" name="{{item[0]}}" id="True" value="True">
 <label for="True">True</label><br>
<input type="radio" class="form-check-input" name="{{item[0]}}" id="False" value="False" checked>
 <label for="False">False</label>
{% end %} {% end %}
</div> <button class="btn btn-success" type="submit">Submit</button>&nbsp;<span id="submit-status"></span>
{% elif isinstance(item[1], int) %}
<input type="number" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}" step="1" min="0" required>
{% else %}
<input type="text" class="form-control" name="{{item[0]}}" id="{{item[0]}}" value="{{ item[1] }}" step="2" min="0" required>
{% end %}
</div>
{% end %}
<button class="btn btn-success" type="submit">Submit</button>&nbsp;<span id="submit-status"></span>
</form> </form>
</div> </div>
</div> </div>
@ -276,44 +283,6 @@
}, },
}); });
}) })
var file;
function sendFile() {
file = $("#file")[0].files[0]
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>'
let xmlHttpRequest = new XMLHttpRequest();
let token = getCookie("_xsrf")
let fileName = file.name
let target = '/upload'
let mimeType = file.type
let size = file.size
let type = 'background'
xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
xmlHttpRequest.setRequestHeader('X-Content-Length', size);
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', type);
xmlHttpRequest.setRequestHeader('X-FileName', fileName);
xmlHttpRequest.addEventListener('load', (event) => {
if (event.target.responseText == 'success') {
console.log('Upload for file', file.name, 'was successful!')
document.getElementById("upload_input").innerHTML = '<div class="card-header header-sm d-flex justify-content-between align-items-center"><span id="file-uploaded" style="color: gray;">' + fileName + '</span> 🔒</div>';
setTimeout(function () {
window.location.reload();
}, 2000);
}
else {
alert('Upload failed with response: ' + event.target.responseText);
doUpload = false;
}
}, false);
xmlHttpRequest.addEventListener('error', (e) => {
console.error('Error while uploading file', file.name + '.', 'Event:', e)
}, false);
xmlHttpRequest.send(file);
}
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/js/bootstrap-select.min.js"> <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.10/js/bootstrap-select.min.js">
</script> </script>

View File

@ -56,11 +56,11 @@
<input type="hidden" value="import_zip" name="create_type"> <input type="hidden" value="import_zip" name="create_type">
<div class="col form-group"> <div class="col form-group">
<span id="upload_input"><input type="file" class="form-control-file" id="file" name="file" <span id="upload_input"><input type="file" class="form-control-file" id="file" name="file"
multiple="false" required></span> multiple="false" required></span>
</div> </div>
<div class="col form-group"> <div class="col form-group">
<button type="button" class="btn btn-info" id="upload-button" onclick="sendFile()" <button type="button" class="btn btn-info" id="upload-button" onclick="sendFile()"
disabled>UPLOAD</button> disabled>UPLOAD</button>
</div> </div>
</form> </form>
<hr> <hr>
@ -74,7 +74,7 @@
<label for="photo" class="col-sm-6 col-form-label">Selected Background Image</label> <label for="photo" class="col-sm-6 col-form-label">Selected Background Image</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select class="form-select form-control form-control-lg select-css form-control-plaintext" <select class="form-select form-control form-control-lg select-css form-control-plaintext"
id="photo" name="photo" form="photo_form" onchange="updateBackgroundPreview()"> id="photo" name="photo" form="photo_form" onchange="updateBackgroundPreview()">
{% for image in data["backgrounds"] %} {% for image in data["backgrounds"] %}
<option value="{{image}}">{{image}}</option> <option value="{{image}}">{{image}}</option>
{% end %} {% end %}
@ -84,8 +84,8 @@
<div id="photo_loading" class="form-group" hidden> <div id="photo_loading" class="form-group" hidden>
<div class="progress"> <div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i
class="fa-solid fa-spinner"></i></div> class="fa-solid fa-spinner"></i></div>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
@ -94,12 +94,12 @@
<label class="col-sm-1" id="opacityValue">{{ data['login_opacity'] }}%</label> <label class="col-sm-1" id="opacityValue">{{ data['login_opacity'] }}%</label>
<div class="range col-sm-8"> <div class="range col-sm-8">
<input type="range" class="form-control-range" id="modal_opacity" name="modal_opacity" <input type="range" class="form-control-range" id="modal_opacity" name="modal_opacity"
onchange="previewOpacity()" min="0" max="100" value="{{ data['login_opacity'] }}"> onchange="previewOpacity()" min="0" max="100" value="{{ data['login_opacity'] }}">
</div> </div>
</div> </div>
<div id="login_preview" style="position: relative;"> <div id="login_preview" style="position: relative;">
<img id="bg-preview" src="../../static/assets/images/auth/{{ data['background'] }}" <img id="bg-preview" src="../../static/assets/images/auth/{{ data['background'] }}"
class="img-fluid" alt="Responsive image"> class="img-fluid" alt="Responsive image">
<div id="login-form-preview"> <div id="login-form-preview">
<div id="login-form-background" class="auto-form-wrapper login-modal"> <div id="login-form-background" class="auto-form-wrapper login-modal">
<div class="text-center auto-form-logo"> <div class="text-center auto-form-logo">
@ -164,19 +164,19 @@
<div id="login_form_data"> <div id="login_form_data">
<input type="hidden" name="_xsrf" <input type="hidden" name="_xsrf"
value="2|1d603267|809fb6bd82f677d440e484dde7c3a310|1671726040" disabled> value="2|1d603267|809fb6bd82f677d440e484dde7c3a310|1671726040" disabled>
<div class="form-group"> <div class="form-group">
<label class="label">Username</label> <label class="label">Username</label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control login-text-input login-input" <input type="text" class="form-control login-text-input login-input"
placeholder="Username" name="username" id="username" required="true" disabled> placeholder="Username" name="username" id="username" required="true" disabled>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label">Password</label> <label class="label">Password</label>
<div class="input-group"> <div class="input-group">
<input type="password" class="form-control login-text-input login-input" <input type="password" class="form-control login-text-input login-input"
placeholder="Password" name="password" id="password" required="true" disabled> placeholder="Password" name="password" id="password" required="true" disabled>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -196,7 +196,7 @@
</div> </div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">
<span class="text-small font-weight-semibold"><a <span class="text-small font-weight-semibold"><a
href="https://craftycontrol.com/">Crafty Control href="https://craftycontrol.com/">Crafty Control
4.0.20</a> </span> 4.0.20</a> </span>
</div> </div>
</div> </div>
@ -352,7 +352,7 @@
var file; var file;
function sendFile() { function sendFile() {
file = $("#file")[0].files[0] file = $("#file")[0].files[0]
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>'; document.getElementById("upload_input").innerHTML = '<div class="progress"><div id="upload-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>';
let xmlHttpRequest = new XMLHttpRequest(); let xmlHttpRequest = new XMLHttpRequest();
let token = getCookie("_xsrf") let token = getCookie("_xsrf")
let fileName = file.name let fileName = file.name
@ -361,6 +361,15 @@
let size = file.size let size = file.size
let type = 'background' let type = 'background'
xmlHttpRequest.upload.addEventListener('progress', function (e) {
if (e.loaded <= size) {
var percent = Math.round(e.loaded / size * 100);
$(`#upload-progress-bar`).css('width', percent + '%');
$(`#upload-progress-bar`).html(percent + '%');
}
});
xmlHttpRequest.open('POST', target, true); xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('X-XSRFToken', token); xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
@ -377,7 +386,15 @@
}, 2000); }, 2000);
} }
else { else {
alert('Upload failed with response: ' + event.target.responseText); let response_text = JSON.parse(event.target.responseText);
var x = document.querySelector('.bootbox');
console.log(JSON.parse(event.target.responseText).info)
bootbox.alert({
message: JSON.parse(event.target.responseText).info,
callback: function () {
window.location.reload();
}
});
doUpload = false; doUpload = false;
} }
}, false); }, false);

View File

@ -110,15 +110,15 @@
<div class="form-group"> <div class="form-group">
<label for="command-check" class="form-check-label ml-4 mb-4"></label> <label for="command-check" class="form-check-label ml-4 mb-4"></label>
{% if data['backup_config']['before'] %} {% if data['backup_config']['before'] %}
<input type="checkbox" class="form-check-input" id="before-check" name="before-check" checked>Run <input type="checkbox" class="form-check-input" id="before-check" name="before-check" checked>{{
Command Before Backup translate('serverBackups', 'before', data['lang']) }}
<br> <br>
<input type="text" class="form-control" name="backup_before" id="backup_before" <input type="text" class="form-control" name="backup_before" id="backup_before"
value="{{ data['backup_config']['before'] }}" placeholder="We enter the / for you" value="{{ data['backup_config']['before'] }}" placeholder="We enter the / for you"
style="display: inline-block;"> style="display: inline-block;">
{% else %} {% else %}
<input type="checkbox" class="form-check-input" id="before-check" name="before-check">Run Command <input type="checkbox" class="form-check-input" id="before-check" name="before-check">{{
Before Backup translate('serverBackups', 'before', data['lang']) }}
<br> <br>
<input type="text" class="form-control" name="backup_before" id="backup_before" value="" <input type="text" class="form-control" name="backup_before" id="backup_before" value=""
placeholder="We enter the / for you." style="display: none;"> placeholder="We enter the / for you." style="display: none;">
@ -127,15 +127,15 @@
<div class="form-group"> <div class="form-group">
<label for="command-check" class="form-check-label ml-4 mb-4"></label> <label for="command-check" class="form-check-label ml-4 mb-4"></label>
{% if data['backup_config']['after'] %} {% if data['backup_config']['after'] %}
<input type="checkbox" class="form-check-input" id="after-check" name="after-check" checked>Run <input type="checkbox" class="form-check-input" id="after-check" name="after-check" checked>{{
Command After Backup translate('serverBackups', 'after', data['lang']) }}
<br> <br>
<input type="text" class="form-control" name="backup_after" id="backup_after" <input type="text" class="form-control" name="backup_after" id="backup_after"
value="{{ data['backup_config']['after'] }}" placeholder="We enter the / for you" value="{{ data['backup_config']['after'] }}" placeholder="We enter the / for you"
style="display: inline-block;"> style="display: inline-block;">
{% else %} {% else %}
<input type="checkbox" class="form-check-input" id="after-check" name="after-check">Run Command <input type="checkbox" class="form-check-input" id="after-check" name="after-check">{{
Before Backup translate('serverBackups', 'after', data['lang']) }}
<br> <br>
<input type="text" class="form-control" name="backup_after" id="backup_after" value="" <input type="text" class="form-control" name="backup_after" id="backup_after" value=""
placeholder="We enter the / for you." style="display: none;"> placeholder="We enter the / for you." style="display: none;">

View File

@ -705,6 +705,15 @@
let mimeType = file.type let mimeType = file.type
let size = file.size let size = file.size
xmlHttpRequest.upload.addEventListener('progress', function (e) {
if (e.loaded <= size) {
var percent = Math.round(e.loaded / size * 100);
$(`#upload-progress-bar-${i + 1}`).css('width', percent + '%');
$(`#upload-progress-bar-${i + 1}`).html(percent + '%');
}
});
xmlHttpRequest.open('POST', target, true); xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('X-XSRFToken', token); xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
@ -751,7 +760,22 @@
$(`#upload-progress-bar-${i + 1}`).html('<i style="color: black;" class="fas fa-box-check"></i>') $(`#upload-progress-bar-${i + 1}`).html('<i style="color: black;" class="fas fa-box-check"></i>')
} }
else { else {
alert('Upload failed with response: ' + event.target.responseText); let response_text = JSON.parse(event.target.responseText);
var x = document.querySelector('.bootbox');
if (x) {
x.remove()
}
var x = document.querySelector('.modal-content');
if (x) {
x.remove()
}
console.log(JSON.parse(event.target.responseText).info)
bootbox.alert({
message: JSON.parse(event.target.responseText).info,
callback: function () {
window.location.reload();
}
});
doUpload = false; doUpload = false;
} }
}, false); }, false);
@ -788,7 +812,7 @@
var height = files.files.length * 50; var height = files.files.length * 50;
var waitMessage = '<p class="text-center mb-0">' + var waitMessage = '<p class="text-center mb-0">' +
'<i class="fa fa-spin fa-cog"></i>' + '<i class="fa fa-spin fa-cog"></i>&nbsp;' +
"{{ translate('serverFiles', 'waitUpload', data['lang']) }}" + '<br>' + "{{ translate('serverFiles', 'waitUpload', data['lang']) }}" + '<br>' +
'<strong>' + "{{ translate('serverFiles', 'stayHere', data['lang']) }}" + '</strong>' + '<strong>' + "{{ translate('serverFiles', 'stayHere', data['lang']) }}" + '</strong>' +
'</p>' + '</p>' +
@ -826,7 +850,7 @@
await sendFile(files.files[i], path, serverId, nFiles - i - 1, i, (progress) => { await sendFile(files.files[i], path, serverId, nFiles - i - 1, i, (progress) => {
$(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress) $(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress)
$(`#upload-progress-bar-${i + 1}`).css('width', progress + '%') $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%');
}); });
} }
hideUploadBox(); hideUploadBox();

View File

@ -562,7 +562,7 @@
var file; var file;
function sendFile() { function sendFile() {
file = $("#file")[0].files[0] file = $("#file")[0].files[0]
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>' document.getElementById("upload_input").innerHTML = '<div class="progress"><div id="upload-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>'
let xmlHttpRequest = new XMLHttpRequest(); let xmlHttpRequest = new XMLHttpRequest();
let token = getCookie("_xsrf") let token = getCookie("_xsrf")
let fileName = encodeURIComponent(file.name) let fileName = encodeURIComponent(file.name)
@ -571,6 +571,15 @@
let size = file.size let size = file.size
let type = 'server_import' let type = 'server_import'
xmlHttpRequest.upload.addEventListener('progress', function (e) {
if (e.loaded <= size) {
var percent = Math.round(e.loaded / size * 100);
$(`#upload-progress-bar`).css('width', percent + '%');
$(`#upload-progress-bar`).html(percent + '%');
}
});
xmlHttpRequest.open('POST', target, true); xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('X-XSRFToken', token); xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
@ -585,7 +594,15 @@
document.getElementById("lower_half").style.visibility = "visible"; document.getElementById("lower_half").style.visibility = "visible";
} }
else { else {
alert('Upload failed with response: ' + event.target.responseText); let response_text = JSON.parse(event.target.responseText);
var x = document.querySelector('.bootbox');
console.log(JSON.parse(event.target.responseText).info)
bootbox.alert({
message: JSON.parse(event.target.responseText).info,
callback: function () {
window.location.reload();
}
});
doUpload = false; doUpload = false;
} }
}, false); }, false);

View File

@ -804,7 +804,7 @@
var file; var file;
function sendFile() { function sendFile() {
file = $("#file")[0].files[0] file = $("#file")[0].files[0]
document.getElementById("upload_input").innerHTML = '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>' document.getElementById("upload_input").innerHTML = '<div class="progress"><div id="upload-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">&nbsp;<i class="fa-solid fa-spinner"></i></div></div>'
let xmlHttpRequest = new XMLHttpRequest(); let xmlHttpRequest = new XMLHttpRequest();
let token = getCookie("_xsrf") let token = getCookie("_xsrf")
let fileName = file.name let fileName = file.name
@ -813,6 +813,15 @@
let size = file.size let size = file.size
let type = 'server_import' let type = 'server_import'
xmlHttpRequest.upload.addEventListener('progress', function (e) {
if (e.loaded <= size) {
var percent = Math.round(e.loaded / size * 100);
$(`#upload-progress-bar`).css('width', percent + '%');
$(`#upload-progress-bar`).html(percent + '%');
}
});
xmlHttpRequest.open('POST', target, true); xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('X-XSRFToken', token); xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
@ -827,7 +836,15 @@
document.getElementById("lower_half").style.visibility = "visible"; document.getElementById("lower_half").style.visibility = "visible";
} }
else { else {
alert('Upload failed with response: ' + event.target.responseText); let response_text = JSON.parse(event.target.responseText);
var x = document.querySelector('.bootbox');
console.log(JSON.parse(event.target.responseText).info)
bootbox.alert({
message: JSON.parse(event.target.responseText).info,
callback: function () {
window.location.reload();
}
});
doUpload = false; doUpload = false;
} }
}, false); }, false);

View File

@ -297,7 +297,9 @@
"shutdown": "Shutdown server for duration of backup", "shutdown": "Shutdown server for duration of backup",
"size": "Size", "size": "Size",
"storageLocation": "Storage Location", "storageLocation": "Storage Location",
"storageLocationDesc": "Where do you want to store backups?" "storageLocationDesc": "Where do you want to store backups?",
"before": "Run command before backup",
"after": "Run command after backup"
}, },
"serverConfig": { "serverConfig": {
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.", "bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",