Skip to content
58 changes: 58 additions & 0 deletions electron/app/js/connectivityUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @license
* Copyright (c) 2021, Oracle and/or its affiliates.
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
'use strict';

const https = require('https');
const HttpsProxyAgent = require('https-proxy-agent');
const { homepage } = require('../../package.json');
const { getLogger } = require('./wktLogging');
const errorUtils = require('./errorUtils');

const CONNECT_TIMEOUT = 5000;

// test connectivity for user settings configuration
async function testConfiguredInternetConnectivity() {
const userSettings = require('./userSettings');
const httpsProxyUrl = await userSettings.getHttpsProxyUrl();
return testInternetConnectivity(httpsProxyUrl);
}

// test connectivity using supplied arguments
async function testInternetConnectivity(httpsProxyUrl) {
const options = {
timeout: CONNECT_TIMEOUT,
method: 'HEAD'
};
if (httpsProxyUrl) {
options.agent = new HttpsProxyAgent(httpsProxyUrl);
}

const logger = getLogger();
return new Promise((resolve) => {
let timeout = false;
const httpsRequest = https.request(homepage, options, (res) => {
logger.debug('Internet connectivity test required HTTP status code %s', res.statusCode);
resolve(true);
});
httpsRequest.on('timeout', () => {
logger.error('Internet connectivity test timed out after %s ms', CONNECT_TIMEOUT);
timeout = true;
httpsRequest.destroy();
});
httpsRequest.on('error', (err) => {
if (!timeout) {
logger.error('Internet connectivity test failed: %s', errorUtils.getErrorMessage(err));
}
resolve(false);
});
httpsRequest.end();
});
}

module.exports = {
testConfiguredInternetConnectivity,
testInternetConnectivity
};
8 changes: 7 additions & 1 deletion electron/app/js/ipcRendererPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const wktApp = new WktApp();

const exeMode = osUtils.getArgv('--wktMode');
const language = osUtils.getArgv('--lang');
const mainModule = osUtils.getArgv('--mainModule');

i18n.changeLanguage(language).then();

Expand Down Expand Up @@ -98,6 +99,7 @@ contextBridge.exposeInMainWorld(
'get-kube-config-files',
'get-latest-wko-image-name',
'get-archive-entry-types',
'get-network-settings',
'choose-archive-file',
'choose-archive-entry',
'choose-domain-home',
Expand All @@ -106,6 +108,8 @@ contextBridge.exposeInMainWorld(
'choose-oracle-home',
'choose-variable-file',
'choose-extra-path-directory',
'restart-network-settings',
'try-network-settings',
'is-dev-mode',
'open-external-link',
'save-user-settings',
Expand All @@ -117,6 +121,7 @@ contextBridge.exposeInMainWorld(
'prompt-save-before-close',
'close-project',
'save-project',
'exit-app',
'run-offline-discover',
'run-online-discover',
'validate-domain-home',
Expand Down Expand Up @@ -215,7 +220,8 @@ contextBridge.exposeInMainWorld(
}
},
'utils': {
generateUuid: () => uuid.v4()
generateUuid: () => uuid.v4(),
mainModule: mainModule
}
}
);
1 change: 1 addition & 0 deletions electron/app/js/wktTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ module.exports = {
downloadLatestWdtInstaller,
getDiscoverDomainShellScript,
getImagetoolShellScript,
getOptions,
getPrepareModelShellScript,
getInstalledWdtReleaseName,
getInstalledWitReleaseName,
Expand Down
66 changes: 56 additions & 10 deletions electron/app/js/wktWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,16 +673,7 @@ async function createWindow() {
newWindow.isReady = false;
newWindow.skipDirtyCheck = false;

if (_isJetDevMode) {
newWindow.loadURL('http://localhost:8000/').then(() => newWindow.webContents.toggleDevTools());
} else {
newWindow.loadFile(path.join(appDir, 'web/index.html')).then();
}

newWindow.on('ready-to-show', () => {
newWindow.setTitle(_wktApp.getApplicationName());
newWindow.show();
});
_initializeWindow(newWindow);

newWindow.on('focus', () => {
createApplicationMenu(newWindow);
Expand Down Expand Up @@ -716,6 +707,55 @@ async function createWindow() {
return newWindow;
}

function createNetworkWindow() {
const width = _isJetDevMode ? 1000 : 640;
const height = 480;
const additionalArguments = _getAdditionalArguments();
additionalArguments.push('--mainModule=network-page');

let newWindow = new BrowserWindow({
show: false,
width: width,
height: height,
menuBarVisible: false,
useContentSize: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
webviewTag: false,
additionalArguments: additionalArguments,
preload: path.join(__dirname, 'ipcRendererPreload.js')
}
});

newWindow.setMenu(null);
newWindow.setMenuBarVisibility(false);

const thisWindowId = newWindow.id;
windowStatus[thisWindowId] = { noMenu: true };

_initializeWindow(newWindow);

newWindow.on('closed', () => {
delete windowStatus[thisWindowId];
newWindow = null;
});
}

function _initializeWindow(newWindow) {
if (_isJetDevMode) {
newWindow.loadURL('http://localhost:8000/').then(() => newWindow.webContents.toggleDevTools());
} else {
newWindow.loadFile(path.join(appDir, 'web/index.html')).then();
}

newWindow.on('ready-to-show', () => {
newWindow.setTitle(_wktApp.getApplicationName());
newWindow.show();
});
}

function setTitleFileName(currentWindow, projectFileName, isEdited) {
let title = `${path.basename(projectFileName)} - ${_wktApp.getApplicationName()}`;
if (isEdited) {
Expand Down Expand Up @@ -817,6 +857,11 @@ async function showErrorMessage(currentWindow, title, message, messageType) {
}

function createApplicationMenu(newWindow) {
const noMenu = getWindowStatus(newWindow, 'noMenu');
if(noMenu) {
return Menu.setApplicationMenu(null);
}

const hasOpenDialog = getWindowStatus(newWindow, 'hasOpenDialog');
const targetType = getWindowStatus(newWindow, 'targetType');
const appMenuTemplate = new WktAppMenu(hasOpenDialog, targetType).appMenuTemplate;
Expand Down Expand Up @@ -884,6 +929,7 @@ module.exports = {
chooseFromFileSystem,
clearWindow,
closeWindow,
createNetworkWindow,
createWindow,
initialize,
isSingleWindow,
Expand Down
10 changes: 10 additions & 0 deletions electron/app/locales/en/webui.json
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,16 @@
"save-all-failed-title": "Save All Failed",
"save-all-catch-all-error-message": "Failed to save all: {{error}}",

"network-page-title": "Network Configuration",
"network-page-proceed": "Connection was established. Click Restart Application to save these settings and restart the application.",
"network-page-warning": "This application requires internet connectivity, and is unable to connect. Please update any network settings that are required.",
"network-page-button-connect": "Try Connection",
"network-page-trying": "Trying...",
"network-page-try-failure": "Connection failed",
"network-page-try-success": "Connection successful",
"network-page-button-restart": "Restart Application",
"network-page-button-exit": "Exit Application",

"validation-helper-validation-error-fields-message": "{{fieldCount}} field(s) were invalid:",
"validation-helper-form-and-field-name-message": "The {{fieldName}} field on the {{formName}} page is invalid:",
"validation-helper-validate-field-value-is-not-defined": "The field is required but its value is not defined.",
Expand Down
93 changes: 68 additions & 25 deletions electron/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ const WktApp = require('./js/wktApp');
const i18n = require('./js/i18next.config');
const { getLogger, initializeLoggingSystem, logRendererMessage } = require('./js/wktLogging');
const userSettings = require('./js/userSettings');
const { chooseFromFileSystem, createWindow, initialize, setHasOpenDialog, setTargetType, showErrorMessage,
promptUserForOkOrCancelAnswer, promptUserForYesOrNoAnswer } = require('./js/wktWindow');
const { chooseFromFileSystem, createNetworkWindow, createWindow, initialize, setHasOpenDialog, setTargetType,
showErrorMessage, promptUserForOkOrCancelAnswer, promptUserForYesOrNoAnswer } = require('./js/wktWindow');
const project = require('./js/project');
const wktTools = require('./js/wktTools');
const wdtArchive = require('./js/wdtArchive');
const wdtDiscovery = require('./js/wdtDiscovery');
const javaUtils = require('./js/javaUtils');
const oracleHomeUtils = require('./js/oracleHomeUtils');
const domainHomeUtils = require('./js/domainHomeUtils');
const connectivityUtils = require('./js/connectivityUtils');
const fsUtils = require('./js/fsUtils');
const witInspect = require('./js/witInspect');
const witCache = require('./js/witCache');
Expand All @@ -43,7 +44,8 @@ class Main {
this._isJetDevMode = isJetDevMode;
this._wktMode = new WktMode(process.argv[0]);
this._wktApp = new WktApp(this._wktMode);
this._openFileCreateWindowAlreadyCalled = false;
// the project file to be opened when the app is ready (command-line, double-click, etc.)
this._initialProjectFile = null;
this._tempDir = app.getPath('temp');
initializeLoggingSystem(this._wktMode, this._wktApp, this._tempDir).then();
wktTools.initialize(this._wktMode);
Expand All @@ -70,19 +72,23 @@ class Main {
}

registerAppListeners(argv) {
// The open-file event is MacOS only.
// This needs to be at the top level to catch OS requests to open the app with a file on MacOS.
//
app.on('open-file', (event, filePath) => {
getLogger().debug(`Received open-file event for ${filePath}`);
if (project.isWktProjectFile(filePath)) {
const existingProjectWindow = project.getWindowForProject(filePath);
if (existingProjectWindow) {
// since MacOS fires both this event and ready, make sure we only open one window...
this._openFileCreateWindowAlreadyCalled = true;
project.showExistingProjectWindow(existingProjectWindow);

} else if(!app.isReady()) {
// when the app is started by double-clicking on a project file,
// the open-file event fires before ready, so remember this file to open on ready.
this._initialProjectFile = filePath;

} else {
// since MacOS fires both this event and ready, make sure we only open one window...
this._openFileCreateWindowAlreadyCalled = true;
// if the app is ready, open a new window for this project file
createWindow(this._isJetDevMode, this._wktApp).then(win => {
win.once('ready-to-show', () => {
this.openProjectFileInWindow(win, filePath);
Expand Down Expand Up @@ -117,32 +123,33 @@ class Main {

app.on('ready', () => {
getLogger().debug('Received ready event');
if (!this._openFileCreateWindowAlreadyCalled) {
let filePath;
if (process.platform !== 'darwin') {
filePath = this.getFileArgFromCommandLine(argv);
if (filePath) {
getLogger().debug(`Found file argument on command-line: ${filePath}`);
const existingProjectWindow = project.getWindowForProject(filePath);
if (existingProjectWindow) {
project.showExistingProjectWindow(existingProjectWindow);
return;
this.checkSetup().then(setupOk => {
if(setupOk) {
// this may have been set in open-file event
let filePath = this._initialProjectFile;

// check the command line for project file
if (!filePath) {
filePath = this.getFileArgFromCommandLine(argv);
if (filePath) {
getLogger().debug(`Found file argument on command-line: ${filePath}`);
const existingProjectWindow = project.getWindowForProject(filePath);
if (existingProjectWindow) {
project.showExistingProjectWindow(existingProjectWindow);
return;
}
}
}
}

createWindow(this._isJetDevMode, this._wktApp).then(win => {
if (process.platform !== 'darwin') {
createWindow(this._isJetDevMode, this._wktApp).then(win => {
if (filePath) {
win.once('ready-to-show', () => {
this.openProjectFileInWindow(win, filePath);
});
}
}
});
} else {
this._openFileCreateWindowAlreadyCalled = false;
}
});
}
});
});

app.on('activate', async (event, hasVisibleWindows) => {
Expand Down Expand Up @@ -777,6 +784,31 @@ class Main {
});
});

ipcMain.handle('get-network-settings', async () => {
const proxyUrl = await getHttpsProxyUrl();
const bypassHosts = await getBypassProxyHosts();
return {
proxyUrl: proxyUrl,
bypassHosts: bypassHosts
};
});

ipcMain.handle('try-network-settings', async (event, settings) => {
return connectivityUtils.testInternetConnectivity(settings['proxyUrl']);
});

ipcMain.handle('restart-network-settings', async (event, settings) => {
await userSettings.setHttpsProxyUrl(settings['proxyUrl']);
await userSettings.setBypassProxyHosts(settings['bypassHosts']);
await userSettings.saveUserSettings();
app.relaunch();
app.quit();
});

ipcMain.handle('exit-app', async () => {
// called before any projects opened, no need for extra checks
app.quit();
});
}

async getLatestWdtInstaller(targetWindow) {
Expand All @@ -798,6 +830,17 @@ class Main {
});
}

// Verify that the network is available with user settings, show proxy setup if there are problems.
//
async checkSetup() {
const { testConfiguredInternetConnectivity } = require('./js/connectivityUtils');
const connected = await testConfiguredInternetConnectivity();
if (!connected) {
createNetworkWindow();
}
return connected;
}

getFileArgFromCommandLine(argv) {
let fileArg;
if (this._wktMode.isExecutableMode() && argv.length > 1) {
Expand Down
Loading