Add proxy support (with hopping) to ConfigEditor

pull/133/head
Kelvin Schoofs 6 years ago
parent 7af55ac29e
commit 60ced3eb49

@ -5,9 +5,11 @@ import { FieldNumber } from 'src/FieldTypes/number';
import { FieldPath } from 'src/FieldTypes/path';
import { FieldString } from 'src/FieldTypes/string';
import { FileSystemConfig, invalidConfigName } from 'src/types/fileSystemConfig';
import { PROXY_FIELD } from './proxyFields';
export type FieldChanged<K = string, V = any> = (field: K, newValue: V) => void;
export type FSCChanged<K extends keyof FileSystemConfig = keyof FileSystemConfig & string> = FieldChanged<K, FileSystemConfig[K]>;
export type FSCChangedMultiple = (newConfig: Partial<FileSystemConfig>) => void;
function pathValidator(value?: string): string | null {
if (!value) return null;
@ -100,5 +102,8 @@ export function passphrase(config: FileSystemConfig, onChange: FSCChanged<'passp
return <FieldDropdownWithInput key="passphrase" label="Passphrase" {...{ value, values, description }} onChange={callback} optional={true} />
}
type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged) => React.ReactElement;
export const FIELDS: FieldFactory[] = [name, merge, label, putty, host, port, root, agent, username, password, privateKeyPath, passphrase];
export type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple) => React.ReactElement | null;
export const FIELDS: FieldFactory[] = [
name, merge, label, putty, host, port,
root, agent, username, password, privateKeyPath, passphrase,
PROXY_FIELD];

@ -31,7 +31,7 @@ class ConfigEditor extends React.Component<StateProps & DispatchProps> {
<h4>{formatConfigLocation(oldConfig._location!)}</h4>
</div>
</div>
{Fields.FIELDS.map(f => f(newConfig, this.onChange)).filter(e => e)}
{Fields.FIELDS.map(f => f(newConfig, this.onChange, this.onChangeMultiple)).filter(e => e)}
<div className="divider" />
<FieldGroup.Consumer>{group => <React.Fragment>
<button className="cancel" onClick={this.props.cancel}>Cancel</button>
@ -45,6 +45,10 @@ class ConfigEditor extends React.Component<StateProps & DispatchProps> {
console.log(`Changed field '${key}' to: ${value}`);
this.props.setNewConfig({ ...this.props.newConfig, [key]: value });
};
protected onChangeMultiple: Fields.FSCChangedMultiple = (newConfig) => {
console.log('Overwriting config fields:', newConfig);
this.props.setNewConfig({ ...this.props.newConfig, ...newConfig });
};
}
interface SubState { view: IConfigEditorState };

@ -0,0 +1,83 @@
import * as React from 'react';
import { FieldDropdown } from 'src/FieldTypes/dropdown';
import { FieldDropdownWithInput } from 'src/FieldTypes/dropdownwithinput';
import { FieldNumber } from 'src/FieldTypes/number';
import { FieldString } from 'src/FieldTypes/string';
import { FileSystemConfig } from 'src/types/fileSystemConfig';
import { FieldFactory, FSCChanged, FSCChangedMultiple } from './fields';
export function proxy(config: FileSystemConfig, onChange: FSCChanged<'proxy'>): React.ReactElement {
const onChangeHost = (host: string) => onChange('proxy', { ...config.proxy!, host });
const onChangePort = (port: number) => onChange('proxy', { ...config.proxy!, port });
console.log('Current config:', config);
return <React.Fragment>
<p>{new Date().toString()}</p>
<FieldString label="Proxy Host" value={config.proxy!.host} onChange={onChangeHost}
description="Hostname or IP address of the proxy." />
<FieldNumber label="Proxy Port" value={config.proxy!.port} onChange={onChangePort}
description="Hostname or IP address of the proxy." />
</React.Fragment>;
}
export function hop(config: FileSystemConfig, onChange: FSCChanged<'hop'>): React.ReactElement {
const callback = (newValue?: string) => onChange('hop', newValue);
const description = 'Use another configuration as proxy, using a SSH tunnel through the targeted config to the actual remote system';
const values = ['TO', ' DO'];
return <FieldDropdownWithInput key="hop" label="Hop" {...{ values, description }} value={config.hop} onChange={callback} optional={true} />;
}
const ProxyTypeToString = {
http: 'HTTP',
socks4: 'SOCKS 4',
socks5: 'SOCKS 5',
} as const;
const ProxyStringToType = {
'HTTP': 'http',
'SOCKS 4': 'socks4',
'SOCKS 5': 'socks5',
'SSH Hop': 'hop',
} as const;
type ProxyStrings = keyof typeof ProxyStringToType;
export function merged(config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple): React.ReactElement | null {
function callback(newValue?: ProxyStrings) {
// Fields starting with _ don't get saved to file
// We use it here so we know when to display the hop stuff
if (!newValue) {
return onChangeMultiple({
['_hop' as any]: undefined,
hop: undefined,
proxy: undefined,
});
}
const newType = ProxyStringToType[newValue];
if (newType === 'hop') {
return onChangeMultiple({
['_hop' as any]: true,
proxy: undefined,
});
}
return onChangeMultiple({
['_hop' as any]: undefined,
hop: undefined,
proxy: {
host: '',
port: 22,
...config.proxy,
type: newType,
}
});
}
const description = 'The type of proxy to use when connecting to the remote system';
const values: ProxyStrings[] = ['SSH Hop', 'SOCKS 4', 'SOCKS 5', 'HTTP'];
const showHop = config.hop || (config as any)._hop;
const type = config.proxy && config.proxy.type;
const value = showHop ? 'SSH Hop' : (type && ProxyTypeToString[type]);
return <React.Fragment>
<FieldDropdown<ProxyStrings | undefined> key="proxy" label="Proxy" {...{ value, values, description }} onChange={callback} optional={true} />
{showHop && hop(config, onChange)}
{config.proxy && proxy(config, onChange)}
</React.Fragment>;
}
export const PROXY_FIELD: FieldFactory = merged;
Loading…
Cancel
Save