UNPKG

bull-arena

Version:

An interactive UI dashboard for Bee Queue

550 lines (500 loc) 17.5 kB
$(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; if (!job) { return {}; } const text = `${job.name} <a href="${basePath}/${encodeURIComponent( flowHost )}/${encodeURIComponent(job.queueName)}/${ job.id }"><span class="label label-default">${job.id}</span></a>`; 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 individual "remove repeatable job" handler $('.js-remove-repeatable-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 confirmationResponse = window.confirm( `Remove repeatable job #${jobId} in queue "${queueHost}/${queueName}"?` ); if (confirmationResponse) { $.ajax({ method: 'DELETE', url: `${basePath}/api/queue/${encodeURIComponent( queueHost )}/${encodeURIComponent(queueName)}/repeatable/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, }; if (action !== 'clean') { $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 count = action === 'clean' ? 1000 : data.jobs.length; const r = window.confirm( `${capitalize(action)} ${count} ${ count > 1 ? 'jobs' : 'job' } in queue "${queueHost}/${queueName}"?` ); if (r) { if (action === 'clean') { $.ajax({ method: 'DELETE', url: `${basePath}/api/queue/${encodeURIComponent( queueHost )}/${encodeURIComponent(queueName)}/jobs/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 { $.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, opts} = JSON.parse(job); window.jsonEditor.set(data); if (window.jsonEditorOpts) window.jsonEditorOpts.set(opts); $('input.js-add-job-name').val(name); } else { window.jsonEditor.set({id: ''}); } }); $('.js-toggle-update-queue-meta').on('click', function () { const updateMetaText = $('.js-toggle-update-queue-meta').text(); const shouldNotHide = updateMetaText === 'Update'; const newUpdateMetaText = shouldNotHide ? 'Cancel' : 'Update'; $('.meta-config-editor').toggleClass('hide', !shouldNotHide); $('.js-toggle-update-queue-meta').text(newUpdateMetaText); }); $('.js-update-queue-meta').on('click', function () { const {queueHost, queueName} = window.arenaInitialPayload; const concurrency = $('input.js-update-meta-concurrency').val() || null; const max = $('input.js-update-meta-rl-max').val() || null; const duration = $('input.js-update-meta-rl-duration').val() || null; const stringifiedData = JSON.stringify({ concurrency: concurrency ? parseInt(concurrency, 10) : null, max: max ? parseInt(max, 10) : null, duration: duration ? parseInt(duration, 10) : null, }); const response = window.confirm( `Are you sure you want to update the queue "${queueHost}/${queueName}" configuration?` ); if (response) { $.ajax({ url: `${basePath}/api/queue/${encodeURIComponent( queueHost )}/${encodeURIComponent(queueName)}/update-meta`, type: 'PUT', data: stringifiedData, contentType: 'application/json', }) .done(() => { window.location.reload(); }) .fail((jqXHR) => { window.alert(`Request failed, check console for error.`); console.error(jqXHR.responseText); }); } }); $('.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 opts = window.jsonEditorOpts ? window.jsonEditorOpts.get() : {}; const job = JSON.stringify({name, data, opts}); 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-update-job-data').on('click', function (e) { e.preventDefault(); const jobId = $(this).data('job-id'); const queueName = $(this).data('queue-name'); const queueHost = $(this).data('queue-host'); const stringifiedData = JSON.stringify(window.jsonEditor.get()); const r = window.confirm( `Update job #${jobId} data in queue "${queueHost}/${queueName}"?` ); if (r) { $.ajax({ url: `${basePath}/api/queue/${encodeURIComponent( queueHost )}/${encodeURIComponent(queueName)}/job/${encodeURIComponent( jobId )}/data`, type: 'PUT', data: stringifiedData, contentType: 'application/json', }) .done(() => { alert('Job data successfully updated!'); window.location.reload(); }) .fail((jqXHR) => { window.alert('Failed to update job data, 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); } }); $('.js-remove-rate-limit-key').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 remove the Rate Limit key for the queue "${queueHost}/${queueName}"?` ); if (response) { $.ajax({ method: 'DELETE', url: `${basePath}/api/queue/${encodeURIComponent( queueHost )}/${encodeURIComponent(queueName)}/rate-limit-key`, }) .done(() => { window.location.reload(); }) .fail((jqXHR) => { window.alert(`Request failed, check console for error.`); console.error(jqXHR.responseText); }); } else { $(this).prop('disabled', false); } }); });