Merged 1.0.8_branch into master

This commit is contained in:
2019-08-31 01:56:49 -05:00
37 changed files with 1524 additions and 934 deletions

View File

@@ -1,5 +1,11 @@
# Changelog # 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 ## 1.0.7
* \#31: New installation displays 'Mount location is not set' on Windows * \#31: New installation displays 'Mount location is not set' on Windows
* \#33: Add 'Microsoft Visual C++ Redistributable' as dependency installation on Windows * \#33: Add 'Microsoft Visual C++ Redistributable' as dependency installation on Windows

View File

@@ -9,11 +9,10 @@ Repertory allows you to mount Sia and/or SiaPrime blockchain storage solutions v
* SiaPrime >=1.4.0 * SiaPrime >=1.4.0
## Downloads ## Downloads
* **Repertory UI v1.0.7 Linux 64-bit** [<Primary\>](https://pixeldrain.com/u/thkU0RHP) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage) * **Repertory UI v1.0.8 Linux 64-bit** [<Primary\>](https://pixeldrain.com/u/a1H78Dc_) [<Alternate\>](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. * NOTE: Linux distributions require `fuse` and `libfuse` to be installed.
* **Repertory UI v1.0.7 OS X 64-bit** [<Primary\>](https://pixeldrain.com/u/gBW5TSR0) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_mac.dmg) * **Repertory UI v1.0.8 OS X 64-bit** [<Primary\>](https://pixeldrain.com/u/6NjT6uEl) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg)
* **Repertory UI v1.0.7 Windows 64-bit** [<Primary\>](https://pixeldrain.com/u/AcsttDQ_) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_win.exe) * **Repertory UI v1.0.8 Windows 64-bit** [<Primary\>](https://pixeldrain.com/u/u_pP3IAk) [<Alternate\>](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_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)
## Supported Platforms ## Supported Platforms
* OS X 64-bit * OS X 64-bit

View File

@@ -1,12 +1,12 @@
{ {
"name": "repertory-ui", "name": "repertory-ui",
"version": "1.0.7", "version": "1.0.8",
"private": true, "private": true,
"author": "scott.e.graves@protonmail.com", "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.", "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": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.19", "@fortawesome/fontawesome-svg-core": "^1.2.22",
"@fortawesome/free-solid-svg-icons": "^5.9.0", "@fortawesome/free-solid-svg-icons": "^5.10.2",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.4",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"axios": "^0.18.1", "axios": "^0.18.1",
@@ -14,10 +14,10 @@
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"node-schedule": "^1.3.2", "node-schedule": "^1.3.2",
"randomstring": "^1.1.5", "randomstring": "^1.1.5",
"react": "^16.8.6", "react": "^16.9.0",
"react-dom": "^16.8.6", "react-dom": "^16.9.0",
"react-loader-spinner": "^2.3.0", "react-loader-spinner": "^2.3.0",
"react-redux": "^7.1.0", "react-redux": "^7.1.1",
"react-scripts": "2.1.8", "react-scripts": "2.1.8",
"react-tooltip": "^3.10.0", "react-tooltip": "^3.10.0",
"redux": "^4.0.4", "redux": "^4.0.4",
@@ -28,7 +28,7 @@
}, },
"devDependencies": { "devDependencies": {
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"electron": "^4.2.8", "electron": "^4.2.9",
"electron-builder": "^20.44.4", "electron-builder": "^20.44.4",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"typescript": "^3.5.3", "typescript": "^3.5.3",
@@ -40,7 +40,7 @@
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"electron-dev": "cross-env ELECTRON_START_URL=http://localhost:3000 electron .", "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", "pack": "npm run build && electron-builder --dir --x64",
"dist": "npm run build && electron-builder --x64", "dist": "npm run build && electron-builder --x64",
"dist-all": "npm run build && electron-builder --x64 --win --linux --mac", "dist-all": "npm run build && electron-builder --x64 --win --linux --mac",
@@ -69,6 +69,7 @@
"build/**/*", "build/**/*",
"node_modules/**/*", "node_modules/**/*",
"src/helpers.js", "src/helpers.js",
"src/renderer/**/*",
"public/detect_linux.sh", "public/detect_linux.sh",
"public/install_linux.sh" "public/install_linux.sh"
], ],

View File

@@ -1,43 +1,48 @@
// Modules to control application life and create native browser window const {
app,
const {app, BrowserWindow, Tray, nativeImage, Menu, dialog} = require('electron'); BrowserWindow,
const {ipcMain} = require('electron'); dialog,
ipcMain,
Menu,
nativeImage,
Tray
} = require('electron');
const AutoLaunch = require('auto-launch');
const Constants = require('../src/constants'); const Constants = require('../src/constants');
const fs = require('fs');
const helpers = require('../src/helpers');
const os = require('os');
const path = require('path'); const path = require('path');
const url = require('url'); const url = require('url');
require('electron-debug/index')(); 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) { require.extensions['.sh'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8'); module.exports = fs.readFileSync(filename, 'utf8');
}; };
const detectScript = require('./detect_linux.sh'); const detectScript = require('./detect_linux.sh');
// Keep a global reference of the window object, if you don't, the window will const AppIPC = require('../src/renderer/ipc/AppIPC');
// be closed automatically when the JavaScript object is garbage collected. const ConfigIPC = require('../src/renderer/ipc/ConfigIPC');
let trayContextMenu; const DaemonIPC = require('../src/renderer/ipc/DaemonIPC');
let mainWindow; const DependencyIPC = require('../src/renderer/ipc/DependencyIPC');
let mainWindowTray; const DownloadIPC = require('../src/renderer/ipc/DownloadIPC');
let mountedData = {}; const FilesystemIPC = require('../src/renderer/ipc/FilesystemIPC');
let mountedLocations = []; const MountsIPC = require('../src/renderer/ipc/MountsIPC');
let expectedUnmount = {}; const PlatformIPC = require('../src/renderer/ipc/PlatformIPC');
let launchHidden = false; const ReleaseIPC = require('../src/renderer/ipc/ReleaseIPC');
let firstMountCheck = true; const StateIPC = require('../src/renderer/ipc/StateIPC');
let manualMountDetection = {}; const SystemIPC = require('../src/renderer/ipc/SystemIPC');
const UpgradeIPC = require('../src/renderer/ipc/UpgradeIPC');
let isShutdown = false; let isShutdown = false;
let isQuiting = false; let isQuiting = false;
let isInstalling = false; let isInstalling = false;
let launchHidden = false;
let mainWindow;
let mainWindowTray;
let trayContextMenu;
app.on('before-quit', function () { const closeApplication = () => {
isQuiting = true;
});
function closeApplication() {
if (!isShutdown) { if (!isShutdown) {
isShutdown = true; isShutdown = true;
if (mainWindowTray) { if (mainWindowTray) {
@@ -45,50 +50,23 @@ function closeApplication() {
} }
app.quit(); 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(); loadUiSettings();
let extra = {}; let extra = {};
@@ -140,7 +118,7 @@ function createWindow() {
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null; mainWindow = null;
unmountAllDrives(); MountsIPC.unmountAllDrives();
}); });
const appPath = (os.platform() === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) : const appPath = (os.platform() === 'win32') ? path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) :
@@ -203,39 +181,10 @@ function createWindow() {
}); });
mainWindow.loadURL(startUrl); 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', () => {
closeApplication();
});
}; };
if (!instanceLock) {
setTimeout(() => {
if ((instanceLock = app.requestSingleInstanceLock())) {
configurePrimaryApp();
} else {
closeApplication();
}
}, 3000);
} else {
configurePrimaryApp();
}
const clearManualMountDetection = (provider) => { const getMainWindow = () => {
if (manualMountDetection[provider]) { return mainWindow;
clearInterval(manualMountDetection[provider]);
delete manualMountDetection[provider];
}
}; };
const loadUiSettings = () => { const loadUiSettings = () => {
@@ -244,49 +193,61 @@ const loadUiSettings = () => {
if (fs.statSync(settingFile).isFile()) { if (fs.statSync(settingFile).isFile()) {
const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8')); const settings = JSON.parse(fs.readFileSync(settingFile, 'utf8'));
launchHidden = settings.launch_hidden; launchHidden = settings.launch_hidden;
PlatformIPC.setPlatformOverride(settings.platform_override);
} }
} catch (e) { } 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 saveUiSettings = () => {
const settingFile = path.join(helpers.getDataDirectory(), 'ui.json'); const settingFile = path.join(helpers.getDataDirectory(), 'ui.json');
try { try {
fs.writeFileSync(settingFile, JSON.stringify({ fs.writeFileSync(settingFile, JSON.stringify({
launch_hidden: launchHidden, launch_hidden: launchHidden,
platform_override: PlatformIPC.getPlatformOverride(),
}), 'utf-8'); }), 'utf-8');
} catch (e) { } 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) => { const standardIPCReply = (event, channel, data, error) => {
if (mainWindow) { if (mainWindow) {
event.sender.send(channel, { 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'], app.on('before-quit', function () {
title: data.Title, isQuiting = true;
}, (filePaths) => { });
if (filePaths && (filePaths.length > 0)) {
event.returnValue = filePaths[0]; let instanceLock = app.requestSingleInstanceLock();
if (!instanceLock) {
setTimeout(() => {
if ((instanceLock = app.requestSingleInstanceLock())) {
configurePrimaryApp();
} else { } else {
event.returnValue = '';
}
});
});
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(); 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, {});
} }
}; }, 3000);
setConfigValue(0); } else {
}); configurePrimaryApp();
}
ipcMain.on(Constants.IPC_Shutdown, () => {
closeApplication();
});
ipcMain.on(Constants.IPC_Show_Window, () => { AppIPC.addListeners(ipcMain, closeApplication, setWindowVisibility);
setWindowVisibility(true); ConfigIPC.addListeners(ipcMain, standardIPCReply);
}); DaemonIPC.addListeners(ipcMain, standardIPCReply);
DependencyIPC.addListeners(ipcMain, standardIPCReply);
ipcMain.on(Constants.IPC_Show_Window + '_sync', event => { DownloadIPC.addListeners(ipcMain, standardIPCReply);
setWindowVisibility(true); FilesystemIPC.addListeners(ipcMain, getMainWindow, dialog);
event.returnValue = true; MountsIPC.addListeners(ipcMain, setTrayImage, standardIPCReply);
}); PlatformIPC.addListeners(ipcMain, detectScript, saveUiSettings);
ReleaseIPC.addListeners(ipcMain, standardIPCReply);
ipcMain.on(Constants.IPC_Unmount_All_Drives, (event, data) => { StateIPC.addListeners(ipcMain);
unmountAllDrives(); SystemIPC.addListeners(ipcMain, closeApplication);
standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply); UpgradeIPC.addListeners(ipcMain, setIsInstalling, MountsIPC.unmountAllDrives, standardIPCReply);
});
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);
});
});

View File

@@ -1,220 +1,220 @@
{ {
"Locations": { "Locations": {
"arch": { "arch": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"centos7": { "centos7": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"darwin": { "darwin": {
"1.0.7": { "1.0.8": {
"sha256": "e4f5cf9765224d4c25472310089e0996f59772573dc59c1baa065810a16c19ba", "sha256": "fa978eeb6f5456c1fcea902cafb039eeda6d13f93770150564a0253341cb3d63",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/gBW5TSR0", "https://pixeldrain.com/api/file/6NjT6uEl",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_mac.dmg" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_mac.dmg"
] ]
} }
}, },
"debian9": { "debian9": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"debian10": { "debian10": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora28": { "fedora28": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora29": { "fedora29": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"fedora30": { "fedora30": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"opensuse15": { "opensuse15": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"opensuse15.1": { "opensuse15.1": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"solus": { "solus": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"tumbleweed": { "tumbleweed": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu18.04": { "ubuntu18.04": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu18.10": { "ubuntu18.10": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"ubuntu19.04": { "ubuntu19.04": {
"1.0.7": { "1.0.8": {
"sha256": "dac898185beb12bd6e0eaf0164f2cdcd517223981678ee18e8ec99983921ecbf", "sha256": "6bbbb94934d12aa125400a4565d645387515ed4eebd4e54e3e2319423966ec66",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/thkU0RHP", "https://pixeldrain.com/api/file/a1H78Dc_",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_linux_x86_64.AppImage" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_linux_x86_64.AppImage"
] ]
} }
}, },
"win32": { "win32": {
"1.0.7": { "1.0.8": {
"sha256": "fbf7611bca712413bd6e6900bcbd49bd3a1545b4d4e70afb2ee9bd59f74b8f97", "sha256": "1dcf2ca95ee9ce1893166e7966e4aaa465735d00476d99414c57d18e7232212f",
"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=", "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": [ "urls": [
"https://pixeldrain.com/api/file/AcsttDQ_", "https://pixeldrain.com/api/file/u_pP3IAk",
"https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.7_win.exe" "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.0.8_win.exe"
] ]
} }
} }
}, },
"Versions": { "Versions": {
"arch": [ "arch": [
"1.0.7" "1.0.8"
], ],
"centos7": [ "centos7": [
"1.0.7" "1.0.8"
], ],
"darwin": [ "darwin": [
"1.0.7" "1.0.8"
], ],
"debian9": [ "debian9": [
"1.0.7" "1.0.8"
], ],
"debian10": [ "debian10": [
"1.0.7" "1.0.8"
], ],
"fedora28": [ "fedora28": [
"1.0.7" "1.0.8"
], ],
"fedora29": [ "fedora29": [
"1.0.7" "1.0.8"
], ],
"fedora30": [ "fedora30": [
"1.0.7" "1.0.8"
], ],
"linux": [ "linux": [
"unavailable" "unavailable"
], ],
"opensuse15": [ "opensuse15": [
"1.0.7" "1.0.8"
], ],
"opensuse15.1": [ "opensuse15.1": [
"1.0.7" "1.0.8"
], ],
"solus": [ "solus": [
"1.0.7" "1.0.8"
], ],
"tumbleweed": [ "tumbleweed": [
"1.0.7" "1.0.8"
], ],
"ubuntu18.04": [ "ubuntu18.04": [
"1.0.7" "1.0.8"
], ],
"ubuntu18.10": [ "ubuntu18.10": [
"1.0.7" "1.0.8"
], ],
"ubuntu19.04": [ "ubuntu19.04": [
"1.0.7" "1.0.8"
], ],
"unknown": [ "unknown": [
"unavailable" "unavailable"
], ],
"win32": [ "win32": [
"1.0.7" "1.0.8"
] ]
} }
} }

View File

@@ -7,6 +7,7 @@ import DependencyList from './components/DependencyList/DependencyList';
import DownloadProgress from './components/DownloadProgress/DownloadProgress'; import DownloadProgress from './components/DownloadProgress/DownloadProgress';
import ErrorDetails from './components/ErrorDetails/ErrorDetails'; import ErrorDetails from './components/ErrorDetails/ErrorDetails';
import Grid from './components/UI/Grid/Grid'; import Grid from './components/UI/Grid/Grid';
import InfoDetails from './components/InfoDetails/InfoDetails';
import IPCContainer from './containers/IPCContainer/IPCContainer'; import IPCContainer from './containers/IPCContainer/IPCContainer';
import Loading from './components/UI/Loading/Loading'; import Loading from './components/UI/Loading/Loading';
import Modal from './components/UI/Modal/Modal'; 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 {notifyError} from './redux/actions/error_actions';
import Reboot from './components/Reboot/Reboot'; import Reboot from './components/Reboot/Reboot';
import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay'; 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 Text from './components/UI/Text/Text';
import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
import UpgradeUI from './components/UpgradeUI/UpgradeUI'; import UpgradeUI from './components/UpgradeUI/UpgradeUI';
@@ -30,13 +35,17 @@ class App extends IPCContainer {
componentDidMount() { componentDidMount() {
const detectUpgrades = () => { const detectUpgrades = () => {
if (this.props.AppPlatform === 'unknown') { if (this.props.AppPlatform === 'unknown') {
if (this.props.Platform === 'linux') {
this.props.displaySelectAppPlatform(true);
} else {
this.props.notifyError('Operating system is not supported.', true); this.props.notifyError('Operating system is not supported.', true);
}
} else { } else {
this.props.loadReleases(); this.props.loadReleases();
} }
}; };
this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', detectUpgrades);
detectUpgrades(); detectUpgrades();
this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', detectUpgrades);
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@@ -113,15 +122,19 @@ class App extends IPCContainer {
!this.props.DismissDependencies && !this.props.DismissDependencies &&
this.props.AllowMount; this.props.AllowMount;
const infoDisplay = this.createModalConditionally(this.props.DisplayInfo, <InfoDetails/>, true);
const rebootDisplay = this.createModalConditionally(this.props.RebootRequired, <Reboot />); const rebootDisplay = this.createModalConditionally(this.props.RebootRequired, <Reboot />);
const configDisplay = this.createModalConditionally(showConfig, <Configuration version={selectedVersion} />); const configDisplay = this.createModalConditionally(showConfig, <Configuration version={selectedVersion} />);
const dependencyDisplay = this.createModalConditionally(showDependencies, <DependencyList/>); const dependencyDisplay = this.createModalConditionally(showDependencies, <DependencyList/>);
const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, <DownloadProgress/>); const downloadDisplay = this.createModalConditionally(this.props.DownloadActive, <DownloadProgress/>);
const errorDisplay = this.createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true); const errorDisplay = this.createModalConditionally(this.props.DisplayError, <ErrorDetails/>, true);
const upgradeDisplay = this.createModalConditionally(showUpgrade, <UpgradeUI/>); const upgradeDisplay = this.createModalConditionally(showUpgrade, <UpgradeUI/>);
const selectAppPlatformDisplay = this.createModalConditionally(this.props.DisplaySelectAppPlatform, <SelectAppPlatform/>);
let mainContent = []; let mainContent = [];
if (this.props.AppReady) { if (this.props.DisplaySelectAppPlatform || !this.props.AppReady) {
mainContent = <Loading/>
} else {
let key = 0; let key = 0;
mainContent.push(( mainContent.push((
<div key={'rvd_' + key++} <div key={'rvd_' + key++}
@@ -140,18 +153,18 @@ class App extends IPCContainer {
</div> </div>
)); ));
} }
} else {
mainContent = <Loading/>
} }
return ( return (
<div className={'App'}> <div className={'App'}>
{errorDisplay} {selectAppPlatformDisplay}
{dependencyDisplay} {dependencyDisplay}
{upgradeDisplay} {upgradeDisplay}
{downloadDisplay}
{configDisplay} {configDisplay}
{infoDisplay}
{downloadDisplay}
{rebootDisplay} {rebootDisplay}
{errorDisplay}
<div className={'AppContainer'}> <div className={'AppContainer'}>
<div className={'AppHeader'}> <div className={'AppHeader'}>
<Box> <Box>
@@ -193,12 +206,15 @@ const mapStateToProps = state => {
DismissDependencies: state.install.DismissDependencies, DismissDependencies: state.install.DismissDependencies,
DisplayConfiguration: state.mounts.DisplayConfiguration, DisplayConfiguration: state.mounts.DisplayConfiguration,
DisplayError: state.error.DisplayError, DisplayError: state.error.DisplayError,
DisplayInfo: state.error.DisplayInfo,
DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform,
DownloadActive: state.download.DownloadActive, DownloadActive: state.download.DownloadActive,
InstallActive: state.install.InstallActive, InstallActive: state.install.InstallActive,
InstalledVersion: state.relver.InstalledVersion, InstalledVersion: state.relver.InstalledVersion,
LocationsLookup: state.relver.LocationsLookup, LocationsLookup: state.relver.LocationsLookup,
MissingDependencies: state.install.MissingDependencies, MissingDependencies: state.install.MissingDependencies,
MountsBusy: state.mounts.MountsBusy, MountsBusy: state.mounts.MountsBusy,
Platform: state.common.Platform,
ProviderState: state.mounts.ProviderState, ProviderState: state.mounts.ProviderState,
RebootRequired: state.common.RebootRequired, RebootRequired: state.common.RebootRequired,
Release: state.relver.Release, Release: state.relver.Release,
@@ -212,6 +228,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
displaySelectAppPlatform: display => dispatch(displaySelectAppPlatform(display)),
loadReleases: ()=> dispatch(loadReleases()), loadReleases: ()=> dispatch(loadReleases()),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
saveState: () => dispatch(saveState()), saveState: () => dispatch(saveState()),

29
src/assets/settings.json Normal file
View File

@@ -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."
}
}

View File

@@ -34,3 +34,7 @@ input.ConfigurationItemInput {
border-color: rgba(10, 10, 20, 0.9); border-color: rgba(10, 10, 20, 0.9);
color: var(--text_color); color: var(--text_color);
} }
.ConfigurationInfo {
cursor: pointer;
}

View File

@@ -1,7 +1,18 @@
import React from 'react'; import React from 'react';
import './ConfigurationItem.css'; 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 handleChanged = (e) => {
const target = e.target; const target = e.target;
if (target.type === 'checkbox') { if (target.type === 'checkbox') {
@@ -10,6 +21,18 @@ export default props => {
props.changed(target); 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 = <a href={void(0)}
className={'ConfigurationInfo'}
onClick={()=>{displayInfo(); return false;}}><FontAwesomeIcon icon={faInfoCircle}/></a>;
}
let data; let data;
switch (props.template.type) { switch (props.template.type) {
case "bool": case "bool":
@@ -96,11 +119,13 @@ export default props => {
width='100%'> width='100%'>
<tbody> <tbody>
<tr> <tr>
<td width='100%'>{props.label}</td> {infoDisplay ?
<td width='100%'>{infoDisplay} {props.label}</td> :
<td width='100%'>{props.label}</td>}
<td>{data}</td> <td>{data}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
); );
}; });

View File

@@ -22,8 +22,7 @@ export default connect(mapStateToProps)(props => {
</td> </td>
<td> <td>
{props.AllowDownload ? {props.AllowDownload ?
<a <a href={void(0)}
href={void(0)}
className={'DependencyLink'} className={'DependencyLink'}
onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> : onClick={()=>{props.onDownload(); return false;}}><u>Install</u></a> :
'Installing...'} 'Installing...'}

View File

@@ -0,0 +1,11 @@
.InfoDetailsHeading {
text-align: center;
margin-bottom: 4px;
}
.InfoDetailsContent {
max-height: 60vh;
min-width: 80vw;
overflow-y: auto;
margin-bottom: 8px;
}

View File

@@ -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 (
<Box dxDark dxStyle={{padding: '8px'}}>
<h1 className={'InfoDetailsHeading'}>{props.InfoMessage.title}</h1>
<div className={'InfoDetailsContent'}>
<p style={{textAlign: 'left', whiteSpace: 'pre-line'}}>{props.InfoMessage.message}</p>
</div>
<Button clicked={props.dismissInfo}>Dismiss</Button>
</Box>
);
});

View File

@@ -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_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 = { exports.DATA_LOCATIONS = {
linux: '~/.local/repertory/ui', linux: '~/.local/repertory/ui',
darwin: '~/Library/Application Support/repertory/ui', darwin: '~/Library/Application Support/repertory/ui',
@@ -62,6 +68,7 @@ exports.RELEASE_TYPES = [
exports.INSTALL_TYPES = { exports.INSTALL_TYPES = {
Dependency: 'dependency', Dependency: 'dependency',
Release: 'release', Release: 'release',
TestRelease: 'test_release',
Upgrade: 'upgrade', 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 = 'set_config_values';
exports.IPC_Set_Config_Values_Reply = 'set_config_values_reply'; 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_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 = 'unmount_all';
exports.IPC_Unmount_All_Drives_Reply = 'unmount_all_reply'; exports.IPC_Unmount_All_Drives_Reply = 'unmount_all_reply';

View File

@@ -231,6 +231,7 @@ class Configuration extends IPCContainer {
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
<ConfigurationItem advanced={k.advanced} <ConfigurationItem advanced={k.advanced}
changed={e=>this.handleItemChanged(e, i)} changed={e=>this.handleItemChanged(e, i)}
grouping={'Settings'}
items={this.state.Template[k.label].items} items={this.state.Template[k.label].items}
key={i} key={i}
label={k.label} label={k.label}
@@ -251,6 +252,7 @@ class Configuration extends IPCContainer {
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ? ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
<ConfigurationItem advanced={k.advanced} <ConfigurationItem advanced={k.advanced}
changed={e=>this.handleObjectItemChanged(e, key, i)} changed={e=>this.handleObjectItemChanged(e, key, i)}
grouping={key}
items={this.state.Template[key].template[k.label].items} items={this.state.Template[key].template[k.label].items}
key={i} key={i}
label={k.label} label={k.label}

View File

@@ -171,9 +171,13 @@ class MountItems extends IPCContainer {
} }
} else { } else {
allowAction = false; allowAction = false;
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); this.displayRetryMount(provider, location, 'Version check failed: ' + result.Error);
} }
} }
}
if (allowAction) { if (allowAction) {
this.props.setMountsBusy(true); this.props.setMountsBusy(true);

View File

@@ -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;
}

View File

@@ -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 (
<Box dxDark dxStyle={{padding: '8px'}}>
<h1 className={'SAPHeading'}>Select Linux Platform</h1>
<div className={'SAPContent'}>
<p>Repertory was unable to detect your Linux distribution. Please select one of the following and click <b>Test</b> to continue:</p>
</div>
<div className={'SAPActions'}>
<DropDown changed={e => this.setState({
...this.state,
Selected: e.target.value
})}
disabled={this.props.InstallTestActive}
items={Constants.LINUX_SELECTABLE_PLATFORMS}
selected={this.state.Selected}/>
<Button clicked={this.handleTestClicked}
disabled={this.props.InstallTestActive}>Test</Button>
</div>
</Box>
);
}
}
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);

View File

@@ -705,6 +705,23 @@ module.exports.stopMountProcessSync = (version, provider) => {
process.unref(); 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) => { module.exports.verifyHash = (file, hash) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const platform = os.platform(); const platform = os.platform();

View File

@@ -4,8 +4,17 @@ import {getIPCRenderer} from '../../utils';
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
export const displaySelectAppPlatform = display => {
return dispatch => {
if (display) {
dispatch(showWindow());
}
dispatch(setDisplaySelectAppPlatform(display));
};
};
export const notifyRebootRequired = createAction('common/notifyRebootRequired'); export const notifyRebootRequired = createAction('common/notifyRebootRequired');
export const setAllowMount = createAction('common/setAllowMount');
export const rebootSystem = () => { export const rebootSystem = () => {
return dispatch => { return dispatch => {
@@ -16,7 +25,19 @@ export const rebootSystem = () => {
} }
}; };
export const setAllowMount = createAction('common/setAllowMount');
export const setApplicationReady = createAction('common/setApplicationReady'); 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 = () => { export const setRebootRequired = () => {
return dispatch => { return dispatch => {
dispatch(showWindow()); dispatch(showWindow());

View File

@@ -3,6 +3,7 @@ import {createAction} from 'redux-starter-kit';
import {getIPCRenderer} from '../../utils'; import {getIPCRenderer} from '../../utils';
import {notifyError} from './error_actions'; import {notifyError} from './error_actions';
import { import {
installAndTestRelease,
installDependency, installDependency,
installRelease, installRelease,
installUpgrade installUpgrade
@@ -25,7 +26,7 @@ export const setDownloadBegin = (name, type, url) => {
export const setDownloadEnd = createAction('download/setDownloadEnd'); export const setDownloadEnd = createAction('download/setDownloadEnd');
export const setDownloadProgress = createAction('download/setDownloadProgress'); 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) => { return (dispatch, getState) => {
if (!Array.isArray(urls)) { if (!Array.isArray(urls)) {
urls = [urls]; urls = [urls];
@@ -40,6 +41,9 @@ export const downloadItem = (name, type, urls, isWinFSP) => {
case Constants.INSTALL_TYPES.Release: case Constants.INSTALL_TYPES.Release:
dispatch(installRelease(result.Destination)); dispatch(installRelease(result.Destination));
break; break;
case Constants.INSTALL_TYPES.TestRelease:
dispatch(installAndTestRelease(result.Destination, testVersion, appPlatform));
break;
case Constants.INSTALL_TYPES.Upgrade: case Constants.INSTALL_TYPES.Upgrade:
//const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]]; //const info = this.props.LocationsLookup[this.props.AppPlatform][this.props.VersionLookup[this.props.AppPlatform][0]];
const sha256 = null;//info.sha256; const sha256 = null;//info.sha256;
@@ -51,6 +55,9 @@ export const downloadItem = (name, type, urls, isWinFSP) => {
break; break;
} }
} else { } else {
if (type === Constants.INSTALL_TYPES.TestRelease) {
this.props.setAllowDownload(false);
}
dispatch(notifyError(result.Error)); dispatch(notifyError(result.Error));
} }
}; };

View File

@@ -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 = () => { export const dismissError = () => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(clearError()); dispatch(clearError());
@@ -27,6 +35,12 @@ export const dismissError = () => {
}; };
}; };
export const dismissInfo = () => {
return dispatch => {
dispatch(clearInfo());
};
};
export const notifyError = (msg, critical, callback) => { export const notifyError = (msg, critical, callback) => {
return dispatch => { return dispatch => {
ErrorActions = [callback, ...ErrorActions]; 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 SET_ERROR_INFO = 'error/setErrorInfo';
export const setErrorInfo = (msg, critical) => { export const setErrorInfo = (msg, critical) => {
return { return {
@@ -46,3 +68,15 @@ export const setErrorInfo = (msg, critical) => {
} }
} }
}; };
export const SET_INFO = 'error/setInfo';
export const setInfo = (title, msg) => {
return {
type: SET_INFO,
payload: {
title,
msg
}
}
};

View File

@@ -7,13 +7,16 @@ import {
import {notifyError} from './error_actions'; import {notifyError} from './error_actions';
import {setAllowDownload} from './download_actions'; import {setAllowDownload} from './download_actions';
import { import {
loadReleases,
setActiveRelease, setActiveRelease,
setInstalledVersion, setInstalledVersion,
setReleaseUpgradeAvailable setReleaseUpgradeAvailable
} from './release_version_actions'; } from './release_version_actions';
import { import {
displaySelectAppPlatform,
setAllowMount, setAllowMount,
setApplicationReady, setApplicationReady,
setLinuxAppPlatform,
setRebootRequired, setRebootRequired,
showWindow, showWindow,
shutdownApplication 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 => { export const installRelease = source => {
return (dispatch, getState) => { return (dispatch, getState) => {
if (ipcRenderer && !getState().install.InstallActive) { if (ipcRenderer && !getState().install.InstallActive) {
@@ -193,5 +233,6 @@ export const installUpgrade = (source, sha256, signature, skipVerification) => {
export const setDismissDependencies = createAction('install/setDismissDependencies'); export const setDismissDependencies = createAction('install/setDismissDependencies');
export const setInstallActive = createAction('install/setInstallActive'); export const setInstallActive = createAction('install/setInstallActive');
export const setInstallTestActive = createAction('install/setInstallTestActive');
export const setInstallComplete = createAction('install/setInstallComplete'); export const setInstallComplete = createAction('install/setInstallComplete');
export const setMissingDependencies = createAction('install/setMissingDependencies'); export const setMissingDependencies = createAction('install/setMissingDependencies');

View File

@@ -3,6 +3,8 @@ import {
notifyRebootRequired, notifyRebootRequired,
setAllowMount, setAllowMount,
setApplicationReady, setApplicationReady,
setLinuxAppPlatform,
SET_DISPLAY_SELECT_APPPLATFORM
} from '../actions/common_actions'; } from '../actions/common_actions';
export const createCommonReducer = (platform, appPlatform, version) => { export const createCommonReducer = (platform, appPlatform, version) => {
@@ -10,10 +12,17 @@ export const createCommonReducer = (platform, appPlatform, version) => {
AllowMount: false, AllowMount: false,
AppPlatform: appPlatform, AppPlatform: appPlatform,
AppReady: false, AppReady: false,
DisplaySelectAppPlatform: false,
Platform: platform, Platform: platform,
RebootRequired: false, RebootRequired: false,
Version: version, Version: version,
}, { }, {
[SET_DISPLAY_SELECT_APPPLATFORM]: (state, action) => {
return {
...state,
DisplaySelectAppPlatform: action.payload,
}
},
[setAllowMount]: (state, action) => { [setAllowMount]: (state, action) => {
return { return {
...state, ...state,
@@ -26,6 +35,12 @@ export const createCommonReducer = (platform, appPlatform, version) => {
AppReady: action.payload, AppReady: action.payload,
}; };
}, },
[setLinuxAppPlatform]: (state, action) => {
return {
...state,
AppPlatform: action.payload,
}
},
[notifyRebootRequired]: (state, action) => { [notifyRebootRequired]: (state, action) => {
return { return {
...state, ...state,

View File

@@ -1,13 +1,17 @@
import {createReducer} from 'redux-starter-kit'; import {createReducer} from 'redux-starter-kit';
import { import {
CLEAR_ERROR, CLEAR_ERROR,
SET_ERROR_INFO CLEAR_INFO,
SET_ERROR_INFO,
SET_INFO
} from '../actions/error_actions'; } from '../actions/error_actions';
export const errorReducer = createReducer({ export const errorReducer = createReducer({
DisplayError: false, DisplayError: false,
DisplayInfo: false,
ErrorCritical: false, ErrorCritical: false,
ErrorStack: [], ErrorStack: [],
InfoStack: [],
}, { }, {
[CLEAR_ERROR]: state => { [CLEAR_ERROR]: state => {
const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : []; const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : [];
@@ -17,6 +21,14 @@ export const errorReducer = createReducer({
ErrorStack: errorStack, 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) => { [SET_ERROR_INFO]: (state, action) => {
const errorStack = [action.payload.msg, ...state.ErrorStack]; const errorStack = [action.payload.msg, ...state.ErrorStack];
return { return {
@@ -25,5 +37,16 @@ export const errorReducer = createReducer({
ErrorCritical: state.ErrorCritical || action.payload.critical, ErrorCritical: state.ErrorCritical || action.payload.critical,
ErrorStack: errorStack, ErrorStack: errorStack,
} }
},
[SET_INFO]: (state, action) => {
const infoStack = [{
title: action.payload.title,
message: action.payload.msg
}, ...state.InfoStack];
return {
...state,
DisplayInfo: true,
InfoStack: infoStack,
}
} }
}); });

View File

@@ -3,6 +3,7 @@ import {
setDismissDependencies, setDismissDependencies,
setInstallActive, setInstallActive,
setInstallComplete, setInstallComplete,
setInstallTestActive,
setMissingDependencies setMissingDependencies
} from '../actions/install_actions'; } from '../actions/install_actions';
@@ -10,6 +11,7 @@ export const installReducer = createReducer({
DismissDependencies: false, DismissDependencies: false,
InstallActive: false, InstallActive: false,
InstallResult: null, InstallResult: null,
InstallTestActive: false,
InstallType: null, InstallType: null,
MissingDependencies: [], MissingDependencies: [],
}, { }, {
@@ -35,6 +37,12 @@ export const installReducer = createReducer({
InstallType: null, InstallType: null,
} }
}, },
[setInstallTestActive]: (state, action) => {
return {
...state,
InstallTestActive: action.payload,
}
},
[setMissingDependencies]: (state, action) => { [setMissingDependencies]: (state, action) => {
return { return {
...state, ...state,

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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