refactored build system back to CRA :(

pull/69/head
James Batt 4 years ago
parent ddd030864c
commit d3f7e6d102

@ -1,19 +1,20 @@
### Build stage for the website frontend
FROM node:10 as website
RUN apt-get update
RUN apt-get install -y protobuf-compiler
RUN apt-get install -y protobuf-compiler libprotobuf-dev
WORKDIR /code
COPY ./website/package.json ./
COPY ./website/package-lock.json ./
RUN npm ci --no-audit --prefer-offline
COPY ./proto/ ./proto/
COPY ./proto/ ../proto/
COPY ./website/ ./
RUN npm run codegen
RUN npm run build
### Build stage for the website backend server
FROM golang:1.13.8 as server
RUN apt-get update
RUN apt-get install -y protobuf-compiler
RUN apt-get install -y protobuf-compiler libprotobuf-dev
WORKDIR /code
ENV GOOS=linux
ENV GARCH=amd64

@ -76,7 +76,7 @@ type AppConfig struct {
// Enabled allows you to turn on/off
// the VPN DNS proxy feature.
// DNS Proxying is enabled by default.
Enabled *bool `yaml:"enabled"`
Enabled bool `yaml:"enabled"`
// Upstream configures the addresses of upstream
// DNS servers to which client DNS requests will be sent to.
// Defaults the host's upstream DNS servers (via resolveconf)
@ -111,7 +111,7 @@ func Read() *AppConfig {
kingpin.MustParse(app.Parse(os.Args[1:]))
// here we're filling out the config struct
// with values from our flags.
// with values from our flags/defaults.
config := AppConfig{}
config.LogLevel = *logLevel
config.Port = *webPort
@ -121,30 +121,13 @@ func Read() *AppConfig {
config.DisableMetadata = *disableMetadata
config.WireGuard.PrivateKey = *privateKey
config.Storage = *storage
if config.DNS.Enabled == nil {
on := true
config.DNS.Enabled = &on
}
if adminPassword != nil {
config.AdminPassword = *adminPassword
config.AdminSubject = *adminUsername
if config.AdminSubject == "" {
config.AdminSubject = "admin"
}
}
config.VPN.AllowedIPs = []string{"0.0.0.0/0"}
config.DNS.Enabled = true
if upstreamDNS != nil && *upstreamDNS != "" {
config.DNS.Upstream = []string{*upstreamDNS}
}
if config.VPN.AllowedIPs == nil || len(config.VPN.AllowedIPs) == 0 {
config.VPN.AllowedIPs = []string{
"0.0.0.0/0",
}
}
if *configPath != "" {
if b, err := ioutil.ReadFile(*configPath); err == nil {
if err := yaml.Unmarshal(b, &config); err != nil {
@ -166,6 +149,14 @@ func Read() *AppConfig {
},
})
if adminPassword != nil {
config.AdminPassword = *adminPassword
config.AdminSubject = *adminUsername
if config.AdminSubject == "" {
config.AdminSubject = "admin"
}
}
if config.DisableMetadata {
logrus.Info("Metadata collection has been disabled. No metrics or device connectivity information will be recorded or shown")
}

@ -1,7 +0,0 @@
{
"extends": "@snowpack/app-scripts-react/babel.config.json",
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Website for the wg-access-server"/>
<link rel="apple-touch-icon" href="logo-192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Wireguard Access Portal</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -1,42 +1,52 @@
{
"scripts": {
"start": "snowpack dev",
"build": "snowpack build",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
"codegen": "node_modules/.bin/grpc-ts-web -o src/sdk ../proto/*.proto"
"codegen": "node_modules/.bin/grpc-ts-web -o src/sdk ../proto/*.proto",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"prettier": "prettier ./src/**/*.{ts,tsx} --write"
},
"dependencies": {
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"common-tags": "^1.8.0",
"date-fns": "^2.14.0",
"date-fns": "^2.15.0",
"google-protobuf": "^4.0.0-rc.1",
"mobx": "^5.15.4",
"mobx-react": "^6.2.2",
"mobx-utils": "^5.6.1",
"numeral": "^2.0.6",
"qrcode": "^1.4.4",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"tweetnacl-ts": "^1.0.3",
"typeface-roboto": "0.0.75"
"react-scripts": "3.4.1",
"tweetnacl-ts": "^1.0.3"
},
"devDependencies": {
"@snowpack/app-scripts-react": "^1.6.0-alpha.0",
"@types/common-tags": "^1.8.0",
"@types/numeral": "0.0.28",
"@types/qrcode": "^1.3.4",
"@types/react": "^16.9.35",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"grpc-ts-web": "0.1.7",
"postcss-import": "^12.0.1",
"prettier": "^2.0.5",
"rollup-plugin-node-polyfills": "^0.2.1",
"snowpack": "^2.6.4",
"typescript": "^3.8.0"
"typescript": "^3.9.7"
},
"proxy": "http://localhost:8000/api",
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

@ -1,27 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Website for the wg-access-server" />
<link rel="apple-touch-icon" href="/logo-192.png" />
<title>Wireguard Access Portal</title>
</head>
<body>
<div id="root"></div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script type="module" src="/_dist_/index.js"></script>
<!--
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Website for the wg-access-server" />
<link rel="apple-touch-icon" href="logo-192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>WireGuard Access Portal</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</body>
</html>

@ -1,23 +0,0 @@
module.exports = {
extends: '@snowpack/app-scripts-react',
scripts: {
"run:codegen": "npm run codegen"
},
plugins: [],
buildOptions: {
minify: false,
},
devOptions: {
port: 3000,
open: 'none'
},
proxy: {
'/api': 'http://localhost:8000/api'
},
installOptions: {
namedExports: ['google-protobuf'],
rollup: {
plugins: [require('rollup-plugin-node-polyfills')()],
}
}
}

@ -28,3 +28,7 @@ export function getPlatform() {
}
return Platform.Unknown;
}
export function isMobile() {
return [Platform.Ios, Platform.Android].includes(getPlatform());
}

@ -81,3 +81,18 @@ export function setClipboard(text: string) {
text: 'Added to clipboard',
});
}
export interface DownloadOpts {
filename: string,
content: string,
}
export function download(opts: DownloadOpts) {
const anchor = document.createElement('a');
anchor.href = URL.createObjectURL(new File([opts.content], opts.filename));
anchor.download = opts.filename;
anchor.style.display = 'none';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
}

@ -1,5 +1,7 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
@ -8,19 +10,17 @@ 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 AddIcon from '@material-ui/icons/Add';
import { codeBlock } from 'common-tags';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
import { box_keyPair } from 'tweetnacl-ts';
import { grpc } from '../Api';
import { AppState } from '../AppState';
import { GetConnected } from './GetConnected';
import { grpc } from '../Api';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import { Info } from './Info';
interface Props {
onAdd: () => void;
@ -39,15 +39,9 @@ export class AddDevice extends React.Component<Props> {
name: '',
};
@observable
qrCode?: string;
@observable
configFile?: string;
@observable
configFileUri?: string;
submit = async (event: React.FormEvent) => {
event.preventDefault();
@ -76,8 +70,6 @@ export class AddDevice extends React.Component<Props> {
`;
this.configFile = configFile;
this.qrCode = await qrcode.toDataURL(configFile);
this.configFileUri = URL.createObjectURL(new Blob([configFile]));
this.dialogOpen = true;
this.reset();
} catch (error) {
@ -120,9 +112,24 @@ export class AddDevice extends React.Component<Props> {
</CardContent>
</Card>
<Dialog disableBackdropClick disableEscapeKeyDown maxWidth="xl" open={this.dialogOpen}>
<DialogTitle>Get Connected</DialogTitle>
<DialogTitle>
Get Connected
<Info>
<Typography component="p" style={{ paddingBottom: 8 }}>
Your VPN connection file is not stored by this portal.
</Typography>
<Typography component="p" style={{ paddingBottom: 8 }}>
If you lose this file you can simply create a new device on this portal to generate a new connection
file.
</Typography>
<Typography component="p">
The connection file contains your WireGuard Private Key (i.e. password) and should{' '}
<strong>never</strong> be shared.
</Typography>
</Info>
</DialogTitle>
<DialogContent>
<GetConnected qrCodeUri={this.qrCode!} configFile={this.configFile!} configFileUri={this.configFileUri!} />
<GetConnected configFile={this.configFile!} />
</DialogContent>
<DialogActions>
<Button color="secondary" variant="outlined" onClick={() => (this.dialogOpen = false)}>

@ -1,35 +0,0 @@
import React from 'react';
import Fab from '@material-ui/core/Fab';
import VerifiedUserIcon from '@material-ui/icons/VerifiedUser';
interface Props {
configFileUri: string;
}
export class DownloadConfig extends React.Component<Props> {
downloadConfig = () => {
console.log('downloading config file', this.props.configFileUri);
const anchor = document.createElement('a');
anchor.href = this.props.configFileUri;
anchor.download = 'wireguard.conf';
anchor.style.display = 'none';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
};
render() {
return (
<Fab
variant="extended"
size="small"
color="primary"
style={{ padding: 30, borderRadius: 60 }}
onClick={this.downloadConfig}
>
Download VPN Config
<VerifiedUserIcon style={{ marginLeft: 15 }} />
</Fab>
);
}
}

@ -1,21 +0,0 @@
import React from 'react';
import Fab from '@material-ui/core/Fab';
interface Props {
label: string;
icon: React.ReactNode;
href: string;
}
export class DownloadLink extends React.Component<Props> {
render() {
return (
<a href={this.props.href} target="__blank" rel="noopener noreferrer">
<Fab variant="extended" size="small" color="primary" style={{ padding: 30, borderRadius: 60 }}>
<span>{this.props.label}</span>
<span style={{ marginLeft: 15 }}>{this.props.icon}</span>
</Fab>
</a>
);
}
}

@ -1,30 +1,44 @@
import React from 'react';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import Paper from '@material-ui/core/Paper';
import { ButtonGroup } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Typography from '@material-ui/core/Typography';
import DescriptionOutlined from '@material-ui/icons/DescriptionOutlined';
import { MacOSIcon, IosIcon, WindowsIcon, LinuxIcon, AndroidIcon } from './Icons';
import Paper from '@material-ui/core/Paper';
import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import { GetApp } from '@material-ui/icons';
import Laptop from '@material-ui/icons/Laptop';
import PhoneIphone from '@material-ui/icons/PhoneIphone';
import React from 'react';
import { isMobile } from '../Platform';
import { download } from '../Util';
import { LinuxIcon, MacOSIcon, WindowsIcon } from './Icons';
import { QRCode } from './QRCode';
import { TabPanel } from './TabPanel';
import { Platform, getPlatform } from '../Platform';
import { DownloadConfig } from './DownloadConfig';
import { DownloadLink } from './DownloadLink';
import Button from '@material-ui/core/Button';
import { setClipboard } from '../Util';
interface Props {
qrCodeUri: string;
configFile: string;
configFileUri: string;
}
export class GetConnected extends React.Component<Props> {
state = {
platform: getPlatform(),
currentTab: isMobile() ? 'mobile' : 'desktop',
};
go = (href: string) => {
window.open(href, '__blank', 'noopener noreferrer');
};
download = () => {
download({
filename: 'WireGuard.conf',
content: this.props.configFile,
});
};
getqr = async () => {
return;
};
render() {
@ -32,103 +46,50 @@ export class GetConnected extends React.Component<Props> {
<React.Fragment>
<Paper>
<Tabs
value={this.state.platform}
onChange={(_, platform) => this.setState({ platform })}
value={this.state.currentTab}
onChange={(_, currentTab) => this.setState({ currentTab })}
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
variant="fullWidth"
>
<Tab icon={<LinuxIcon />} value={Platform.Linux} />
<Tab icon={<MacOSIcon />} value={Platform.Mac} />
<Tab icon={<WindowsIcon />} value={Platform.Windows} />
<Tab icon={<IosIcon />} value={Platform.Ios} />
<Tab icon={<AndroidIcon />} value={Platform.Android} />
<Tab icon={<DescriptionOutlined />} value={Platform.Unknown} />
<Tab icon={<Laptop />} value="desktop" />
<Tab icon={<PhoneIphone />} value="mobile" />
</Tabs>
</Paper>
<TabPanel for={Platform.Linux} value={this.state.platform}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item xs={12} sm={6}>
<List>
<ListItem>
<ListItemText primary="1. Install WireGuard for Linux" />
</ListItem>
<ListItem>
<ListItemText primary="2. Download your connection file" />
</ListItem>
<ListItem>
<ListItemText
primary="3. Copy it to /etc/wireguard/wg0.conf"
secondary="This will allow you to use wg-quick to bring the interface up and down easily."
/>
</ListItem>
</List>
</Grid>
<Grid item direction="column" container spacing={3} xs={12} sm={6}>
<Grid item>
<DownloadConfig configFileUri={this.props.configFileUri} />
</Grid>
<Grid item>
<DownloadLink
label="Download WireGuard"
href="https://www.wireguard.com/install/"
icon={<LinuxIcon />}
/>
</Grid>
</Grid>
</Grid>
</TabPanel>
<TabPanel for={Platform.Mac} value={this.state.platform}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item xs={12} sm={6}>
<List>
<ListItem>
<ListItemText primary="1. Install WireGuard for MacOS" />
</ListItem>
<ListItem>
<ListItemText primary="2. Download your connection file" />
</ListItem>
<ListItem>
<ListItemText primary="3. Add tunnel from file" />
</ListItem>
</List>
</Grid>
<Grid item direction="column" container spacing={3} xs={12} sm={6}>
<Grid item>
<DownloadConfig configFileUri={this.props.configFileUri} />
</Grid>
<Grid item>
<DownloadLink
label="Download WireGuard"
href="https://itunes.apple.com/us/app/wireguard/id1451685025?ls=1&mt=12"
icon={<MacOSIcon />}
/>
</Grid>
</Grid>
</Grid>
</TabPanel>
<TabPanel for={Platform.Ios} value={this.state.platform}>
<TabPanel for="desktop" value={this.state.currentTab}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item>
<List>
<ListItem>
<ListItemText primary="1. Install the WireGuard app" />
</ListItem>
<ListItem>
<ListItemText primary="2. Add a tunnel" />
</ListItem>
<ListItem>
<ListItemText primary="3. Create from QR code" />
</ListItem>
</List>
</Grid>
<Grid item>
<img alt="wireguard qr code" src={this.props.qrCodeUri} />
</Grid>
<List>
<ListItem>
<ListItemText style={{ width: 300 }} primary="1. Install the WireGuard App" />
<ButtonGroup size="large" color="primary" aria-label="large outlined primary button group">
<Button onClick={() => this.go('https://www.WireGuard.com/install/')}>
<LinuxIcon />
</Button>
<Button
onClick={() => this.go('https://download.WireGuard.com/windows-client/WireGuard-amd64-0.1.1.msi')}
>
<WindowsIcon />
</Button>
<Button onClick={() => this.go('https://itunes.apple.com/us/app/WireGuard/id1451685025?ls=1&mt=12')}>
<MacOSIcon />
</Button>
</ButtonGroup>
</ListItem>
<ListItem>
<ListItemText style={{ width: 300 }} primary="2. Download your connection file" />
<Button variant="outlined" color="primary" onClick={this.download}>
<GetApp /> Connection File
</Button>
</ListItem>
<ListItem>
<ListItemText style={{ width: 300 }} primary="3. Import your connection file in the App" />
</ListItem>
</List>
</Grid>
</TabPanel>
<TabPanel for={Platform.Android} value={this.state.platform}>
<TabPanel for="mobile" value={this.state.currentTab}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item>
<List>
@ -144,59 +105,10 @@ export class GetConnected extends React.Component<Props> {
</List>
</Grid>
<Grid item>
<img alt="wireguard qr code" src={this.props.qrCodeUri} />
</Grid>
</Grid>
</TabPanel>
<TabPanel for={Platform.Windows} value={this.state.platform}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item xs={12} sm={6}>
<List>
<ListItem>
<ListItemText primary="1. Install WireGuard for Windows" />
</ListItem>
<ListItem>
<ListItemText primary="2. Download your connection file" />
</ListItem>
<ListItem>
<ListItemText primary="3. Add tunnel from file" />
</ListItem>
</List>
<QRCode content={this.props.configFile} />
</Grid>
<Grid item direction="column" container spacing={3} xs={12} sm={6}>
<Grid item>
<DownloadConfig configFileUri={this.props.configFileUri} />
</Grid>
<Grid item>
<DownloadLink
label="Download WireGuard"
href="https://download.wireguard.com/windows-client/wireguard-amd64-0.0.32.msi"
icon={<WindowsIcon />}
/>
</Grid>
</Grid>
</Grid>
</TabPanel>
<TabPanel for={Platform.Unknown} value={this.state.platform}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Typography variant="body1" component="pre" style={{ maxWidth: '100%', overflow: 'auto' }}>
{this.props.configFile}
</Typography>
<Button color="primary" onClick={() => setClipboard(this.props.configFile)}>
Copy To Clipboard
</Button>
</Grid>
</TabPanel>
<Grid container justify="center">
<Typography style={{ fontStyle: 'italic', maxWidth: 600 }}>
The VPN configuration file or QR code will not be available again.
<br />
If you lose your connection settings or reset your device, you can remove and re-add it to generate a new
connection file or QR code.
<br />
They contain your WireGuard Private Key and should never be shared.
</Typography>
</Grid>
</React.Fragment>
);
}

@ -1,10 +1,10 @@
import React from 'react';
import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon';
import SvgIcon from '@material-ui/core/SvgIcon';
// from https://worldvectorlogo.com
export const MacOSIcon = (props: SvgIconProps) => (
<SvgIcon {...props} viewBox="0 0 256 256">
export const MacOSIcon = () => (
<SvgIcon viewBox="0 0 256 256">
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="a">
<stop stopColor="#58B0E3" offset="0%" />
@ -25,8 +25,8 @@ export const IosIcon = () => (
</SvgIcon>
);
export const WindowsIcon = (props: SvgIconProps) => (
<SvgIcon {...props} viewBox="0 0 256 257">
export const WindowsIcon = () => (
<SvgIcon viewBox="0 0 256 257">
<path
d="M0 36.357L104.62 22.11l.045 100.914-104.57.595L0 36.358zm104.57 98.293l.08 101.002L.081 221.275l-.006-87.302 104.494.677zm12.682-114.405L255.968 0v121.74l-138.716 1.1V20.246zM256 135.6l-.033 121.191-138.716-19.578-.194-101.84L256 135.6z"
fill="#00ADEF"
@ -34,8 +34,8 @@ export const WindowsIcon = (props: SvgIconProps) => (
</SvgIcon>
);
export const LinuxIcon = (props: SvgIconProps) => (
<SvgIcon {...props} viewBox="0 0 192.756 192.756">
export const LinuxIcon = () => (
<SvgIcon viewBox="0 0 192.756 192.756">
<g>
<path fill="transparent" d="M0 0h192.756v192.756H0V0z" />
<path

@ -0,0 +1,41 @@
import React from 'react';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import Popover from '@material-ui/core/Popover';
import IconButton from '@material-ui/core/IconButton';
import InfoIcon from '@material-ui/icons/Info';
interface Props {
children: React.ReactNode;
}
@observer
export class Info extends React.Component<Props> {
@observable
anchor?: HTMLElement;
render() {
return (
<>
<IconButton onClick={(event) => (this.anchor = event.currentTarget)}>
<InfoIcon />
</IconButton>
<Popover
open={!!this.anchor}
anchorEl={this.anchor}
onClose={() => (this.anchor = undefined)}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
<div style={{ padding: 16 }}>{this.props.children}</div>
</Popover>
</>
);
}
}

@ -1,12 +1,11 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import Typography from '@material-ui/core/Typography';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
export function present<T>(content: (close: (result: T) => void) => React.ReactNode) {
const root = document.createElement('div');

@ -0,0 +1,21 @@
import React from 'react';
import qrcode from 'qrcode';
import { lazy } from '../Util';
import { CircularProgress } from '@material-ui/core';
interface Props {
content: string;
}
export class QRCode extends React.Component<Props> {
uri = lazy(async () => {
return await qrcode.toDataURL(this.props.content);
});
render() {
if (!this.uri.current) {
return <CircularProgress color="secondary" />;
}
return <img alt="WireGuard QR code" src={this.uri.current} />;
}
}

@ -9,9 +9,3 @@ ReactDOM.render(
</React.StrictMode>,
document.getElementById('root'),
);
// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
// Learn more: https://www.snowpack.dev/#hot-module-replacement
if (import.meta.hot) {
import.meta.hot.accept();
}

@ -0,0 +1 @@
/// <reference types="react-scripts" />

@ -1,17 +1,26 @@
{
"include": ["src", "types"],
"exclude": ["node_modules"],
"extends": "@snowpack/app-scripts-react/tsconfig.base.json",
"compilerOptions": {
// You can't currently define paths in your 'extends' config,
// so we have to set 'baseUrl' & 'paths' here.
// Don't change these unless you know what you're doing.
// See: https://github.com/microsoft/TypeScript/issues/25430
"baseUrl": "./",
"paths": { "*": ["web_modules/.types/*"] },
// Feel free to add/edit new config options below:
// ...
"experimentalDecorators": true,
"importsNotUsedAsValues": "preserve"
}
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"experimentalDecorators": true
},
"include": [
"src"
]
}

Loading…
Cancel
Save