You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
4.4 KiB
TypeScript
154 lines
4.4 KiB
TypeScript
import React from 'react';
|
|
import Button from '@material-ui/core/Button';
|
|
import Dialog from '@material-ui/core/Dialog';
|
|
import DialogActions from '@material-ui/core/DialogActions';
|
|
import DialogContent from '@material-ui/core/DialogContent';
|
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
|
import FormControl from '@material-ui/core/FormControl';
|
|
import FormHelperText from '@material-ui/core/FormHelperText';
|
|
import Input from '@material-ui/core/Input';
|
|
import InputLabel from '@material-ui/core/InputLabel';
|
|
import AddIcon from '@material-ui/icons/Add';
|
|
import Typography from '@material-ui/core/Typography';
|
|
import Card from '@material-ui/core/Card';
|
|
import CardHeader from '@material-ui/core/CardHeader';
|
|
import CardContent from '@material-ui/core/CardContent';
|
|
import qrcode from 'qrcode';
|
|
import { codeBlock } from 'common-tags';
|
|
import { box_keyPair } from 'tweetnacl-ts';
|
|
import { AppState } from '../AppState';
|
|
import { GetConnected } from './GetConnected';
|
|
import { grpc } from '../Api';
|
|
import { observable } from 'mobx';
|
|
import { observer } from 'mobx-react';
|
|
|
|
interface Props {
|
|
onAdd: () => void;
|
|
}
|
|
|
|
@observer
|
|
export class AddDevice extends React.Component<Props> {
|
|
|
|
@observable
|
|
dialogOpen = false;
|
|
|
|
@observable
|
|
error?: string;
|
|
|
|
@observable
|
|
formState = {
|
|
name: '',
|
|
};
|
|
|
|
@observable
|
|
qrCode?: string;
|
|
|
|
@observable
|
|
configFileUri?: string;
|
|
|
|
submit = async (event: React.FormEvent) => {
|
|
event.preventDefault();
|
|
|
|
const keypair = box_keyPair();
|
|
const publicKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.publicKey) as any)));
|
|
const privateKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.secretKey) as any)));
|
|
|
|
try {
|
|
const device = await grpc.devices.addDevice({
|
|
name: this.formState.name,
|
|
publicKey,
|
|
});
|
|
this.props.onAdd();
|
|
|
|
const info = AppState.info!;
|
|
const configFile = codeBlock`
|
|
[Interface]
|
|
PrivateKey = ${privateKey}
|
|
Address = ${device.address}
|
|
DNS = ${info.hostVpnIp}
|
|
|
|
[Peer]
|
|
PublicKey = ${info.publicKey}
|
|
AllowedIPs = 0.0.0.0/1, 128.0.0.0/1, ::/0
|
|
Endpoint = ${`${info.host?.value || window.location.hostname}:${info.port || '51820'}`}
|
|
`;
|
|
|
|
this.qrCode = await qrcode.toDataURL(configFile);
|
|
this.configFileUri = URL.createObjectURL(new Blob([configFile]));
|
|
this.dialogOpen = true;
|
|
this.reset();
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
// TODO: unwrap grpc error message
|
|
this.error = 'failed';
|
|
}
|
|
}
|
|
|
|
reset = () => {
|
|
this.formState.name = '';
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<>
|
|
<Card>
|
|
<CardHeader
|
|
title="Add A Device"
|
|
/>
|
|
<CardContent>
|
|
<form onSubmit={this.submit}>
|
|
<FormControl error={!!this.error} fullWidth>
|
|
<InputLabel htmlFor="device-name">Device Name</InputLabel>
|
|
<Input
|
|
id="device-name"
|
|
value={this.formState.name}
|
|
onChange={(event) => this.formState.name = event.currentTarget.value}
|
|
aria-describedby="device-name-text"
|
|
/>
|
|
<FormHelperText id="device-name-text">{this.error}</FormHelperText>
|
|
</FormControl>
|
|
<Typography component="div" align="right">
|
|
<Button
|
|
color="secondary"
|
|
type="button"
|
|
onClick={this.reset}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
color="primary"
|
|
variant="contained"
|
|
endIcon={<AddIcon />}
|
|
type="submit"
|
|
>
|
|
Add
|
|
</Button>
|
|
</Typography>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
<Dialog
|
|
disableBackdropClick
|
|
disableEscapeKeyDown
|
|
maxWidth="xl"
|
|
open={this.dialogOpen}
|
|
>
|
|
<DialogTitle>Get Connected</DialogTitle>
|
|
<DialogContent>
|
|
<GetConnected
|
|
qrCodeUri={this.qrCode!}
|
|
configFileUri={this.configFileUri!}
|
|
/>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button color="secondary" variant="outlined" onClick={() => this.dialogOpen = false}>
|
|
Done
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
}
|
|
}
|