@xtcry/bull-arena
Version:
An interactive UI dashboard for Bee/Bull Queue
390 lines (351 loc) • 12.2 kB
JavaScript
$(document).ready(() => {
const basePath = $('#basePath').val();
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function formatToTreeView(flow, flowHost) {
const { job, children } = flow;
const text = `${job.name} <a href="${basePath}/${encodeURIComponent(
flowHost
)}/${encodeURIComponent(job.queueName)}/${
job.id
}"><span class="label label-default">${job.id}</span></a>`;
const href = `${basePath}/${encodeURIComponent(
flowHost
)}/${encodeURIComponent(job.queueName)}/${job.id}`;
if (children && children.length > 0) {
return {
text,
nodes: children.map((child) => formatToTreeView(child, flowHost)),
};
} else {
return {
text,
};
}
}
// Set up individual "retry job" handler
$('.js-retry-job').on('click', function (e) {
e.preventDefault();
$(this).prop('disabled', true);
const jobId = $(this).data('job-id');
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const r = window.confirm(
`Retry job #${jobId} in queue "${queueHost}/${queueName}"?`
);
if (r) {
$.ajax({
method: 'PATCH',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/job/${encodeURIComponent(jobId)}`,
})
.done(() => {
window.location.reload();
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
// Set up individual "promote job" handler
$('.js-promote-job').on('click', function (e) {
e.preventDefault();
$(this).prop('disabled', true);
const jobId = $(this).data('job-id');
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const r = window.confirm(
`Promote job #${jobId} in queue "${queueHost}/${queueName}"?`
);
if (r) {
$.ajax({
method: 'PATCH',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/delayed/job/${encodeURIComponent(
jobId
)}`,
})
.done(() => {
window.location.reload();
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
// Set up individual "remove job" handler
$('.js-remove-job').on('click', function (e) {
e.preventDefault();
$(this).prop('disabled', true);
const jobId = $(this).data('job-id');
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const jobState = $(this).data('job-state');
const r = window.confirm(
`Remove job #${jobId} in queue "${queueHost}/${queueName}"?`
);
if (r) {
$.ajax({
method: 'DELETE',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/job/${encodeURIComponent(jobId)}`,
})
.done(() => {
window.location.href = `${basePath}/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/${jobState}`;
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
// Set up "select all jobs" button handler
$('.js-select-all-jobs').change(function () {
const $jobBulkCheckboxes = $('.js-bulk-job');
$jobBulkCheckboxes.prop('checked', this.checked);
});
// Set up "shift-click" multiple checkbox selection handler
(function () {
// https://stackoverflow.com/questions/659508/how-can-i-shift-select-multiple-checkboxes-like-gmail
let lastChecked = null;
let $jobBulkCheckboxes = $('.js-bulk-job');
$jobBulkCheckboxes.click(function (e) {
if (!lastChecked) {
lastChecked = this;
return;
}
if (e.shiftKey) {
let start = $jobBulkCheckboxes.index(this);
let end = $jobBulkCheckboxes.index(lastChecked);
$jobBulkCheckboxes
.slice(Math.min(start, end), Math.max(start, end) + 1)
.prop('checked', lastChecked.checked);
}
lastChecked = this;
});
})();
// Set up bulk actions handler
$('.js-bulk-action').on('click', function (e) {
$(this).prop('disabled', true);
const $bulkActionContainer = $('.js-bulk-action-container');
const action = $(this).data('action');
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const queueState = $(this).data('queue-state');
let data = {
queueName,
action,
jobs: [],
queueState,
};
$bulkActionContainer.each((index, value) => {
const isChecked = $(value).find('[name=jobChecked]').is(':checked');
const id = encodeURIComponent($(value).find('[name=jobId]').val());
if (isChecked) {
data.jobs.push(id);
}
});
const r = window.confirm(
`${capitalize(action)} ${data.jobs.length} ${
data.jobs.length > 1 ? 'jobs' : 'job'
} in queue "${queueHost}/${queueName}"?`
);
if (r) {
$.ajax({
method: action === 'remove' ? 'POST' : 'PATCH',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/${
action === 'promote' ? 'delayed/' : ''
}job/bulk`,
data: JSON.stringify(data),
contentType: 'application/json',
})
.done(() => {
window.location.reload();
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
$('.js-toggle-add-job-editor').on('click', function () {
const addJobText = $('.js-toggle-add-job-editor').text();
const shouldNotHide = addJobText === 'Add Job';
const newAddJobText = shouldNotHide ? 'Cancel' : 'Add Job';
$('.jsoneditorx').toggleClass('hide', !shouldNotHide);
$('.js-toggle-add-job-editor').text(newAddJobText);
const job = localStorage.getItem('arena:savedJob');
if (job) {
const { name, data } = JSON.parse(job);
window.jsonEditor.set(data);
$('input.js-add-job-name').val(name);
} else {
window.jsonEditor.set({ id: '' });
}
});
$('.js-toggle-add-flow-editor').on('click', function () {
const addFlowText = $('.js-toggle-add-flow-editor').text();
const shouldNotHide = addFlowText === 'Add Flow';
const newAddFlowText = shouldNotHide ? 'Cancel Add' : 'Add Flow';
$('.jsoneditorx').toggleClass('hide', !shouldNotHide);
$('.js-toggle-add-flow-editor').text(newAddFlowText);
const flow = localStorage.getItem('arena:savedFlow');
if (flow) {
const { data } = JSON.parse(flow);
window.jsonEditor.set(data);
} else {
window.jsonEditor.set({});
}
});
$('.js-toggle-search-flow').on('click', function () {
const searchFlowText = $('.js-toggle-search-flow').text();
const shouldNotHide = searchFlowText === 'Search Flow';
const newSearchFlowText = shouldNotHide ? 'Cancel Search' : 'Search Flow';
$('.searchflowx').toggleClass('hide', !shouldNotHide);
$('.js-toggle-search-flow').text(newSearchFlowText);
});
$('.js-add-job').on('click', function () {
const name = $('input.js-add-job-name').val() || null;
const data = window.jsonEditor.get();
const job = JSON.stringify({ name, data });
localStorage.setItem('arena:savedJob', job);
const { queueHost, queueName } = window.arenaInitialPayload;
$.ajax({
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/job`,
type: 'POST',
data: job,
contentType: 'application/json',
})
.done(() => {
alert('Job successfully added!');
localStorage.removeItem('arena:savedJob');
})
.fail((jqXHR) => {
window.alert('Failed to save job, check console for error.');
console.error(jqXHR.responseText);
});
});
$('.js-add-flow').on('click', function () {
const data = window.jsonEditor.get();
const flow = JSON.stringify({ data });
localStorage.setItem('arena:savedFlow', flow);
const { flowHost, connectionName } = window.arenaInitialPayload;
$.ajax({
url: `${basePath}/api/flow/${encodeURIComponent(
flowHost
)}/${encodeURIComponent(connectionName)}/flow`,
type: 'POST',
data: flow,
contentType: 'application/json',
})
.done((res) => {
const flowTree = formatToTreeView(res, flowHost);
alert('Flow successfully added!');
localStorage.removeItem('arena:savedFlow');
$('#tree').treeview({ data: [flowTree], enableLinks: true });
$('.js-tree').toggleClass('hide', false);
})
.fail((jqXHR) => {
window.alert('Failed to save flow, check console for error.');
console.error(jqXHR.responseText);
});
});
$('.js-search-flow').on('click', function (e) {
e.preventDefault();
const queueName = $('#queue-name-input-search').val();
const jobId = $('#job-id-input-search').val();
const depth = $('#depth-input-search').val();
const maxChildren = $('#max-children-input-search').val();
const { flowHost, connectionName } = window.arenaInitialPayload;
$.ajax({
url: `${basePath}/api/flow/${encodeURIComponent(
flowHost
)}/${encodeURIComponent(
connectionName
)}/flow?jobId=${jobId}&queueName=${queueName}&depth=${depth}&maxChildren=${maxChildren}`,
type: 'GET',
contentType: 'application/json',
})
.done((res) => {
const flowTree = formatToTreeView(res, flowHost);
alert('Flow info successfully fetched!');
$('#tree').treeview({ data: [flowTree], enableLinks: true });
$('.js-tree').toggleClass('hide', false);
})
.fail((jqXHR) => {
window.alert('Failed to get flow info, check console for error.');
console.error(jqXHR.responseText);
});
});
$('.js-pause-queue').on('click', function (e) {
e.preventDefault();
$(this).prop('disabled', true);
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const response = window.confirm(
`Are you sure you want to pause the queue "${queueHost}/${queueName}"?`
);
if (response) {
$.ajax({
method: 'PUT',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/pause`,
})
.done(() => {
window.location.reload();
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
$('.js-resume-queue').on('click', function (e) {
e.preventDefault();
const queueName = $(this).data('queue-name');
const queueHost = $(this).data('queue-host');
const response = window.confirm(
`Are you sure you want to resume the queue "${queueHost}/${queueName}"?`
);
if (response) {
$.ajax({
method: 'PUT',
url: `${basePath}/api/queue/${encodeURIComponent(
queueHost
)}/${encodeURIComponent(queueName)}/resume`,
})
.done(() => {
window.location.reload();
})
.fail((jqXHR) => {
window.alert(`Request failed, check console for error.`);
console.error(jqXHR.responseText);
});
} else {
$(this).prop('disabled', false);
}
});
});