///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2020, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// define('misc.bgprocess', [ 'sources/pgadmin', 'sources/gettext', 'sources/url_for', 'underscore', 'jquery', 'pgadmin.browser', 'alertify', ], function( pgAdmin, gettext, url_for, _, $, pgBrowser, Alertify ) { pgBrowser.BackgroundProcessObsorver = pgBrowser.BackgroundProcessObsorver || {}; if (pgBrowser.BackgroundProcessObsorver.initialized) { return pgBrowser.BackgroundProcessObsorver; } var wcDocker = window.wcDocker; var BGProcess = function(info, notify) { var self = this; setTimeout( function() { self.initialize.apply(self, [info, notify]); }, 1 ); }; _.extend( BGProcess.prototype, { success_status_tpl: _.template(`
<%-status_text%>
`), failed_status_tpl: _.template(`
<%-status_text%>
`), other_status_tpl: _.template(`
<%-status_text%>
`), initialize: function(info, notify) { _.extend(this, { details: false, notify: (_.isUndefined(notify) || notify), curr_status: null, state: 0, // 0: NOT Started, 1: Started, 2: Finished, 3: Terminated completed: false, id: info['id'], type_desc: null, desc: null, detailed_desc: null, stime: null, exit_code: null, acknowledge: info['acknowledge'], execution_time: null, out: -1, err: -1, lot_more: false, notifier: null, container: null, panel: null, logs: $('
    ', { class: 'pg-bg-process-logs', }), }); if (this.notify) { pgBrowser.Events && pgBrowser.Events.on( 'pgadmin-bgprocess:started:' + this.id, function(process) { if (!process.notifier) process.show.apply(process); } ); pgBrowser.Events && pgBrowser.Events.on( 'pgadmin-bgprocess:finished:' + this.id, function(process) { if (!process.notifier) process.show.apply(process); } ); } var self = this; setTimeout( function() { self.update.apply(self, [info]); }, 1 ); }, bgprocess_url: function(type) { switch (type) { case 'status': if (this.details && this.out != -1 && this.err != -1) { return url_for( 'bgprocess.detailed_status', { 'pid': this.id, 'out': this.out, 'err': this.err, } ); } return url_for('bgprocess.status', { 'pid': this.id, }); case 'acknowledge': return url_for('bgprocess.acknowledge', { 'pid': this.id, }); case 'stop_process': return url_for('bgprocess.stop_process', { 'pid': this.id, }); default: return url_for('bgprocess.list'); } }, update: function(data) { var self = this, out = [], err = []; if ('stime' in data) self.stime = new Date(data.stime); if ('execution_time' in data) self.execution_time = parseFloat(data.execution_time); if ('type_desc' in data) self.type_desc = data.type_desc; if ('desc' in data) self.desc = data.desc; if ('details' in data) self.detailed_desc = data.details; if ('exit_code' in data) self.exit_code = data.exit_code; if ('process_state' in data) self.state = data.process_state; if ('out' in data) { self.out = data.out && data.out.pos; if (data.out && data.out.lines) { out = data.out.lines; } } if ('err' in data) { self.err = data.err && data.err.pos; if (data.err && data.err.lines) { err = data.err.lines; } } self.completed = self.completed || ( 'err' in data && 'out' in data && data.err.done && data.out.done ) || (!self.details && !_.isNull(self.exit_code)); var io = 0, ie = 0, res = [], escapeEl = document.createElement('textarea'), escapeHTML = function(html) { escapeEl.textContent = html; return escapeEl.innerHTML; }; while (io < out.length && ie < err.length) { if (pgAdmin.natural_sort(out[io][0], err[ie][0]) <= 0) { res.push('
  1. ' + escapeHTML(out[io++][1]) + '
  2. '); } else { res.push('
  3. ' + escapeHTML(err[ie++][1]) + '
  4. '); } } while (io < out.length) { res.push('
  5. ' + escapeHTML(out[io++][1]) + '
  6. '); } while (ie < err.length) { res.push('
  7. ' + escapeHTML(err[ie++][1]) + '
  8. '); } if (res.length) { self.logs.append(res.join('')); setTimeout(function() { self.logs[0].scrollTop = self.logs[0].scrollHeight; }); } if(self.logs_loading) { self.logs_loading.remove(); self.logs_loading = null; } if (self.stime) { self.curr_status = self.other_status_tpl({status_text:gettext('Started')}); if (self.execution_time >= 2) { self.curr_status = self.other_status_tpl({status_text:gettext('Running...')}); } if (!_.isNull(self.exit_code)) { if (self.state === 3) { self.curr_status = self.failed_status_tpl({status_text:gettext('Terminated by user.')}); } else if (self.exit_code == 0) { self.curr_status = self.success_status_tpl({status_text:gettext('Successfully completed.')}); } else { self.curr_status = self.failed_status_tpl( {status_text:gettext('Failed (exit code: %s).', String(self.exit_code))} ); } } if (self.state == 0 && self.stime) { self.state = 1; pgBrowser.Events && pgBrowser.Events.trigger( 'pgadmin-bgprocess:started:' + self.id, self, self ); } if (self.state == 1 && !_.isNull(self.exit_code)) { self.state = 2; pgBrowser.Events && pgBrowser.Events.trigger( 'pgadmin-bgprocess:finished:' + self.id, self, self ); } setTimeout(function() { self.show.apply(self); }, 10); } if (!self.completed) { setTimeout( function() { self.status.apply(self); }, 1000 ); } }, status: function() { var self = this; $.ajax({ typs: 'GET', timeout: 30000, url: self.bgprocess_url('status'), cache: false, async: true, contentType: 'application/json', }) .done(function(res) { setTimeout(function() { self.update(res); }, 10); }) .fail(function(res) { // Try after some time only if job id present if (res.status != 410) setTimeout(function() { self.update(res); }, 10000); }); }, show: function() { var self = this; if (self.notify && !self.details) { if (!self.notifier) { let content = $(`
    ${self.type_desc}
    ${self.desc}
    ${self.stime.toString()}
    `); let for_details = content.find('.pg-bg-more-details'); let close_me = content.find('.pg-bg-close'); self.container = content; self.notifier = Alertify.notify( content.get(0), 'bg-bgprocess', 0, null ); for_details.on('click', function(ev) { ev = ev || window.event; ev.cancelBubble = true; ev.stopPropagation(); this.notifier.dismiss(); this.notifier = null; this.completed = false; this.show_detailed_view.apply(this); }.bind(self)); close_me.on('click', function() { this.notifier.dismiss(); this.notifier = null; this.acknowledge_server.apply(this); }.bind(this)); // Do not close the notifier, when clicked on the container, which // is a default behaviour. self.container.on('click', function(ev) { ev = ev || window.event; ev.cancelBubble = true; ev.stopPropagation(); return; }); // On Click event to stop the process. content.find('.bg-process-stop').off('click').on('click', self.stop_process.bind(this)); } // TODO:: Formatted execution time self.container.find('.pg-bg-etime').empty().append( $('').text( String(self.execution_time) ) ).append( $('').text(' ' + gettext('seconds')) ); var $status_bar = $(self.container.find('.pg-bg-status')); $status_bar.html(self.curr_status); // Enable/Disable stop process button var $btn_stop_process = $(self.container.find('.bg-process-stop')); if (isNaN(parseInt(self.exit_code))) { $btn_stop_process.attr('disabled', false); } else { $btn_stop_process.attr('disabled', true); } } else { self.show_detailed_view.apply(self); } }, show_detailed_view: function() { var self = this, panel = this.panel, is_new = false; if (!self.panel) { is_new = true; panel = this.panel = pgBrowser.BackgroundProcessObsorver.create_panel(); panel.title(gettext('Process Watcher - %s', self.type_desc)); panel.focus(); } var container = panel.$container, $logs = container.find('.bg-process-watcher'), $header = container.find('.bg-process-details'), $footer = container.find('.bg-process-footer'), $btn_stop_process = container.find('.bg-process-stop'); // Enable/Disable stop process button if (isNaN(parseInt(self.exit_code))) { $btn_stop_process.attr('disabled', false); } else { $btn_stop_process.attr('disabled', true); } // On Click event to stop the process. $btn_stop_process.off('click').on('click', self.stop_process.bind(this)); if (is_new) { // set logs $logs.html(self.logs); setTimeout(function() { self.logs[0].scrollTop = self.logs[0].scrollHeight; }); self.logs_loading = $('
  9. ' + gettext('Loading process logs...') + '
  10. '); self.logs.append(self.logs_loading); // set bgprocess detailed description $header.find('.bg-detailed-desc').html(self.detailed_desc); } // set bgprocess start time $header.find('.bg-process-stats .bgprocess-start-time').html( self.stime ); // set status $footer.find('.bg-process-status').html(self.curr_status); // set bgprocess execution time $footer.find('.bg-process-exec-time p').empty().append( $('').text( String(self.execution_time) ) ).append( $('').text(' ' + gettext('seconds')) ); if (is_new) { self.details = true; self.err = 0; self.out = 0; setTimeout( function() { self.status.apply(self); }, 1000 ); var resize_log_container = function($logs, $header, $footer) { var h = $header.outerHeight() + $footer.outerHeight(); $logs.css('padding-bottom', h); }.bind(panel, $logs, $header, $footer); panel.on(wcDocker.EVENT.RESIZED, resize_log_container); panel.on(wcDocker.EVENT.ATTACHED, resize_log_container); panel.on(wcDocker.EVENT.DETACHED, resize_log_container); resize_log_container(); panel.on(wcDocker.EVENT.CLOSED, function(process) { process.panel = null; process.details = false; if (process.exit_code != null) { process.acknowledge_server.apply(process); } }.bind(panel, this)); } }, acknowledge_server: function() { var self = this; $.ajax({ type: 'PUT', timeout: 30000, url: self.bgprocess_url('acknowledge'), cache: false, async: true, contentType: 'application/json', }) .done(function() { return; }) .fail(function() { console.warn(arguments); }); }, stop_process: function() { var self = this; // Set the state to terminated. self.state = 3; $.ajax({ type: 'PUT', timeout: 30000, url: self.bgprocess_url('stop_process'), cache: false, async: true, contentType: 'application/json', }) .done(function() { return; }) .fail(function() { console.warn(arguments); }); }, }); _.extend( pgBrowser.BackgroundProcessObsorver, { bgprocesses: {}, init: function() { var self = this; if (self.initialized) { return; } self.initialized = true; setTimeout( function() { self.update_process_list.apply(self); }, 1000 ); pgBrowser.Events.on( 'pgadmin-bgprocess:created', function() { setTimeout( function() { pgBrowser.BackgroundProcessObsorver.update_process_list(true); }, 1000 ); } ); }, update_process_list: function(recheck) { var observer = this; $.ajax({ typs: 'GET', timeout: 30000, url: url_for('bgprocess.list'), cache: false, async: true, contentType: 'application/json', }) .done(function(res) { if (!res || !_.isArray(res)) { return; } for (var idx in res) { var process = res[idx]; if ('id' in process) { if (!(process.id in observer.bgprocesses)) { observer.bgprocesses[process.id] = new BGProcess(process); } } } if (recheck && res.length == 0) { // Recheck after some more time setTimeout( function() { observer.update_process_list(false); }, 3000 ); } }) .fail(function() { // FIXME:: What to do now? console.warn(arguments); }); }, create_panel: function() { this.register_panel(); return pgBrowser.docker.addPanel( 'bg_process_watcher', wcDocker.DOCK.FLOAT, null, { w: (screen.width < 700 ? screen.width * 0.95 : screen.width * 0.5), h: (screen.height < 500 ? screen.height * 0.95 : screen.height * 0.5), x: (screen.width < 700 ? '2%' : '25%'), y: (screen.height < 500 ? '2%' : '25%'), }); }, register_panel: function() { var w = pgBrowser.docker, panels = w.findPanels('bg_process_watcher'); if (panels && panels.length >= 1) return; var p = new pgBrowser.Panel({ name: 'bg_process_watcher', showTitle: true, isCloseable: true, isPrivate: true, isLayoutMember: false, content: '
    ' + '
    ' + '
    ' + '
    ' + '' + '
    ' + '
    '+ '' + gettext('Start time') + ': ' + ''+ '
    ' + '
    ' + '' + '
    ' + '
    ' + '
    ' + '
    ' + '
    ' + '', onCreate: function(myPanel, $container) { $container.addClass('pg-no-overflow p-2'); }, }); p.load(pgBrowser.docker); }, }); return pgBrowser.BackgroundProcessObsorver; });