Add upload import functionality.

Update gitignore
This commit is contained in:
Andrew 2022-09-27 22:06:22 -04:00
parent 8cada1c450
commit b9bd654e58
6 changed files with 840 additions and 426 deletions

1
.gitignore vendored
View File

@ -18,6 +18,7 @@ env.bak/
venv.bak/ venv.bak/
.idea/ .idea/
/imports/
/servers/ /servers/
/backups/ /backups/
/temp/ /temp/

View File

@ -476,6 +476,12 @@ class AjaxHandler(BaseHandler):
elif page == "unzip_server": elif page == "unzip_server":
path = self.get_argument("path", None) path = self.get_argument("path", None)
if not path:
path = os.path.join(
self.controller.project_root,
"imports",
self.get_argument("file", ""),
)
if Helpers.check_file_exists(path): if Helpers.check_file_exists(path):
self.helper.unzip_server(path, exec_user["user_id"]) self.helper.unzip_server(path, exec_user["user_id"])
else: else:

View File

@ -4,6 +4,7 @@ import time
import tornado.web import tornado.web
import tornado.options import tornado.options
import tornado.httpserver import tornado.httpserver
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.models.server_permissions import EnumPermissionsServer
from app.classes.shared.console import Console from app.classes.shared.console import Console
@ -33,115 +34,293 @@ class UploadHandler(BaseHandler):
def prepare(self): def prepare(self):
# Class & Function Defination # Class & Function Defination
api_key, _token_data, exec_user = self.current_user api_key, _token_data, exec_user = self.current_user
server_id = self.get_argument("server_id", None) self.upload_type = str(self.request.headers.get("X-Content-Upload-Type"))
superuser = exec_user["superuser"] print(self.upload_type)
if api_key is not None:
superuser = superuser and api_key.superuser
user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB")
max_streamed_size = (1024 * 1024 * 1024) * stream_size_value if self.upload_type == "server_import":
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB")
self.content_len = int(self.request.headers.get("Content-Length")) max_streamed_size = (1024 * 1024 * 1024) * stream_size_value
if self.content_len > max_streamed_size:
logger.error(
f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size."
)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error",
"fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id),
),
},
)
return
self.do_upload = True
if superuser: self.content_len = int(self.request.headers.get("Content-Length"))
exec_user_server_permissions = ( if self.content_len > max_streamed_size:
self.controller.server_perms.list_defined_permissions() logger.error(
) f"User with ID {user_id} attempted to upload a file that"
elif api_key is not None: f" exceeded the max body size."
exec_user_server_permissions = (
self.controller.server_perms.get_api_key_permissions_list(
api_key, server_id
) )
) self.helper.websocket_helper.broadcast_user(
else: user_id,
exec_user_server_permissions = ( "send_start_error",
self.controller.server_perms.get_user_id_permissions_list( {
exec_user["user_id"], server_id "error": self.helper.translation.translate(
"error",
"fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id),
),
},
) )
) return
self.do_upload = True
server_id = self.request.headers.get("X-ServerId", None) if superuser:
exec_user_server_permissions = (
self.controller.server_perms.list_defined_permissions()
)
elif api_key is not None:
exec_user_server_permissions = (
self.controller.crafty_perms.get_api_key_permissions_list(api_key)
)
else:
exec_user_server_permissions = (
self.controller.crafty_perms.get_crafty_permissions_list(
exec_user["user_id"]
)
)
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")
self.do_upload = False self.do_upload = False
if server_id is None: if (
logger.warning("Server ID not found in upload handler call") EnumPermissionsCrafty.SERVER_CREATION
Console.warning("Server ID not found in upload handler call") not in exec_user_server_permissions
self.do_upload = False and not exec_user["superuser"]
):
logger.warning(
f"User {user_id} tried to upload a server" " without permissions!"
)
Console.warning(
f"User {user_id} tried to upload a server" " without permissions!"
)
self.do_upload = False
if EnumPermissionsServer.FILES not in exec_user_server_permissions: path = os.path.join(self.controller.project_root, "imports")
logger.warning( # Delete existing files
f"User {user_id} tried to upload a file to " if len(os.listdir(path)) > 0:
f"{server_id} without permissions!" for item in os.listdir():
) try:
Console.warning( os.remove(os.path.join(path, item))
f"User {user_id} tried to upload a file to " except:
f"{server_id} without permissions!" logger.debug("Could not delete file on user server upload")
)
self.do_upload = False
path = self.request.headers.get("X-Path", None) self.helper.ensure_dir_exists(path)
filename = self.request.headers.get("X-FileName", None) filename = self.request.headers.get("X-FileName", None)
full_path = os.path.join(path, filename) full_path = os.path.join(path, filename)
if not Helpers.in_path( if self.do_upload:
Helpers.get_os_understandable_path( try:
self.controller.servers.get_server_data_by_id(server_id)["path"] self.f = open(full_path, "wb")
), except Exception as e:
full_path, logger.error(f"Upload failed with error: {e}")
): self.do_upload = False
print( # If max_body_size is not set, you cannot upload files > 100MB
user_id, self.request.connection.set_max_body_size(max_streamed_size)
server_id,
elif self.upload_type == "background":
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB")
max_streamed_size = (1024 * 1024 * 1024) * stream_size_value
self.content_len = int(self.request.headers.get("Content-Length"))
if self.content_len > max_streamed_size:
logger.error(
f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size."
)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error",
"fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id),
),
},
)
return
self.do_upload = True
if superuser:
exec_user_server_permissions = (
self.controller.server_perms.list_defined_permissions()
)
elif api_key is not None:
exec_user_server_permissions = (
self.controller.server_perms.get_api_key_permissions_list(
api_key, server_id
)
)
else:
exec_user_server_permissions = (
self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
)
)
server_id = self.request.headers.get("X-ServerId", None)
if server_id is None:
logger.warning("Server ID not found in upload handler call")
Console.warning("Server ID not found in upload handler call")
self.do_upload = False
if user_id is None:
logger.warning("User ID not found in upload handler call")
Console.warning("User ID not found in upload handler call")
self.do_upload = False
if EnumPermissionsServer.FILES not in exec_user_server_permissions:
logger.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
Console.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
self.do_upload = False
path = self.request.headers.get("X-Path", None)
filename = self.request.headers.get("X-FileName", None)
full_path = os.path.join(path, filename)
if not Helpers.in_path(
Helpers.get_os_understandable_path( Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"] self.controller.servers.get_server_data_by_id(server_id)["path"]
), ),
full_path, full_path,
) ):
logger.warning( logger.warning(
f"User {user_id} tried to upload a file to {server_id} " f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!" f"but the path is not inside of the server!"
) )
Console.warning( Console.warning(
f"User {user_id} tried to upload a file to {server_id} " f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!" f"but the path is not inside of the server!"
) )
self.do_upload = False
if self.do_upload:
try:
self.f = open(full_path, "wb")
except Exception as e:
logger.error(f"Upload failed with error: {e}")
self.do_upload = False self.do_upload = False
# If max_body_size is not set, you cannot upload files > 100MB
self.request.connection.set_max_body_size(max_streamed_size) if self.do_upload:
try:
self.f = open(full_path, "wb")
except Exception as e:
logger.error(f"Upload failed with error: {e}")
self.do_upload = False
# If max_body_size is not set, you cannot upload files > 100MB
self.request.connection.set_max_body_size(max_streamed_size)
else:
server_id = self.get_argument("server_id", None)
superuser = exec_user["superuser"]
if api_key is not None:
superuser = superuser and api_key.superuser
user_id = exec_user["user_id"]
stream_size_value = self.helper.get_setting("stream_size_GB")
max_streamed_size = (1024 * 1024 * 1024) * stream_size_value
self.content_len = int(self.request.headers.get("Content-Length"))
if self.content_len > max_streamed_size:
logger.error(
f"User with ID {user_id} attempted to upload a file that"
f" exceeded the max body size."
)
self.helper.websocket_helper.broadcast_user(
user_id,
"send_start_error",
{
"error": self.helper.translation.translate(
"error",
"fileTooLarge",
self.controller.users.get_user_lang_by_id(user_id),
),
},
)
return
self.do_upload = True
if superuser:
exec_user_server_permissions = (
self.controller.server_perms.list_defined_permissions()
)
elif api_key is not None:
exec_user_server_permissions = (
self.controller.server_perms.get_api_key_permissions_list(
api_key, server_id
)
)
else:
exec_user_server_permissions = (
self.controller.server_perms.get_user_id_permissions_list(
exec_user["user_id"], server_id
)
)
server_id = self.request.headers.get("X-ServerId", None)
if server_id is None:
logger.warning("Server ID not found in upload handler call")
Console.warning("Server ID not found in upload handler call")
self.do_upload = False
if user_id is None:
logger.warning("User ID not found in upload handler call")
Console.warning("User ID not found in upload handler call")
self.do_upload = False
if EnumPermissionsServer.FILES not in exec_user_server_permissions:
logger.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
Console.warning(
f"User {user_id} tried to upload a file to "
f"{server_id} without permissions!"
)
self.do_upload = False
path = self.request.headers.get("X-Path", None)
filename = self.request.headers.get("X-FileName", None)
full_path = os.path.join(path, filename)
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
full_path,
):
logger.warning(
f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!"
)
Console.warning(
f"User {user_id} tried to upload a file to {server_id} "
f"but the path is not inside of the server!"
)
self.do_upload = False
if self.do_upload:
try:
self.f = open(full_path, "wb")
except Exception as e:
logger.error(f"Upload failed with error: {e}")
self.do_upload = False
# If max_body_size is not set, you cannot upload files > 100MB
self.request.connection.set_max_body_size(max_streamed_size)
def post(self): def post(self):
logger.info("Upload completed") logger.info("Upload completed")
files_left = int(self.request.headers.get("X-Files-Left", None)) if self.upload_type == "server_files":
files_left = int(self.request.headers.get("X-Files-Left", None))
else:
files_left = 0
if self.do_upload: if self.do_upload:
time.sleep(5) time.sleep(5)

View File

@ -711,6 +711,7 @@
xmlHttpRequest.setRequestHeader('X-Content-Length', size); xmlHttpRequest.setRequestHeader('X-Content-Length', size);
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"'); xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
xmlHttpRequest.setRequestHeader('X-Path', path); xmlHttpRequest.setRequestHeader('X-Path', path);
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', 'server_files')
xmlHttpRequest.setRequestHeader('X-Files-Left', left); xmlHttpRequest.setRequestHeader('X-Files-Left', left);
xmlHttpRequest.setRequestHeader('X-FileName', fileName); xmlHttpRequest.setRequestHeader('X-FileName', fileName);
xmlHttpRequest.setRequestHeader('X-ServerId', serverId); xmlHttpRequest.setRequestHeader('X-ServerId', serverId);

View File

@ -431,388 +431,614 @@
</p> </p>
</div> </div>
</div> </div>
<div class="col-sm-6 grid-margin"> <div class="col-sm-6 grid-margin stretch-card">
<img id="op_logo" style="filter: grayscale(10%); opacity: .1;" src="../../static/assets/images/logo_small.svg" <div class="card">
alt="Crafty logo" /> <div class="card-body">
<h4>{{ translate('serverWizard', 'uploadZip', data['lang']) }}</h4>
<br />
<p class="card-description">
<form name="zip" method="post" class="server-wizard" onSubmit="wait_msg(true)">
{% raw xsrf_form_html() %}
<input type="hidden" value="import_zip" name="create_type">
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" value=""
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server">Server Upload </label><br>
<span id="upload_input">
<input type="file" multiple="false" class="form-control" id="file" name="file" required
style="width: 70%;">
<button type="button" class="btn btn-info" onclick="sendFile()">UPLOAD</button>
</span>
</div>
</div>
</div>
<div id="lower_half" style="visibility: hidden;">
<div class="col-sm-12">
<div class="form-group">
<label for="server">{{ translate('serverWizard', 'selectRoot', data['lang']) }} <small>{{
translate('serverWizard', 'explainRoot', data['lang']) }}</small></label>
<br>
<button class="btn btn-primary mr-2" id="root_upload_button" type="button">{{
translate('serverWizard', 'clickRoot', data['lang']) }}</button>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value=""
placeholder="paper.jar" required>
</div>
</div>
<div class="col-sm-12">
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
data['lang']) }}</small></h4>
<hr>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="min_memory3">{{ translate('serverWizard', 'minMem', data['lang']) }} <small> - {{
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
<input type="number" class="form-control" id="min_memory3" name="min_memory" value="1"
step="0.5" min="0.5" required>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="max_memory3">{{ translate('serverWizard', 'maxMem', data['lang']) }} <small> - {{
translate('serverWizard', 'sizeInGB', data['lang']) }}</small></label>
<input type="number" class="form-control" id="max_memory3" name="max_memory" value="2"
step="0.5" min="0.5" required>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small> - {{
translate('serverWizard', 'defaultPort', data['lang']) }}</small></label>
<input type="number" class="form-control" id="port3" name="port" value="25565" step="1" min="1"
required>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<div id="accordion-3">
<div class="card">
<div class="card-header p-2" id="Role-3">
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3"
aria-expanded="true" aria-controls="collapseRole-3">
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole',
data['lang'])
}} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
data['lang']) }}</small>
</p>
</div>
<div id="collapseRole-3" class="collapse" aria-labelledby="Role-3" data-parent="">
<div class="card-body scroll">
<div class="form-group">
{% for r in data['roles'] %}
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
type="checkbox">&nbsp;
{{ r['role_name'].capitalize() }}</label></span>
{% end %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-12" style="visibility: hidden;">
<div class="form-group">
<input type="text" class="form-control" id="zip_root_path" name="zip_root_path">
</div>
</div>
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverWizard',
'selectZipDir', data['lang']) }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="tree-ctx-item" id="main-tree-div" data-path=""
style="overflow: scroll; max-height:75%;">
<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked>
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
{{ translate('serverFiles', 'files', data['lang']) }}
</span>
</input>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{
translate('serverWizard', 'close', data['lang']) }}</button>
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{
translate('serverWizard', 'save', data['lang']) }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
<button id="upload_submit" type="submit" title="You must select server root dir first" disabled
class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
}}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
}}</button>
</div>
</div>
</form>
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> <style>
<style> .refresh-class:hover {
.refresh-class:hover { cursor: grab;
cursor: grab;
}
.scroll {
max-height: 12em;
overflow-y: auto;
}
.menu-btn {
font-size: 0.9em;
padding: 2px 10px;
}
.menu {
padding-top: 10px;
z-index: 200;
margin-top: 4px;
position: absolute;
background-color: #2a2c44;
}
.menu-option {
padding: 6px 20px 6px;
color: white;
}
#overlay {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 100;
}
</style>
<style>
/* Remove default bullets */
.tree-view,
.tree-nested {
list-style-type: none;
margin: 0;
padding: 0;
margin-left: 10px;
}
/* Style the items */
.tree-item,
.files-tree-title {
cursor: pointer;
user-select: none;
/* Prevent text selection */
}
/* Create the caret/arrow with a unicode, and style it */
.tree-caret .fa-folder {
display: inline-block;
}
.tree-caret .fa-folder-open {
display: none;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.tree-caret-down .fa-folder {
display: none;
}
.tree-caret-down .fa-folder-open {
display: inline-block;
}
/* Hide the nested list */
.tree-nested {
display: none;
}
#op_logo {
position: relative;
top: 50%;
transform: translateY(-50%);
}
</style>
{% end %}
{% block js%}
<script>
document.getElementById("root_files_button").addEventListener("click", function () {
if (document.forms["zip"]["server_path"].value != "") {
if (document.getElementById('root_files_button').classList.contains('clicked')) {
document.getElementById('main-tree-div').innerHTML = '<input type="radio" id="main-tree-input" name="root_path" value="" checked><span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
} else {
document.getElementById('root_files_button').classList.add('clicked')
}
path = document.forms["zip"]["server_path"].value;
console.log(document.forms["zip"]["server_path"].value)
var token = getCookie("_xsrf");
var dialog = bootbox.dialog({
message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Please wait while we gather your files...</p>',
closeButton: false
});
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&path=' + path,
});
} else {
bootbox.alert("You must input a path before selecting this button");
} }
});
</script>
<script> .scroll {
function dropDown(event) { max-height: 12em;
event.target.parentElement.children[1].classList.remove("d-none"); overflow-y: auto;
document.getElementById("overlay").classList.remove("d-none");
}
function hide(event) {
var items = document.getElementsByClassName('menu');
items.forEach(item => {
item.classList.add("d-none");
})
document.getElementById("overlay").classList.add("d-none");
}
$(document).ready(function () {
console.log('ready');
var forms = $('form.server-wizard');
forms.each(function (i, formEl) {
var form = $(formEl);
var min = form.find('[name=min_memory]');
var max = form.find('[name=max_memory]');
console.log(form, min, max)
min.change(function () {
check_sizes(max, min, 'min');
});
max.change(function () {
check_sizes(max, min, 'max');
});
});
});
function wait_msg(importing) {
bootbox.alert({
title: importing ? '{% raw translate("serverWizard", "importing", data["lang"]) %}' : '{% raw translate("serverWizard", "downloading", data["lang"]) %}',
message: '<i class="fas fa-cloud-download"></i> {% raw translate("serverWizard", "bePatient", data["lang"]) %}',
});
}
function show_file_tree() {
$("#dir_select").modal();
}
function check_sizes(a, b, changed) {
max_mem = parseFloat(a.val());
min_mem = parseFloat(b.val());
if (max_mem < min_mem && changed === 'min') {
a.val(min_mem)
} }
if (max_mem < min_mem && changed === 'max') {
b.val(max_mem) .menu-btn {
font-size: 0.9em;
padding: 2px 10px;
} }
}
function getTreeView(path) { .menu {
document.getElementById('zip_submit').disabled = false; padding-top: 10px;
path = path z-index: 200;
margin-top: 4px;
position: absolute;
background-color: #2a2c44;
}
$.ajax({ .menu-option {
type: "GET", padding: 6px 20px 6px;
url: '/ajax/get_zip_tree?id=-1&path=' + path, color: white;
dataType: 'text', }
success: function (data) {
console.log("got response:");
console.log(data);
dataArr = data.split('\n'); #overlay {
serverDir = dataArr.shift(); // Remove & return first element (server directory) position: absolute;
text = dataArr.join('\n'); top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 100;
}
</style>
<style>
/* Remove default bullets */
.tree-view,
.tree-nested {
list-style-type: none;
margin: 0;
padding: 0;
margin-left: 10px;
}
try { /* Style the items */
document.getElementById('main-tree-div').innerHTML += text; .tree-item,
document.getElementById('main-tree').parentElement.classList.add("clicked"); .files-tree-title {
} catch { cursor: pointer;
document.getElementById('files-tree').innerHTML = text; user-select: none;
/* Prevent text selection */
}
/* Create the caret/arrow with a unicode, and style it */
.tree-caret .fa-folder {
display: inline-block;
}
.tree-caret .fa-folder-open {
display: none;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.tree-caret-down .fa-folder {
display: none;
}
.tree-caret-down .fa-folder-open {
display: inline-block;
}
/* Hide the nested list */
.tree-nested {
display: none;
}
#op_logo {
position: relative;
top: 50%;
transform: translateY(-50%);
}
</style>
{% end %}
{% block js%}
<script>
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 = 'server_import'
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>';
document.getElementById("lower_half").style.visibility = "visible";
} }
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);
}
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir); document.getElementById("root_files_button").addEventListener("click", function () {
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files'); if (document.forms["zip"]["server_path"].value != "") {
if (document.getElementById('root_files_button').classList.contains('clicked')) {
}, document.getElementById('main-tree-div').innerHTML = '<input type="radio" id="main-tree-input" name="root_path" value="" checked><span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
}); } else {
} document.getElementById('root_files_button').classList.add('clicked')
}
function getToggleMain(event) { path = document.forms["zip"]["server_path"].value;
path = event.target.parentElement.getAttribute('data-path'); console.log(document.forms["zip"]["server_path"].value)
document.getElementById("files-tree").classList.toggle("d-block"); var token = getCookie("_xsrf");
document.getElementById(path + "span").classList.toggle("tree-caret-down"); var dialog = bootbox.dialog({
document.getElementById(path + "span").classList.toggle("tree-caret"); message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Please wait while we gather your files...</p>',
} closeButton: false
});
$.ajax({
function getDirView(event) { type: "POST",
path = event.target.parentElement.getAttribute('data-path'); headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&path=' + path,
if (document.getElementById(path).classList.contains('clicked')) { });
} else {
var toggler = document.getElementById(path + "span"); bootbox.alert("You must input a path before selecting this button");
if (toggler.classList.contains('files-tree-title')) {
document.getElementById(path + "ul").classList.toggle("d-block");
document.getElementById(path + "span").classList.toggle("tree-caret-down");
} }
return; });
} else {
document.getElementById("root_upload_button").addEventListener("click", function () {
if (file) {
if (document.getElementById('root_upload_button').classList.contains('clicked')) {
document.getElementById('main-tree-div').innerHTML = '<input type="radio" id="main-tree-input-upload" name="root_path" value="" checked><span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""><i class="far fa-folder"></i><i class="far fa-folder-open"></i>{{ translate("serverFiles", "files", data["lang"]) }}</span></input>'
} else {
document.getElementById('root_upload_button').classList.add('clicked')
}
var token = getCookie("_xsrf");
var dialog = bootbox.dialog({
message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Please wait while we gather your files...</p>',
closeButton: false
});
$.ajax({
type: "POST",
headers: { 'X-XSRFToken': token },
url: '/ajax/unzip_server?id=-1&file=' + file.name,
});
} else {
bootbox.alert("You must input a path before selecting this button");
}
});
</script>
<script>
function dropDown(event) {
event.target.parentElement.children[1].classList.remove("d-none");
document.getElementById("overlay").classList.remove("d-none");
}
function hide(event) {
var items = document.getElementsByClassName('menu');
items.forEach(item => {
item.classList.add("d-none");
})
document.getElementById("overlay").classList.add("d-none");
}
$(document).ready(function () {
console.log('ready');
var forms = $('form.server-wizard');
forms.each(function (i, formEl) {
var form = $(formEl);
var min = form.find('[name=min_memory]');
var max = form.find('[name=max_memory]');
console.log(form, min, max)
min.change(function () {
check_sizes(max, min, 'min');
});
max.change(function () {
check_sizes(max, min, 'max');
});
});
});
function wait_msg(importing) {
bootbox.alert({
title: importing ? '{% raw translate("serverWizard", "importing", data["lang"]) %}' : '{% raw translate("serverWizard", "downloading", data["lang"]) %}',
message: '<i class="fas fa-cloud-download"></i> {% raw translate("serverWizard", "bePatient", data["lang"]) %}',
});
}
function show_file_tree() {
$("#dir_select").modal();
}
function check_sizes(a, b, changed) {
max_mem = parseFloat(a.val());
min_mem = parseFloat(b.val());
if (max_mem < min_mem && changed === 'min') {
a.val(min_mem)
}
if (max_mem < min_mem && changed === 'max') {
b.val(max_mem)
}
}
function getTreeView(path) {
//If this value is still hidden we know the user is executing a zip import and not an upload
if (document.getElementById("lower_half").visibility == "hidden") {
document.getElementById('zip_submit').disabled = false;
} else {
document.getElementById('upload_submit').disabled = false;
}
path = path
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: '/ajax/get_zip_dir?id=-1&path=' + path, url: '/ajax/get_zip_tree?id=-1&path=' + path,
dataType: 'text', dataType: 'text',
success: function (data) { success: function (data) {
console.log("got response:"); console.log("got response:");
console.log(data);
dataArr = data.split('\n'); dataArr = data.split('\n');
serverDir = dataArr.shift(); // Remove & return first element (server directory) serverDir = dataArr.shift(); // Remove & return first element (server directory)
text = dataArr.join('\n'); text = dataArr.join('\n');
try { try {
document.getElementById(path + "span").classList.add('tree-caret-down'); document.getElementById('main-tree-div').innerHTML += text;
document.getElementById(path).innerHTML += text; document.getElementById('main-tree').parentElement.classList.add("clicked");
document.getElementById(path).classList.add("clicked");
} catch { } catch {
console.log("Bad") document.getElementById('files-tree').innerHTML = text;
} }
var toggler = document.getElementById(path);
if (toggler.classList.contains('files-tree-title')) { document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir);
document.getElementById(path + "span").addEventListener("click", function caretListener() { document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files');
document.getElementById(path + "ul").classList.toggle("d-block");
document.getElementById(path + "span").classList.toggle("tree-caret-down");
});
}
}, },
}); });
} }
}
if (webSocket) { function getToggleMain(event) {
webSocket.on('send_temp_path', function (data) { path = event.target.parentElement.getAttribute('data-path');
setTimeout(function () { document.getElementById("files-tree").classList.toggle("d-block");
var x = document.querySelector('.bootbox'); document.getElementById(path + "span").classList.toggle("tree-caret-down");
if (x) { document.getElementById(path + "span").classList.toggle("tree-caret");
x.remove() }
function getDirView(event) {
path = event.target.parentElement.getAttribute('data-path');
if (document.getElementById(path).classList.contains('clicked')) {
var toggler = document.getElementById(path + "span");
if (toggler.classList.contains('files-tree-title')) {
document.getElementById(path + "ul").classList.toggle("d-block");
document.getElementById(path + "span").classList.toggle("tree-caret-down");
} }
var x = document.querySelector('.modal-backdrop'); return;
if (x) { } else {
x.remove() $.ajax({
} type: "GET",
document.getElementById('main-tree-input').setAttribute('value', data.path) url: '/ajax/get_zip_dir?id=-1&path=' + path,
getTreeView(data.path); dataType: 'text',
show_file_tree(); success: function (data) {
console.log("got response:");
}, 5000); dataArr = data.split('\n');
}); serverDir = dataArr.shift(); // Remove & return first element (server directory)
} text = dataArr.join('\n');
function refreshCache() { try {
var token = getCookie("_xsrf") document.getElementById(path + "span").classList.add('tree-caret-down');
document.getElementById("refresh-cache").classList.add("fa-spin") document.getElementById(path).innerHTML += text;
$.ajax({ document.getElementById(path).classList.add("clicked");
type: "POST", } catch {
headers: { 'X-XSRFToken': token }, console.log("Bad")
url: '/ajax/jar_cache', }
success: function () {
document.getElementById("refresh-cache").classList.remove("fa-sync");
document.getElementById("refresh-cache").classList.remove("fa-spin");
document.getElementById("refresh-cache").classList.add("fa-check");
var toggler = document.getElementById(path);
if (toggler.classList.contains('files-tree-title')) {
document.getElementById(path + "span").addEventListener("click", function caretListener() {
document.getElementById(path + "ul").classList.toggle("d-block");
document.getElementById(path + "span").classList.toggle("tree-caret-down");
});
}
},
});
}
}
if (webSocket) {
webSocket.on('send_temp_path', function (data) {
setTimeout(function () { setTimeout(function () {
location.reload(); var x = document.querySelector('.bootbox');
}, 2000); if (x) {
}, x.remove()
}); }
} var x = document.querySelector('.modal-backdrop');
if (x) {
x.remove()
}
document.getElementById('main-tree-input').setAttribute('value', data.path)
document.getElementById('main-tree-input-upload').setAttribute('value', data.path)
getTreeView(data.path);
show_file_tree();
</script> }, 5000);
<script type="text/javascript"> });
var text = '{% raw data["js_server_types"] %}'; }
var serverTypesLists = JSON.parse(text);
/* CountryChange() is called from the onchange event of a select element. function refreshCache() {
* param selectObj - the select object which fired the on change event. var token = getCookie("_xsrf")
*/ document.getElementById("refresh-cache").classList.add("fa-spin")
function serverTypeChange(selectObj) { $.ajax({
// get the index of the selected option type: "POST",
var idx = document.getElementById('server_type').selectedIndex; headers: { 'X-XSRFToken': token },
// get the value of the selected option url: '/ajax/jar_cache',
var cSelect = document.getElementById("server"); success: function () {
try { document.getElementById("refresh-cache").classList.remove("fa-sync");
var which = document.getElementById('server_type').options[idx].value; document.getElementById("refresh-cache").classList.remove("fa-spin");
} catch { document.getElementById("refresh-cache").classList.add("fa-check");
setTimeout(function () {
location.reload();
}, 2000);
},
});
}
</script>
<script type="text/javascript">
var text = '{% raw data["js_server_types"] %}';
var serverTypesLists = JSON.parse(text);
/* CountryChange() is called from the onchange event of a select element.
* param selectObj - the select object which fired the on change event.
*/
function serverTypeChange(selectObj) {
// get the index of the selected option
var idx = document.getElementById('server_type').selectedIndex;
// get the value of the selected option
var cSelect = document.getElementById("server");
try {
var which = document.getElementById('server_type').options[idx].value;
} catch {
while (cSelect.options.length > 0) {
cSelect.remove(0);
}
return;
}
let server_type = which.split('|')[0];
let server = which.split('|')[1];
// use the selected option value to retrieve the list of items from the serverTypesLists array
let cList = serverTypesLists[server_type];
// get the country select element via its known id
var cSelect = document.getElementById("server");
// remove the current options from the country select
var len = cSelect.options.length;
while (cSelect.options.length > 0) { while (cSelect.options.length > 0) {
cSelect.remove(0); cSelect.remove(0);
} }
return; var newOption;
// create new options ordered by ascending
cList[server].forEach(type => {
newOption = document.createElement("option");
newOption.value = which + "|" + type; // assumes option string and value are the same
newOption.text = type;
// add the new option
try {
cSelect.add(newOption); // this will fail in DOM browsers but is needed for IE
}
catch (e) {
cSelect.appendChild(newOption);
}
})
} }
let server_type = which.split('|')[0];
let server = which.split('|')[1];
// use the selected option value to retrieve the list of items from the serverTypesLists array
let cList = serverTypesLists[server_type];
// get the country select element via its known id
var cSelect = document.getElementById("server");
// remove the current options from the country select
var len = cSelect.options.length;
while (cSelect.options.length > 0) {
cSelect.remove(0);
}
var newOption;
// create new options ordered by ascending
cList[server].forEach(type => {
newOption = document.createElement("option");
newOption.value = which + "|" + type; // assumes option string and value are the same
newOption.text = type;
// add the new option
try {
cSelect.add(newOption); // this will fail in DOM browsers but is needed for IE
}
catch (e) {
cSelect.appendChild(newOption);
}
})
}
function serverJarChange(selectObj) { function serverJarChange(selectObj) {
let type_select = document.getElementById('server_jar') let type_select = document.getElementById('server_jar')
let tidx = type_select.selectedIndex; let tidx = type_select.selectedIndex;
let val = type_select.options[tidx].value; let val = type_select.options[tidx].value;
if (val == 'None') { if (val == 'None') {
var jcSelect = document.getElementById("server_type");
while (jcSelect.options.length > 0) {
jcSelect.remove(0);
}
serverTypeChange(selectObj);
return;
}
// get the index of the selected option
var jidx = selectObj.selectedIndex;
// get the value of the selected option
var jwhich = selectObj.options[jidx].value;
// use the selected option value to retrieve the list of items from the serverTypesLists array
jcList = Object.keys(serverTypesLists[jwhich]);
// get the country select element via its known id
var jcSelect = document.getElementById("server_type"); var jcSelect = document.getElementById("server_type");
// remove the current options from the country select
var jlen = jcSelect.options.length;
while (jcSelect.options.length > 0) { while (jcSelect.options.length > 0) {
jcSelect.remove(0); jcSelect.remove(0);
} }
var jnewOption;
// create new options ordered by ascending
jcList.forEach(type => {
jnewOption = document.createElement("option");
jnewOption.value = jwhich + "|" + type; // assumes option string and value are the same
jnewOption.text = type;
// add the new option
try {
jcSelect.add(jnewOption); // this will fail in DOM browsers but is needed for IE
}
catch (e) {
jcSelect.appendChild(jnewOption);
}
})
serverTypeChange(selectObj); serverTypeChange(selectObj);
return;
} }
// get the index of the selected option </script>
var jidx = selectObj.selectedIndex; {% end %}
// get the value of the selected option
var jwhich = selectObj.options[jidx].value;
// use the selected option value to retrieve the list of items from the serverTypesLists array
jcList = Object.keys(serverTypesLists[jwhich]);
// get the country select element via its known id
var jcSelect = document.getElementById("server_type");
// remove the current options from the country select
var jlen = jcSelect.options.length;
while (jcSelect.options.length > 0) {
jcSelect.remove(0);
}
var jnewOption;
// create new options ordered by ascending
jcList.forEach(type => {
jnewOption = document.createElement("option");
jnewOption.value = jwhich + "|" + type; // assumes option string and value are the same
jnewOption.text = type;
// add the new option
try {
jcSelect.add(jnewOption); // this will fail in DOM browsers but is needed for IE
}
catch (e) {
jcSelect.appendChild(jnewOption);
}
})
serverTypeChange(selectObj);
}
</script>
{% end %}

View File

@ -502,6 +502,7 @@
"importServer": "Import an Existing Server", "importServer": "Import an Existing Server",
"importServerButton": "Import Server!", "importServerButton": "Import Server!",
"importZip": "Import from a Zip File", "importZip": "Import from a Zip File",
"uploadZip": "Upload Zip File For Server Import",
"maxMem": "Maximum Memory", "maxMem": "Maximum Memory",
"minMem": "Minimum Memory", "minMem": "Minimum Memory",
"myNewServer": "My New Server", "myNewServer": "My New Server",