Files
pgadmin4/runtime/src/js/misc.js

462 lines
13 KiB
JavaScript

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
const fs = require('fs');
const path = require('path');
const net = require('net');
const {platform, homedir} = require('os');
let pgadminServerProcess = null;
let pgAdminWindowObject = null;
let zoomInShortcut = null;
let zoomOutShortcut = null;
let actualSizeShortcut = null;
let toggleFullScreenShortcut = null;
// This function is used to check whether directory is present or not
// if not present then create it recursively
const createDir = (dirName) => {
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, {recursive: true});
}
};
export const insideFlatpak = () => {
return platform() === 'linux' && fs.existsSync('/.flatpak-info');
}
// This function is used to get the python executable path
// based on the platform. Use this for deployment.
const getPythonPath = () => {
let pythonPath;
switch (platform()) {
case 'win32':
pythonPath = '../python/python.exe';
break;
case 'darwin':
pythonPath = '../../Frameworks/Python.framework/Versions/Current/bin/python3';
break;
case 'linux':
pythonPath = '../venv/bin/python3';
if (insideFlatpak()) {
pythonPath = '/usr/bin/python';
}
break;
default:
if (platform().startsWith('win')) {
pythonPath = '../python/python.exe';
} else {
pythonPath = '../venv/bin/python3';
}
}
return pythonPath;
};
// This function is used to get the [roaming] app data path
// based on the platform. Use this for config etc.
const getAppDataPath = () => {
let appDataPath;
switch (platform()) {
case 'win32':
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
break;
case 'darwin':
appDataPath = path.join(homedir(), 'Library', 'Preferences', 'pgadmin');
break;
case 'linux':
if ('XDG_CONFIG_HOME' in process.env) {
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
} else {
appDataPath = path.join(homedir(), '.config', 'pgadmin');
}
break;
default:
if (platform().startsWith('win')) {
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
} else if ('XDG_CONFIG_HOME' in process.env) {
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
} else {
appDataPath = path.join(homedir(), '.config', 'pgadmin');
}
}
// Create directory if not exists
createDir(appDataPath);
return appDataPath;
};
// This function is used to get the [local] app data path
// based on the platform. Use this for logs etc.
const getLocalAppDataPath = () => {
let localAppDataPath;
switch (platform()) {
case 'win32':
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
break;
case 'darwin':
localAppDataPath = path.join(homedir(), 'Library', 'Application Support', 'pgadmin');
break;
case 'linux':
if ('XDG_DATA_HOME' in process.env) {
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
} else {
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
}
break;
default:
if (platform().startsWith('win')) {
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
} else if ('XDG_DATA_HOME' in process.env) {
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
} else {
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
}
}
// Create directory if not exists
createDir(localAppDataPath);
return localAppDataPath;
};
// This function is used to get the random available TCP port
// if fixedPort is set to 0. Else check whether port is in used or not.
const getAvailablePort = (fixedPort) => {
return new Promise(function(resolve, reject) {
const server = net.createServer();
server.listen(fixedPort, '127.0.0.1');
server.on('error', (e) => {
reject(e.code);
});
server.on('listening', () => {
let serverPort = server.address().port;
server.close();
resolve(serverPort);
});
});
};
// Get the app data folder path
const currentTime = (new Date()).getTime();
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + currentTime.toString() + '.log');
const configFileName = path.join(getAppDataPath(), 'runtime_config.json');
const DEFAULT_CONFIG_DATA = {'fixedPort': false, 'portNo': 5050, 'connectionTimeout': 90, 'zoomLevel': 0, 'openDocsInBrowser': true};
// This function is used to read the file and return the content
const readServerLog = () => {
let data = null;
if (fs.existsSync(serverLogFile)) {
data = fs.readFileSync(serverLogFile, 'utf8');
} else {
let errMsg = 'Unable to read file ' + serverLogFile + '.';
console.warn(errMsg);
return errMsg;
}
return data;
};
// This function is used to write the data into the log file
const writeServerLog = (data) => {
data = data + '\n';
if (fs.existsSync(serverLogFile)) {
fs.writeFileSync(serverLogFile, data, {flag: 'a+'});
} else {
fs.writeFileSync(serverLogFile, data, {flag: 'w'});
}
};
// This function is used to remove the log file
const removeLogFile = () => {
if (fs.existsSync(serverLogFile)) {
fs.rmSync(serverLogFile);
}
};
// This function used to set the object of pgAdmin server process.
const setProcessObject = (processObject) => {
pgadminServerProcess = processObject;
};
// This function used to set the object of pgAdmin window.
const setPgAdminWindowObject = (windowObject) => {
pgAdminWindowObject = windowObject;
};
// This function is used to get the server log file.
const getServerLogFile = () => {
return serverLogFile;
};
// This function is used to get the runtime config file.
const getRunTimeConfigFile = () => {
return configFileName;
};
// This function is used to kill the server process, remove the log files
// and quit the application.
const cleanupAndQuitApp = () => {
// Remove the server log file on exit
removeLogFile();
// Killing pgAdmin4 server process if application quits
if (pgadminServerProcess != null) {
try {
process.kill(pgadminServerProcess.pid);
}
catch (e) {
console.warn('Failed to kill server process.');
}
}
if (pgAdminWindowObject != null) {
// Remove all the cookies.
pgAdminWindowObject.cookies.getAll({}, function(cookies) {
try {
cookies.forEach(function(cookie) {
pgAdminWindowObject.cookies.remove({url: 'http://' + cookie.domain, name: cookie.name});
});
} catch (error) {
console.warn('Failed to remove cookies.');
} finally {
pgAdminWindowObject = null;
// Quit Application
nw.App.quit();
}
});
} else {
// Quit Application
nw.App.quit();
}
};
// This function is used to create zoom events based on platform
const setZoomEvents = () => {
if (platform() == 'darwin') {
zoomInShortcut = new nw.Shortcut({key: 'Command+Equal'});
zoomOutShortcut = new nw.Shortcut({key: 'Command+Minus'});
actualSizeShortcut = new nw.Shortcut({key: 'Command+0'});
toggleFullScreenShortcut = new nw.Shortcut({key: 'Command+Ctrl+F'});
} else {
zoomInShortcut = new nw.Shortcut({key: 'Ctrl+Equal'});
zoomOutShortcut = new nw.Shortcut({key: 'Ctrl+Minus'});
actualSizeShortcut = new nw.Shortcut({key: 'Ctrl+0'});
// Use F10 instead of F11. F11 does not work possibly due to Chromium reserving it for their function.
toggleFullScreenShortcut = new nw.Shortcut({key: 'F10'});
}
zoomInShortcut.on('active', function() {
zoomIn();
});
zoomOutShortcut.on('active', function() {
zoomOut();
});
actualSizeShortcut.on('active', function() {
actualSize();
});
toggleFullScreenShortcut.on('active', function() {
toggleFullScreen();
});
zoomInShortcut.on('failed', function(msg) {
let errMsg = 'Failed to register zoom in shortcut with error: ' + msg;
console.warn(errMsg);
});
zoomOutShortcut.on('failed', function(msg) {
let errMsg = 'Failed to register zoom out shortcut with error: ' + msg;
console.warn(errMsg);
});
actualSizeShortcut.on('failed', function(msg) {
let errMsg = 'Failed to register actual size shortcut with error: ' + msg;
console.warn(errMsg);
});
toggleFullScreenShortcut.on('failed', function(msg) {
let errMsg = 'Failed to register toggle full screen shortcut with error: ' + msg;
console.warn(errMsg);
});
};
// This function is used to iterate all open windows and set the zoom level.
const setZoomLevelForAllWindows = () => {
nw.Window.getAll(function(winArray) {
for (let arr_val of winArray) {
arr_val.zoomLevel = pgAdminWindowObject.zoomLevel;
}
});
};
// This function used to zoom in the pgAdmin window.
const zoomIn = () => {
if (pgAdminWindowObject != null) {
pgAdminWindowObject.zoomLevel += 0.5;
setZoomLevelForAllWindows();
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
ConfigureStore.saveConfig();
}
};
// This function used to zoom out the pgAdmin window.
const zoomOut = () => {
if (pgAdminWindowObject != null) {
pgAdminWindowObject.zoomLevel -= 0.5;
setZoomLevelForAllWindows();
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
ConfigureStore.saveConfig();
}
};
// This function used to reset the zoom level of pgAdmin window.
const actualSize = () => {
if (pgAdminWindowObject != null) {
pgAdminWindowObject.zoomLevel = 0;
setZoomLevelForAllWindows();
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
ConfigureStore.saveConfig();
}
};
const toggleFullScreen = () => {
if (pgAdminWindowObject != null) {
// Toggle full screen
pgAdminWindowObject.toggleFullscreen();
// Change the menu label.
let menu_label = pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML;
if (menu_label.indexOf('Enter Full Screen') >= 0) {
pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML = menu_label.replace('Enter', 'Exit');
} else if (menu_label.indexOf('Exit Full Screen') >= 0) {
pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML = menu_label.replace('Exit', 'Enter');
}
}
};
// This function is used to register zoom events.
const registerZoomEvents = () => {
nw.App.registerGlobalHotKey(zoomInShortcut);
nw.App.registerGlobalHotKey(zoomOutShortcut);
nw.App.registerGlobalHotKey(actualSizeShortcut);
nw.App.registerGlobalHotKey(toggleFullScreenShortcut);
};
// This function is used to unregister zoom events.
const unregisterZoomEvents = () => {
nw.App.unregisterGlobalHotKey(zoomInShortcut);
nw.App.unregisterGlobalHotKey(zoomOutShortcut);
nw.App.unregisterGlobalHotKey(actualSizeShortcut);
nw.App.unregisterGlobalHotKey(toggleFullScreenShortcut);
};
let ConfigureStore = {
fileName: configFileName,
jsonData: {},
init: function() {
if (!this.readConfig()){
this.jsonData = DEFAULT_CONFIG_DATA;
this.saveConfig();
}
},
// This function is used to write configuration data
saveConfig: function() {
fs.writeFileSync(this.fileName, JSON.stringify(this.jsonData, null, 4), {flag: 'w'});
},
// This function is used to read the configuration data
readConfig: function() {
if (fs.existsSync(this.fileName)) {
try {
this.jsonData = JSON.parse(fs.readFileSync(this.fileName));
} catch (error) {
/* If the file is not present or invalid JSON data in file */
this.jsonData = {};
}
} else {
let errMsg = 'Unable to read file ' + this.fileName + ' not found.';
console.warn(errMsg);
return false;
}
return true;
},
getConfigData: function() {
return this.jsonData;
},
get: function(key, if_not_value) {
if(this.jsonData[key] !== undefined) {
return this.jsonData[key];
} else {
return if_not_value;
}
},
set: function(key, value) {
if(typeof key === 'object'){
this.jsonData = {
...this.jsonData,
...key,
};
} else if(value === '' || value == null || typeof(value) == 'undefined') {
if(this.jsonData[key] !== undefined) {
delete this.jsonData[key];
}
} else {
this.jsonData[key] = value;
}
},
};
function parseConsoleArgs(_method, args) {
const retData = Array.from(args).map(arg => {
try {
if(arg.stack) return arg.stack;
return JSON.stringify(arg);
} catch (e) {
return arg
}
});
return retData?.join(' ');
}
module.exports = {
readServerLog: readServerLog,
writeServerLog: writeServerLog,
getAvailablePort: getAvailablePort,
getPythonPath: getPythonPath,
setProcessObject: setProcessObject,
cleanupAndQuitApp: cleanupAndQuitApp,
getServerLogFile: getServerLogFile,
getRunTimeConfigFile: getRunTimeConfigFile,
setPgAdminWindowObject: setPgAdminWindowObject,
zoomIn: zoomIn,
zoomOut: zoomOut,
actualSize: actualSize,
toggleFullScreen: toggleFullScreen,
setZoomEvents: setZoomEvents,
registerZoomEvents: registerZoomEvents,
unregisterZoomEvents: unregisterZoomEvents,
setZoomLevelForAllWindows: setZoomLevelForAllWindows,
ConfigureStore: ConfigureStore,
parseConsoleArgs: parseConsoleArgs,
};