Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
pgadmin.js689 linesDownload Raw Back to js
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9const axios = require('axios');10const fs = require('fs');11const path = require('path');12const misc = require('../js/misc.js');13const spawn = require('child_process').spawn;14const {EOL} = require('os');15 16let pgadminServerProcess = null;17let startPageUrl = null;18let serverCheckUrl = null;19let addMenuCompleted = false;20let pgAdminMainScreen = null;21 22let serverPort = 5050;23let appStartTime = (new Date()).getTime();24 25let docsURLSubStrings = ['www.enterprisedb.com', 'www.postgresql.org', 'www.pgadmin.org', 'help/help'];26 27// Paths to the rest of the app28let pythonPath = misc.getPythonPath();29let pgadminFile = '../web/pgAdmin4.py';30let configFile = '../web/config.py';31 32// Override the paths above, if a developer needs to33if (fs.existsSync('dev_config.json')) {34  try {35    let dev_config = JSON.parse(fs.readFileSync('dev_config.json'));36    pythonPath = dev_config['pythonPath'];37    pgadminFile = dev_config['pgadminFile'];38  } catch (error) {39    // Meh.40  }41}42 43// This functions is used to start the pgAdmin4 server by spawning a44// separate process.45function startDesktopMode() {46  // Return if pgAdmin server process is already spawned47  // Added check for debugging purpose.48  if (pgadminServerProcess != null)49    return;50 51  let UUID = crypto.randomUUID();52  // Set the environment variables so that pgAdmin 4 server53  // starts listening on the appropriate port.54  process.env.PGADMIN_INT_PORT = serverPort;55  process.env.PGADMIN_INT_KEY = UUID;56  process.env.PGADMIN_SERVER_MODE = 'OFF';57 58  // Start Page URL59  startPageUrl = 'http://127.0.0.1:' + serverPort + '/?key=' + UUID;60  serverCheckUrl = 'http://127.0.0.1:' + serverPort + '/misc/ping?key=' + UUID;61 62  document.getElementById('loader-text-status').innerHTML = 'Starting pgAdmin 4...';63 64  // Write Python Path, pgAdmin file path and command in log file.65  misc.writeServerLog('pgAdmin Runtime Environment');66  misc.writeServerLog('--------------------------------------------------------');67  let command = path.resolve(pythonPath) + ' -s ' + path.resolve(pgadminFile);68  misc.writeServerLog('Python Path: "' + path.resolve(pythonPath) + '"');69  misc.writeServerLog('Runtime Config File: "' + path.resolve(misc.getRunTimeConfigFile()) + '"');70  misc.writeServerLog('pgAdmin Config File: "' + path.resolve(configFile) + '"');71  misc.writeServerLog('Webapp Path: "' + path.resolve(pgadminFile) + '"');72  misc.writeServerLog('pgAdmin Command: "' + command + '"');73  misc.writeServerLog('Environment: ');74  Object.keys(process.env).forEach(function (key) {75    // Below code is included only for Mac OS as default path for azure CLI76    // installation path is not included in PATH variable while spawning77    // runtime environment.78    if (platform() === 'darwin' && key === 'PATH') {79      let updated_path = process.env[key] + ':/usr/local/bin';80      process.env[key] = updated_path;81    }82 83    if (platform() === 'win32' && key.toUpperCase() === 'PATH') {84      let _libpq_path = path.join(path.dirname(path.dirname(path.resolve(pgadminFile))), 'runtime');85      process.env[key] = _libpq_path + ';' + process.env[key];86    }87 88    misc.writeServerLog('  - ' + key + ': ' + process.env[key]);89  });90  misc.writeServerLog('--------------------------------------------------------\n');91 92  // Spawn the process to start pgAdmin4 server.93  let spawnStartTime = (new Date).getTime();94  pgadminServerProcess = spawn(path.resolve(pythonPath), ['-s', path.resolve(pgadminFile)]);95  pgadminServerProcess.on('error', function (err) {96    // Log the error into the log file if process failed to launch97    misc.writeServerLog('Failed to launch pgAdmin4. Error:');98    misc.writeServerLog(err);99  });100  let spawnEndTime = (new Date).getTime();101  misc.writeServerLog('Total spawn time to start the pgAdmin4 server: ' + (spawnEndTime - spawnStartTime) / 1000 + ' Sec');102 103  pgadminServerProcess.stdout.setEncoding('utf8');104  pgadminServerProcess.stdout.on('data', (chunk) => {105    misc.writeServerLog(chunk);106  });107 108  pgadminServerProcess.stderr.setEncoding('utf8');109  pgadminServerProcess.stderr.on('data', (chunk) => {110    misc.writeServerLog(chunk);111  });112 113  // This function is used to ping the pgAdmin4 server whether it114  // it is started or not.115  function pingServer() {116    return axios.get(serverCheckUrl);117  }118 119  let connectionTimeout = misc.ConfigureStore.get('connectionTimeout', 90) * 1000;120  let currentTime = (new Date).getTime();121  let endTime = currentTime + connectionTimeout;122  let midTime1 = currentTime + (connectionTimeout / 2);123  let midTime2 = currentTime + (connectionTimeout * 2 / 3);124  let pingInProgress = false;125 126  // ping pgAdmin server every 1 second.127  let pingStartTime = (new Date).getTime();128  let intervalID = setInterval(function () {129    // If ping request is already send and response is not130    // received no need to send another request.131    if (pingInProgress)132      return;133 134    pingServer().then(() => {135      pingInProgress = false;136      document.getElementById('loader-text-status').innerHTML = 'pgAdmin 4 started';137      // Set the pgAdmin process object to misc138      misc.setProcessObject(pgadminServerProcess);139 140      clearInterval(intervalID);141      let appEndTime = (new Date).getTime();142      misc.writeServerLog('------------------------------------------');143      misc.writeServerLog('Total time taken to ping pgAdmin4 server: ' + (appEndTime - pingStartTime) / 1000 + ' Sec');144      misc.writeServerLog('------------------------------------------');145      misc.writeServerLog('Total launch time of pgAdmin4: ' + (appEndTime - appStartTime) / 1000 + ' Sec');146      misc.writeServerLog('------------------------------------------');147      launchPgAdminWindow();148    }).catch(() => {149      pingInProgress = false;150      let curTime = (new Date).getTime();151      // if the connection timeout has lapsed then throw an error152      // and stop pinging the server.153      if (curTime >= endTime) {154        clearInterval(intervalID);155        splashWindow.hide();156 157        nw.Window.open('src/html/server_error.html', {158          'frame': true,159          'width': 790,160          'height': 430,161          'position': 'center',162          'resizable': false,163          'focus': true,164          'show': true,165        });166      }167 168      if (curTime > midTime1) {169        if (curTime < midTime2) {170          document.getElementById('loader-text-status').innerHTML = 'Taking longer than usual...';171        } else {172          document.getElementById('loader-text-status').innerHTML = 'Almost there...';173        }174      } else {175        document.getElementById('loader-text-status').innerHTML = 'Waiting for pgAdmin 4 to start...';176      }177    });178 179    pingInProgress = true;180  }, 250);181}182 183// This function is used to hide the splash screen and create/launch184// new window to render pgAdmin4 page.185function launchPgAdminWindow() {186  // Create and launch new window and open pgAdmin url187  misc.writeServerLog('Application Server URL: ' + startPageUrl);188  nw.Window.open(startPageUrl, {189    'id': 'pgadmin-main',190    'icon': '../../assets/pgAdmin4.png',191    'frame': true,192    'position': 'center',193    'resizable': true,194    'min_width': 640,195    'min_height': 480,196    'width': 1024,197    'height': 768,198    'focus': true,199    'show': false,200  }, (pgadminWindow) => {201    pgAdminMainScreen = pgadminWindow;202    // Set pgAdmin4 Windows Object203    misc.setPgAdminWindowObject(pgadminWindow);204 205    // Set the zoom level stored in the config file.206    pgadminWindow.zoomLevel = misc.ConfigureStore.get('zoomLevel', 0);207 208    // Set zoom in and out events.209    misc.setZoomEvents();210 211    pgadminWindow.on('closed', function () {212      misc.cleanupAndQuitApp();213    });214 215    // set up handler for new-win-policy event.216    // Set the width and height for the new window.217    pgadminWindow.on('new-win-policy', function (frame, url, policy) {218      if (!frame) {219        let openDocsInBrowser = misc.ConfigureStore.get('openDocsInBrowser', true);220        let isDocURL = false;221        docsURLSubStrings.forEach(function (key) {222          if (url.indexOf(key) >= 0) {223            isDocURL = true;224          }225 226        });227 228 229        if (openDocsInBrowser && isDocURL) {230          // Do not open the window231          policy.ignore();232          // Open URL in the external browser.233          nw.Shell.openExternal(url);234        } else {235          policy.setNewWindowManifest({236            'icon': '../../assets/pgAdmin4.png',237            'frame': true,238            'position': 'center',239            'min_width': 640,240            'min_height': 480,241            'width': pgadminWindow.width,242            'height': pgadminWindow.height,243          });244        }245      }246    });247 248    pgadminWindow.on('loaded', function () {249      /* Make the new window opener to null as it is250       * nothing but a splash screen. We will have to make it null,251       * so that open in new browser tab will work.252       */253      pgadminWindow.window.hookConsole((method, args)=>{254        misc.writeServerLog(255          `--------------[UI ${method}]---------------${EOL}${misc.parseConsoleArgs(method, args)}${EOL}------------[UI End]----------------`);256      });257      pgadminWindow.window.opener = null;258 259      // Show new window260      pgadminWindow.show();261      pgadminWindow.focus();262 263      nativeMenu = new gui.Menu({ type: 'menubar' });264      // Create Mac Builtin Menu265      if (platform() === 'darwin') {266        nativeMenu.createMacBuiltin('pgAdmin 4');267        // Remove 'About pgAdmin 4' submenu268        nativeMenu?.items[0].submenu.removeAt(0);269        // Remove 'Close Window' submenu270        nativeMenu?.items[2].submenu.removeAt(1);271        pgAdminMainScreen.menu = nativeMenu;272      }273 274      try {275        pgAdminMainScreen.isCustomMenusAdded = false;276        let addMenuInterval = setInterval(() => {277          if (pgadminWindow?.window?.pgAdmin?.Browser?.Events && pgadminWindow?.window?.pgAdmin?.Browser?.MainMenus?.length > 0) {278            pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-enable-disable-menu-items', enableDisableMenuItem);279            pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-refresh-menu-item', refreshMenuItems);280            pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-update-checked-menu-item', updateCheckedMenuItem);281            pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-set-new-window-open-size', setNewWindowSize)282            // Add Main Menus to native menu.283            pgadminWindow.window.pgAdmin.Browser.MainMenus.forEach((menu)=> {284              addMenu(menu)285            })286            clearInterval(addMenuInterval);287          }288        }, 250)289      } catch (e) {290        console.error('Error in add native menus');291      }292 293      // Hide the splash screen294      splashWindow.hide();295    });296 297    pgadminWindow.on('blur', function () {298      misc.unregisterZoomEvents();299    });300 301    pgadminWindow.on('focus', function () {302      misc.registerZoomEvents();303    });304  });305}306 307// Get the gui object of NW.js308let gui = require('nw.gui');309let splashWindow = gui.Window.get();310 311// Enable dragging on the splash screen.312let isDragging = false;313let dragOrigin = { x: 0, y: 0 };314document.mouseleave = () => isDragging = false;315document.onmouseup = () => isDragging = false;316 317document.onmousedown = (e) => {318  isDragging = true;319  dragOrigin.x = e.x;320  dragOrigin.y = e.y;321};322 323document.onmousemove = (e) => {324  if (isDragging) {325    splashWindow.moveTo(e.screenX - dragOrigin.x, e.screenY - dragOrigin.y);326  }327};328 329// Always clear the cache before starting the application.330nw.App.clearCache();331 332let nativeMenu;333 334splashWindow.on('loaded', function () {335  // Initialize the ConfigureStore336  misc.ConfigureStore.init();337 338  let fixedPortCheck = misc.ConfigureStore.get('fixedPort', false);339  if (fixedPortCheck) {340    serverPort = misc.ConfigureStore.get('portNo');341    //Start the pgAdmin in Desktop mode.342    startDesktopMode();343  } else {344    // get the available TCP port by sending port no to 0.345    misc.getAvailablePort(0)346      .then((pythonApplicationPort) => {347        serverPort = pythonApplicationPort;348        //Start the pgAdmin in Desktop mode.349        startDesktopMode();350      })351      .catch((errCode) => {352        if (errCode === 'EADDRINUSE') {353          alert('The port specified is already in use. Please enter a free port number.');354        } else {355          alert(errCode);356        }357      });358  }359});360 361splashWindow.on('close', function () {362  misc.cleanupAndQuitApp();363});364 365function setNewWindowSize(){366  misc.setZoomLevelForAllWindows();367}368 369 370function addCommonMenus(menu) {371  let _menu = new gui.Menu();372 373  menu.menuItems.forEach((menuItem) => {374    let submenu = getSubMenu(menuItem);375 376    let _menuItem = new gui.MenuItem({377      label: menuItem.label,378      enabled: !menuItem.isDisabled,379      type: menuItem.type || 'normal',380      priority: menuItem.priority,381      ...(submenu.items.length > 0) && {382        submenu: submenu,383      },384      click: function () {385        menuItem.callback();386      },387    });388    _menu.append(_menuItem);389  });390 391  if (menu.name == 'file') {392    let runtimeMenu = getRuntimeMenu();393    _menu.append(runtimeMenu);394  }395 396  if (menu.menuItems.length == 0) {397    let _menuItem = new gui.MenuItem({398      label: 'No object selected',399      enabled: false,400      priority: 0,401    });402    _menu.append(_menuItem);403  }404 405  if (platform() == 'darwin') {406    pgAdminMainScreen.menu.insert(new gui.MenuItem({407      label: menu.label,408      name: menu.name,409      submenu: _menu,410    }), menu.index);411  } else {412    nativeMenu.append(new gui.MenuItem({413      label: menu.label,414      name: menu.name,415      submenu: _menu,416    }));417    pgAdminMainScreen.menu = nativeMenu;418  }419 420}421 422function getRuntimeMenu() {423  let subMenus = new gui.Menu();424  let rtmenudt = pgAdminMainScreen.window.pgAdmin.Browser.RUNTIME_MENUS_OPTIONS['runtime']425  let runtimeSubMenus = pgAdminMainScreen.window.pgAdmin.Browser.RUNTIME_MENUS_OPTIONS['runtime']['submenus']426  subMenus.append(new gui.MenuItem({427    label: runtimeSubMenus['configure'].label,428    enabled: runtimeSubMenus['configure'].enable,429    priority: runtimeSubMenus['configure'].priority,430    type: 'normal',431    checked: false,432    click: function () {433      // Create and launch new window and open pgAdmin url434      nw.Window.open('src/html/configure.html', {435        'frame': true,436        'width': 600,437        'height': 585,438        'position': 'center',439        'resizable': false,440        'focus': true,441        'show': true,442      });443    },444  }));445  subMenus.append(new gui.MenuItem({446    label: runtimeSubMenus['view_log'].label,447    enabled: runtimeSubMenus['view_log'].enable,448    priority: runtimeSubMenus['view_log'].priority,449    type: 'normal',450    checked: false,451    click: function () {452      // Create and launch new window and open pgAdmin url453      nw.Window.open('src/html/view_log.html', {454        'frame': true,455        'width': 790,456        'height': 425,457        'position': 'center',458        'resizable': false,459        'focus': true,460        'show': true,461      });462    },463  }));464  subMenus.append(new nw.MenuItem({ type: 'separator' }));465    subMenus.append(new gui.MenuItem({466      label: pgAdminMainScreen?.isFullscreen ? runtimeSubMenus['exit_full_screen'].label : runtimeSubMenus['enter_full_screen'].label,467      enabled: runtimeSubMenus['enter_full_screen'].enable,468      priority: runtimeSubMenus['enter_full_screen'].priority,469      type: 'normal',470      checked: false,471      key: runtimeSubMenus['enter_full_screen'].key,472      modifiers: runtimeSubMenus['enter_full_screen'].modifiers,473      click: function () {474        this.label = !pgAdminMainScreen?.isFullscreen ? runtimeSubMenus['exit_full_screen'].label : runtimeSubMenus['enter_full_screen'].label;475        misc.toggleFullScreen();476      },477    }));478  subMenus.append(new gui.MenuItem({479    label: runtimeSubMenus['actual_size'].label,480    enabled: runtimeSubMenus['actual_size'].enable,481    priority: runtimeSubMenus['actual_size'].priority,482    type: 'normal',483    checked: false,484    key: runtimeSubMenus['actual_size'].key,485    modifiers: runtimeSubMenus['actual_size'].modifiers,486    click: function () {487      misc.actualSize();488    },489  }));490  subMenus.append(new gui.MenuItem({491    label: runtimeSubMenus['zoom_in'].label,492    enabled: runtimeSubMenus['zoom_in'].enable,493    priority: runtimeSubMenus['zoom_in'].priority,494    type: 'normal',495    checked: false,496    key: runtimeSubMenus['zoom_in'].key,497    modifiers: runtimeSubMenus['zoom_in'].modifiers,498    click: function () {499      misc.zoomIn();500    },501  }));502  subMenus.append(new gui.MenuItem({503    label: runtimeSubMenus['zoom_out'].label,504    enabled: runtimeSubMenus['zoom_out'].enable,505    priority: runtimeSubMenus['zoom_out'].priority,506    type: 'normal',507    checked: false,508    key: runtimeSubMenus['zoom_out'].key,509    modifiers: runtimeSubMenus['zoom_out'].modifiers,510    click: function () {511      misc.zoomOut();512    },513  }));514 515  let runtimeMenu = new gui.MenuItem({516    label: rtmenudt.label,517    enabled: true,518    priority: rtmenudt.priority,519    type: 'normal',520    checked: false,521    submenu: subMenus,522})523 524  return runtimeMenu;525 526}527 528function getSubMenu(menuItem) {529  let submenu = new gui.Menu();530  if (menuItem.menu_items) {531    menuItem.menu_items.forEach((item) => {532      let menuType = typeof item.checked == 'boolean' ? 'checkbox' : item.type;533      submenu.append(new gui.MenuItem({534        label: item.label,535        enabled: !item.isDisabled,536        priority: item.priority,537        type: menuType,538        checked: item.checked,539        click: function () {540          if (menuType == 'checkbox') {541            pgAdminMainScreen.menu.items.forEach(el => {542              el.submenu.items.forEach((sub) => {543                if (sub.submenu?.items?.length) {544                  sub.submenu.items.forEach((m) => {545                    if (m.type == 'checkbox') {546                      m.label == item.label ? m.checked = true : m.checked = false;547                    }548                  });549                }550              });551            });552          }553          item.callback();554        },555      }));556    });557  }558  return submenu;559}560 561function addMacMenu(menu) {562  if (menu.name == 'file' && platform() === 'darwin') {563    let rootMenu = nativeMenu.items[0].submenu;564    let indx = 0;565    menu.menuItems.forEach((menuItem) => {566      let submenu = getSubMenu(menuItem);567 568      rootMenu.insert(569        new gui.MenuItem({570          label: menuItem.label,571          type: menuItem.type || 'normal',572          enabled: !menuItem.isDisabled,573          priority: menuItem.priority,574          ...(submenu.items.length > 0) && {575            submenu: submenu,576          },577          click: function () {578            // Callback functions for actions579            menuItem.callback();580          },581        }), indx);582      indx++;583    });584    let runtimeMenu = getRuntimeMenu();585    rootMenu.insert(runtimeMenu, indx++);586    let separator_menu = new nw.MenuItem({ type: 'separator' });587    rootMenu.insert(separator_menu, indx);588    indx++;589 590    pgAdminMainScreen.menu = nativeMenu;591  } else {592    addCommonMenus(menu)593  }594}595 596function addOtherOsMenu(menu) {597  addCommonMenus(menu)598}599 600 601function addMenu(menu) {602  pgAdminMainScreen.isCustomMenusAdded = true;603  if (platform() === 'darwin') {604    addMacMenu(menu);605  } else {606    addOtherOsMenu(menu);607  }608  addMenuCompleted = true;609}610 611function enableDisableMenuItem(menu, menuItem) {612  if (addMenuCompleted) {613    // Enable or Disabled specific menu item614    pgAdminMainScreen.menu.items.forEach(el => {615      if (el?.label == menu?.label) {616        el.submenu.items.forEach((sub) => {617          if (sub.label == menuItem.label) {618            sub.enabled = !menuItem.isDisabled;619          }620        });621      }622    });623  }624}625 626function updateCheckedMenuItem(menuItem) {627  // check/ uncheck specific menu item628  pgAdminMainScreen.menu.items.forEach(el => {629    el.submenu.items.forEach((sub) => {630      if(sub.label == menuItem.parentMenu.label) {631        sub.submenu.items.forEach((sm)=> {632          if (sm.label == menuItem.label && sm.type == 'checkbox') {633            sm.checked = menuItem.checked634          }635        })636      } else if (sub.label == menuItem.label && type == 'checkbox') {637        sub.checked = menuItem.checked638      }639    });640  });641}642 643function refreshMenuItems(menu) {644  // Add menu item/option in specific menu.645  pgAdminMainScreen.menu.items.forEach(el => {646    if (el.label == menu.label) {647      let totalSubItems = el.submenu.items.length;648 649      // Remove exisitng menu options to add new options.650      for (let i = 0; i < totalSubItems; i++) {651        el.submenu.removeAt(0);652      }653      menu.menuItems.forEach((item) => {654 655        let submenu = new gui.Menu();656        if (item.menu_items) {657          item.menu_items.forEach((subItem) => {658            submenu.append(new gui.MenuItem({659              label: subItem.label,660              enabled: !subItem.isDisabled,661              priority: subItem.priority,662              type: [true, false].includes(subItem.checked) ? 'checkbox' : 'normal',663              checked: subItem.checked,664              click: function () {665                subItem.callback();666              },667            }));668          });669        }670        let _menuItem = new gui.MenuItem({671          label: item.label,672          enabled: !item.isDisabled,673          priority: item.priority,674          type: item.type,675          ...(submenu.items.length > 0) && {676            submenu: submenu,677          },678          click: function () {679            item.callback();680          },681        });682 683        el.submenu.append(_menuItem);684      });685    }686  });687 688}689 
codekingpro/portable-devtools · Team Ai