mirror of
https://github.com/ziirish/burp-ui.git
synced 2026-05-21 06:45:24 -06:00
454 lines
13 KiB
JavaScript
454 lines
13 KiB
JavaScript
|
|
/***
|
|
* Here is our tree to browse a specific backup
|
|
* The tree is first initialized with the 'root' part of the backup.
|
|
* JSON example:
|
|
* [
|
|
* {
|
|
* "name": "/",
|
|
* "parent": "",
|
|
* "type": "d"
|
|
* }
|
|
* ]
|
|
* This JSON is then parsed into another one to initialize our tree.
|
|
* Each 'directory' is expandable.
|
|
* A new JSON is returned for each one of them on-demand.
|
|
* JSON output:
|
|
* [
|
|
* {
|
|
* "name": "etc",
|
|
* "parent": "/",
|
|
* "type": "d"
|
|
* },
|
|
* {
|
|
* "name": "home",
|
|
* "parent": "/",
|
|
* "type": "d"
|
|
* }
|
|
* ]
|
|
*/
|
|
$( document ).ready(function() {
|
|
var fixNeeded = false;
|
|
|
|
var treeCollapsed = function() {
|
|
var btn = $("#btn-expand-collapse-tree");
|
|
btn.data('collapsed', true);
|
|
btn.html('<i class="fa fa-expand" aria-hidden="true"></i> {{ _("Expand tree")|escape }}</button>');
|
|
};
|
|
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
|
|
$("#tree").fancytree({
|
|
checkbox: true,
|
|
selectMode: 3,
|
|
extensions: ["glyph", "table", "gridnav", "filter"],
|
|
glyph: {
|
|
preset: "bootstrap3",
|
|
map: {
|
|
doc: "glyphicon-file",
|
|
docOpen: "glyphicon-file",
|
|
checkbox: "glyphicon-unchecked",
|
|
checkboxSelected: "glyphicon-check",
|
|
checkboxUnknown: "glyphicon-share",
|
|
dragHelper: "glyphicon-play",
|
|
dropMarker: "glyphicon-arrow-right",
|
|
error: "glyphicon-warning-sign",
|
|
expanderClosed: "glyphicon-plus-sign",
|
|
expanderLazy: "glyphicon-plus-sign",
|
|
// expanderLazy: "glyphicon-expand",
|
|
expanderOpen: "glyphicon-minus-sign",
|
|
// expanderOpen: "glyphicon-collapse-down",
|
|
folder: "glyphicon-folder-close",
|
|
folderOpen: "glyphicon-folder-open",
|
|
loading: "glyphicon-refresh fancytree-helper-spin"
|
|
}
|
|
},
|
|
persist: {
|
|
expandLazy: false,
|
|
overrideSource: false,
|
|
store: "cookie",
|
|
cookie: {
|
|
path: '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}',
|
|
},
|
|
},
|
|
source: function() {
|
|
{% if edit and edit.found -%}
|
|
url = '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server, root=edit.roots, recursive=True, selected=True) }}';
|
|
{% else -%}
|
|
url = '{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}';
|
|
{% endif -%}
|
|
return $.getJSON(url, function(data) {
|
|
$("#waiting-container").hide();
|
|
$("#tree-container").show();
|
|
|
|
return data;
|
|
})
|
|
.fail(buiFail);
|
|
},
|
|
lazyLoad: function(event, data) {
|
|
fixNeeded = true;
|
|
var node = data.node;
|
|
p = node.key;
|
|
if (p !== "/") p += '/';
|
|
p = encodeURIComponent(p);
|
|
data.result = $.getJSON('{{ url_for("api.client_tree", name=cname, backup=nbackup, server=server) }}?root='+p);
|
|
},
|
|
loadChildren: function(event, data) {
|
|
// This is a hack to select all children of a selected node after
|
|
// lazy loading.
|
|
// The 'fixNeeded' flag is here to ensure we apply the fix only after
|
|
// a lazy loading.
|
|
if (fixNeeded) {
|
|
data.node.fixSelection3AfterClick();
|
|
}
|
|
},
|
|
init: function() {
|
|
$('#tree').floatThead({
|
|
position: 'auto',
|
|
autoReflow: true,
|
|
top: $('.navbar').height(),
|
|
});
|
|
},
|
|
scrollParent: $(window),
|
|
renderColumns: function(event, data) {
|
|
var node = data.node;
|
|
$tdList = $(node.tr).find(">td");
|
|
|
|
$tdList.eq(1).text(node.data.mode);
|
|
$tdList.eq(2).text(node.data.uid);
|
|
$tdList.eq(3).text(node.data.gid);
|
|
$tdList.eq(4).text(node.data.size);
|
|
$tdList.eq(5).html('<span title="'+node.data.date+'">'+moment(node.data.date).tz(TIMEZONE).format({{ g.date_format|tojson }})+'</span>');
|
|
},
|
|
select: function(event, data) {
|
|
toggleRestorationForms(data.tree);
|
|
},
|
|
collapse: function(event, data) {
|
|
if (!data.node.data.parent) {
|
|
treeCollapsed();
|
|
}
|
|
},
|
|
expand: function(event, data) {
|
|
var btn = $("#btn-expand-collapse-tree");
|
|
if (btn.data('collapsed')) {
|
|
btn.data('collapsed', false);
|
|
btn.html('<i class="fa fa-compress" aria-hidden="true"></i> {{ _("Collapse tree")|escape }}');
|
|
}
|
|
}
|
|
});
|
|
|
|
var tree = $("#tree").fancytree("getTree");
|
|
|
|
var toggleRestorationForms = function(my_tree) {
|
|
var s = my_tree.getSelectedNodes();
|
|
if (s.length > 0) {
|
|
$("#restore-form").show();
|
|
$("#server-initiated-form").show();
|
|
v = [];
|
|
$.each(s, function(i, n) {
|
|
v.push({key: n.key, folder: n.folder});
|
|
});
|
|
r = {restore:v};
|
|
$("input[name=list]").val(JSON.stringify(r));
|
|
$("input[name=list-sc]").val(JSON.stringify(r));
|
|
} else {
|
|
$("#restore-form").hide();
|
|
$("#server-initiated-form").hide();
|
|
$("input[name=list]").val('');
|
|
$("input[name=list-sc]").val('');
|
|
}
|
|
};
|
|
|
|
$('#tree').on('fancytreeinit', function() {
|
|
toggleRestorationForms(tree);
|
|
});
|
|
|
|
$("input[name=search-tree]").keyup(function(e){
|
|
var n,
|
|
leavesOnly = $("#leavesOnly").is(":checked"),
|
|
match = $(this).val();
|
|
|
|
if(e && e.which === $.ui.keyCode.ESCAPE || $.trim(match) === "") {
|
|
$("#btnResetSearch").click();
|
|
return;
|
|
}
|
|
if($("#regex").is(":checked")) {
|
|
// Pass function to perform match
|
|
n = tree.filterNodes(function(node) {
|
|
return new RegExp(match, "i").test(node.title);
|
|
}, leavesOnly);
|
|
} else {
|
|
// Pass a string to perform case insensitive matching
|
|
n = tree.filterNodes(match, leavesOnly);
|
|
}
|
|
$("#btnResetSearch").attr("disabled", false);
|
|
$("span#matches").text("(" + n + " matches)");
|
|
});
|
|
|
|
$("#btnResetSearch").click(function(e){
|
|
$("input[name=search-tree]").val("");
|
|
$("span#matches").text("");
|
|
tree.clearFilter();
|
|
$("#btnResetSearch").attr("disabled", true);
|
|
}).attr("disabled", true);
|
|
|
|
$("input#hideMode").change(function(e){
|
|
tree.options.filter.mode = $(this).is(":checked") ? "hide" : "dimm";
|
|
tree.clearFilter();
|
|
$("input[name=search-tree]").keyup();
|
|
e.stopPropagation();
|
|
});
|
|
$("input#leavesOnly").change(function(e){
|
|
tree.clearFilter();
|
|
$("input[name=search-tree]").keyup();
|
|
e.stopPropagation();
|
|
});
|
|
$("input#regex").change(function(e){
|
|
tree.clearFilter();
|
|
$("input[name=search-tree]").keyup();
|
|
e.stopPropagation();
|
|
});
|
|
|
|
{% if config.WITH_CELERY -%}
|
|
var _check_task_schedule = undefined;
|
|
$('#cancel-running-restore').on('click', function(e) {
|
|
var task_id = $(this).data('task_id');
|
|
var url = '{{ url_for("api.task_status", task_type="restore", task_id="", server=server) }}'+task_id;
|
|
if (!task_id) {
|
|
return;
|
|
}
|
|
if (_check_task_schedule) {
|
|
clearTimeout(_check_task_schedule);
|
|
_check_task_schedule = undefined;
|
|
}
|
|
$.ajax({
|
|
url: url,
|
|
type: 'DELETE',
|
|
});
|
|
});
|
|
{% endif -%}
|
|
|
|
$("#form-restore").on('submit', function(e) {
|
|
var $preparingFileModal = $("#restore-modal");
|
|
|
|
$preparingFileModal.modal('toggle');
|
|
|
|
{% if config.WITH_CELERY -%}
|
|
var check_task = function(task_id) {
|
|
$.getJSON('{{ url_for("api.task_status", task_type="restore", task_id="", server=server) }}'+task_id)
|
|
.done(function(data) {
|
|
if (data.state != 'SUCCESS') {
|
|
_check_task_schedule = setTimeout(function() {
|
|
check_task(task_id);
|
|
}, 2000);
|
|
} else {
|
|
$.fileDownload(data.location, {
|
|
successCallback: function (url) {
|
|
$preparingFileModal.modal('hide');
|
|
},
|
|
failCallback: function (responseHtml, url) {
|
|
$preparingFileModal.modal('hide');
|
|
if (responseHtml == 'encrypted') {
|
|
msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}';
|
|
} else {
|
|
msg = responseHtml;
|
|
}
|
|
$("#error-response").empty().text(msg);
|
|
$("#error-modal").modal('toggle');
|
|
},
|
|
});
|
|
}
|
|
})
|
|
.fail(function(xhr, stat, err) {
|
|
$preparingFileModal.modal('hide');
|
|
if (xhr.status != 502) {
|
|
buiFail(xhr, stat, err);
|
|
}
|
|
if ('responseJSON' in xhr && 'message' in xhr.responseJSON) {
|
|
resp = xhr.responseJSON.message;
|
|
} else if ('responseText' in xhr) {
|
|
resp = xhr.responseText;
|
|
} else {
|
|
return false;
|
|
}
|
|
if (resp == 'encrypted') {
|
|
msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}';
|
|
} else {
|
|
msg = resp;
|
|
}
|
|
$("#error-response").empty().text(msg);
|
|
$("#error-modal").modal('toggle');
|
|
});
|
|
};
|
|
$.post($(this).prop('action'), $(this).serialize())
|
|
.done(function(data) {
|
|
check_task(data.id);
|
|
$('#cancel-running-restore').data('task_id', data.id);
|
|
})
|
|
.fail(function(xhr, stat, err) {
|
|
$preparingFileModal.modal('hide');
|
|
buiFail(xhr, stat, err);
|
|
});
|
|
{% else -%}
|
|
$.fileDownload($(this).prop('action'), {
|
|
successCallback: function (url) {
|
|
$preparingFileModal.modal('hide');
|
|
},
|
|
failCallback: function (responseHtml, url) {
|
|
$preparingFileModal.modal('hide');
|
|
if (responseHtml == 'encrypted') {
|
|
msg = '{{ _("The backup seems encrypted, please provide the encryption key in the \'Download options\' form.")|escape }}';
|
|
} else {
|
|
msg = responseHtml;
|
|
}
|
|
$("#error-response").empty().text(msg);
|
|
$("#error-modal").modal('toggle');
|
|
},
|
|
httpMethod: "POST",
|
|
data: $(this).serialize()
|
|
});
|
|
{% endif -%}
|
|
e.preventDefault();
|
|
return false;
|
|
});
|
|
$("#form-server-initiated").on('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
var form = $(this);
|
|
var url = "{{ url_for('api.server_restore', name=cname, backup=nbackup, server=server) }}";
|
|
$.ajax({
|
|
url: url,
|
|
headers: { 'X-From-UI': true },
|
|
type: 'PUT',
|
|
data: form.serialize(),
|
|
dataType: 'text json',
|
|
})
|
|
.fail(buiFail)
|
|
.done(function(data) {
|
|
notifAll(data);
|
|
});
|
|
|
|
return false;
|
|
});
|
|
|
|
$("#btn-load-all-tree").on('click', function() {
|
|
var btn = $("#btn-load-all-tree");
|
|
btn.prop('disabled', true);
|
|
btn.html('<i class="fa fa-spinner fa-pulse fa-fw"></i> {{ _("Loading") }}');
|
|
var load_all_tree = function(url) {
|
|
$.get(url)
|
|
.fail(buiFail)
|
|
.done(function(data) {
|
|
tree.reload(data);
|
|
btn.html('<i class="fa fa-check-square-o" aria-hidden="true"></i> {{ _("Nodes loaded") }}');
|
|
$('#'+btn.attr('aria-describedby')).remove();
|
|
btn.blur();
|
|
});
|
|
};
|
|
{% if config.WITH_CELERY -%}
|
|
var url = "{{ url_for('api.task_client_tree_all', name=cname, backup=nbackup, server=server) }}";
|
|
var _task_status_schedule = undefined;
|
|
var task_status = function(task_id) {
|
|
$.getJSON('{{ url_for("api.task_status", task_type="browse", task_id="", server=server) }}'+task_id)
|
|
.fail(buiFail)
|
|
.done(function(data) {
|
|
if (data.state != 'SUCCESS') {
|
|
_task_status_schedule = setTimeout(function() {
|
|
task_status(task_id);
|
|
}, 2000);
|
|
} else {
|
|
load_all_tree(data.location);
|
|
}
|
|
});
|
|
};
|
|
$.post(url)
|
|
.fail(buiFail)
|
|
.done(function(data) {
|
|
task_status(data.id);
|
|
});
|
|
{% else -%}
|
|
var url = "{{ url_for('api.client_tree_all', name=cname, backup=nbackup, server=server) }}";
|
|
load_all_tree(url);
|
|
{% endif -%}
|
|
});
|
|
$("#btn-expand-collapse-tree").on('click', function() {
|
|
var btn = $("#btn-expand-collapse-tree");
|
|
var collapsed = btn.data('collapsed');
|
|
tree.getRootNode().visit(function(node) {
|
|
if (collapsed) {
|
|
if (node.lazy && !node.children && node.title != "/") {
|
|
return 'skip';
|
|
}
|
|
}
|
|
node.setExpanded(collapsed);
|
|
});
|
|
if (!collapsed) {
|
|
treeCollapsed();
|
|
}
|
|
});
|
|
$("#btn-clear").on('click', function() {
|
|
tree.visit(function(node){
|
|
node.setSelected(false);
|
|
});
|
|
return false;
|
|
});
|
|
$(".dropdown-menu label, .dropdown-menu input, .dropdown-menu li").click(function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
|
|
{% if encrypted -%}
|
|
$("#perform").attr("disabled", "disabled");
|
|
$("#pass").on('change', function() {
|
|
if ($(this).val().length > 0) {
|
|
$("#perform").removeAttr("disabled");
|
|
$("#notice").hide();
|
|
} else {
|
|
$("#perform").attr("disabled", "disabled");
|
|
$("#notice").show();
|
|
}
|
|
});
|
|
{% endif -%}
|
|
|
|
{% if edit -%}
|
|
$('#btn-cancel-restore').on('click', function(e) {
|
|
{% if edit.orig_client -%}
|
|
url = '{{ url_for("api.is_server_restore", name=edit.to, server=server) }}';
|
|
{% else -%}
|
|
url = '{{ url_for("api.is_server_restore", name=cname, server=server) }}';
|
|
{% endif -%}
|
|
$.ajax({
|
|
url: url,
|
|
headers: { 'X-From-UI': true },
|
|
type: 'DELETE'
|
|
}).done(function(data) {
|
|
notifAll(data);
|
|
if (data[0] == 0) {
|
|
$('#btn-cancel-restore').hide();
|
|
}
|
|
}).fail(buiFail);
|
|
});
|
|
{% endif -%}
|
|
});
|
|
|
|
var app = angular.module('MainApp', ['ngSanitize']);
|
|
|
|
app.controller('BrowseCtrl', function($scope, $http) {
|
|
$scope.sc_restore = {};
|
|
$scope.load_all = false;
|
|
{% if edit and edit.orig_client -%}
|
|
$scope.sc_restore.to = '{{ edit.to }}';
|
|
{% endif -%}
|
|
$http.get('{{ url_for("api.clients_all", server=server) }}', { headers: { 'X-From-UI': true } })
|
|
.then(function(response) {
|
|
$scope.sc_restore.clients = response.data;
|
|
}, function(response) {
|
|
notifAll(response.data);
|
|
});
|
|
$http.get('{{ url_for("api.setting_options", server=server) }}', { headers: { 'X-From-UI': true } })
|
|
.then(function(response) {
|
|
$scope.sc_restore.enabled = response.data.server_can_restore;
|
|
$scope.load_all = response.data.batch_list_supported;
|
|
}, function(response) {
|
|
notifAll(response.data);
|
|
});
|
|
});
|