rabbitmq-server/deps/rabbitmq_management/priv/www/js/main.js

1848 lines
56 KiB
JavaScript

$(document).ready(function() {
var url_string = window.location.href;
var url = new URL(url_string);
var error = url.searchParams.get('error');
if (error) {
if (oauth.enabled) {
renderWarningMessageInLoginStatus(oauth, fmt_escape_html(error));
}
} else {
if (oauth.enabled) {
startWithOAuthLogin(oauth);
} else {
startWithLoginPage();
}
}
});
function startWithLoginPage() {
replace_content('outer', format('login', {}));
start_app_login();
}
function removeDuplicates(array){
let output = []
for(let item of array) {
if(!output.includes(item)) {
output.push(item)
}
}
return output
}
function startWithOAuthLogin (oauth) {
store_pref("oauth-return-to", window.location.hash);
if (!oauth.logged_in) {
hasAnyResourceServerReady(oauth, (oauth, warnings) => { render_login_oauth(oauth, warnings); start_app_login(); })
} else {
start_app_login()
}
}
function render_login_oauth(oauth, messages) {
let formatData = {}
formatData.warnings = []
formatData.notAuthorized = false
formatData.resource_servers = oauth.resource_servers
formatData.declared_resource_servers_count = oauth.declared_resource_servers_count
formatData.oauth_disable_basic_auth = oauth.oauth_disable_basic_auth
if (Array.isArray(messages)) {
formatData.warnings = messages
} else if (typeof messages == "string") {
formatData.warnings = [messages]
formatData.notAuthorized = messages == "Not authorized"
}
replace_content('outer', format('login_oauth', formatData))
setup_visibility()
$('#login').off('click', 'div.section h2, div.section-hidden h2');
$('#login').on('click', 'div.section h2, div.section-hidden h2', function() {
toggle_visibility($(this));
});
}
function renderWarningMessageInLoginStatus(oauth, message) {
render_login_oauth(oauth, message)
}
function dispatcher_add(fun) {
dispatcher_modules.push(fun);
if (dispatcher_modules.length == extension_count) {
start_app();
}
}
function dispatcher() {
for (var i in dispatcher_modules) {
dispatcher_modules[i](this);
}
}
function getParameterByName(name) {
var match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
function getAccessToken() {
return getParameterByName('access_token');
}
function start_app_login () {
app = new Sammy.Application(function () {
this.get('/', function () {})
this.get('#/', function () {})
if (!oauth.enabled || !oauth.oauth_disable_basic_auth) {
this.put('#/login', function() {
set_basic_auth(this.params['username'], this.params['password'])
check_login()
});
}
})
if (oauth.enabled) {
if (has_auth_credentials()) {
check_login();
} else {
app.run();
}
} else
if (!has_auth_credentials() || !check_login()) {
app.run();
}
}
function check_login () {
user = JSON.parse(sync_get('/whoami'));
if (user == false || user.error) {
clear_auth();
if (oauth.enabled) {
//hide_popup_warn();
renderWarningMessageInLoginStatus(oauth, 'Not authorized');
} else {
//hide_popup_warn();
replace_content('login-status', '<p>Login failed</p>');
}
return false;
}
hide_popup_warn()
replace_content('outer', format('layout', {}))
var user_login_session_timeout = parseInt(user.login_session_timeout)
if (!isNaN(user_login_session_timeout)) {
update_login_session_timeout(user_login_session_timeout)
}
ui_data_model.vhosts = JSON.parse(sync_get('/vhosts'));
ac.update(user, ui_data_model)
if (ac.isMonitoringUser()) {
ui_data_model.nodes = JSON.parse(sync_get('/nodes'))
}
var overview = JSON.parse(sync_get('/overview'))
display.update(overview, ui_data_model)
setup_global_vars(overview)
setup_constant_events()
update_vhosts()
update_interval()
setup_extensions()
return true
}
function start_app() {
if (app !== undefined) {
app.unload();
}
// Oh boy. Sammy uses various different methods to determine if
// the URL hash has changed. Unsurprisingly this is a native event
// in modern browsers, and falls back to an icky polling function
// in MSIE. But it looks like there's a bug. The polling function
// should get installed when the app is started. But it's guarded
// behind if (Sammy.HashLocationProxy._interval != null). And of
// course that's not specific to the application; it's pretty
// global. So we need to manually clear that in order for links to
// work in MSIE.
// Filed as https://github.com/quirkey/sammy/issues/171
//
// Note for when we upgrade: HashLocationProxy has become
// DefaultLocationProxy in later versions, but otherwise the issue
// remains.
// updated to the version 0.7.6 this _interval = null is fixed
// just leave the history here.
//Sammy.HashLocationProxy._interval = null;
var url = this.location.toString();
var hash = this.location.hash;
var pathname = this.location.pathname;
if (url.indexOf('#') == -1) {
this.location = url + '#/';
} else if (hash.indexOf('#token_type') != - 1 && pathname == '/') {
// This is equivalent to previous `if` clause when uaa authorisation is used.
// Tokens are passed in the url hash, so the url always contains a #.
// We need to check the current path is `/` and token is present,
// so we can redirect to `/#/`
this.location = url.replace(/#token_type.+/gi, '#/');
}
app = new Sammy.Application(dispatcher);
app.run();
}
function setup_constant_events() {
$('#update-every').on('change', function() {
var interval = $(this).val();
store_pref('interval', interval);
if (interval == '')
interval = null;
else
interval = parseInt(interval);
set_timer_interval(interval);
});
$('#show-vhost').on('change', function() {
current_vhost = $(this).val();
store_pref('vhost', current_vhost);
update();
});
if (!vhosts_interesting) {
$('#vhost-form').hide();
}
}
function update_vhosts() {
if (display.vhosts) {
$('#vhost-form').show();
$('li#vhost').show();
}else {
$('#vhost-form').hide();
$('li#vhost').hide();
}
var select = $('#show-vhost').get(0);
select.options.length = ui_data_model.vhosts.length + 1;
var index = 0;
for (var i = 0; i < ui_data_model.vhosts.length; i++) {
var vhost = ui_data_model.vhosts[i].name;
select.options[i + 1] = new Option(vhost, vhost);
if (vhost == current_vhost) index = i + 1;
}
select.selectedIndex = index;
current_vhost = select.options[index].value;
store_pref('vhost', current_vhost);
}
function setup_extensions() {
var extensions = JSON.parse(sync_get('/extensions'));
extension_count = 0;
for (var i in extensions) {
var extension = extensions[i];
if ($.isPlainObject(extension) && extension.hasOwnProperty('javascript')) {
dynamic_load(extension.javascript);
extension_count++;
}
}
}
function dynamic_load(filename) {
var element = document.createElement('script');
element.setAttribute('type', 'text/javascript');
element.setAttribute('src', 'js/' + filename);
document.getElementsByTagName('head')[0].appendChild(element);
return element;
}
function update_interval() {
var intervalStr = get_pref('interval');
var interval;
if (intervalStr == null) interval = 5000;
else if (intervalStr == '') interval = null;
else interval = parseInt(intervalStr);
if (isNaN(interval)) interval = null; // Prevent DoS if cookie malformed
set_timer_interval(interval);
var select = $('#update-every').get(0);
var opts = select.options;
for (var i = 0; i < opts.length; i++) {
if (opts[i].value == intervalStr) {
select.selectedIndex = i;
break;
}
}
}
function go_to(url) {
this.location = url;
}
function go_to_home() {
// location.href = rabbit_path_prefix() + "/"
location.href = "/"
}
function set_timer_interval(interval) {
timer_interval = interval;
reset_timer();
}
function reset_timer() {
if (timer != null) {
clearInterval(timer);
}
if (timer_interval != null) {
timer = setInterval(partial_update, timer_interval);
}
}
function update_manual(div, query) {
var path;
var template;
if (query == 'memory' || query == 'binary') {
path = current_reqs['node']['path'] + '?' + query + '=true';
template = query;
}
var data = JSON.parse(sync_get(path));
replace_content(div, format(template, data));
postprocess_partial();
}
function render(reqs, template, highlight) {
var old_template = current_template;
current_template = template;
current_reqs = reqs;
for (var i in outstanding_reqs) {
outstanding_reqs[i].abort();
}
outstanding_reqs = [];
current_highlight = highlight;
if (old_template !== current_template) {
window.scrollTo(0, 0);
}
update();
}
function update() {
replace_content('debug', '');
clearInterval(timer);
with_update(function(html) {
update_navigation();
update_warnings();
replace_content('main', html);
postprocess();
postprocess_partial();
render_charts();
maybe_scroll();
reset_timer();
});
}
function partial_update() {
if (!$(".pagination_class").is(":focus")) {
if ($('.updatable').length > 0) {
if (update_counter >= 200) {
update_counter = 0;
full_refresh();
return;
}
with_update(function(html) {
update_counter++;
replace_content('scratch', html);
var befores = $('#main .updatable');
var afters = $('#scratch .updatable');
if (befores.length != afters.length) {
console.log("before/after mismatch! Doing a full reload...");
full_refresh();
}
for (var i = 0; i < befores.length; i++) {
$(befores[i]).empty().append($(afters[i]).contents());
}
replace_content('scratch', '');
postprocess_partial();
render_charts();
});
}
}
}
function update_navigation() {
var l1 = '';
var l2 = '';
var descend = null;
for (var k in NAVIGATION) {
var val = NAVIGATION[k];
var path = val;
while (!leaf(path)) {
path = first_showable_child(path);
}
var selected = false;
if (contains_current_highlight(val)) {
selected = true;
if (!leaf(val) && val[2] && ac.canAccessVhosts()) {
descend = nav(val)
}
}
if (show(path)) {
if (val.length < 3 || ( val[2] && ac.canAccessVhosts() )) {
l1 += '<li id="' + navigation_tab_id(k) + '"><a href="' + nav(path) + '"' +
(selected ? ' class="selected"' : '') + '>' + k + '</a></li>'
}
}
}
if (descend) {
l2 = obj_to_ul(descend);
$('#main').addClass('with-rhs');
}
else {
$('#main').removeClass('with-rhs');
}
replace_content('tabs', l1);
replace_content('rhs', l2);
}
function update_warnings() {
feature_flags = JSON.parse(sync_get('/feature-flags'));
var needs_enabling = false;
for (var i = 0; i < feature_flags.length; i++) {
var feature_flag = feature_flags[i];
if (feature_flag.state == "disabled" && feature_flag.stability != "experimental") {
needs_enabling = true;
}
}
deprecated_features = JSON.parse(sync_get('/deprecated-features/used'));
var needs_deprecating = false;
if (deprecated_features.length > 0) {
needs_deprecating = true;
}
var l1 = '<p class="warning">';
if (needs_enabling) {
l1 += '<span>&#9888;</span> All stable feature flags must be enabled after completing an upgrade. <a href="https://www.rabbitmq.com/feature-flags.html">[Learn more]</a>';
}
if (needs_deprecating) {
if (needs_enabling) {
l1 += '<br/>'
}
l1 += '<span>&#9888;</span> Deprecated features are being used. <a href="https://www.rabbitmq.com/feature-flags.html">[Learn more]</a>'
}
l1 += '</p>';
if (needs_enabling || needs_deprecating) {
$('#main').addClass('with-warnings');
$('#rhs').addClass('with-warnings');
replace_content('warnings', l1);
} else {
$('#main').removeClass('with-warnings');
$('#rhs').removeClass('with-warnings');
}
}
function navigation_tab_id(value) {
return value.toLowerCase().replaceAll(/\s/g, "-")
}
function nav(pair) {
return pair[0];
}
function show(pair) {
var hasUserTag = jQuery.inArray(pair[1], user_tags) != -1
if (pair.length > 2 && pair[2]) {
return hasUserTag && ac.canAccessVhosts()
} else {
return hasUserTag
}
}
function leaf(pair) {
return typeof(nav(pair)) == 'string';
}
function first_showable_child(pair) {
var items = pair[0];
var ks = keys(items);
for (var i = 0; i < ks.length; i++) {
var child = items[ks[i]];
if (show(child)) return child;
}
return items[ks[0]]; // We'll end up not showing it anyway
}
function contains_current_highlight(val) {
if (leaf(val)) {
return current_highlight == nav(val);
}
else {
var b = false;
for (var k in val) {
b |= contains_current_highlight(val[k]);
}
return b;
}
}
function obj_to_ul(val) {
var res = '<ul>';
for (var k in val) {
var obj = val[k];
if (show(obj)) {
res += '<li>';
if (leaf(obj)) {
res += '<a href="' + nav(obj) + '"' +
(current_highlight == nav(obj) ? ' class="selected"' : '') +
'>' + k + '</a>';
}
else {
res += obj_to_ul(nav(obj));
}
res += '</li>';
}
}
return res + '</ul>';
}
function full_refresh() {
store_pref('position', x_position() + ',' + y_position());
location.reload();
}
function maybe_scroll() {
var pos = get_pref('position');
if (pos) {
clear_pref('position');
var xy = pos.split(",");
window.scrollTo(parseInt(xy[0]), parseInt(xy[1]));
}
}
function x_position() {
return window.pageXOffset ?
window.pageXOffset :
document.documentElement.scrollLeft ?
document.documentElement.scrollLeft :
document.body.scrollLeft;
}
function y_position() {
return window.pageYOffset ?
window.pageYOffset :
document.documentElement.scrollTop ?
document.documentElement.scrollTop :
document.body.scrollTop;
}
function with_update(fun) {
if(outstanding_reqs.length > 0){
return false;
}
var model = [];
model['extra_content'] = []; // magic key for extension point
with_reqs(apply_state(current_reqs), model, function(json) {
var html = format(current_template, json);
fun(html);
update_status('ok');
});
return true;
}
function apply_state(reqs) {
var reqs2 = {};
for (k in reqs) {
var req = reqs[k];
var options = {};
if (typeof(req) == "object") {
options = req.options;
req = req.path;
}
var req2;
if (options['vhost'] != undefined && current_vhost != '') {
var indexPage = req.indexOf("?page=");
if (indexPage >- 1) {
pageUrl = req.substr(indexPage);
req2 = req.substr(0,indexPage) + '/' + esc(current_vhost) + pageUrl;
} else
req2 = req + '/' + esc(current_vhost);
}
else {
req2 = req;
}
var qs = [];
if (options['sort'] != undefined && current_sort != null) {
qs.push('sort=' + current_sort);
qs.push('sort_reverse=' + current_sort_reverse);
}
if (options['ranges'] != undefined) {
for (i in options['ranges']) {
var type = options['ranges'][i];
var range = get_pref('chart-range').split('|');
var prefix;
if (type.substring(0, 8) == 'lengths-') {
prefix = 'lengths';
}
else if (type.substring(0, 10) == 'msg-rates-') {
prefix = 'msg_rates';
}
else if (type.substring(0, 11) == 'data-rates-') {
prefix = 'data_rates';
}
else if (type == 'node-stats') {
prefix = 'node_stats';
}
qs.push(prefix + '_age=' + parseInt(range[0]));
qs.push(prefix + '_incr=' + parseInt(range[1]));
}
}
/* Unknown options are used as query parameters as is. */
Object.keys(options).forEach(function (key) {
/* Skip known keys we already handled and undefined parameters. */
if (key == 'vhost' || key == 'sort' || key == 'ranges')
return;
if (!key || options[key] == undefined)
return;
qs.push(esc(key) + '=' + esc(options[key]));
});
qs = qs.join('&');
if (qs != '')
if (req2.indexOf("?page=") >- 1)
qs = '&' + qs;
else
qs = '?' + qs;
reqs2[k] = req2 + qs;
}
return reqs2;
}
function show_popup(type, text, _mode) {
var cssClass = '.form-popup-' + type;
function hide() {
$(cssClass).fadeOut(100, function() {
$(this).remove();
});
}
hide();
$('#outer').after(format('popup', {'type': type, 'text': text}));
$(cssClass).fadeIn(100);
var closeButtonCssClass = cssClass + ' span';
$('div#outer,' + closeButtonCssClass).on('click', function(event) {
if ($(event.target).eq($(closeButtonCssClass)) || !$(event.target).closest(cssClass).length) {
$('.popup-owner').removeClass('popup-owner');
hide();
}
});
}
function hide_popup_warn() {
var cssClass = '.form-popup-warn';
$('.popup-owner').removeClass('popup-owner');
$(cssClass).fadeOut(100, function() {
$(this).remove();
});
}
function submit_import(form) {
if (form.file.value) {
var confirm_upload = confirm('Are you sure you want to import a definitions file? Some entities (vhosts, users, queues, etc) may be overwritten!');
if (confirm_upload === true) {
var file = form.file.files[0]; // FUTURE: limit upload file size (?)
var vhost_upload = $("select[name='vhost-upload'] option:selected");
var vhost_selected = vhost_upload.index() > 0;
var vhost_name = null;
if (vhost_selected) {
vhost_name = vhost_upload.val();
}
var vhost_part = '';
if (vhost_name) {
vhost_part = '/' + esc(vhost_name);
}
var form_action = "/definitions" + vhost_part;
var fd = new FormData();
fd.append('file', file);
with_req('POST', form_action, fd, function(resp) {
show_popup('info', 'Your definitions were imported successfully.');
});
}
}
return false;
};
function postprocess() {
$('form.confirm-queue').on('submit', function() {
return confirm("Are you sure? The queue is going to be deleted. " +
"Messages cannot be recovered after deletion.");
});
$('form.confirm-purge-queue').on('submit', function() {
return confirm("Are you sure? Messages cannot be recovered after purging.");
});
$('form.confirm').on('submit', function() {
return confirm("Are you sure? This object cannot be recovered " +
"after deletion.");
});
$('form.enable-feature-flag').on('submit', function() {
full_refresh();
});
$('label').map(function() {
if ($(this).attr('for') == '') {
var id = 'auto-label-' + Math.floor(Math.random()*1000000000);
var input = $(this).parents('tr').first().find('input, select');
if (input.attr('id') == '') {
$(this).attr('for', id);
input.attr('id', id);
}
}
});
$('#download-definitions').on('click', function() {
var idx = $("select[name='vhost-download'] option:selected").index()
var vhost = ((idx <=0 ) ? "" : "/" + esc($("select[name='vhost-download'] option:selected").val()))
var download_filename = esc($('#download-filename').val())
var path = '/definitions' + vhost
with_req('GET', path, null, function(resp) {
if (resp.status >= 200 && resp.status <= 299) {
var type = resp.getResponseHeader('Content-Type')
var blob = new Blob([resp.response], { type: type })
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, download_filename)
} else {
var URL = window.URL || window.webkitURL
var downloadUrl = URL.createObjectURL(blob)
var a = document.createElement("a")
if (typeof a.download === 'undefined') {
window.location = downloadUrl
} else {
a.href = downloadUrl
a.download = download_filename
document.body.appendChild(a)
a.click()
}
var cleanup = function () {
URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
};
setTimeout(cleanup, 1000)
}
} else {
// Unsuccessful status
show_popup('warn', 'Error downloading definitions')
}
});
});
$('.update-manual').on('click', function() {
update_manual($(this).attr('for'), $(this).attr('query'));
});
$(document).on('keyup', '.multifield input', function() {
update_multifields();
});
$(document).on('change', '.multifield select', function() {
update_multifields();
});
$('.controls-appearance').on('change', function() {
var params = $(this).get(0).options;
var selected = $(this).val();
for (i = 0; i < params.length; i++) {
var param = params[i].value;
if (param == selected) {
$('#' + param + '-div').slideDown(100);
} else {
$('#' + param + '-div').slideUp(100);
}
}
});
$(document).on('click', '.help', function() {
show_popup('help', HELP[$(this).attr('id')]);
});
$(document).on('click', '.popup-options-link', function() {
$('.popup-owner').removeClass('popup-owner');
$(this).addClass('popup-owner');
var template = $(this).attr('type') + '-options';
show_popup('options', format(template, {span: $(this)}), 'fade');
});
$(document).on('click', '.rate-visibility-option', function() {
var k = $(this).attr('data-pref');
var show = get_pref(k) !== 'true';
store_pref(k, '' + show);
partial_update();
});
$(document).on('focus', 'input, select', function() {
update_counter = 0; // If there's interaction, reset the counter.
});
$('.tag-link').on('click', function() {
$('#tags').val($(this).attr('tag'));
});
$('.argument-link').on('click', function() {
var field = $(this).attr('field');
var row = $('#' + field).find('.mf').last();
var key = row.find('input').first();
var value = row.find('input').last();
var type = row.find('select').last();
key.val($(this).attr('key'));
value.val($(this).attr('value'));
type.val($(this).attr('type'));
update_multifields();
});
$(document).on('click', 'form.auto-submit select, form.auto-submit input', function(){
$(this).parents('form').submit();
});
$('#filter').on('keyup', debounce(update_filter, 500));
$('#filter-regex-mode').on('change', update_filter_regex_mode);
$('#truncate').on('keyup', debounce(update_truncate, 500));
if (! user_administrator) {
$('.administrator-only').remove();
}
update_multifields();
}
function is_valid_regexp(value) {
try {
var _ = new RegExp(value, 'i');
return true;
} catch (e) {
return false;
}
}
function url_pagination_template_context(template, context, defaultPage, defaultPageSize){
var page_number_request = fmt_page_number_request(context, defaultPage);
var page_size = fmt_page_size_request(context, defaultPageSize);
var name_request = fmt_filter_name_request(context, "");
var use_regex = fmt_regex_request(context, "") == "checked";
if (use_regex) {
// rabbitmq/rabbitmq-server#8008: if the expression cannot be compiled to a reg exp,
// assume a regular text filter
var valid_regexp = is_valid_regexp(name_request);
if (!valid_regexp) {
show_popup('warn', fmt_escape_html(`Filter expression '${name_request}' is not a valid regular expression, will perform a regular text query`));
use_regex = false;
}
if (use_regex && valid_regexp) {
name_request = esc(name_request);
}
}
return '/' + template +
'?page=' + page_number_request +
'&page_size=' + page_size +
'&name=' + name_request +
'&use_regex=' + use_regex;
}
function url_pagination_template(template, defaultPage, defaultPageSize){
return url_pagination_template_context(template, template, defaultPage, defaultPageSize);
}
function stored_page_info(template, page_start){
var pageSize = fmt_strip_tags($('#' + template+'-pagesize').val());
var filterName = fmt_strip_tags($('#' + template+'-name').val());
store_pref(template + '_current_page_number', page_start);
if (filterName != null && filterName != undefined) {
store_pref(template + '_current_filter_name', filterName);
}
var regex_on = $("#" + template + "-filter-regex-mode").is(':checked');
if (regex_on != null && regex_on != undefined) {
store_pref(template + '_current_regex', regex_on ? "checked" : " " );
}
if (pageSize != null && pageSize != undefined) {
store_pref(template + '_current_page_size', pageSize);
}
}
function update_pages(template, page_start){
stored_page_info(template, page_start);
switch (template) {
case 'queues' : renderQueues(); break;
case 'exchanges' : renderExchanges(); break;
case 'connections' : renderConnections(); break;
case 'channels' : renderChannels(); break;
case 'users' : renderUsers(); break;
default:
renderCallback = RENDER_CALLBACKS[template];
if (renderCallback != undefined) {
renderCallback();
}
break;
}
}
function renderQueues() {
ensure_queues_chart_range();
render({'queues': {
path: url_pagination_template('queues', 1, 100),
options: {
sort: true,
vhost: true,
pagination: true
}
}, 'vhosts': '/vhosts'}, 'queues', '#/queues');
}
function renderUsers() {
render({'users': {path: url_pagination_template('users', 1, 100),
options: {
sort: true,
pagination: true
}
},
'permissions': '/permissions'}, 'users', '#/users');
}
function renderExchanges() {
render({'exchanges': {path: url_pagination_template('exchanges', 1, 100),
options: {sort:true, vhost:true, pagination:true}},
'vhosts': '/vhosts'}, 'exchanges', '#/exchanges');
}
function renderConnections() {
render({'connections': {path: url_pagination_template('connections', 1, 100),
options: {sort:true}}},
'connections', '#/connections');
}
function renderChannels() {
render({'channels': {path: url_pagination_template('channels', 1, 100),
options: {sort:true}}},
'channels', '#/channels');
}
function update_pages_from_ui(sender) {
var val = $(sender).val();
var raw = !!$(sender).attr('data-page-start') ? $(sender).attr('data-page-start') : val;
var s = fmt_escape_html(fmt_strip_tags(raw));
update_pages(current_template, s);
}
function postprocess_partial() {
$('.pagination_class_input').on('keypress', function(e) {
if (e.keyCode == 13) {
update_pages_from_ui(this);
}
});
$('.pagination_class_checkbox').on('click', function(e) {
update_pages_from_ui(this);
});
$('.pagination_class_select').on('change', function(e) {
update_pages_from_ui(this);
});
setup_visibility();
$('#main').off('click', 'div.section h2, div.section-hidden h2');
$('#main').on('click', 'div.section h2, div.section-hidden h2', function() {
toggle_visibility($(this));
});
$('.sort').on('click', function() {
var sort = $(this).attr('sort');
if (current_sort == sort) {
current_sort_reverse = ! current_sort_reverse;
}
else {
current_sort = sort;
current_sort_reverse = false;
}
update();
});
// TODO remove this hack when we get rid of "updatable"
if ($('#filter-warning-show').length > 0) {
$('#filter-truncate').addClass('filter-warning');
}
else {
$('#filter-truncate').removeClass('filter-warning');
}
}
function update_multifields() {
$('div.multifield').each(function(index) {
update_multifield($(this), true);
});
}
function update_multifield(multifield, dict) {
var largest_id = 0;
var empty_found = false;
var name = multifield.attr('id');
var type_inputs = $('#' + name + ' *[name$="_mftype"]');
type_inputs.each(function(index) {
var re = new RegExp(name + '_([0-9]*)_mftype');
var match = $(this).attr('name').match(re);
if (!match) return;
var id = parseInt(match[1]);
largest_id = Math.max(id, largest_id);
var prefix = name + '_' + id;
var type = $(this).val();
var input = $('#' + prefix + '_mfvalue');
if (type == 'list') {
if (input.length == 1) {
input.replaceWith('<div class="multifield-sub" id="' + prefix +
'"></div>');
}
update_multifield($('#' + prefix), false);
}
else {
if (input.length == 1) {
var key = dict ? $('#' + prefix + '_mfkey').val() : '';
var value = input.val();
if (key == '' && value == '') {
if (index == type_inputs.length - 1) {
empty_found = true;
}
else {
$(this).parents('.mf').first().remove();
}
}
}
else {
$('#' + prefix).replaceWith(multifield_input(prefix, 'value',
'text'));
}
}
});
if (!empty_found) {
var prefix = name + '_' + (largest_id + 1);
var t = multifield.hasClass('string-only') ? 'hidden' : 'select';
var val_type = multifield_input(prefix, 'value', 'text') + ' ' +
multifield_input(prefix, 'type', t);
if (dict) {
multifield.append('<table class="mf"><tr><td>' +
multifield_input(prefix, 'key', 'text') +
'</td><td class="equals"> = </td><td>' +
val_type + '</td></tr></table>');
}
else {
multifield.append('<div class="mf">' + val_type + '</div>');
}
}
}
function multifield_input(prefix, suffix, type) {
if (type == 'hidden' ) {
return '<input type="hidden" id="' + prefix + '_mf' + suffix +
'" name="' + prefix + '_mf' + suffix + '" value="string"/>';
}
else if (type == 'text' ) {
return '<input type="text" id="' + prefix + '_mf' + suffix +
'" name="' + prefix + '_mf' + suffix + '" value=""/>';
}
else if (type == 'select' ) {
return '<select id="' + prefix + '_mf' + suffix + '" name="' + prefix +
'_mf' + suffix + '">' +
'<option value="string">String</option>' +
'<option value="number">Number</option>' +
'<option value="boolean">Boolean</option>' +
'<option value="list">List</option>' +
'</select>';
}
}
function update_filter_regex(jElem) {
current_filter_regex = null;
jElem.parents('.filter').children('.status-error').remove();
if (current_filter_regex_on && $.trim(current_filter).length > 0) {
try {
current_filter_regex = new RegExp(current_filter,'i');
} catch (e) {
jElem.parents('.filter').append('<p class="status-error">' +
fmt_escape_html(e.message) + '</p>');
}
}
}
function update_filter_regex_mode() {
current_filter_regex_on = $(this).is(':checked');
update_filter_regex($(this));
partial_update();
}
function update_filter() {
current_filter = $(this).val();
var table = $(this).parents('table').first();
table.removeClass('filter-active');
if ($(this).val() != '') {
table.addClass('filter-active');
}
update_filter_regex($(this));
partial_update();
}
function update_truncate() {
var current_truncate_str =
$(this).val().replace(new RegExp('\\D', 'g'), '');
if (current_truncate_str == '') {
current_truncate_str = '0';
}
if ($(this).val() != current_truncate_str) {
$(this).val(current_truncate_str);
}
var current_truncate = parseInt(current_truncate_str, 10);
store_pref('truncate', current_truncate);
partial_update();
}
function setup_visibility() {
$('div.section,div.section-hidden').each(function(_index) {
var pref = section_pref(current_template,
$(this).children('h2').text());
var show = get_pref(pref);
if (show == null) {
show = $(this).hasClass('section');
}
else {
show = show == 't';
}
if (show) {
$(this).addClass('section-visible');
// Workaround for... something. Although div.hider is
// display:block anyway, not explicitly setting this
// prevents the first slideToggle() from animating
// successfully; instead the element just vanishes.
$(this).find('.hider').attr('style', 'display:block;');
}
else {
$(this).addClass('section-invisible');
}
});
}
function toggle_visibility(item) {
var hider = item.next();
var all = item.parent();
var pref = section_pref(current_template, item.text());
item.next().slideToggle(100);
if (all.hasClass('section-visible')) {
if (all.hasClass('section'))
store_pref(pref, 'f');
else
clear_pref(pref);
all.removeClass('section-visible');
all.addClass('section-invisible');
}
else {
if (all.hasClass('section-hidden')) {
store_pref(pref, 't');
} else {
clear_pref(pref);
}
all.removeClass('section-invisible');
all.addClass('section-visible');
}
}
function publish_msg(params0) {
try {
var params = params_magic(params0);
publish_msg0(params);
} catch (e) {
show_popup('warn', fmt_escape_html(e));
return false;
}
}
function publish_msg0(params) {
var path = fill_path_template('/exchanges/:vhost/:name/publish', params);
params['properties'] = {};
params['properties']['delivery_mode'] = parseInt(params['delivery_mode']);
if (params['headers'] != '')
params['properties']['headers'] = params['headers'];
var props = [['content_type', 'str'],
['content_encoding', 'str'],
['correlation_id', 'str'],
['reply_to', 'str'],
['expiration', 'str'],
['message_id', 'str'],
['type', 'str'],
['user_id', 'str'],
['app_id', 'str'],
['cluster_id', 'str'],
['priority', 'int'],
['timestamp', 'int']];
for (var i in props) {
var name = props[i][0];
var type = props[i][1];
if (params['props'][name] != undefined && params['props'][name] != '') {
var value = params['props'][name];
if (type == 'int') value = parseInt(value);
params['properties'][name] = value;
}
}
with_req('POST', path, JSON.stringify(params), function(resp) {
var result = JSON.parse(resp.responseText);
if (result.routed) {
show_popup('info', 'Message published.');
} else {
show_popup('warn', 'Message published, but not routed.');
}
});
}
function get_msgs(params) {
var path = fill_path_template('/queues/:vhost/:name/get', params);
with_req('POST', path, JSON.stringify(params), function(resp) {
var msgs = JSON.parse(resp.responseText);
if (msgs.length == 0) {
show_popup('info', 'Queue is empty');
} else {
$('#msg-wrapper').slideUp(200);
replace_content('msg-wrapper', format('messages', {'msgs': msgs}));
$('#msg-wrapper').slideDown(200);
}
});
}
function with_reqs(reqs, acc, fun) {
if (keys(reqs).length > 0) {
var key = keys(reqs)[0];
with_req('GET', reqs[key], null, function(resp) {
if (key.startsWith("extra_")) {
var extraContent = acc["extra_content"];
extraContent[key] = JSON.parse(resp.responseText);
acc["extra_content"] = extraContent;
} else {
acc[key] = JSON.parse(resp.responseText);
}
var remainder = {};
for (var k in reqs) {
if (k != key) remainder[k] = reqs[k];
}
with_reqs(remainder, acc, fun);
});
}
else {
fun(acc);
}
}
function replace_content(id, html) {
$("#" + id).html(html);
}
var ejs_cached = {};
function format(template, json) {
try {
var cache = true;
if (!(template in ejs_cached)) {
ejs_cached[template] = true;
cache = false;
}
var tmpl = new EJS({url: 'js/tmpl/' + template + '.ejs', cache: cache});
return tmpl.render(json);
} catch (err) {
clearInterval(timer);
console.log("Uncaught error: " + err);
console.log("Stack: " + err['stack']);
debug(err['name'] + ": " + err['message'] + "\n" + err['stack'] + "\n");
}
}
function maybe_format_extra_queue_content(queue, extraContent) {
var content = '';
for (var i = 0; i < QUEUE_EXTRA_CONTENT.length; i++) {
content += QUEUE_EXTRA_CONTENT[i](queue, extraContent);
}
return content;
}
function update_status(status) {
var text;
if (status == 'ok')
text = "Refreshed " + fmt_date(new Date());
else if (status == 'error') {
var next_try = new Date(new Date().getTime() + timer_interval);
text = "Error: could not connect to server since " +
fmt_date(last_successful_connect) + ". Will retry at " +
fmt_date(next_try) + ".";
}
else
throw("Unknown status " + status);
var html = format('status', {status: status, text: text});
replace_content('status', html);
}
function with_req(method, path, body, fun) {
if(!has_auth_credentials()) {
// navigate to the login form
location.reload();
return;
}
var json;
var req = xmlHttpRequest();
req.open(method, 'api' + path, true );
var header = authorization_header();
if (header !== null) {
req.setRequestHeader('authorization', header);
}
req.setRequestHeader('x-vhost', current_vhost);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var ix = jQuery.inArray(req, outstanding_reqs);
if (ix != -1) {
outstanding_reqs.splice(ix, 1);
}
if (check_bad_response(req, true)) {
last_successful_connect = new Date();
fun(req);
}
}
};
outstanding_reqs.push(req);
req.send(body);
}
function get(url, accept, callback) {
var req = new XMLHttpRequest();
req.open("GET", url);
req.setRequestHeader("Accept", accept);
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
callback(req);
}
};
req.send();
}
function sync_get(path) {
return sync_req('GET', [], path);
}
function sync_put(sammy, path_template) {
return sync_req('PUT', sammy.params, path_template);
}
function sync_delete(sammy, path_template, options) {
return sync_req('DELETE', sammy.params, path_template, options);
}
function sync_post(sammy, path_template) {
return sync_req('POST', sammy.params, path_template);
}
function sync_req(type, params0, path_template, options) {
var params;
var path;
try {
params = params_magic(params0);
path = fill_path_template(path_template, params);
} catch (e) {
show_popup('warn', fmt_escape_html(e));
return false;
}
var req = xmlHttpRequest();
req.open(type, 'api' + path, false);
req.setRequestHeader('content-type', 'application/json');
req.setRequestHeader('authorization', authorization_header());
if (options != undefined || options != null) {
if (options.headers != undefined || options.headers != null) {
jQuery.each(options.headers, function (k, v) {
req.setRequestHeader(k, v);
});
}
}
try {
if (type == 'GET')
req.send(null);
else
req.send(JSON.stringify(params));
}
catch (e) {
if (e.number == 0x80004004) {
// 0x80004004 means "Operation aborted."
// https://support.microsoft.com/kb/186063
// MSIE6 appears to do this in response to HTTP 204.
}
}
if (check_bad_response(req, false)) {
if (type == 'GET')
return req.responseText;
else
// rabbitmq/rabbitmq-management#732
// https://developer.mozilla.org/en-US/docs/Glossary/Truthy
return {result: true, http_status: req.status, req_params: params, responseText: req.responseText};
}
else {
return false;
}
}
function initiate_logout(oauth, error = "") {
clear_pref('auth');
clear_cookie_value('auth');
renderWarningMessageInLoginStatus(oauth, error);
}
function check_bad_response(req, full_page_404) {
// 1223 == 204 - see https://www.enhanceie.com/ie/bugs.asp
// MSIE7 and 8 appear to do this in response to HTTP 204.
if ((req.status >= 200 && req.status < 300) || req.status == 1223) {
return true;
}
else if (req.status == 404 && full_page_404) {
var html = format('404', {});
replace_content('main', html);
}
else if (req.status >= 400 && req.status <= 404) {
var reason = JSON.parse(req.responseText).reason;
if (typeof(reason) != 'string') reason = JSON.stringify(reason);
var error = JSON.parse(req.responseText).error;
if (typeof(error) != 'string') error = JSON.stringify(error);
if (error == 'bad_request' || error == 'not_found' || error == 'not_authorised' || error == 'not_authorized') {
if ((req.status == 401 || req.status == 403) && oauth.enabled) {
initiate_logout(oauth, reason);
} else {
show_popup('warn', fmt_escape_html(reason));
}
} else if (error == 'page_out_of_range') {
var seconds = 60;
if (last_page_out_of_range_error > 0)
seconds = (new Date().getTime() - last_page_out_of_range_error.getTime())/1000;
if (seconds > 3) {
Sammy.log('server reports page is out of range, redirecting to page 1');
var contexts = ["queues", "exchanges", "connections", "channels"];
var matches = /api\/(.*)\?/.exec(req.responseURL);
if (matches != null && matches.length > 1) {
contexts.forEach(function(item) {
if (matches[1].indexOf(item) == 0) {update_pages(item, 1)};
});
} else update_pages(current_template, 1);
last_page_out_of_range_error = new Date();
}
}
}
else if (req.status == 408) {
update_status('timeout');
}
else if (req.status == 0) { // Non-MSIE: could not connect
update_status('error');
}
else if (req.status > 12000) { // MSIE: could not connect
update_status('error');
}
else if (req.status == 503) { // Proxy: could not connect
update_status('error');
}
else {
debug("Management API returned status code " + req.status + " - <strong>" + fmt_escape_html_one_line(req.responseText) + "</strong>");
clearInterval(timer);
}
return false;
}
function fill_path_template(template, params) {
var re = /:[a-zA-Z_]*/g;
return template.replace(re, function(m) {
var str = esc(params[m.substring(1)]);
if (str == '') {
throw(m.substring(1) + " is required");
}
return str;
});
}
function params_magic(params) {
return check_password(maybe_remove_fields(collapse_multifields(params)));
}
function collapse_multifields(params0) {
function set(x) { return x != '' && x != undefined }
var params = {};
var ks = keys(params0);
var ids = [];
for (i in ks) {
var key = ks[i];
var match = key.match(/([a-z]*)_([0-9_]*)_mftype/);
var match2 = key.match(/[a-z]*_[0-9_]*_mfkey/);
var match3 = key.match(/[a-z]*_[0-9_]*_mfvalue/);
if (match == null && match2 == null && match3 == null) {
params[key] = params0[key];
}
else if (match == null) {
// Do nothing, value is handled below
}
else {
var name = match[1];
var id = match[2];
ids.push([name, id]);
}
}
ids.sort();
var id_map = {};
for (i in ids) {
var name = ids[i][0];
var id = ids[i][1];
if (params[name] == undefined) {
params[name] = {};
id_map[name] = {};
}
var id_parts = id.split('_');
var k = params0[name + '_' + id_parts[0] + '_mfkey'];
var v = params0[name + '_' + id + '_mfvalue'];
var t = params0[name + '_' + id + '_mftype'];
var val = null;
var top_level = id_parts.length == 1;
if (t == 'list') {
val = [];
id_map[name][id] = val;
}
else if ((set(k) && top_level) || set(v)) {
if (t == 'boolean') {
if (v != 'true' && v != 'false')
throw(k + ' must be "true" or "false"; got ' + v);
val = (v == 'true');
}
else if (t == 'number') {
var n = parseFloat(v);
if (isNaN(n))
throw(k + ' must be a number; got ' + v);
val = n;
}
else {
val = v;
}
}
if (val != null) {
if (top_level) {
params[name][k] = val;
}
else {
var prefix = id_parts.slice(0, id_parts.length - 1).join('_');
id_map[name][prefix].push(val);
}
}
}
if (params.hasOwnProperty('queuetype')) {
delete params['queuetype'];
if (queue_type != 'default') {
params['arguments']['x-queue-type'] = queue_type;
}
if (queue_type == 'quorum' ||
queue_type == 'stream') {
params['durable'] = true;
params['auto_delete'] = false;
}
}
return params;
}
function check_password(params) {
if (params['password'] != undefined) {
if (params['password'] == '') {
throw("Please specify a password.");
}
if (params['password'] != params['password_confirm']) {
throw("Passwords do not match.");
}
delete params['password_confirm'];
}
return params;
}
function maybe_remove_fields(params) {
$('.controls-appearance').each(function(index) {
var options = $(this).get(0).options;
var selected = $(this).val();
for (i = 0; i < options.length; i++) {
var option = options[i].value;
if (option != selected) {
delete params[option];
}
}
delete params[$(this).attr('name')];
});
return params;
}
function put_parameter(sammy, mandatory_keys, num_keys, bool_keys,
arrayable_keys) {
for (var i in sammy.params) {
if (i === 'length' || !sammy.params.hasOwnProperty(i)) continue;
if (sammy.params[i] == '' && jQuery.inArray(i, mandatory_keys) == -1) {
delete sammy.params[i];
}
else if (jQuery.inArray(i, num_keys) != -1) {
sammy.params[i] = parseInt(sammy.params[i]);
}
else if (jQuery.inArray(i, bool_keys) != -1) {
sammy.params[i] = sammy.params[i] == 'true';
}
else if (jQuery.inArray(i, arrayable_keys) != -1) {
sammy.params[i] = sammy.params[i].split(' ');
if (sammy.params[i].length == 1) {
sammy.params[i] = sammy.params[i][0];
}
}
}
var params = {"component": sammy.params.component,
"vhost": sammy.params.vhost,
"name": sammy.params.name,
"value": params_magic(sammy.params)};
delete params.value.vhost;
delete params.value.component;
delete params.value.name;
sammy.params = params;
if (sync_put(sammy, '/parameters/:component/:vhost/:name')) update();
}
function put_cast_params(sammy, path, mandatory_keys, num_keys, bool_keys) {
for (var i in sammy.params) {
if (i === 'length' || !sammy.params.hasOwnProperty(i)) continue;
if (sammy.params[i] == '' && jQuery.inArray(i, mandatory_keys) == -1) {
delete sammy.params[i];
}
else if (jQuery.inArray(i, num_keys) != -1) {
sammy.params[i] = parseInt(sammy.params[i]);
}
else if (jQuery.inArray(i, bool_keys) != -1) {
sammy.params[i] = sammy.params[i] == 'true';
}
}
if (sync_put(sammy, path)) update();
}
function update_column_options(sammy) {
var mode = sammy.params['mode'];
for (var group in COLUMNS[mode]) {
var options = COLUMNS[mode][group];
for (var i = 0; i < options.length; i++) {
var key = options[i][0];
var value = sammy.params[mode + '-' + key] != undefined;
store_pref('column-' + mode + '-' + key, value);
}
}
partial_update();
}
function debug(str) {
$('<p>' + str + '</p>').appendTo('#debug');
}
function keys(obj) {
var ks = [];
for (var k in obj) {
ks.push(k);
}
return ks;
}
// Don't use the jQuery AJAX support, it seems to have trouble reporting
// server-down type errors.
function xmlHttpRequest() {
var res;
try {
res = new XMLHttpRequest();
}
catch(e) {
res = new ActiveXObject("Microsoft.XMLHttp");
}
return res;
}
(function($){
$.fn.extend({
center: function () {
return this.each(function() {
var top = ($(window).height() - $(this).outerHeight()) / 2;
var left = ($(window).width() - $(this).outerWidth()) / 2;
$(this).css({margin:0, top: (top > 0 ? top : 0)+'px', left: (left > 0 ? left : 0)+'px'});
});
}
});
})(jQuery);
function debounce(f, delay) {
var timeout = null;
return function() {
var obj = this;
var args = arguments;
function delayed () {
f.apply(obj, args);
timeout = null;
}
if (timeout) clearTimeout(timeout);
timeout = setTimeout(delayed, delay);
}
}
function rename_multifield(params, from, to) {
var new_params = {};
for(var key in params){
var match = key.match("^" + from + "_[0-9_]*_mftype$");
var match2 = key.match("^" + from + "_[0-9_]*_mfkey$");
var match3 = key.match("^" + from + "_[0-9_]*_mfvalue$");
if (match != null) {
new_params[match[0].replace(from, to)] = params[match];
}
else if (match2 != null) {
new_params[match2[0].replace(from, to)] = params[match2];
}
else if (match3 != null) {
new_params[match3[0].replace(from, to)] = params[match3];
}
else {
new_params[key] = params[key]
}
}
return new_params;
}
function select_queue_type(queuetype) {
queue_type = queuetype.value;
update();
}
function is_quorum(queue) {
if (queue["arguments"]) {
if (queue["arguments"]["x-queue-type"]) {
return queue["arguments"]["x-queue-type"] === "quorum";
} else {
return false;
}
} else {
return false;
}
}
function is_stream(queue) {
if (queue["arguments"]) {
if (queue["arguments"]["x-queue-type"]) {
return queue["arguments"]["x-queue-type"] === "stream";
} else {
return false;
}
} else {
return false;
}
}
function is_classic(queue) {
if (queue["arguments"]) {
if (queue["arguments"]["x-queue-type"]) {
return queue["arguments"]["x-queue-type"] === "classic";
} else {
return true;
}
} else {
return true;
}
}
function ensure_queues_chart_range() {
var range = get_pref('chart-range');
// Note: the queues page uses the 'basic' range type
var fixup_range;
var valid_range = false;
var range_type = get_chart_range_type('queues');
var chart_periods = CHART_RANGES[range_type];
for (var i = 0; i < chart_periods.length; ++i) {
var data = chart_periods[i];
var val = data[0];
if (range === val) {
valid_range = true;
break;
}
// If the range needs to be adjusted, use the last
// valid one
fixup_range = val;
}
if (!valid_range) {
store_pref('chart-range', fixup_range);
}
}
function get_chart_range_type(arg) {
/*
* 'arg' can be:
* lengths-over for the Overview page
* lengths-q for the per-queue page
* queues for setting up the queues range
*/
if (arg === 'lengths-over') {
return 'global';
}
if (arg === 'msg-rates-over') {
return 'global';
}
if (arg === 'lengths-q') {
return 'basic';
}
if (arg === 'msg-rates-q') {
return 'basic';
}
if (arg === 'queues') {
return 'basic';
}
if (arg === 'queue-churn') {
return 'basic';
}
if (arg === 'channel-churn') {
return 'basic';
}
if (arg === 'connection-churn') {
return 'basic';
}
console.log('[WARNING]: range type not found for arg: ' + arg);
return 'basic';
}