mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Add backups API
This commit is contained in:
parent
2f509b46ee
commit
76e1ee471a
@ -1580,68 +1580,7 @@ class PanelHandler(BaseHandler):
|
|||||||
role = self.controller.roles.get_role(r)
|
role = self.controller.roles.get_role(r)
|
||||||
exec_user_role.add(role["role_name"])
|
exec_user_role.add(role["role_name"])
|
||||||
|
|
||||||
if page == "server_backup":
|
if page == "config_json":
|
||||||
logger.debug(self.request.arguments)
|
|
||||||
|
|
||||||
server_id = self.check_server_id()
|
|
||||||
if not server_id:
|
|
||||||
return
|
|
||||||
|
|
||||||
if (
|
|
||||||
not permissions["Backup"]
|
|
||||||
in self.controller.server_perms.get_user_id_permissions_list(
|
|
||||||
exec_user["user_id"], server_id
|
|
||||||
)
|
|
||||||
and not superuser
|
|
||||||
):
|
|
||||||
self.redirect(
|
|
||||||
"/panel/error?error=Unauthorized access: User not authorized"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
|
||||||
compress = self.get_argument("compress", False)
|
|
||||||
shutdown = self.get_argument("shutdown", False)
|
|
||||||
check_changed = self.get_argument("changed")
|
|
||||||
before = self.get_argument("backup_before", "")
|
|
||||||
after = self.get_argument("backup_after", "")
|
|
||||||
if str(check_changed) == str(1):
|
|
||||||
checked = self.get_body_arguments("root_path")
|
|
||||||
else:
|
|
||||||
checked = self.controller.management.get_excluded_backup_dirs(server_id)
|
|
||||||
if superuser:
|
|
||||||
backup_path = self.get_argument("backup_path", None)
|
|
||||||
if Helpers.is_os_windows():
|
|
||||||
backup_path.replace(" ", "^ ")
|
|
||||||
backup_path = Helpers.wtol_path(backup_path)
|
|
||||||
else:
|
|
||||||
backup_path = server_obj.backup_path
|
|
||||||
max_backups = bleach.clean(self.get_argument("max_backups", None))
|
|
||||||
|
|
||||||
server_obj = self.controller.servers.get_server_obj(server_id)
|
|
||||||
|
|
||||||
server_obj.backup_path = backup_path
|
|
||||||
self.controller.servers.update_server(server_obj)
|
|
||||||
self.controller.management.set_backup_config(
|
|
||||||
server_id,
|
|
||||||
max_backups=max_backups,
|
|
||||||
excluded_dirs=checked,
|
|
||||||
compress=bool(compress),
|
|
||||||
shutdown=bool(shutdown),
|
|
||||||
before=before,
|
|
||||||
after=after,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.controller.management.add_to_audit_log(
|
|
||||||
exec_user["user_id"],
|
|
||||||
f"Edited server {server_id}: updated backups",
|
|
||||||
server_id,
|
|
||||||
self.get_remote_ip(),
|
|
||||||
)
|
|
||||||
self.tasks_manager.reload_schedule_from_db()
|
|
||||||
self.redirect(f"/panel/server_detail?id={server_id}&subpage=backup")
|
|
||||||
|
|
||||||
elif page == "config_json":
|
|
||||||
try:
|
try:
|
||||||
data = {}
|
data = {}
|
||||||
with open(self.helper.settings_file, "r", encoding="utf-8") as f:
|
with open(self.helper.settings_file, "r", encoding="utf-8") as f:
|
||||||
|
@ -10,13 +10,13 @@ logger = logging.getLogger(__name__)
|
|||||||
backup_patch_schema = {
|
backup_patch_schema = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"path": {"type": "string", "minLength": 1},
|
"backup_path": {"type": "string", "minLength": 1},
|
||||||
"max": {"type": "int"},
|
"max_backups": {"type": "integer"},
|
||||||
"compress": {"type": "boolean"},
|
"compress": {"type": "boolean"},
|
||||||
"shutdown": {"type": "boolean"},
|
"shutdown": {"type": "boolean"},
|
||||||
"before_command": {"type": "string"},
|
"backup_before": {"type": "string"},
|
||||||
"after_command": {"type": "string"},
|
"backup_after": {"type": "string"},
|
||||||
"exclusions": {"type": "string"},
|
"exclusions": {"type": "array"},
|
||||||
},
|
},
|
||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
@ -25,12 +25,12 @@ backup_patch_schema = {
|
|||||||
basic_backup_patch_schema = {
|
basic_backup_patch_schema = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"max": {"type": "int"},
|
"max_backups": {"type": "integer"},
|
||||||
"compress": {"type": "boolean"},
|
"compress": {"type": "boolean"},
|
||||||
"shutdown": {"type": "boolean"},
|
"shutdown": {"type": "boolean"},
|
||||||
"before_command": {"type": "string"},
|
"backup_before": {"type": "string"},
|
||||||
"after_command": {"type": "string"},
|
"backup_after": {"type": "string"},
|
||||||
"exclusions": {"type": "string"},
|
"exclusions": {"type": "array"},
|
||||||
},
|
},
|
||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
@ -65,7 +65,10 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if auth_data[4]["superuser"]:
|
||||||
validate(data, backup_patch_schema)
|
validate(data, backup_patch_schema)
|
||||||
|
else:
|
||||||
|
validate(data, basic_backup_patch_schema)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
return self.finish_json(
|
return self.finish_json(
|
||||||
400,
|
400,
|
||||||
@ -90,13 +93,31 @@ class ApiServersServerBackupsIndexHandler(BaseApiHandler):
|
|||||||
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"})
|
||||||
|
|
||||||
self.controller.management.set_backup_config(
|
self.controller.management.set_backup_config(
|
||||||
data["server_id"],
|
server_id,
|
||||||
data["backup_path"],
|
data.get(
|
||||||
data["max_backups"],
|
"backup_path",
|
||||||
data["excluded_dirs"],
|
self.controller.management.get_backup_config(server_id)["backup_path"],
|
||||||
data["compress"],
|
),
|
||||||
data["shutdown"],
|
data.get(
|
||||||
data["before"],
|
"max_backups",
|
||||||
data["after"],
|
self.controller.management.get_backup_config(server_id)["max_backups"],
|
||||||
|
),
|
||||||
|
data.get("exclusions"),
|
||||||
|
data.get(
|
||||||
|
"compress",
|
||||||
|
self.controller.management.get_backup_config(server_id)["compress"],
|
||||||
|
),
|
||||||
|
data.get(
|
||||||
|
"shutdown",
|
||||||
|
self.controller.management.get_backup_config(server_id)["shutdown"],
|
||||||
|
),
|
||||||
|
data.get(
|
||||||
|
"backup_before",
|
||||||
|
self.controller.management.get_backup_config(server_id)["before"],
|
||||||
|
),
|
||||||
|
data.get(
|
||||||
|
"backup_after",
|
||||||
|
self.controller.management.get_backup_config(server_id)["after"],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return self.finish(200, {"status": "ok"})
|
return self.finish_json(200, {"status": "ok"})
|
||||||
|
@ -44,9 +44,7 @@
|
|||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<form id="backup-form" class="forms-sample" method="post" action="/panel/server_backup">
|
<form id="backup-form" class="forms-sample">
|
||||||
{% raw xsrf_form_html() %}
|
|
||||||
|
|
||||||
{% if data['backing_up'] %}
|
{% if data['backing_up'] %}
|
||||||
<div class="progress" style="height: 15px;">
|
<div class="progress" style="height: 15px;">
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar"
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar"
|
||||||
@ -146,8 +144,6 @@
|
|||||||
data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{
|
data-server_path="{{ data['server_stats']['server_id']['path']}}" type="button">{{
|
||||||
translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
translate('serverBackups', 'clickExclude', data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" class="form-control" name="changed" id="changed" value="0"
|
|
||||||
style="visibility: hidden;"></input>
|
|
||||||
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
@ -400,29 +396,48 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function replacer(key, value) {
|
||||||
|
if (key != "backup_before" && key != "backup_after") {
|
||||||
|
if (typeof value == "boolean" || key === "executable_update_url") {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return (isNaN(value) ? value : +value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$("#config_form").on("submit", async function (e) {
|
$("#backup-form").on("submit", async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var token = getCookie("_xsrf")
|
var token = getCookie("_xsrf")
|
||||||
let backupForm = document.getElementById("backup_form");
|
let backupForm = document.getElementById("backup-form");
|
||||||
//Remove checks that we don't need in form data.
|
|
||||||
$(this).children("before-check").remove();
|
|
||||||
$(this).children("after-check").remove();
|
|
||||||
let formData = new FormData(backupForm);
|
let formData = new FormData(backupForm);
|
||||||
|
//Remove checks that we don't need in form data.
|
||||||
|
formData.delete("after-check");
|
||||||
|
formData.delete("before-check");
|
||||||
//Create an object from the form data entries
|
//Create an object from the form data entries
|
||||||
let formDataObject = Object.fromEntries(formData.entries());
|
let formDataObject = Object.fromEntries(formData.entries());
|
||||||
//We need to make sure these are sent regardless of whether or not they're checked
|
//We need to make sure these are sent regardless of whether or not they're checked
|
||||||
formDataObject.compress = $("#compress").prop('checked');
|
formDataObject.compress = $("#compress").prop('checked');
|
||||||
formDataObject.shutdown = $("#shutdown").prop('checked');
|
formDataObject.shutdown = $("#shutdown").prop('checked');
|
||||||
|
let excluded = [];
|
||||||
|
$('input.excluded:checkbox:checked').each(function () {
|
||||||
|
excluded.push($(this).val());
|
||||||
|
});
|
||||||
|
if ($("#root_files_button").hasClass("clicked")){
|
||||||
|
formDataObject.exclusions = excluded;
|
||||||
|
}
|
||||||
|
console.log(excluded);
|
||||||
console.log(formDataObject);
|
console.log(formDataObject);
|
||||||
// Format the plain form data as JSON
|
// Format the plain form data as JSON
|
||||||
let formDataJsonString = JSON.stringify(formDataObject, replacer);
|
let formDataJsonString = JSON.stringify(formDataObject, replacer);
|
||||||
formDataJsonString["ignored_exits"] = toString(formDataJsonString["ignored_exits"]);
|
|
||||||
console.log(formDataJsonString.ignored_exits)
|
|
||||||
|
|
||||||
console.log(formDataJsonString);
|
console.log(formDataJsonString);
|
||||||
|
|
||||||
let res = await fetch(`/api/v2/servers/${serverId}`, {
|
let res = await fetch(`/api/v2/servers/${server_id}/backups/`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
'X-XSRFToken': token
|
'X-XSRFToken': token
|
||||||
@ -550,7 +565,6 @@
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('root_files_button').classList.add('clicked');
|
document.getElementById('root_files_button').classList.add('clicked');
|
||||||
document.getElementById("changed").value = 1;
|
|
||||||
}
|
}
|
||||||
path = $("#root_files_button").data('server_path')
|
path = $("#root_files_button").data('server_path')
|
||||||
console.log($("#root_files_button").data('server_path'))
|
console.log($("#root_files_button").data('server_path'))
|
||||||
@ -643,7 +657,7 @@
|
|||||||
}
|
}
|
||||||
text += `<li class="tree-item" data-path="${dpath}">
|
text += `<li class="tree-item" data-path="${dpath}">
|
||||||
\n<div id="${dpath}" data-path="${dpath}" data-name="${filename}" class="tree-caret tree-ctx-item tree-folder">
|
\n<div id="${dpath}" data-path="${dpath}" data-name="${filename}" class="tree-caret tree-ctx-item tree-folder">
|
||||||
<input type="checkbox" class="checkBoxClass" name="root_path" value="${dpath}" ${checked}>
|
<input type="checkbox" class="checkBoxClass excluded" value="${dpath}" ${checked}>
|
||||||
<span id="${dpath}span" class="files-tree-title" data-path="${dpath}" data-name="${filename}" onclick="getDirView(event)">
|
<span id="${dpath}span" class="files-tree-title" data-path="${dpath}" data-name="${filename}" onclick="getDirView(event)">
|
||||||
<i style="color: var(--info);" class="far fa-folder"></i>
|
<i style="color: var(--info);" class="far fa-folder"></i>
|
||||||
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
<i style="color: var(--info);" class="far fa-folder-open"></i>
|
||||||
@ -655,7 +669,7 @@
|
|||||||
class="d-block tree-ctx-item tree-file"
|
class="d-block tree-ctx-item tree-file"
|
||||||
data-path="${dpath}"
|
data-path="${dpath}"
|
||||||
data-name="${filename}"
|
data-name="${filename}"
|
||||||
onclick=""><input type='checkbox' class="checkBoxClass" name='root_path' value="${dpath}" ${checked}><span style="margin-right: 6px;">
|
onclick=""><input type='checkbox' class="checkBoxClass excluded" name='root_path' value="${dpath}" ${checked}><span style="margin-right: 6px;">
|
||||||
<i class="far fa-file"></i></span></input>${filename}</li>`
|
<i class="far fa-file"></i></span></input>${filename}</li>`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user