[Continue S3 support] [Handle set configuration errors]

This commit is contained in:
2020-08-15 22:29:55 -05:00
parent 25e689b033
commit d8c87872d2
8 changed files with 119 additions and 78 deletions

View File

@@ -51,6 +51,14 @@ exports.DATA_LOCATIONS = {
win32: '%LOCALAPPDATA%\\repertory\\ui' win32: '%LOCALAPPDATA%\\repertory\\ui'
}; };
exports.S3_PROVIDER_LIST = [
'Goobox S3',
];
exports.S3_PROVIDER_URL = {
'Goobox S3': 'https://sias3.goobox.io',
};
exports.PROVIDER_LIST = [ exports.PROVIDER_LIST = [
'Sia', 'Sia',
'Skynet', 'Skynet',

View File

@@ -8,6 +8,8 @@ import Text from '../../components/UI/Text/Text';
import {notifyError} from '../../redux/actions/error_actions'; import {notifyError} from '../../redux/actions/error_actions';
import {addRemoteMount, addS3Mount} from '../../redux/actions/mount_actions'; import {addRemoteMount, addS3Mount} from '../../redux/actions/mount_actions';
import {createModalConditionally} from '../../utils'; import {createModalConditionally} from '../../utils';
import DropDown from '../../components/UI/DropDown/DropDown';
import * as Constants from '../../constants';
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
@@ -19,7 +21,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)), addRemoteMount: (hostNameOrIp, port, token) => dispatch(addRemoteMount(hostNameOrIp, port, token)),
addS3Mount: (name, accessKey, secretKey, region, bucketName) => dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName)), addS3Mount: (name, accessKey, secretKey, region, bucketName, url) => dispatch(addS3Mount(name, accessKey, secretKey, region, bucketName, url)),
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
} }
}; };
@@ -32,6 +34,7 @@ const default_state = {
HostNameOrIp: '', HostNameOrIp: '',
Name: '', Name: '',
Port: 20000, Port: 20000,
Provider: '',
Region: 'any', Region: 'any',
SecretKey: '', SecretKey: '',
Token: '', Token: '',
@@ -51,7 +54,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
this.props.notifyError('Remote host already exists'); this.props.notifyError('Remote host already exists');
} else { } else {
this.setState({ this.setState({
Display: false DisplayRemote: false
}, () => { }, () => {
this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token); this.props.addRemoteMount(this.state.HostNameOrIp, this.state.Port, this.state.Token);
this.setState({ this.setState({
@@ -63,7 +66,28 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
}; };
addS3Mount = () => { addS3Mount = () => {
if (this.state.Name.length === 0) {
this.props.notifyError('Name cannot be empty.');
} else if (this.state.AccessKey.length === 0) {
this.props.notifyError('AccessKey cannot be empty.');
} else if (this.state.SecretKey.length === 0) {
this.props.notifyError('SecretKey cannot be empty.')
} else {
const provider = 'S3' + this.state.Name;
if (this.props.S3Mounts.includes(provider)) {
this.props.notifyError('Remote host already exists');
} else {
this.setState({
DisplayS3: false
}, () => {
this.props.addS3Mount(this.state.Name, this.state.AccessKey, this.state.SecretKey,
this.state.Region, this.state.BucketName, Constants.S3_PROVIDER_URL[this.state.Provider]);
this.setState({
...default_state,
});
});
}
}
}; };
handleAddS3Mount = () => { handleAddS3Mount = () => {
@@ -112,21 +136,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
type={'text'} type={'text'}
value={this.state.Token}/> value={this.state.Token}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{paddingTop: 'var(--default_spacing)'}}/>
<table cellSpacing={1} <div style={{display: 'flex', flexDirection: 'row'}}>
width="100%"> <Button buttonStyles={{width: '100%'}}
<tbody> clicked={() => this.addRemoteMount()}>OK</Button>
<tr> <div style={{paddingLeft: 'var(--default_spacing)'}}/>
<td width="50%"> <Button buttonStyles={{width: '100%'}}
<Button buttonStyles={{width: '100%'}} clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button>
clicked={() => this.addRemoteMount()}>OK</Button> </div>
</td>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayRemote: false})}>Cancel</Button>
</td>
</tr>
</tbody>
</table>
</Box> </Box>
)); ));
@@ -135,33 +151,49 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}> dxStyle={{width: 'auto', height: 'auto', padding: 'var(--default_spacing)'}}>
<h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3 <h1 style={{textAlign: 'center', paddingBottom: 'var(--default_spacing)'}}>Add S3
Mount</h1> Mount</h1>
<Text text={'Name'} <div style={{display: 'flex', flexDirection: 'row'}}>
textAlign={'left'} <Text text={'Name'}
type={'Heading2'}/> textAlign={'left'}
<input onChange={e => this.setState({Name: e.target.value.trim()})} type={'Heading2'}/>
className={'ConfigurationItemInput'} <div style={{paddingLeft: 'var(--default_spacing)'}}/>
style={{width: '100%'}} <Text text={'Provider'}
type={'text'} textAlign={'left'}
value={this.state.Name}/> type={'Heading2'}/>
</div>
<div style={{display: 'flex', flexDirection: 'row'}}>
<input onChange={e => this.setState({Name: e.target.value.trim()})}
className={'ConfigurationItemInput'}
style={{width: '100%'}}
type={'text'}
value={this.state.Name}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<DropDown changed={e => this.setState({Provider: e.target.value})}
items={Constants.S3_PROVIDER_LIST}
selected={Constants.S3_PROVIDER_LIST[0]}/>
</div>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Bucket Name (optional)'} <div style={{display: 'flex', flexDirection: 'row'}}>
textAlign={'left'} <Text text={'Bucket Name (optional)'}
type={'Heading2'}/> textAlign={'left'}
<input onChange={e => this.setState({BucketName: e.target.value})} type={'Heading2'}/>
className={'ConfigurationItemInput'} <div style={{paddingLeft: 'var(--default_spacing)'}}/>
style={{width: '100%'}} <Text text={'Region'}
type={'text'} textAlign={'left'}
value={this.state.BucketName}/> type={'Heading2'}/>
</div>
<div style={{display: 'flex', flexDirection: 'row'}}>
<input onChange={e => this.setState({BucketName: e.target.value})}
className={'ConfigurationItemInput'}
style={{width: '100%'}}
type={'text'}
value={this.state.BucketName}/>
<div style={{paddingLeft: 'var(--default_spacing)'}}/>
<input onChange={e => this.setState({Region: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Region}/>
</div>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{paddingTop: 'var(--default_spacing)'}}/>
<Text text={'Region'}
textAlign={'left'}
type={'Heading2'}/>
<input onChange={e => this.setState({Region: e.target.value})}
className={'ConfigurationItemInput'}
type={'text'}
value={this.state.Region}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{display: 'flex', flexDirection: 'row'}}>
<Text text={'Access Key'} <Text text={'Access Key'}
textAlign={'left'} textAlign={'left'}
@@ -182,23 +214,15 @@ export default connect(mapStateToProps, mapDispatchToProps)(class extends Compon
type={'text'} type={'text'}
value={this.state.SecretKey}/> value={this.state.SecretKey}/>
</div> </div>
<div style={{paddingTop: 'calc(var(--default_spacing) * 2)'}}/>
<div style={{paddingTop: 'var(--default_spacing)'}}/> <div style={{display: 'flex', flexDirection: 'row'}}>
<table cellSpacing={1} <div style={{width: '200%'}}/>
width="100%"> <Button buttonStyles={{width: '100%'}}
<tbody> clicked={() => this.addS3Mount()}>OK</Button>
<tr> <div style={{paddingLeft: 'var(--default_spacing)'}}/>
<td width="50%"> <Button buttonStyles={{width: '100%'}}
<Button buttonStyles={{width: '100%'}} clicked={() => this.setState({DisplayS3: false})}>Cancel</Button>
clicked={() => this.addS3Mount()}>OK</Button> </div>
</td>
<td width="50%">
<Button buttonStyles={{width: '100%'}}
clicked={() => this.setState({DisplayS3: false})}>Cancel</Button>
</td>
</tr>
</tbody>
</table>
</Box> </Box>
)); ));

View File

@@ -183,7 +183,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => {
<Text <Text
col={configButton ? 6 : 0} col={configButton ? 6 : 0}
rowSpan={5} rowSpan={5}
text={props.remote ? props.provider.substr(6) : props.provider} text={props.remote ? props.provider.substr(6) : props.s3 ? props.provider.substr(2) : props.provider}
type={'Heading2'}/> type={'Heading2'}/>
{((props.provider === 'Skynet') && (props.MState.Mounted)) ? ( {((props.provider === 'Skynet') && (props.MState.Mounted)) ? (
<a href={'#'} <a href={'#'}

View File

@@ -80,6 +80,7 @@ class MountItems extends IPCContainer {
this.sendRequest(Constants.IPC_Detect_Mount, { this.sendRequest(Constants.IPC_Detect_Mount, {
RemoteMounts: this.props.RemoteMounts, RemoteMounts: this.props.RemoteMounts,
S3Mounts: this.props.S3Mounts,
Provider: provider, Provider: provider,
Version: this.props.InstalledVersion, Version: this.props.InstalledVersion,
}); });

View File

@@ -424,7 +424,7 @@ module.exports.downloadFile = (url, destination, progressCallback, completeCallb
try { try {
const total = parseInt(response.headers['content-length'], 10); const total = parseInt(response.headers['content-length'], 10);
if (total === 0) { if (total === 0) {
completeCallback(Error('No data available for download')); completeCallback(new Error('No data available for download'));
} else { } else {
const stream = fs.createWriteStream(destination); const stream = fs.createWriteStream(destination);
@@ -440,9 +440,9 @@ module.exports.downloadFile = (url, destination, progressCallback, completeCallb
response.data.on('end', () => { response.data.on('end', () => {
stream.end(() => { stream.end(() => {
if (downloaded === 0) { if (downloaded === 0) {
completeCallback(Error('Received 0 bytes')); completeCallback(new Error('Received 0 bytes'));
} else if (downloaded !== total) { } else if (downloaded !== total) {
completeCallback(Error('Received incorrect number of bytes')); completeCallback(new Error('Received incorrect number of bytes'));
} else { } else {
completeCallback(); completeCallback();
} }
@@ -731,7 +731,7 @@ module.exports.getDataDirectory = _getDataDirectory;
module.exports.getMissingDependencies = dependencies => { module.exports.getMissingDependencies = dependencies => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!dependencies) { if (!dependencies) {
reject(Error('Dependency list is invalid')); reject(new Error('Dependency list is invalid'));
} }
let missing = []; let missing = [];
@@ -782,7 +782,7 @@ module.exports.getMissingDependencies = dependencies => {
hive = Registry.HKU; hive = Registry.HKU;
break; break;
default: default:
throw Error('Invalid registry hive: ' + hiveName); throw new Error('Invalid registry hive: ' + hiveName);
} }
const key = dep.registry[index].substr(hiveName.length); const key = dep.registry[index].substr(hiveName.length);
@@ -996,8 +996,12 @@ module.exports.setConfigValue = (name, value, provider, remote, s3, version) =>
reject(err); reject(err);
}); });
process.on('exit', () => { process.on('exit', code => {
resolve(); if (code !== 0) {
reject(new Error('Failed to set configuration value: ' + code));
} else {
resolve();
}
}); });
process.unref(); process.unref();
@@ -1059,7 +1063,7 @@ module.exports.testRepertoryBinary = version => {
if (code === 0) { if (code === 0) {
resolve(); resolve();
} else { } else {
reject(Error('Invalid exit code: ' + code)); reject(new Error('Invalid exit code: ' + code));
} }
}) })
.catch(error => { .catch(error => {
@@ -1080,7 +1084,7 @@ module.exports.verifyHash = (file, hash) => {
command = 'sha256sum'; command = 'sha256sum';
args = ['-b', file, '-z']; args = ['-b', file, '-z'];
} else { } else {
reject(Error('Platform not supported: ' + os.platform())) reject(new Error('Platform not supported: ' + os.platform()))
} }
if (command) { if (command) {
execFile(command, args, (err, stdout) => { execFile(command, args, (err, stdout) => {
@@ -1091,7 +1095,7 @@ module.exports.verifyHash = (file, hash) => {
if (hash2 === hash.toLowerCase()) { if (hash2 === hash.toLowerCase()) {
resolve(hash2); resolve(hash2);
} else { } else {
reject(Error('Checksum failed for file')); reject(new Error('Checksum failed for file'));
} }
} }
}); });
@@ -1129,13 +1133,13 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => {
} }
}); });
} else { } else {
reject(Error('Failed to locate \'openssl.exe\'')); reject(new Error('Failed to locate \'openssl.exe\''));
} }
}); });
} else if (os.platform() === 'linux') { } else if (os.platform() === 'linux') {
executeVerify('openssl'); executeVerify('openssl');
} else { } else {
reject(Error('Platform not supported: ' + os.platform())) reject(new Error('Platform not supported: ' + os.platform()))
} }
}); });
}; };

View File

@@ -11,14 +11,15 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
const provider = 'Remote' + hostNameOrIp + ':' + port; const provider = 'Remote' + hostNameOrIp + ':' + port;
dispatch(addRemoteMount2(provider));
dispatch(setBusy(true)); dispatch(setBusy(true));
ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => { ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
dispatch(addRemoteMount2(provider));
ipcRenderer.send(Constants.IPC_Detect_Mount, { ipcRenderer.send(Constants.IPC_Detect_Mount, {
Provider: provider, Provider: provider,
RemoteMounts: getState().mounts.RemoteMounts, RemoteMounts: getState().mounts.RemoteMounts,
S3Mounts: getState().mounts.S3Mounts,
Version: getState().relver.InstalledVersion, Version: getState().relver.InstalledVersion,
}); });
} else { } else {
@@ -42,18 +43,19 @@ export const addRemoteMount = (hostNameOrIp, port, token) => {
}; };
}; };
export const addS3Mount = (name, accessKey, secretKey, region, bucketName) => { export const addS3Mount = (name, accessKey, secretKey, region, bucketName, url) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const ipcRenderer = getIPCRenderer(); const ipcRenderer = getIPCRenderer();
const provider = 'S3' + name; const provider = 'S3' + name;
dispatch(addS3Mount2(provider));
dispatch(setBusy(true)); dispatch(setBusy(true));
ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => { ipcRenderer.once(Constants.IPC_Set_Config_Values_Reply, (_, arg) => {
if (arg.data.Success) { if (arg.data.Success) {
dispatch(addS3Mount2(provider));
ipcRenderer.send(Constants.IPC_Detect_Mount, { ipcRenderer.send(Constants.IPC_Detect_Mount, {
Provider: provider, Provider: provider,
RemoteMounts: getState().mounts.RemoteMounts,
S3Mounts: getState().mounts.S3Mounts, S3Mounts: getState().mounts.S3Mounts,
Version: getState().relver.InstalledVersion, Version: getState().relver.InstalledVersion,
}); });
@@ -70,6 +72,7 @@ export const addS3Mount = (name, accessKey, secretKey, region, bucketName) => {
{Name: 'S3Config.SecretKey', Value: secretKey}, {Name: 'S3Config.SecretKey', Value: secretKey},
{Name: 'S3Config.Region', Value: region}, {Name: 'S3Config.Region', Value: region},
{Name: 'S3Config.BucketName', Value: bucketName}, {Name: 'S3Config.BucketName', Value: bucketName},
{Name: 'S3Config.URL', Value: url},
], ],
Provider: provider, Provider: provider,
S3: true, S3: true,

View File

@@ -40,8 +40,8 @@ const addListeners = (ipcMain, {standardIPCReply}) => {
.then(() => { .then(() => {
setConfigValue(++i); setConfigValue(++i);
}) })
.catch(() => { .catch(error => {
setConfigValue(++i); standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}, error);
}); });
} else { } else {
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {}); standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});

View File

@@ -93,6 +93,7 @@ const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
const providerList = [ const providerList = [
...Constants.PROVIDER_LIST, ...Constants.PROVIDER_LIST,
...data.RemoteMounts, ...data.RemoteMounts,
...data.S3Mounts,
]; ];
for (const provider of providerList) { for (const provider of providerList) {
driveLetters[provider] = []; driveLetters[provider] = [];