Refactoring Electron IPC

This commit is contained in:
2019-08-29 16:24:05 -05:00
parent 083b2192d7
commit f8ec89413a
15 changed files with 988 additions and 836 deletions

View File

@@ -69,6 +69,7 @@
"build/**/*", "build/**/*",
"node_modules/**/*", "node_modules/**/*",
"src/helpers.js", "src/helpers.js",
"src/renderer/**/*",
"public/detect_linux.sh", "public/detect_linux.sh",
"public/install_linux.sh" "public/install_linux.sh"
], ],

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,7 @@ exports.DEV_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n' +
'-----END PUBLIC KEY-----'; '-----END PUBLIC KEY-----';
//const REPERTORY_BRANCH = '1.1.0-release_branch'; const REPERTORY_BRANCH = '1.1.0-release_branch';
const REPERTORY_BRANCH = 'master';
const REPERTORY_UI_BRANCH = '1.0.8_branch'; const REPERTORY_UI_BRANCH = '1.0.8_branch';
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/releases.json'; exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/releases.json';

View File

@@ -0,0 +1,20 @@
const Constants = require('../../constants');
const addListeners = (ipcMain, closeApplication, setWindowVisibility) => {
ipcMain.on(Constants.IPC_Shutdown, () => {
closeApplication();
});
ipcMain.on(Constants.IPC_Show_Window, () => {
setWindowVisibility(true);
});
ipcMain.on(Constants.IPC_Show_Window + '_sync', event => {
setWindowVisibility(true);
event.returnValue = true;
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,56 @@
const Constants = require('../../constants');
const helpers = require('../../helpers');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
helpers
.getConfig(data.Version, data.Provider)
.then((data) => {
if (data.Code === 0) {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {
Config: data.Data,
});
} else {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code);
}
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error);
});
});
ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => {
helpers
.getConfigTemplate(data.Version, data.Provider)
.then((data) => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
Template: data,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error);
});
});
ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => {
const setConfigValue = (i) => {
if (i < data.Items.length) {
helpers
.setConfigValue(data.Items[i].Name, data.Items[i].Value, data.Provider, data.Version)
.then(() => {
setConfigValue(++i);
})
.catch(() => {
setConfigValue(++i);
});
} else {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});
}
};
setConfigValue(0);
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,47 @@
const Constants = require('../../constants');
const helpers = require('../../helpers');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Check_Daemon_Version, (event, data) => {
helpers
.checkDaemonVersion(data.Version, data.Provider)
.then(code => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
Valid: (code === 0),
Code: code,
});
})
.catch(e => {
standardIPCReply(event, Constants.IPC_Check_Daemon_Version_Reply, {
Valid: false,
}, e);
});
});
ipcMain.on(Constants.IPC_Check_Daemon_Version + '_sync', (event, data) => {
helpers
.checkDaemonVersion(data.Version, data.Provider)
.then(code => {
event.returnValue = {
data: {
Success: true,
Valid: (code === 0),
Code: code,
},
};
})
.catch(e => {
event.returnValue = {
data: {
Error: e.toString(),
Success: false,
Valid: false
},
};
});
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,102 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Check_Dependency_Installed, (event, data) => {
try {
const exists = fs.lstatSync(data.File).isFile();
standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, {
data: {
Exists: exists,
},
});
} catch (e) {
standardIPCReply(event, Constants.IPC_Check_Dependency_Installed_Reply, {
data : {
Exists: false,
},
});
}
});
ipcMain.on(Constants.IPC_Check_Dependency_Installed + '_sync', (event, data) => {
try {
const ls = fs.lstatSync(data.File);
event.returnValue = {
data: {
Exists: ls.isFile() || ls.isSymbolicLink(),
},
};
} catch (e) {
event.returnValue = {
data: {
Exists: false
},
};
}
});
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
if (data.Source.toLowerCase().endsWith('.dmg')) {
helpers
.executeAsync('open', ['-a', 'Finder', '-W', data.Source])
.then(() => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
});
})
.catch(error=> {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
}, error);
});
} else {
const execInstall = () => {
helpers
.executeAndWait(data.Source)
.then(() => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
}, error);
});
};
if (data.IsWinFSP) {
helpers
.performWindowsUninstall(["WinFsp 2019.1", "WinFsp 2019.2", "WinFsp 2019.3 B1", "WinFsp 2019.3 B2"])
.then(uninstalled => {
if (uninstalled) {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
RebootRequired: true,
Source: data.Source,
URL: data.URL,
});
} else {
execInstall();
}
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
Source: data.Source,
URL: data.URL,
}, error);
});
} else {
execInstall();
}
}
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,24 @@
const Constants = require('../../constants');
const helpers = require('../../helpers');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
const destination = path.join(helpers.getDataDirectory(), data.Filename);
helpers.downloadFile(data.URL, destination, (progress) => {
standardIPCReply(event, Constants.IPC_Download_File_Progress, {
Destination: destination,
Progress: progress,
URL: data.URL,
});
}, error => {
standardIPCReply(event, Constants.IPC_Download_File_Complete, {
Destination: destination,
URL: data.URL,
}, error);
});
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,31 @@
const Constants = require('../../constants');
const fs = require('fs');
const addListeners = (ipcMain, getMainWindow, dialog) => {
ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => {
dialog.showOpenDialog(getMainWindow(), {
defaultPath: data.Location,
properties: ['openDirectory'],
title: data.Title,
}, (filePaths) => {
if (filePaths && (filePaths.length > 0)) {
event.returnValue = filePaths[0];
} else {
event.returnValue = '';
}
});
});
ipcMain.on(Constants.IPC_Delete_File, (event, data) => {
try {
if (fs.existsSync(data.FilePath)) {
fs.unlinkSync(data.FilePath);
}
} catch (e) {
}
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,249 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
let expectedUnmount = {};
let firstMountCheck = true;
let manualMountDetection = {};
let mountedData = {};
let mountedLocations = [];
const clearManualMountDetection = provider => {
if (manualMountDetection[provider]) {
clearInterval(manualMountDetection[provider]);
delete manualMountDetection[provider];
}
};
const monitorMount = (sender, provider, version, pid, location) => {
manualMountDetection[provider] = setInterval(() => {
helpers
.detectRepertoryMounts(version)
.then(result => {
if (result[provider].PID !== pid) {
if (result[provider].PID === -1) {
clearManualMountDetection(provider);
sender.send(Constants.IPC_Unmount_Drive_Reply, {
data: {
Expected: expectedUnmount[provider],
Location: location,
Provider: provider,
Error: Error(provider + ' Unmounted').toString(),
Success: false,
}
});
} else {
pid = result[provider].PID;
}
}
})
.catch(e => {
console.log(e);
});
},6000);
};
const unmountAllDrives = () => {
// Reset mount states
for (const provider of Constants.PROVIDER_LIST) {
clearManualMountDetection(provider);
expectedUnmount[provider] = true;
}
// Unmount all items
for (const i in mountedLocations) {
const data = mountedData[mountedLocations[i]];
helpers.stopMountProcessSync(data.Version, data.Provider);
}
mountedLocations = [];
mountedData = {};
};
const addListeners = (ipcMain, setTrayImage, standardIPCReply) => {
ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => {
let response = {
Success: true,
Error: ''
};
try {
if (fs.existsSync(data.Location) && fs.statSync(data.Location).isDirectory()) {
if (fs.readdirSync(data.Location).length !== 0) {
response.Success = false;
response.Error = 'Directory not empty: ' + data.Location;
}
} else {
response.Success = false;
response.Error = 'Directory not found: ' + data.Location;
}
} catch (e) {
response.Success = false;
response.Error = e.toString();
}
event.returnValue = response;
});
ipcMain.on(Constants.IPC_Detect_Mounts, (event, data) => {
let driveLetters = {};
for (const provider of Constants.PROVIDER_LIST) {
driveLetters[provider] = [];
}
const grabDriveLetters = (locations) => {
for (let i = 'c'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) {
const drive = (String.fromCharCode(i) + ':').toUpperCase();
let driveInUse;
if (Object.keys(locations).length > 0) {
for (const provider of Constants.PROVIDER_LIST) {
driveInUse = locations[provider].startsWith(drive);
if (driveInUse)
break;
}
}
if (!driveInUse) {
try {
if (!fs.existsSync(drive)) {
for (const provider of Constants.PROVIDER_LIST) {
driveLetters[provider].push(drive);
}
}
} catch (e) {
}
}
}
if (Object.keys(locations).length > 0) {
for (const provider of Constants.PROVIDER_LIST) {
if (locations[provider].length > 0) {
if (!driveLetters[provider].find((driveLetter) => {
return driveLetter === locations[provider];
})) {
driveLetters[provider].push(locations[provider]);
}
}
}
}
};
const setImage = (locations) => {
let driveInUse;
if (Object.keys(locations).length > 0) {
for (const provider of Constants.PROVIDER_LIST) {
driveInUse = locations[provider].length > 0;
if (driveInUse)
break;
}
}
setTrayImage(driveInUse)
};
helpers
.detectRepertoryMounts(data.Version)
.then((results) => {
let storageData = {};
let locations = {};
for (const provider of Constants.PROVIDER_LIST) {
storageData[provider] = results[provider] ? results[provider] : {
Active: false,
Location: '',
PID: -1,
};
locations[provider] = storageData[provider].Location;
if (storageData[provider].PID !== -1) {
expectedUnmount[provider] = false;
if (firstMountCheck) {
monitorMount(event.sender, provider, data.Version, storageData[provider].PID, storageData[provider].Location);
}
}
}
if (os.platform() === 'win32') {
grabDriveLetters(locations);
}
setImage(locations);
if (firstMountCheck) {
firstMountCheck = false;
}
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
DriveLetters: driveLetters,
Locations: locations,
});
})
.catch(error => {
if (os.platform() === 'win32') {
grabDriveLetters({});
}
setImage({});
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
DriveLetters: driveLetters,
}, error);
});
});
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
expectedUnmount[data.Provider] = false;
if (mountedLocations.indexOf(data.Location) !== -1) {
console.log(data.Provider + ' already mounted: ' + data.Location);
} else {
mountedLocations.push(data.Location);
mountedData[data.Location] = {
Version: data.Version,
Provider: data.Provider,
};
const errorHandler = (pid, error) => {
if (mountedLocations.indexOf(data.Location) !== -1) {
mountedLocations.splice(mountedLocations.indexOf(data.Location), 1);
delete mountedData[data.Location];
}
standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, {
Expected: expectedUnmount[data.Provider],
Location: data.Location,
Provider: data.Provider,
}, error || Error(data.Provider + ' Unmounted'));
};
helpers
.executeMount(data.Version, data.Provider, data.Location, data.NoConsoleSupported, (error, pid) => {
errorHandler(pid, error);
})
.then(() => {
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
Provider: data.Provider,
});
})
.catch(error => {
errorHandler(-1, error);
});
}
});
ipcMain.on(Constants.IPC_Unmount_All_Drives, (event, data) => {
unmountAllDrives();
standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply);
});
ipcMain.on(Constants.IPC_Unmount_Drive, (event, data) => {
clearManualMountDetection(data.Provider);
expectedUnmount[data.Provider] = true;
helpers
.stopMountProcess(data.Version, data.Provider)
.then(result => {
console.log(result);
})
.catch(e => {
console.log(e);
});
});
};
module.exports = {
addListeners,
unmountAllDrives
};

View File

@@ -0,0 +1,79 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
const path = require('path');
let _platformOverride;
const getPlatformOverride = () => {
return _platformOverride;
};
const setPlatformOverride = platformOverride => {
_platformOverride = platformOverride;
};
const addListeners = (ipcMain, detectScript, saveUiSettings) => {
ipcMain.on(Constants.IPC_Get_Platform, (event) => {
const sendResponse = (appPlatform, platform) => {
event.sender.send(Constants.IPC_Get_Platform_Reply, {
AppPlatform: appPlatform,
Platform: platform,
});
};
const platform = os.platform();
if (platform === 'linux') {
if (_platformOverride && (_platformOverride.length > 0)) {
sendResponse(_platformOverride, 'linux');
} else {
const scriptFile = path.join(os.tmpdir(), 'repertory_detect_linux.sh');
fs.writeFileSync(scriptFile, detectScript);
helpers
.executeScript(scriptFile)
.then(data => {
let appPlatform = data.replace(/(\r\n|\n|\r)/gm, "");
if (appPlatform === 'unknown') {
helpers
.downloadFile(Constants.LINUX_DETECT_SCRIPT_URL, scriptFile, null, err => {
if (err) {
sendResponse(appPlatform, platform);
} else {
helpers
.executeScript(scriptFile)
.then(data => {
appPlatform = data.replace(/(\r\n|\n|\r)/gm, "");
sendResponse(appPlatform, platform);
})
.catch(() => {
sendResponse(appPlatform, platform);
});
}
});
} else {
sendResponse(appPlatform, platform);
}
})
.catch(() => {
sendResponse(platform, platform);
});
}
} else {
sendResponse(platform, platform);
}
});
ipcMain.on(Constants.IPC_Set_Linux_AppPlatform, (event, data) => {
setPlatformOverride(data.AppPlatform);
saveUiSettings();
event.returnValue = true;
});
};
module.exports = {
getPlatformOverride,
setPlatformOverride,
addListeners
};

View File

@@ -0,0 +1,87 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
const path = require('path');
const unzip = require('unzipper');
const addListeners = (ipcMain, standardIPCReply) => {
ipcMain.on(Constants.IPC_Check_Installed, (event, data) => {
const destination = path.join(helpers.getDataDirectory(), data.Version);
helpers
.getMissingDependencies(data.Dependencies)
.then((dependencies) => {
let exists = false;
try {
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
} catch (e) {
}
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
Dependencies: dependencies,
Exists: exists,
Version: data.Version,
});
}).catch(error => {
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
Dependencies: [],
Version: data.Version,
}, error);
});
});
ipcMain.on(Constants.IPC_Extract_Release, (event, data) => {
const destination = path.join(helpers.getDataDirectory(), data.Version);
helpers.removeDirectoryRecursively(destination);
helpers.mkDirByPathSync(destination);
const stream = fs.createReadStream(data.Source);
stream
.pipe(unzip.Extract({ path: destination }))
.on('error', error => {
try {
helpers.removeDirectoryRecursively(destination);
} catch (e) {
}
stream.close();
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
}, error);
})
.on('finish', () => {
stream.close();
if (os.platform() !== 'win32') {
helpers
.executeAndWait("chmod +x \"" + path.join(destination, 'repertory') + "\"")
.then(() => {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
}, error);
})
} else {
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
Source: data.Source,
});
}
});
});
ipcMain.on(Constants.IPC_Test_Release, (event, data) => {
helpers
.testRepertoryBinary(data.Version)
.then(() => {
standardIPCReply(event, Constants.IPC_Test_Release_Reply, {});
})
.catch(error => {
standardIPCReply(event, Constants.IPC_Test_Release_Reply, {}, error);
});
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,30 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const path = require('path');
const addListeners = ipcMain => {
ipcMain.on(Constants.IPC_Get_State, event => {
helpers.mkDirByPathSync(helpers.getDataDirectory());
const configFile = path.join(helpers.getDataDirectory(), 'settings.json');
if (fs.existsSync(configFile)) {
event.sender.send(Constants.IPC_Get_State_Reply, {
data: JSON.parse(fs.readFileSync(configFile, 'utf8')),
});
} else {
event.sender.send(Constants.IPC_Get_State_Reply, {
data: null,
});
}
});
ipcMain.on(Constants.IPC_Save_State, (event, data) => {
helpers.mkDirByPathSync(helpers.getDataDirectory());
const configFile = path.join(helpers.getDataDirectory(), 'settings.json');
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,16 @@
const Constants = require('../../constants');
const os = require('os');
const helpers = require('../../helpers');
const addListeners = (ipcMain, closeApplication) => {
ipcMain.on(Constants.IPC_Reboot_System, () => {
if (os.platform() === 'win32') {
helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']);
}
closeApplication();
});
};
module.exports = {
addListeners
};

View File

@@ -0,0 +1,116 @@
const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
const addListeners = (ipcMain, setIsInstalling, unmountAllDrives, standardIPCReply) => {
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
let allowSkipVerification = true;
unmountAllDrives();
let tempSig;
let tempPub;
const cleanupFiles = () => {
try {
if (tempSig) {
fs.unlinkSync(tempSig);
}
if (tempPub) {
fs.unlinkSync(tempPub);
}
} catch (e) {
}
};
const errorHandler = err => {
cleanupFiles();
setIsInstalling(false);
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, {
AllowSkipVerification: allowSkipVerification,
Source: data.Source,
}, err);
};
// TODO Enable verification in 1.0.4
const hasSignature = false;//!data.SkipVerification && data.Signature && (data.Signature.length > 0);
const hasHash = false;//!data.SkipVerification && data.Sha256 && (data.Sha256.length > 0);
if (hasSignature) {
try {
const files = helpers.createSignatureFiles(data.Signature, Constants.DEV_PUBLIC_KEY);
tempPub = files.PublicKeyFile;
tempSig = files.SignatureFile;
} catch (e) {
errorHandler(e);
return;
}
}
let command;
let args;
const platform = os.platform();
if (platform === 'win32') {
command = data.Source;
} else if (platform === 'darwin') {
command = 'open';
args = ['-a', 'Finder', data.Source];
} else if (platform === 'linux') {
try {
command = data.Source;
fs.chmodSync(command, '750');
} catch (e) {
errorHandler(e);
}
} else {
errorHandler(Error('Platform not supported: ' + os.platform()));
}
if (command) {
const executeInstall = () => {
setIsInstalling(true);
helpers
.executeAsync(command, args)
.then(() => {
cleanupFiles();
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply)
})
.catch(error => {
setIsInstalling(false);
errorHandler(error);
});
};
if (hasSignature) {
helpers
.verifySignature(data.Source, tempSig, tempPub)
.then(() => {
executeInstall();
})
.catch(() => {
errorHandler(Error('Failed to verify installation package signature'));
});
} else if (hasHash) {
helpers
.verifyHash(data.Source, data.Sha256)
.then(()=> {
executeInstall();
})
.catch(() => {
errorHandler(Error('Failed to verify installation package hash'));
});
} else {
if (platform === 'darwin') {
setTimeout(executeInstall, 3000);
} else {
executeInstall();
}
}
} else {
errorHandler(Error('Unsupported upgrade: ' + data.Source));
}
});
};
module.exports = {
addListeners
};