diff --git a/CHANGELOG.md b/CHANGELOG.md index e782deb..f133fa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.0.8 +* \#8: Add tooltips to settings +* \#36: Add ability to select Linux distribution type if OS is unsupported +* \#37: Version check fails with incorrect message when VC runtime is missing +* Added additional WinFsp uninstall strings + ## 1.0.7 * \#31: New installation displays 'Mount location is not set' on Windows * \#33: Add 'Microsoft Visual C++ Redistributable' as dependency installation on Windows diff --git a/README.md b/README.md index d2bc48f..13f1962 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,10 @@ Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions v * SiaPrime >=1.4.0 ## Downloads -* **Repertory UI v1.0.7 Linux 64-bit** [](https://pixeldrain.com/u/thkU0RHP) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage) +* **Repertory UI v1.0.8 Linux 64-bit** [](https://pixeldrain.com/u/a1H78Dc_) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage) * NOTE: Linux distributions require `fuse` and `libfuse` to be installed. -* **Repertory UI v1.0.7 OS X 64-bit** [](https://pixeldrain.com/u/gBW5TSR0) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_mac.dmg) -* **Repertory UI v1.0.7 Windows 64-bit** [](https://pixeldrain.com/u/AcsttDQ_) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_win.exe) - * NOTE: Windows systems require [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019](https://aka.ms/vs/16/release/vc_redist.x64.exe) +* **Repertory UI v1.0.8 OS X 64-bit** [](https://pixeldrain.com/u/6NjT6uEl) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg) +* **Repertory UI v1.0.8 Windows 64-bit** [](https://pixeldrain.com/u/u_pP3IAk) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_win.exe) ## Supported Platforms * OS X 64-bit diff --git a/package.json b/package.json index 939aac5..373cb7e 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "repertory-ui", - "version": "1.0.7", + "version": "1.0.8", "private": true, "author": "scott.e.graves@protonmail.com", "description": "GUI for Repertory - Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.19", - "@fortawesome/free-solid-svg-icons": "^5.9.0", + "@fortawesome/fontawesome-svg-core": "^1.2.22", + "@fortawesome/free-solid-svg-icons": "^5.10.2", "@fortawesome/react-fontawesome": "^0.1.4", "auto-launch": "^5.0.5", "axios": "^0.18.1", @@ -14,10 +14,10 @@ "font-awesome": "^4.7.0", "node-schedule": "^1.3.2", "randomstring": "^1.1.5", - "react": "^16.8.6", - "react-dom": "^16.8.6", + "react": "^16.9.0", + "react-dom": "^16.9.0", "react-loader-spinner": "^2.3.0", - "react-redux": "^7.1.0", + "react-redux": "^7.1.1", "react-scripts": "2.1.8", "react-tooltip": "^3.10.0", "redux": "^4.0.4", @@ -28,7 +28,7 @@ }, "devDependencies": { "cross-env": "^5.2.0", - "electron": "^4.2.8", + "electron": "^4.2.9", "electron-builder": "^20.44.4", "extract-text-webpack-plugin": "^3.0.2", "typescript": "^3.5.3", @@ -40,7 +40,7 @@ "test": "react-scripts test", "eject": "react-scripts eject", "electron-dev": "cross-env ELECTRON_START_URL=http://localhost:3000 electron .", - "electron-dev-unix": "cross-env ELECTRON_START_URL=http://localhost:3000 electron $NODE_DEBUG_OPTION .", + "electron-dev-unix": "cross-env ELECTRON_START_URL=http://localhost:3000 electron .", "pack": "npm run build && electron-builder --dir --x64", "dist": "npm run build && electron-builder --x64", "dist-all": "npm run build && electron-builder --x64 --win --linux --mac", @@ -69,6 +69,7 @@ "build/**/*", "node_modules/**/*", "src/helpers.js", + "src/renderer/**/*", "public/detect_linux.sh", "public/install_linux.sh" ], diff --git a/public/electron.js b/public/electron.js index dd4ab76..a4741d6 100644 --- a/public/electron.js +++ b/public/electron.js @@ -1,43 +1,48 @@ -// Modules to control application life and create native browser window - -const {app, BrowserWindow, Tray, nativeImage, Menu, dialog} = require('electron'); -const {ipcMain} = require('electron'); +const { + app, + BrowserWindow, + dialog, + ipcMain, + Menu, + nativeImage, + Tray +} = require('electron'); +const AutoLaunch = require('auto-launch'); const Constants = require('../src/constants'); +const fs = require('fs'); +const helpers = require('../src/helpers'); +const os = require('os'); const path = require('path'); const url = require('url'); require('electron-debug/index')(); -const os = require('os'); -const helpers = require('../src/helpers'); -const fs = require('fs'); -const unzip = require('unzipper'); -const AutoLaunch = require('auto-launch'); require.extensions['.sh'] = function (module, filename) { module.exports = fs.readFileSync(filename, 'utf8'); }; const detectScript = require('./detect_linux.sh'); -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let trayContextMenu; -let mainWindow; -let mainWindowTray; -let mountedData = {}; -let mountedLocations = []; -let expectedUnmount = {}; -let launchHidden = false; -let firstMountCheck = true; -let manualMountDetection = {}; +const AppIPC = require('../src/renderer/ipc/AppIPC'); +const ConfigIPC = require('../src/renderer/ipc/ConfigIPC'); +const DaemonIPC = require('../src/renderer/ipc/DaemonIPC'); +const DependencyIPC = require('../src/renderer/ipc/DependencyIPC'); +const DownloadIPC = require('../src/renderer/ipc/DownloadIPC'); +const FilesystemIPC = require('../src/renderer/ipc/FilesystemIPC'); +const MountsIPC = require('../src/renderer/ipc/MountsIPC'); +const PlatformIPC = require('../src/renderer/ipc/PlatformIPC'); +const ReleaseIPC = require('../src/renderer/ipc/ReleaseIPC'); +const StateIPC = require('../src/renderer/ipc/StateIPC'); +const SystemIPC = require('../src/renderer/ipc/SystemIPC'); +const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC'); let isShutdown = false; let isQuiting = false; let isInstalling = false; +let launchHidden = false; +let mainWindow; +let mainWindowTray; +let trayContextMenu; -app.on('before-quit', function () { - isQuiting = true; -}); - -function closeApplication() { +const closeApplication = () => { if (!isShutdown) { isShutdown = true; if (mainWindowTray) { @@ -45,52 +50,25 @@ function closeApplication() { } app.quit(); } -} - -function setWindowVisibility(show) { - if (show) { - mainWindow.show(); - if (os.platform() === 'darwin') { - app.dock.show(); - } - - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - mainWindow.focus(); - } else { - mainWindow.hide(); - if (os.platform() === 'darwin') { - app.dock.hide(); - } - } - - if (trayContextMenu && mainWindowTray) { - trayContextMenu.items[0].checked = show; - mainWindowTray.setContextMenu(trayContextMenu) - } -} - -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 = {}; }; -function createWindow() { +const configurePrimaryApp = () => { + app.on('second-instance', () => { + if (!isInstalling && mainWindow) { + setWindowVisibility(true); + } + }); + + app.on('ready', createWindow); + + app.on('window-all-closed', () => { + closeApplication(); + }); +}; + +const createWindow = () => { loadUiSettings(); - + let extra = {}; if (os.platform() === 'linux') { extra = { @@ -140,12 +118,12 @@ function createWindow() { // when you should delete the corresponding element. mainWindow = null; - unmountAllDrives(); + MountsIPC.unmountAllDrives(); }); const appPath = (os.platform() === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) : (os.platform() === 'darwin') ? path.resolve(path.join(path.dirname(app.getAppPath()), '../MacOS/repertory-ui')) : - process.env.APPIMAGE; + process.env.APPIMAGE; const autoLauncher = new AutoLaunch({ name: 'Repertory UI', @@ -192,50 +170,21 @@ function createWindow() { mainWindowTray = new Tray(image); autoLauncher - .isEnabled() - .then((enabled) => { - trayContextMenu.items[1].checked = enabled; - mainWindowTray.setToolTip('Repertory UI'); - mainWindowTray.setContextMenu(trayContextMenu) - }) - .catch(() => { - closeApplication(); - }); - - mainWindow.loadURL(startUrl); -} - -let instanceLock = app.requestSingleInstanceLock(); -const configurePrimaryApp = () => { - app.on('second-instance', () => { - if (!isInstalling && mainWindow) { - setWindowVisibility(true); - } - }); - - app.on('ready', createWindow); - - app.on('window-all-closed', () => { + .isEnabled() + .then((enabled) => { + trayContextMenu.items[1].checked = enabled; + mainWindowTray.setToolTip('Repertory UI'); + mainWindowTray.setContextMenu(trayContextMenu) + }) + .catch(() => { closeApplication(); }); -}; -if (!instanceLock) { - setTimeout(() => { - if ((instanceLock = app.requestSingleInstanceLock())) { - configurePrimaryApp(); - } else { - closeApplication(); - } - }, 3000); -} else { - configurePrimaryApp(); -} -const clearManualMountDetection = (provider) => { - if (manualMountDetection[provider]) { - clearInterval(manualMountDetection[provider]); - delete manualMountDetection[provider]; - } + mainWindow.loadURL(startUrl); +}; + +const getMainWindow = () => { + return mainWindow; }; const loadUiSettings = () => { @@ -244,49 +193,61 @@ const loadUiSettings = () => { if (fs.statSync(settingFile).isFile()) { const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8')); launchHidden = settings.launch_hidden; + PlatformIPC.setPlatformOverride(settings.platform_override); } } catch (e) { } }; -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 saveUiSettings = () => { const settingFile = path.join(helpers.getDataDirectory(), 'ui.json'); try { fs.writeFileSync(settingFile, JSON.stringify({ launch_hidden: launchHidden, + platform_override: PlatformIPC.getPlatformOverride(), }), 'utf-8'); } catch (e) { } }; +const setIsInstalling = installing => { + isInstalling = installing; +}; + +const setTrayImage = driveInUse => { + let image; + if (driveInUse) { + image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_both_mac.png' : '../build/logo_both.png')); + } else { + image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png')); + } + mainWindowTray.setImage(image); +}; + +const setWindowVisibility = show => { + if (show) { + mainWindow.show(); + if (os.platform() === 'darwin') { + app.dock.show(); + } + + if (mainWindow.isMinimized()) { + mainWindow.restore(); + } + mainWindow.focus(); + } else { + mainWindow.hide(); + if (os.platform() === 'darwin') { + app.dock.hide(); + } + } + + if (trayContextMenu && mainWindowTray) { + trayContextMenu.items[0].checked = show; + mainWindowTray.setContextMenu(trayContextMenu) + } +}; + const standardIPCReply = (event, channel, data, error) => { if (mainWindow) { event.sender.send(channel, { @@ -299,677 +260,36 @@ const standardIPCReply = (event, channel, data, error) => { } }; -ipcMain.on(Constants.IPC_Browse_Directory + '_sync', (event, data) => { - dialog.showOpenDialog(mainWindow, { - defaultPath: data.Location, - properties: ['openDirectory'], - title: data.Title, - }, (filePaths) => { - if (filePaths && (filePaths.length > 0)) { - event.returnValue = filePaths[0]; + + + +app.on('before-quit', function () { + isQuiting = true; +}); + +let instanceLock = app.requestSingleInstanceLock(); +if (!instanceLock) { + setTimeout(() => { + if ((instanceLock = app.requestSingleInstanceLock())) { + configurePrimaryApp(); } else { - event.returnValue = ''; + closeApplication(); } - }); -}); + }, 3000); +} else { + configurePrimaryApp(); +} -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 - }, - }; - }); -}); - -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_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_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_Delete_File, (event, data) => { - try { - if (fs.existsSync(data.FilePath)) { - fs.unlinkSync(data.FilePath); - } - } catch (e) { - } -}); - -ipcMain.on(Constants.IPC_Delete_File + '_sync', (event, data) => { - try { - if (fs.existsSync(data.FilePath)) { - fs.unlinkSync(data.FilePath); - } - event.returnValue = { - data: true, - }; - } catch (e) { - event.returnValue = { - data: false, - }; - } -}); - -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; - } - } - - let image; - if (driveInUse) { - image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_both_mac.png' : '../build/logo_both.png')); - } else { - image = nativeImage.createFromPath(path.join(__dirname, os.platform() === 'darwin' ? '../build/logo_mac.png' : '../build/logo.png')); - } - - mainWindowTray.setImage(image); - }; - - 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_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); - }); -}); - -ipcMain.on(Constants.IPC_Extract_Release, (event, data) => { - const destination = path.join(helpers.getDataDirectory(), data.Version); - 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_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_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') { - 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_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_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.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(); - } - } -}); - -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(); - 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 = () => { - isInstalling = true; - helpers - .executeAsync(command, args) - .then(() => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply) - }) - .catch(error => { - isInstalling = 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)); - } -}); - -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_Reboot_System, () => { - if (os.platform() === 'win32') { - helpers.executeAsync('shutdown.exe', ['/r', '/t', '30']); - } - closeApplication(); -}); - -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'); -}); - -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); -}); - -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; -}); - -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); - }); -}); +AppIPC.addListeners(ipcMain, closeApplication, setWindowVisibility); +ConfigIPC.addListeners(ipcMain, standardIPCReply); +DaemonIPC.addListeners(ipcMain, standardIPCReply); +DependencyIPC.addListeners(ipcMain, standardIPCReply); +DownloadIPC.addListeners(ipcMain, standardIPCReply); +FilesystemIPC.addListeners(ipcMain, getMainWindow, dialog); +MountsIPC.addListeners(ipcMain, setTrayImage, standardIPCReply); +PlatformIPC.addListeners(ipcMain, detectScript, saveUiSettings); +ReleaseIPC.addListeners(ipcMain, standardIPCReply); +StateIPC.addListeners(ipcMain); +SystemIPC.addListeners(ipcMain, closeApplication); +UpgradeIPC.addListeners(ipcMain, setIsInstalling, MountsIPC.unmountAllDrives, standardIPCReply); \ No newline at end of file diff --git a/releases.json b/releases.json index f783752..6b8adbf 100644 --- a/releases.json +++ b/releases.json @@ -1,220 +1,220 @@ { "Locations": { "arch": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "centos7": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "darwin": { - "1.0.7": { - "sha256": "e4f5cf9765224d4c25472310089e0996f59772573dc59c1baa065810a16c19ba", - "sig": "A11yvEkveuPo2yyJaDhw9xt/+/xw7/4T3D2Kd/Px1LwCREio25gCr5BWPSIef5yALCS1nDHIzG8CIaDS+OHc4w87YAqIqVOcQaDi2E2t1El7B1noW8yXs+Ew/Ro3AwqN0lnJrtr8fZWVFKqxUTvxLLOJmh5jiud9vdrFzlaI2MNEvC0dY62T0hS1o4uij5/NIXHrZJuz43sa8WftL1RSD/z0lQELF43MaFYCfcDeyCZn2N/htyyvlOEi0bVbjZg1N2cyWAhKbAZu47t5Z5uyoV+VYcIPJT0IsGK2Vr/Js6ziSr3IqdpWl44ztXRLGp4Es17MMnjne30AbhOt+qIOPIcwyRQ7ENEu6MEgT3nA35y5fd1YK7Nz3IkJMxMe1XwyjLE82MgXg9tywb46zKeEJ9zzWk4No71Z6CPwo7kGEl7kzKLw61GYL+1AY+o2dmiDrO+l+DmTzHqGJ3GqBuCnG+5yKM+6klEKU+zmtuaQsPHamY5YLa302G59ecnH90+XIP9veB8jj0ipID8UoGZqJNZgUvkNN3CFoV3TMqA01M5VH7xqWTGPib+aoxCwFsaYiaKTbZBYjw9PQ/ECGXakfX3YpkR1YniNq+Y6UHpfnQNznqZBC+lrud67R2SmRN5JO6wIKiqaKwUT5B5k1ze3Ltdfb1i8bkhk1yNJZBmnr3MA4n5w7wvS/Fw4Uq1zkeUb+No4lP3nH1TcAwU+shesB44MUaLiTlnWUii1AGivEsTtqNp1YoSKVdX1c5GOia0F3iJBqyO1T++bgRpm433AkwaFLqvOr66CirCRtelhZaZtZPStBXATzSBlXEn1J2NbEA6rS185Twco4Cp92+otMFfACm59Ew94E0I8/NSXWEtGkaF7OadMdjS9WPf/5jHWoMnqA8dpYZZXXnZN3xU1eUaVxlu7bdoQxwjllkVMElLp30nOE7Gg2sHuV9xTZ5L2zaueDhZToiItZqce1mlG7zqN0fPlJ8SY0V5nGkm91L90rVi7/9YLKa3jE63Kkv6rMd/o1MAqaFIu1KOgcEbVzexIh1f3ikphIfgxT0PdEuhYa5OGhXwHI/7oaNT8I9XebFRVXCIRHoaYz+zxqCIDQVqDWnW62aTKBwdmGBAp0b9RsCNekCheR2SHfiJi2qAvMWbQR1c3MZpC9A5xAIJZ+FJpTqpagR5U09RIkMjfewpCrfXQYdIWXDJ7CDm2pYGxVn2IIrxvc5kpbsh7VpvNh8bA/TaHyxEKqRWFbua9JZcGP17jliBx3R2ZLt45BYET63Z7z6C8A/Js4bfEspXDd8KfF+rmu83crc1txaJx4WRpbVx8KTLlU/b9o+B5PaoV+BTt/CV3Jgf6uLLXKLB/EXU=", + "1.0.8": { + "sha256": "fa978eeb6f5456c1fcea902cafb039eeda6d13f93770150564a0253341cb3d63", + "sig": "CfzV0Jl/Saq6Yqtwz21Qm5F7C/geGtmotifZrUcSFpGTaniYbtWtwJSap1tAep1EzL43l4wtxmpDM9EJ3qkquD6JMQjKK89twYotzMoWneZyyEB7obhpi/M3mfih8fYrPCgxaUkVm4DZ4DNbZn2GwjMmdCV698OnUfg2PKhVH4LnjqekqOEsU02B/cvlym2zPLLoEhjJoL8+s2ICIV3WBVDHpcF4ZF3lnI8HgfjjpvhQ+V8LQCk+LQTzIwwBDj6C1sH5V9om7ZUyZZEQk8ALcvo6EJHhhOoQ6extou5NIfBSkDir9IJW8hnTVykgYwoFXSUSAf7tvLn/fx+xpvp27hh+MQqA7KPhVyUD0I2qobm87KV9RBUuW0eqC/ijkcfacyhBnTEikSrHbew3cy69KtBTs4xaicIErQogJXcRfn0bQsVYxk1H3lRyoZnnaFliLwBw/8NQnrvjZqMiqlVXijZj6w0gWZjfrJ/s7UZ0K+yofVAyp3Qzf8JsdlUeCS+lqsv65ANWOBrUeF2Tz2neIWWISnVADmBMeacp/Xuz/bwuGLc4FnMWyhIBjBGwYLBTiaq0nk0oC6wn7a+LZoib8WtU9CAJ4v82k0e8YgiwBDPZPfU03wWWVJoRC2yUu89/6x8AQaClwNh364XWmF7bgbCyjThjC70iz2Feu0ORkj4D0vjClYnP1Kz5HOAvzCRlJTtwVnB9xrEBtV45ONaw6wRwVPkADLg/624FTHzwpi7BTTV2xCAn3kx2JvSH7n9ISySu8ZG6Z2pZ5IBs7+7jWQKmtbC8b6wB0VpYwoCcLjGIduaYtx2YKv0FvjMxOeMmrfu3X33Ab9KL/T4meu5LhqcW01JDP2MlzZ8UYebykd+0KI2mts+hGHNhCT3/d7fEh/ccmaJQ1sNl8VoKos1DlwUL+U8t7PVUNj6e4SXZK+Na9pPLI3pMPKvaoPZimbXRKShF+mYUGlLEcQLcjFTTmOnFSkIP39pa98K7mtTwy50dgcqroTm9rAoBGLcicjdlOi76XcSikYrYrBupx2LtAiWUuXrD0Zm9lQ/Lfwp3smUaC6aFNmnyxPmDJ1XscbQr3m/5oxfNopBNliZCvlGLUc/HGUpy0XcEZymPA77AZog4cHcjgAi9tsdnPHfxMmFTniThDD/SgVjTolzSu4Sm/RY2K2hCJ8qFeSuvbhmgPPWOBbNMOfsv9Nswa4Rd57uEqxxSrpAo1dzxdJPklrDsCxeexVz1/6xW3Qj+I12abMacCCa8oX+PLfpdT6hDqBSlzT3KbVz6ApWNRheM/z8kwpDCDHKGIKPZ5OjRm0SRn/SlogRCUHZ93TSOClIPRr37TDYDpvzeoSjAxnwQ2jTxuIs=", "urls": [ - "https://pixeldrain.com/api/file/gBW5TSR0", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_mac.dmg" + "https://pixeldrain.com/api/file/6NjT6uEl", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg" ] } }, "debian9": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "debian10": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "fedora28": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "fedora29": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "fedora30": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "opensuse15": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "opensuse15.1": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "solus": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "tumbleweed": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "ubuntu18.04": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "ubuntu18.10": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "ubuntu19.04": { - "1.0.7": { - "sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", - "sig": "A/jHXV4OFVc4jjczUlB+CzHZbmaCWIygzshHVS3wjpPYBuQjZRbqn0Lj7yIV7PBoaT3xBAykI0Nd6BAxFHdWwwt10d/Snz8jbW94U7LpQMYjhbmy/7FxlbVOcLJSGs1AeoZUIMsYZpyu74IFD0eHn7x37z5Sg62HBE03uggiwVdwKi5g5qfvgzpBkQnzlSAYRQFOb3wELjLu5GRupJQiKdYsUaSkh8xLeloxhsb3UD8VKB427lfyDG7Da+ZMUFROJN9e+1afkGshlJmLu+/6G+Wiyik7gnjVvNEIQyZOoUMfZNZfhO2oL+5YH1a5Sc7vXcPo1CqHxC9ZT9d5L2QLi/9isAk72FemdiRYhPcMHMJ52Zkp4s5WP/2HxCGunAMLx+qC+Zp4NIihkC99fGJcsJfJ4MX7W9ENdxtBA9al4E+Au2CftrxFWXuARhyCy3X6+b1JlQ1jeMWBqixEqthcsAHvWgVTJUOJKCcEJb4BzpQQt2iEIAqNg2nxIKcSeal86GSvz27Z7Cf7CaNXCxrc0CAJ8XeIm8ZzB17Fg/GnKfEDRzaxweL3yyMIrzpIZnGECc8xHBVa1cLpOpuL3s5DxsLRB4XvcNEQ9ywcyuK0ZiEDRHJMW7lWeKEHgvxhMZNTKFnBp72Fgla9Sb7r+ZGXkwJjSn2chwPkKyFr4HlQgMLU386C2fieL8aqaScbb9VpZnPC9gfXDE/ZaFnLug1ORQYwE45DFXoUSnLNVrFUAMne3ISDaXJ946dMQm/6lrAQm1PglS7WKk+1V+GYmrszx9YeHi+Mqz8Dmlww7PCOPbCYsDFHqeruxxcfisq7SoFoHEOzmlq1FNqT3h0hNatpGIKJ2/FM4tQnEuDIGb6gQZMUZrLrLiVYW4ZmeNBe/GoqOt6c8REbtbt4Hpo9UgueKhZtCZOppZZBXu6i59eJXOIgBtNqX+6Kg8R9iM8vhTNbpec5hC0Q9yyELdkoRZkbUDmKWObKZQSr6h82LlsDSggHFYKRtZ4mOqJNxR7U8T8XEr7xqSaC0jmCl9bbxNND1Ir1x6PREfOuSNABxCYHRvZBAqp3PI+T41OsN/2Gcbz5FKc1ShfRcEZKH6npg/mPeYB6KNtuTtgpTJefAAgEra6t3mrV84sjEPig0IfQhfp8VeZItB09OXV02aOOXHKOBU2VilXWF7+Lapjk51I6+BnWRulBkZg6k70YZP/6icOaR+Z60PS8kTqplELfsk4r41nsGlo7iQPHONd5jOa4vhRdmAws7Fan2o5x6wfCJyn1nljNTysLktSm9y8PzJFUVq/kscXac5Q7Rfl4F8JIqbVvmUT740/tJv1zTB4YLfgYJI5cnwVbFuTzTXeDOR/KPCk=", + "1.0.8": { + "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66", + "sig": "AgRclnw5BALJpZFKkvqkXsu8nt5gzYU62xLrPwa5CEsQVjv55LnFiPjQp8pDHi7rH+r2fh+PludH59muiYvmG1WXc52CYZih0rH3I3+Zv169bq+HoooCOuzCOgDkuGlVT7KCc0lCV5UfaWu4tme7NOiOBecEdBAG6ppyq432PjjNPdupe/MGwT4IAYfHAsoR2lPH9nyNKtzTnbMGX1EXEUPOjnsFxyVKKbfer3QKUst7aF5xh30vSzDvq5OpXseA0sCKroWIndaDHW6dLnywnaUqOEW/IyuMNuksAw9LTMFovbULRrxhF4S9VTMZ1wp8X99JlM4MBtTcN2homtqP3Mr7MEmChQR4XyqwF9lrLcq8nbGK5ni6ZpV7AfMLqTB+K+jH5rdNY2H812TYjAJA14ntGBKTRJSgPn/mzKLbBVQtpAMMZ/G/280QKkDH1E6ULEJPdBxDGRDNvDCE7Goe5L4tG9VKn8ncFLPGJIttP3qJreRyx3lBVnSbp0Ya+IUs1YrXctbbUsiM7dubql2H+rLnhzsX33PgkpPcOyR01P1pymI8LLNBLotCDJjwCNIF+hOB+a34txJFiOWIX7Uw8U4h5HWO6XAZFy8/UNG5Miljv+Y6xFIVv1HQxeivKGNBWHlh/m2nvg9sSf2FEydk78zHoBaeB/XFZNgQmhfolQSnywyugPZMxxAmYqI64pvgrNpo+tLp5XFy0vmCvKJNEdrjkKfYNYks66+aBHsNd76jKocaFNu/nc24X5nqpTpBowJXLG7jAGeo7+yqj6V8mK5yNMhGT5HbcyhB+5DSkeTTHcVD+x2Hdimy0VNNXI6qjAaE3R5SlUnv5a/lt+t5HM248LwAbYSzk//Jjk1rR+AnNuBJ5HSxuVdLKqUIkwZ+k0EjFebATmHx+89ieliOv0VAuoMlFr06tjoIyvn2QojOICaB/gHdeOMM6NYoTEl3K4xPbM/Ve1n2ZdYq/PapM8IEJ33H9l3zVjb5oC5lW1B02qiD+qu4BK39RV2nYrCXZbr9Xs7F4M+YifeYVA7oMSa+qDJvNGBj5XGPnBidEDp+jWWIsRoykdjpt0vJA1PoX659UuW/cr1vB9wkI8IxgXEya8mOxEQ7QrPW5RKlha1oi1GVhdzM7tJPzehSyhe+y9zSyVELmhfcJRjC0sy39WYaaDQuCoVhz1gztR6innjUkKFBuEYRQYOsdraBitRkQC48gMPrKe/XmsvJM1o6Uo06GIq9Xiv/VEQrN7uKd7Xqy+nKkdKzy/BtkxsnXrUjc+tAuM89z33trkGYdRDdbAefYz7nVQ2HZ6SPEhT6LGhW8uBa1RU9bIryFBQHStbvbrGvdwDBZvt8z8qfyKUYzhQ=", "urls": [ - "https://pixeldrain.com/api/file/thkU0RHP", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" + "https://pixeldrain.com/api/file/a1H78Dc_", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage" ] } }, "win32": { - "1.0.7": { - "sha256": "fbf7611bca712413bd6e6900bcbd49bd3a1545b4d4e70afb2ee9bd59f74b8f97", - "sig": "B3URP0wqwJcxuisX/qkoLg19Tm1tUu0rQbkBrCtpscaSa5PhUS/p+6ID/eApKMy91SWqCeqw0C071OvTVfTmWRoB/v+9Kkftqs9IISIMDBZgJEAcwf5h0CYqRp/cLyl5XfZLnZoMDR6x7cIVejBaDu1EWtWtACTxfDy3SvtbHkXTfo5ojKk5DScTSNAeDVSL1dY7BBsGuy5WDR0GYpB+519//V4uNCeLSxmHMekEQdyMmjpaO4iKDOkqccwYNSrw3LpL6TQ6YpTX8g2I1tD1ucTKob5Z44LANceL/PMjk7XYRCplufZBIiAnK6mBQFjFFMm4dDTSWqFQKn+pJboTE1E20Gvs/1MFwO1S30mwOIuYebZ4Xfa/D26rFjjxsA8a62Xrxw5YenKczRWs1iNRK+BZUUNw3pbm0KtEwvJZX/XhaVBBuHwT4CDTLIK0tvVsVF6cMNXLdgJEBmItkSw8LdDta81SwHVco0oMxwExqFbwhXqZf7hQBx4NaytBft0QeXNGF0T1B7GTi+D2qr9F5YcIALzYPh5b6RC8yTuTuVgIW+f3LSY8yPTvJaFS3idjTMbzebWAC2aR0BNW1dPyEEoLzsQEfBDbXqSN4m7j+Nt2l5HGfpy370uOaVeKhIFgASJTvhp6FJ0+3FdJfv5E6E0h6EXCXsGCXmh51kcz5kPpmeLXa3OaoF8G5277hICztyUfqskmgz6cu5udRglW57ZwVM8nWRYo9yO5oiLn9ygq4ALOhUfQhbaCpICsDO5nG1R3rAj0t4a2yzQg+CqN9GXzf0+xlbuO0wdg92s2Tgs1O2F5U8yPvgp7fJIil8/GYmmJHj9zqj+kWjFm+ZPdreXns5m+jr58/hepaVyu7KVV/I/o5Q2dse5Xf+jvt7B8u2TC+JWmitzBJkDGRM0Rml+3QIb2F1lKMfPSs34aI7HJHjrfvExAcFiXDVsFXudVZqayjKWreA4gsgZ07GUi1lZ6qX00K7trxWz0BmlZqtXGofrDUQGgsiZ5FoGW5tFSs9t7DqUnpQAZCiVfPki8bphbPMeRsPXBMXsHKTK6TzbU8nukq8urMJWURetWQP5N4/dSI5zrgByBVI7n1TQvdE2bzMQrJP/xTLWGZUCRXfi2J7FmW7fbUtcAStVC2TPWPuzEC9Lvb3ybg0wepgEbMn1kGfb7mohJ5jeLHqhGzY9mx9fO191cduH9zI+yV5wrnYqWd5wN9J4NY66ULxeGxt03rzjMd2hv2tRRVRsT+/4ACUZcIV8JwUZgK51s5DuUWUsl2e5DBPAND/RVpThXJVFyAtBo/vDqoY3lg/pc4RaQZ6gNMoFeHbXwD/yu6TDDTWq5u00ZwNCbR0PaBTwDabs=", + "1.0.8": { + "sha256": "1dcf2ca95ee9ce1893166e7966e4aaa465735d00476d99414c57d18e7232212f", + "sig": "AzXf0WkuGx4lXoGPgxsj2LDCAHwotKnvZHBFZv1f2A77sNtJaDQAgS3b4Hjg0AiWCRL9HzjJNprBv5lEUwc7AeRLXuj+4AI6Z7PeRaVs2NBgEHlIqldPwjime8bE2NBrkKfdDDdiAzhm3AqxgRzwhZ4sxkb3vNDdKIj4WnH79LsVCEcBgtuWalNvt3Q0i5ppBvu6+Iy/gZuGa7z+Jv2WU+Ijm7WjkmmmTdpcvP1UQTBL2UkriYOxVMIfrjHM0b2ch6RGBKUxS2h26h0xmJ7SzFoZ6lHcHDIxi8fsi7DIO/QDpottJPVgZLRuufSTyXLCbehLRI3dd1IWhQns0OTU11MBAUTRczJTaZ8nrjMesKdwbWqDDGMWOHyl39EuGIaGm3Mbi4iQWI3gz4RmX9kIWf0OSaT73okPvfjWA2Onn8IOs57TDw5fIyKKFjMkxqAPy0O+WonjJ5bd4Q/8HUpVx9M9NoClbztMQUhom2hVPkPiD3jQ8I9YcrmD9phZYkMf5As1G6RBsITsbpRA0bu9426y6K+nNJLPAM0TrW33oOziYV2z5HD1/Z+TsUMcusAPTbatjPLyLZ9kWIUqybPga8iPZW9iqszei2ywKl3EgAP/rZx2fn3qko9PvdpsoV6VBp9FxT2mguoRnkIo3lPGYXssBlfVBz0treGyjDoqcOrtciVLJbal54FTPU5o+Acuffi0Ft2F4qW7d3VglhEeA2i4zzPH2l8R0CuStei8ePMhOpebtByTPrclkvlbcOCg5adF+3/zTH9H261+P/IyrPct1ucvVpa6FlpcKfErWttRmxc9QhqiepyPUonLkHgB4Ur/ft6PzlE/+wD6arA7avSKdZqzeSksFSdnX/41fD8gL5CHfsCdbeqPvPd97CliiMCeGjVngQ5qFbC1+5DHuDopvs0yWcmDAO+nFrcQt3K01VjDYT+5plb263Ba3HKHqkT9N2gH8yWK/DdfT4Y8WAABomcgPeRSg6lAtNFv5RtWbSq9Q4dLFuhGM55EtXPG15zYAZqePCcyWYieP0wNuszcSsVNDScU8gqilwtn1oKO0ISZSYZUbdzI3fDc6SbaJNLwYdT/AlELZy0fAKILmJJN8MUXvcC7we6zyPOqwEf1ESLOb84Zw3+gPtH+Ao9dWHKdHOlyNyRzPvTYrcqSn2Uf3iHOLeoazqEEJ5Bh7jbDgVBkB4XEwijb+BvFV3cBcLdSg1Id3zZF02jkpaN4SMbEmqPbUv393CKRNs47p6ffFKKS5oUEpF6GpZq/wtjmKkdMSIzGVurbHPIaZI8JfLrEu/GAxAMGM7G3O5AiKOf0AH0wexhP5Vh6iE3MLNMA7n8SEQL5i/YUIL6b0tDL/wc=", "urls": [ - "https://pixeldrain.com/api/file/AcsttDQ_", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_win.exe" + "https://pixeldrain.com/api/file/u_pP3IAk", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_win.exe" ] } } }, "Versions": { "arch": [ - "1.0.7" + "1.0.8" ], "centos7": [ - "1.0.7" + "1.0.8" ], "darwin": [ - "1.0.7" + "1.0.8" ], "debian9": [ - "1.0.7" + "1.0.8" ], "debian10": [ - "1.0.7" + "1.0.8" ], "fedora28": [ - "1.0.7" + "1.0.8" ], "fedora29": [ - "1.0.7" + "1.0.8" ], "fedora30": [ - "1.0.7" + "1.0.8" ], "linux": [ "unavailable" ], "opensuse15": [ - "1.0.7" + "1.0.8" ], "opensuse15.1": [ - "1.0.7" + "1.0.8" ], "solus": [ - "1.0.7" + "1.0.8" ], "tumbleweed": [ - "1.0.7" + "1.0.8" ], "ubuntu18.04": [ - "1.0.7" + "1.0.8" ], "ubuntu18.10": [ - "1.0.7" + "1.0.8" ], "ubuntu19.04": [ - "1.0.7" + "1.0.8" ], "unknown": [ "unavailable" ], "win32": [ - "1.0.7" + "1.0.8" ] } } diff --git a/src/App.js b/src/App.js index b0ae7f8..780b5e5 100644 --- a/src/App.js +++ b/src/App.js @@ -7,6 +7,7 @@ import DependencyList from './components/DependencyList/DependencyList'; import DownloadProgress from './components/DownloadProgress/DownloadProgress'; import ErrorDetails from './components/ErrorDetails/ErrorDetails'; import Grid from './components/UI/Grid/Grid'; +import InfoDetails from './components/InfoDetails/InfoDetails'; import IPCContainer from './containers/IPCContainer/IPCContainer'; import Loading from './components/UI/Loading/Loading'; import Modal from './components/UI/Modal/Modal'; @@ -14,7 +15,11 @@ import MountItems from './containers/MountItems/MountItems'; import {notifyError} from './redux/actions/error_actions'; import Reboot from './components/Reboot/Reboot'; import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay'; -import {saveState} from './redux/actions/common_actions'; +import { + displaySelectAppPlatform, + saveState +} from './redux/actions/common_actions'; +import SelectAppPlatform from './containers/SelectAppPlatform/SelectAppPlatform'; import Text from './components/UI/Text/Text'; import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; import UpgradeUI from './components/UpgradeUI/UpgradeUI'; @@ -30,13 +35,17 @@ class App extends IPCContainer { componentDidMount() { const detectUpgrades = () => { if (this.props.AppPlatform === 'unknown') { - this.props.notifyError('Operating system is not supported.', true); + if (this.props.Platform === 'linux') { + this.props.displaySelectAppPlatform(true); + } else { + this.props.notifyError('Operating system is not supported.', true); + } } else { this.props.loadReleases(); } }; - this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', detectUpgrades); detectUpgrades(); + this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', detectUpgrades); } componentDidUpdate(prevProps) { @@ -113,15 +122,19 @@ class App extends IPCContainer { !this.props.DismissDependencies && this.props.AllowMount; + const infoDisplay = this.createModalConditionally(this.props.DisplayInfo, , true); const rebootDisplay = this.createModalConditionally(this.props.RebootRequired, ); const configDisplay = this.createModalConditionally(showConfig, ); const dependencyDisplay = this.createModalConditionally(showDependencies, ); const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, ); const errorDisplay = this.createModalConditionally(this.props.DisplayError, , true); const upgradeDisplay = this.createModalConditionally(showUpgrade, ); + const selectAppPlatformDisplay = this.createModalConditionally(this.props.DisplaySelectAppPlatform, ); let mainContent = []; - if (this.props.AppReady) { + if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) { + mainContent = + } else { let key = 0; mainContent.push((
)); } - } else { - mainContent = } return (
- {errorDisplay} + {selectAppPlatformDisplay} {dependencyDisplay} {upgradeDisplay} - {downloadDisplay} {configDisplay} + {infoDisplay} + {downloadDisplay} {rebootDisplay} + {errorDisplay}
@@ -193,12 +206,15 @@ const mapStateToProps = state => { DismissDependencies: state.install.DismissDependencies, DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayError: state.error.DisplayError, + DisplayInfo: state.error.DisplayInfo, + DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform, DownloadActive: state.download.DownloadActive, InstallActive: state.install.InstallActive, InstalledVersion: state.relver.InstalledVersion, LocationsLookup: state.relver.LocationsLookup, MissingDependencies: state.install.MissingDependencies, MountsBusy: state.mounts.MountsBusy, + Platform: state.common.Platform, ProviderState: state.mounts.ProviderState, RebootRequired: state.common.RebootRequired, Release: state.relver.Release, @@ -212,6 +228,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { + displaySelectAppPlatform: display => dispatch(displaySelectAppPlatform(display)), loadReleases: ()=> dispatch(loadReleases()), notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), saveState: () => dispatch(saveState()), diff --git a/src/assets/settings.json b/src/assets/settings.json new file mode 100644 index 0000000..2642db6 --- /dev/null +++ b/src/assets/settings.json @@ -0,0 +1,29 @@ +{ + "HostConfig": { + "AgentString": "'User-Agent' used when communicating with Sia/SiaPrime's API.", + "ApiPassword": "Password used when communicating with Sia/SiaPrime's API.\n\nThis is not the same as your wallet's password, so please do not enter it here. Sia/SiaPrime typically auto-generate this value. It is exclusively used for API authentication purposes.", + "ApiPort": "API port used to connect to Sia/SiaPrime's daemon.", + "HostNameOrIp": "IP address or host name of Sia/SiaPrime daemon.", + "TimeoutMs": "Number of milliseconds to wait for Sia/SiaPrime API responses before timing out." + }, + "Settings": { + "ApiAuth": "Password used to communicate with Repertory's API. Auto-generated by default.", + "ApiPort": "Repertory API port to use for JSON-RPC requests.", + "ApiUser": "Username used to communicate with Repertory's API.", + "ChunkDownloaderTimeoutSeconds": "Files that are not cached locally will download data in ChunkSize chunks when a read or write operation occurs. This timeout value specifies the amount of time chunks should continue downloading after the last file handle has been closed.", + "ChunkSize": "This is the minimum data size (converted to KiB - value of 8 means 8KiB) used for downloads. This value cannot be less than 8 and should also be a multiple of 8.", + "EnableChunkDownloaderTimeout": "This setting applies to full allocation downloads. When set to true, downloads will timeout after ChunkDownloaderTimeoutSeconds if the file has no more open handles. If set to false, the entire file will always download.", + "EnableDriveEvents": "When set to true, additional logging for FUSE on UNIX or WinFSP on Windows will occur. It's best to leave this value set to 'false' unless troubleshooting an issue as enabling it may have an adverse affect on performance.", + "EnableMaxCacheSize": "If set to true, files will begin to be removed from the local cache as soon as MaxCacheSizeBytes and MinimumRedundancy have been met. This does not mean further attempts to write will fail when MaxCacheSizeBytes is reached. Writes will continue as long as there is enough local drive space to accommodate the operation.\n\nIf set to false, files will begin to be removed from the local cache as soon as MinimumRedundancy has been met.\n\nIn both cases, files that do not have any open handles will be chosen by oldest modification date for removal.", + "EventLevel": "Internally, events are fired during certain operations. This setting determines which events should be logged to repertory.log. Valid values are Error, Warn, Normal, Debug, and Verbose.", + "EvictionDelaySeconds": "Number of seconds to wait after all file handles are closed before allowing file to be evicted from cache.", + "EvictionDelayMinutes": "Number of minutes to wait after all file handles are closed before allowing file to be evicted from cache.", + "MaxCacheSizeBytes": "This value specifies the maximum amount of local space to consume before files are removed from cache. EnableMaxCacheSize must also be set to true for this value to take affect.", + "MinimumRedundancy": "Files are elected for removal once this value has been reached. Be aware that this value cannot be set to less than 1.5x.", + "OnlineCheckRetrySeconds": "Number of seconds to wait for Sia/SiaPrime daemon to become available/connectable.", + "OrphanedFileRetentionDays": "Repertory attempts to keep modifications between Sia-UI and the mounted location in sync as much as possible. In the event a file is removed from Sia-UI, it will be marked as orphaned in Repertory and moved into an 'orphaned' directory within Repertory's data directory. This setting specifies the number of days this file is retained before final deletion.", + "PreferredDownloadType": "Repertory supports 3 download modes for reading files that are not cached locally: full file allocation, ring buffer mode and direct mode.\n\nFull file allocation mode pre-allocates the entire file prior to downloading. This mode is required for writes but also ensures the best performance when reading data.\n\nRing buffer mode utilizes a fixed size file buffer to enable a reasonable amount of seeking. This alleviates the need to fully allocate a file. By default, it is 512MiB. When the buffer is full, it attempts to maintain the ability to seek 50% ahead or behind the current read location without the need to re-download data from Sia/SiaPrime.\n\nDirect mode utilizes no disk space. All data is read directly from Sia/SiaPrime.\n\nPreferred download type modes are:\n\nFallback - If there isn't enough local space to allocate the full file, ring buffer mode is used. If there isn't enough local space to create the ring buffer's file, then direct mode is used.\nRingBuffer - Full file allocation is always bypassed; however, if there isn't enough space to create the ring buffer's file, then direct mode will be chosen.\nDirect - All files will be read directly from Sia/SiaPrime.", + "ReadAheadCount": "This value specifies the number of read-ahead chunks to use when downloading a file. This is a per-open file setting and will result in the creation of an equal number of threads.", + "RingBufferFileSize": "The size of the ring buffer file in MiB. Default is 512. Valid values are: 64, 128, 256, 512, 1024." + } +} \ No newline at end of file diff --git a/src/components/ConfigurationItem/ConfigurationItem.css b/src/components/ConfigurationItem/ConfigurationItem.css index c6a6e77..328ae2c 100644 --- a/src/components/ConfigurationItem/ConfigurationItem.css +++ b/src/components/ConfigurationItem/ConfigurationItem.css @@ -33,4 +33,8 @@ input.ConfigurationItemInput { background: rgba(10, 10, 15, 0.8); border-color: rgba(10, 10, 20, 0.9); color: var(--text_color); +} + +.ConfigurationInfo { + cursor: pointer; } \ No newline at end of file diff --git a/src/components/ConfigurationItem/ConfigurationItem.js b/src/components/ConfigurationItem/ConfigurationItem.js index d2dc240..f143839 100644 --- a/src/components/ConfigurationItem/ConfigurationItem.js +++ b/src/components/ConfigurationItem/ConfigurationItem.js @@ -1,7 +1,18 @@ import React from 'react'; import './ConfigurationItem.css'; +import settings from '../../assets/settings'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faInfoCircle} from '@fortawesome/free-solid-svg-icons'; +import {connect} from 'react-redux'; +import {notifyInfo} from '../../redux/actions/error_actions'; -export default props => { +const mapDispatchToProps = dispatch => { + return { + notifyInfo: (title, msg) => dispatch(notifyInfo(title, msg)) + } +}; + +export default connect(null, mapDispatchToProps)(props => { const handleChanged = (e) => { const target = e.target; if (target.type === 'checkbox') { @@ -9,6 +20,18 @@ export default props => { } props.changed(target); }; + + let infoDisplay; + if (settings[props.grouping] && settings[props.grouping][props.label]) { + const displayInfo = () => { + const description = settings[props.grouping][props.label]; + props.notifyInfo(props.label, description); + }; + + infoDisplay = {displayInfo(); return false;}}>; + } let data; switch (props.template.type) { @@ -96,11 +119,13 @@ export default props => { width='100%'> - {props.label} + {infoDisplay ? + {infoDisplay} {props.label} : + {props.label}} {data}
); -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/components/DependencyList/Dependency/Dependency.js b/src/components/DependencyList/Dependency/Dependency.js index e979f10..f869874 100644 --- a/src/components/DependencyList/Dependency/Dependency.js +++ b/src/components/DependencyList/Dependency/Dependency.js @@ -22,8 +22,7 @@ export default connect(mapStateToProps)(props => { {props.AllowDownload ? - {props.onDownload(); return false;}}>Install : 'Installing...'} diff --git a/src/components/InfoDetails/InfoDetails.css b/src/components/InfoDetails/InfoDetails.css new file mode 100644 index 0000000..ae9a3ae --- /dev/null +++ b/src/components/InfoDetails/InfoDetails.css @@ -0,0 +1,11 @@ +.InfoDetailsHeading { + text-align: center; + margin-bottom: 4px; +} + +.InfoDetailsContent { + max-height: 60vh; + min-width: 80vw; + overflow-y: auto; + margin-bottom: 8px; +} \ No newline at end of file diff --git a/src/components/InfoDetails/InfoDetails.js b/src/components/InfoDetails/InfoDetails.js new file mode 100644 index 0000000..2bbcc7e --- /dev/null +++ b/src/components/InfoDetails/InfoDetails.js @@ -0,0 +1,30 @@ +import React from 'react'; +import {dismissInfo} from '../../redux/actions/error_actions'; +import {connect} from 'react-redux'; +import Box from '../UI/Box/Box'; +import Button from '../UI/Button/Button'; +import './InfoDetails.css'; + +const mapStateToProps = state => { + return { + InfoMessage: state.error.InfoStack.length > 0 ? state.error.InfoStack[0] : '', + }; +}; + +const mapDispatchToProps = dispatch => { + return { + dismissInfo: () => dispatch(dismissInfo()), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(props => { + return ( + +

{props.InfoMessage.title}

+
+

{props.InfoMessage.message}

+
+ +
+ ); +}); \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index 9ffe2b8..7582f13 100644 --- a/src/constants.js +++ b/src/constants.js @@ -36,6 +36,12 @@ exports.UI_RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory-ui/raw/' exports.LINUX_DETECT_SCRIPT_URL = 'https://bitbucket.org/blockstorage/repertory/raw/' + REPERTORY_BRANCH + '/detect_linux.sh'; +exports.LINUX_SELECTABLE_PLATFORMS = [ + 'ubuntu18.04', + 'ubuntu18.10', + 'ubuntu19.04' +]; + exports.DATA_LOCATIONS = { linux: '~/.local/repertory/ui', darwin: '~/Library/Application Support/repertory/ui', @@ -62,6 +68,7 @@ exports.RELEASE_TYPES = [ exports.INSTALL_TYPES = { Dependency: 'dependency', Release: 'release', + TestRelease: 'test_release', Upgrade: 'upgrade', }; @@ -120,8 +127,13 @@ exports.IPC_Show_Window = 'show_window'; exports.IPC_Set_Config_Values = 'set_config_values'; exports.IPC_Set_Config_Values_Reply = 'set_config_values_reply'; +exports.IPC_Set_Linux_AppPlatform = 'IPC_Set_Linux_AppPlatform'; + exports.IPC_Shutdown = 'shutdown'; +exports.IPC_Test_Release = 'test_release'; +exports.IPC_Test_Release_Reply = 'test_release_reply'; + exports.IPC_Unmount_All_Drives = 'unmount_all'; exports.IPC_Unmount_All_Drives_Reply = 'unmount_all_reply'; diff --git a/src/containers/Configuration/Configuration.js b/src/containers/Configuration/Configuration.js index 46ac070..f62cefd 100644 --- a/src/containers/Configuration/Configuration.js +++ b/src/containers/Configuration/Configuration.js @@ -231,6 +231,7 @@ class Configuration extends IPCContainer { ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? this.handleItemChanged(e, i)} + grouping={'Settings'} items={this.state.Template[k.label].items} key={i} label={k.label} @@ -251,6 +252,7 @@ class Configuration extends IPCContainer { ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? this.handleObjectItemChanged(e, key, i)} + grouping={key} items={this.state.Template[key].template[k.label].items} key={i} label={k.label} diff --git a/src/containers/MountItems/MountItems.js b/src/containers/MountItems/MountItems.js index 58a84ce..209502c 100644 --- a/src/containers/MountItems/MountItems.js +++ b/src/containers/MountItems/MountItems.js @@ -171,7 +171,11 @@ class MountItems extends IPCContainer { } } else { allowAction = false; - this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error); + if (this.props.Platform === 'win32') { + this.props.notifyError('Failed to launch repertory. Please install Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019.'); + } else { + this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error); + } } } diff --git a/src/containers/SelectAppPlatform/SelectAppPlatform.css b/src/containers/SelectAppPlatform/SelectAppPlatform.css new file mode 100644 index 0000000..723c7f6 --- /dev/null +++ b/src/containers/SelectAppPlatform/SelectAppPlatform.css @@ -0,0 +1,18 @@ +.SAPHeading { + color: var(--text_color_error); + text-align: center; + margin-bottom: 4px; +} + +.SAPContent { + max-height: 60vh; + width: 255px; + overflow-y: auto; + margin-bottom: 8px; +} + +.SAPActions { + margin-top: 4px; + display: flex; + flex-direction: row; +} diff --git a/src/containers/SelectAppPlatform/SelectAppPlatform.js b/src/containers/SelectAppPlatform/SelectAppPlatform.js new file mode 100644 index 0000000..c24e200 --- /dev/null +++ b/src/containers/SelectAppPlatform/SelectAppPlatform.js @@ -0,0 +1,89 @@ +import React from 'react'; +import './SelectAppPlatform.css'; +import axios from 'axios'; +import {connect} from 'react-redux'; +import * as Constants from '../../constants'; +import Box from '../../components/UI/Box/Box'; +import Button from '../../components/UI/Button/Button'; +import { + downloadItem, + setAllowDownload +} from '../../redux/actions/download_actions'; +import DropDown from '../../components/UI/DropDown/DropDown'; +import IPCContainer from '../IPCContainer/IPCContainer'; +import {notifyError} from '../../redux/actions/error_actions'; +import {setInstallTestActive} from '../../redux/actions/install_actions'; + +class SelectAppPlatform extends IPCContainer { + state = { + Selected: 0, + }; + + grabLatestRelease = appPlatform => { + const errorHandler = error => { + this.props.notifyError(error); + this.props.setInstallTestActive(false); + this.props.setAllowDownload(false); + }; + axios + .get(Constants.RELEASES_URL) + .then(response => { + try { + const releases = response.data.Versions.Release[appPlatform]; + const latestVersion = releases[releases.length - 1]; + const release = response.data.Locations[appPlatform][latestVersion]; + this.props.downloadItem(latestVersion + '.zip', Constants.INSTALL_TYPES.TestRelease, release.urls, false, latestVersion, appPlatform); + } catch (error) { + errorHandler(error); + } + }) + .catch(error => { + errorHandler(error); + }); + }; + + handleTestClicked = () => { + this.props.setInstallTestActive(true); + this.props.setAllowDownload(true); + this.grabLatestRelease(Constants.LINUX_SELECTABLE_PLATFORMS[this.state.Selected]) + }; + + render() { + return ( + +

Select Linux Platform

+
+

Repertory was unable to detect your Linux distribution. Please select one of the following and click Test to continue:

+
+
+ this.setState({ + ...this.state, + Selected: e.target.value + })} + disabled={this.props.InstallTestActive} + items={Constants.LINUX_SELECTABLE_PLATFORMS} + selected={this.state.Selected}/> + +
+
+ ); + } +} + +const mapStateToProps = state => { + return { + InstallTestActive: state.install.InstallTestActive, + } +}; + +const mapDispatchToProps = dispatch => { + return { + downloadItem: (name, type, urls, isWinFSP, testVersion, appPlatform) => dispatch(downloadItem(name, type, urls, isWinFSP, testVersion, appPlatform)), + notifyError: msg => dispatch(notifyError(msg)), + setAllowDownload: allow => dispatch(setAllowDownload(allow)), + setInstallTestActive: active => dispatch(setInstallTestActive(active)), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(SelectAppPlatform); \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index dfb955a..c11e5c1 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -705,6 +705,23 @@ module.exports.stopMountProcessSync = (version, provider) => { process.unref(); }; +module.exports.testRepertoryBinary = version => { + return new Promise((resolve, reject) => { + const command = path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); + _executeProcess(command, ['-dc']) + .then(code => { + if (code === 0) { + resolve(); + } else { + reject(Error('Invalid exit code: ' + code)); + } + }) + .catch(error => { + reject(error); + }); + }); +}; + module.exports.verifyHash = (file, hash) => { return new Promise((resolve, reject) => { const platform = os.platform(); diff --git a/src/redux/actions/common_actions.js b/src/redux/actions/common_actions.js index 72854fb..ca9ce7a 100644 --- a/src/redux/actions/common_actions.js +++ b/src/redux/actions/common_actions.js @@ -4,8 +4,17 @@ import {getIPCRenderer} from '../../utils'; const ipcRenderer = getIPCRenderer(); +export const displaySelectAppPlatform = display => { + return dispatch => { + if (display) { + dispatch(showWindow()); + } + + dispatch(setDisplaySelectAppPlatform(display)); + }; +}; + export const notifyRebootRequired = createAction('common/notifyRebootRequired'); -export const setAllowMount = createAction('common/setAllowMount'); export const rebootSystem = () => { return dispatch => { @@ -16,7 +25,19 @@ export const rebootSystem = () => { } }; +export const setAllowMount = createAction('common/setAllowMount'); export const setApplicationReady = createAction('common/setApplicationReady'); + +export const SET_DISPLAY_SELECT_APPPLATFORM = 'common/displaySelectAppPlatform'; +export const setDisplaySelectAppPlatform = display => { + return { + type: SET_DISPLAY_SELECT_APPPLATFORM, + payload: display, + }; +}; + +export const setLinuxAppPlatform = createAction('common/setLinuxAppPlatform'); + export const setRebootRequired = () => { return dispatch => { dispatch(showWindow()); diff --git a/src/redux/actions/download_actions.js b/src/redux/actions/download_actions.js index 2f8367e..8d35fc8 100644 --- a/src/redux/actions/download_actions.js +++ b/src/redux/actions/download_actions.js @@ -3,6 +3,7 @@ import {createAction} from 'redux-starter-kit'; import {getIPCRenderer} from '../../utils'; import {notifyError} from './error_actions'; import { + installAndTestRelease, installDependency, installRelease, installUpgrade @@ -25,7 +26,7 @@ export const setDownloadBegin = (name, type, url) => { export const setDownloadEnd = createAction('download/setDownloadEnd'); export const setDownloadProgress = createAction('download/setDownloadProgress'); -export const downloadItem = (name, type, urls, isWinFSP) => { +export const downloadItem = (name, type, urls, isWinFSP, testVersion, appPlatform) => { return (dispatch, getState) => { if (!Array.isArray(urls)) { urls = [urls]; @@ -40,6 +41,9 @@ export const downloadItem = (name, type, urls, isWinFSP) => { case Constants.INSTALL_TYPES.Release: dispatch(installRelease(result.Destination)); break; + case Constants.INSTALL_TYPES.TestRelease: + dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform)); + break; case Constants.INSTALL_TYPES.Upgrade: //const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]]; const sha256 = null;//info.sha256; @@ -51,6 +55,9 @@ export const downloadItem = (name, type, urls, isWinFSP) => { break; } } else { + if (type === Constants.INSTALL_TYPES.TestRelease) { + this.props.setAllowDownload(false); + } dispatch(notifyError(result.Error)); } }; diff --git a/src/redux/actions/error_actions.js b/src/redux/actions/error_actions.js index f46c8cc..0fdcf4c 100644 --- a/src/redux/actions/error_actions.js +++ b/src/redux/actions/error_actions.js @@ -13,6 +13,14 @@ export const clearError = () => { }; }; +export const CLEAR_INFO = 'error/clearInfo'; +export const clearInfo = () => { + return { + type: CLEAR_INFO, + payload: null, + }; +}; + export const dismissError = () => { return (dispatch, getState) => { dispatch(clearError()); @@ -27,6 +35,12 @@ export const dismissError = () => { }; }; +export const dismissInfo = () => { + return dispatch => { + dispatch(clearInfo()); + }; +}; + export const notifyError = (msg, critical, callback) => { return dispatch => { ErrorActions = [callback, ...ErrorActions]; @@ -36,6 +50,14 @@ export const notifyError = (msg, critical, callback) => { }; }; +export const notifyInfo = (title, msg) => { + return dispatch => { + title = title ? title.toString() : 'Information'; + msg = msg ? msg.toString() : ''; + dispatch(setInfo(title, msg)); + }; +}; + export const SET_ERROR_INFO = 'error/setErrorInfo'; export const setErrorInfo = (msg, critical) => { return { @@ -45,4 +67,16 @@ export const setErrorInfo = (msg, critical) => { critical } } +}; + + +export const SET_INFO = 'error/setInfo'; +export const setInfo = (title, msg) => { + return { + type: SET_INFO, + payload: { + title, + msg + } + } }; \ No newline at end of file diff --git a/src/redux/actions/install_actions.js b/src/redux/actions/install_actions.js index 4c11816..ae44dec 100644 --- a/src/redux/actions/install_actions.js +++ b/src/redux/actions/install_actions.js @@ -7,13 +7,16 @@ import { import {notifyError} from './error_actions'; import {setAllowDownload} from './download_actions'; import { + loadReleases, setActiveRelease, setInstalledVersion, setReleaseUpgradeAvailable } from './release_version_actions'; import { + displaySelectAppPlatform, setAllowMount, setApplicationReady, + setLinuxAppPlatform, setRebootRequired, showWindow, shutdownApplication @@ -134,6 +137,43 @@ export const installDependency = (source, url, isWinFSP) => { }; }; +export const installAndTestRelease = (source, version, appPlatform) => { + return (dispatch, getState) => { + if (ipcRenderer && getState().install.InstallTestActive) { + const extractReleaseComplete = (event, arg) => { + ipcRenderer.send(Constants.IPC_Delete_File, { + FilePath: source, + }); + + ipcRenderer.once(Constants.IPC_Test_Release_Reply, (event, arg) => { + if (arg.data.Success) { + ipcRenderer.sendSync(Constants.IPC_Set_Linux_AppPlatform, { + AppPlatform: appPlatform, + }); + dispatch(setLinuxAppPlatform(appPlatform)); + dispatch(setInstallTestActive(false)); + dispatch(displaySelectAppPlatform(false)); + dispatch(setAllowDownload(false)); + dispatch(loadReleases()); + } else { + dispatch(notifyError(arg.data.Error)); + dispatch(setInstallTestActive(false)); + } + }); + ipcRenderer.send(Constants.IPC_Test_Release, { + Version: version, + }) + }; + + ipcRenderer.once(Constants.IPC_Extract_Release_Complete, extractReleaseComplete); + ipcRenderer.send(Constants.IPC_Extract_Release, { + Source: source, + Version: version, + }); + } + }; +}; + export const installRelease = source => { return (dispatch, getState) => { if (ipcRenderer && !getState().install.InstallActive) { @@ -193,5 +233,6 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => { export const setDismissDependencies = createAction('install/setDismissDependencies'); export const setInstallActive = createAction('install/setInstallActive'); +export const setInstallTestActive = createAction('install/setInstallTestActive'); export const setInstallComplete = createAction('install/setInstallComplete'); export const setMissingDependencies = createAction('install/setMissingDependencies'); diff --git a/src/redux/reducers/common_reducer.js b/src/redux/reducers/common_reducer.js index a2599bc..1502396 100644 --- a/src/redux/reducers/common_reducer.js +++ b/src/redux/reducers/common_reducer.js @@ -3,6 +3,8 @@ import { notifyRebootRequired, setAllowMount, setApplicationReady, + setLinuxAppPlatform, + SET_DISPLAY_SELECT_APPPLATFORM } from '../actions/common_actions'; export const createCommonReducer = (platform, appPlatform, version) => { @@ -10,10 +12,17 @@ export const createCommonReducer = (platform, appPlatform, version) => { AllowMount: false, AppPlatform: appPlatform, AppReady: false, + DisplaySelectAppPlatform: false, Platform: platform, RebootRequired: false, Version: version, }, { + [SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => { + return { + ...state, + DisplaySelectAppPlatform: action.payload, + } + }, [setAllowMount]: (state, action) => { return { ...state, @@ -26,6 +35,12 @@ export const createCommonReducer = (platform, appPlatform, version) => { AppReady: action.payload, }; }, + [setLinuxAppPlatform]: (state, action) => { + return { + ...state, + AppPlatform: action.payload, + } + }, [notifyRebootRequired]: (state, action) => { return { ...state, diff --git a/src/redux/reducers/error_reducer.js b/src/redux/reducers/error_reducer.js index 8a25120..0bb20d2 100644 --- a/src/redux/reducers/error_reducer.js +++ b/src/redux/reducers/error_reducer.js @@ -1,13 +1,17 @@ import {createReducer} from 'redux-starter-kit'; import { CLEAR_ERROR, - SET_ERROR_INFO + CLEAR_INFO, + SET_ERROR_INFO, + SET_INFO } from '../actions/error_actions'; export const errorReducer = createReducer({ DisplayError: false, + DisplayInfo: false, ErrorCritical: false, ErrorStack: [], + InfoStack: [], }, { [CLEAR_ERROR]: state => { const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : []; @@ -17,6 +21,14 @@ export const errorReducer = createReducer({ ErrorStack: errorStack, } }, + [CLEAR_INFO]: state => { + const infoStack = (state.InfoStack.length > 0) ? state.InfoStack.slice(1) : []; + return { + ...state, + DisplayInfo: (infoStack.length > 0), + InfoStack: infoStack, + } + }, [SET_ERROR_INFO]: (state, action) => { const errorStack = [action.payload.msg, ...state.ErrorStack]; return { @@ -25,5 +37,16 @@ export const errorReducer = createReducer({ ErrorCritical: state.ErrorCritical || action.payload.critical, ErrorStack: errorStack, } + }, + [SET_INFO]: (state, action) => { + const infoStack = [{ + title: action.payload.title, + message: action.payload.msg + }, ...state.InfoStack]; + return { + ...state, + DisplayInfo: true, + InfoStack: infoStack, + } } }); \ No newline at end of file diff --git a/src/redux/reducers/install_reducer.js b/src/redux/reducers/install_reducer.js index 9237040..78c7b95 100644 --- a/src/redux/reducers/install_reducer.js +++ b/src/redux/reducers/install_reducer.js @@ -3,6 +3,7 @@ import { setDismissDependencies, setInstallActive, setInstallComplete, + setInstallTestActive, setMissingDependencies } from '../actions/install_actions'; @@ -10,6 +11,7 @@ export const installReducer = createReducer({ DismissDependencies: false, InstallActive: false, InstallResult: null, + InstallTestActive: false, InstallType: null, MissingDependencies: [], }, { @@ -35,6 +37,12 @@ export const installReducer = createReducer({ InstallType: null, } }, + [setInstallTestActive]: (state, action) => { + return { + ...state, + InstallTestActive: action.payload, + } + }, [setMissingDependencies]: (state, action) => { return { ...state, diff --git a/src/renderer/ipc/AppIPC.js b/src/renderer/ipc/AppIPC.js new file mode 100644 index 0000000..4fdd3da --- /dev/null +++ b/src/renderer/ipc/AppIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/ConfigIPC.js b/src/renderer/ipc/ConfigIPC.js new file mode 100644 index 0000000..6f15ba0 --- /dev/null +++ b/src/renderer/ipc/ConfigIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/DaemonIPC.js b/src/renderer/ipc/DaemonIPC.js new file mode 100644 index 0000000..c563c1c --- /dev/null +++ b/src/renderer/ipc/DaemonIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/DependencyIPC.js b/src/renderer/ipc/DependencyIPC.js new file mode 100644 index 0000000..6e884f7 --- /dev/null +++ b/src/renderer/ipc/DependencyIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/DownloadIPC.js b/src/renderer/ipc/DownloadIPC.js new file mode 100644 index 0000000..1492ea9 --- /dev/null +++ b/src/renderer/ipc/DownloadIPC.js @@ -0,0 +1,25 @@ +const Constants = require('../../constants'); +const helpers = require('../../helpers'); +const path = require('path'); + +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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/FilesystemIPC.js b/src/renderer/ipc/FilesystemIPC.js new file mode 100644 index 0000000..1294e76 --- /dev/null +++ b/src/renderer/ipc/FilesystemIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/MountsIPC.js b/src/renderer/ipc/MountsIPC.js new file mode 100644 index 0000000..28add62 --- /dev/null +++ b/src/renderer/ipc/MountsIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/PlatformIPC.js b/src/renderer/ipc/PlatformIPC.js new file mode 100644 index 0000000..f55a35c --- /dev/null +++ b/src/renderer/ipc/PlatformIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/ReleaseIPC.js b/src/renderer/ipc/ReleaseIPC.js new file mode 100644 index 0000000..5d1eb66 --- /dev/null +++ b/src/renderer/ipc/ReleaseIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/StateIPC.js b/src/renderer/ipc/StateIPC.js new file mode 100644 index 0000000..f6f1a7c --- /dev/null +++ b/src/renderer/ipc/StateIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/SystemIPC.js b/src/renderer/ipc/SystemIPC.js new file mode 100644 index 0000000..52ebb2f --- /dev/null +++ b/src/renderer/ipc/SystemIPC.js @@ -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 +}; \ No newline at end of file diff --git a/src/renderer/ipc/UpgradeIPC.js b/src/renderer/ipc/UpgradeIPC.js new file mode 100644 index 0000000..7bdc24c --- /dev/null +++ b/src/renderer/ipc/UpgradeIPC.js @@ -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 +}; \ No newline at end of file