@@ -51,16 +58,20 @@ export function SetAdminPassword({onChangeStep, secret}: Props) {
Set a password for the admin user. At the next screen you'll be able to login with the username "admin" and the password you'll set now.
{passwordMutation.isPending ? (
-
Setting Password...
+
Setting Password for user 'admin'...
) : (
Your password
-
setPassword(event.currentTarget.value)}
value={password}
error={passwordError}
+ onKeyDown={(e) => captureEnter(e)}
/>
Repeat password
@@ -68,9 +79,11 @@ export function SetAdminPassword({onChangeStep, secret}: Props) {
setPassword2(event.currentTarget.value)}
value={password2}
- error={password2Error}
+ error={password2Error}
+ onKeyDown={(e) => captureEnter(e)}
/>
changePassword()}>Set Admin Password
diff --git a/webapp/src/AppInit/SetSecret.tsx b/webapp/src/AppInit/SetSecret.tsx
index a726e74..327f76a 100644
--- a/webapp/src/AppInit/SetSecret.tsx
+++ b/webapp/src/AppInit/SetSecret.tsx
@@ -1,44 +1,97 @@
-import { Text, Title, TextInput, Button } from '@mantine/core';
+import { Text, Title, TextInput, Button, Card, Grid, Container, Center, Alert, ActionIcon } from '@mantine/core';
+import { useClipboard } from '@mantine/hooks';
+import { TbCheck, TbCopy } from 'react-icons/tb';
import classes from './SetupBanner.module.css';
import {useState} from 'react';
-import axios from 'axios';
+import axios, { AxiosError } from 'axios';
import { AppSettings } from '../Constants/Constants';
import {
useQueryClient,
useMutation,
} from '@tanstack/react-query'
+import { TbInfoCircle } from 'react-icons/tb';
type Props = {
onChangeStep: (newType: number) => void;
onChangeSecret: (newType: string) => void;
- };
+ cloudType: string;
+};
-export function SetSecret({onChangeStep, onChangeSecret}: Props) {
+type SetupResponse = {
+ secret: string;
+ tagHash: string;
+ instanceID: string;
+}
+type SetupResponseError = {
+ error: string;
+}
+
+const randomHex = (length:number) => {
+ const bytes = window.crypto.getRandomValues(new Uint8Array(length))
+ var hexstring='', h;
+ for(var i=0; i("");
+ const [setupResponse, setSetupResponse] = useState({secret: "", tagHash: "", instanceID: ""});
const [secretError, setSecretError] = useState("");
+ const [randomHexValue] = useState(randomHex(16))
const secretMutation = useMutation({
- mutationFn: (newSecret: string) => {
+ mutationFn: (setupResponse: SetupResponse) => {
setSecretError("")
- return axios.post(AppSettings.url + '/context', {secret: newSecret})
+ return axios.post(AppSettings.url + '/context', setupResponse)
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['context'] })
- onChangeSecret(secret)
+ onChangeSecret(setupResponse.secret)
onChangeStep(1)
},
- onError: (error) => {
- if(error.message.includes("status code 403")) {
- setSecretError("Invalid secret")
- } else {
+ onError: (error:AxiosError) => {
+ const errorMessage = error.response?.data as SetupResponseError
+ if(errorMessage?.error === undefined) {
setSecretError("Error: "+ error.message)
+ } else {
+ setSecretError(errorMessage.error)
}
- }
+ },
})
+ const captureEnter = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ secretMutation.mutate(setupResponse)
+ }
+ }
+ const alertIcon =
+ const hasMoreOptions = cloudType === "aws" || cloudType === "digitalocean" ? true : false
+ const colSpanWithSSH = hasMoreOptions ? 3 : 6
+
return (
-
-
-
Start Setup...
+
+
+ Start Setup
+
+ {secretError !== "" ?
+
+
+
+ {secretError}
+
+
+ :
+ null
+ }
+
+
+
+
+ {hasMoreOptions ? "Option 1: " : ""}With SSH Access
Enter the secret to start the setup.
@@ -57,14 +110,69 @@ export function SetSecret({onChangeStep, onChangeSecret}: Props) {
setSecret(event.currentTarget.value)}
- value={secret}
- error={secretError}
+ onChange={(event) => setSetupResponse({ ...setupResponse, secret: event.currentTarget.value})}
+ value={setupResponse.secret}
+ onKeyDown={(e) => captureEnter(e)}
/>
- secretMutation.mutate(secret)}>Continue
+ secretMutation.mutate({ secret: setupResponse.secret, tagHash: "", instanceID: ""})}>Continue
)}
-
-
+
+
+ {cloudType === "aws" ?
+
+
+ {hasMoreOptions ? "Option 2: " : ""}Without SSH Access
+
+
+ Enter the EC2 Instance ID of the VPN Server
+
+ {secretMutation.isPending ? (
+ Checking Instance ID...
+ ) : (
+
+ setSetupResponse({ ...setupResponse, instanceID: event.currentTarget.value})}
+ value={setupResponse.instanceID}
+ onKeyDown={(e) => captureEnter(e)}
+ />
+ secretMutation.mutate({ secret: "", tagHash: "", instanceID: setupResponse.instanceID})}>Check Instance ID
+
+ )}
+
+
+ : null }
+ {cloudType === "digitalocean" ?
+
+
+ {hasMoreOptions ? "Option 2: " : ""}Without SSH Access
+
+
+ Add the following tag to the droplet by going to the droplet settings and opening the Tags page.
+
+ {secretMutation.isPending ? (
+ Checking tag...
+ ) : (
+
+ clipboard.copy(randomHexValue)}>
+ { clipboard.copied ? : }
+
+ }
+ />
+ secretMutation.mutate({ secret: "", tagHash: randomHexValue, instanceID: ""})}>Check tag
+
+ )}
+
+
+ : null }
+
+
);
}
\ No newline at end of file
diff --git a/webapp/src/AppInit/SetupBanner.module.css b/webapp/src/AppInit/SetupBanner.module.css
index fc2debc..37e8dfd 100644
--- a/webapp/src/AppInit/SetupBanner.module.css
+++ b/webapp/src/AppInit/SetupBanner.module.css
@@ -2,9 +2,6 @@
display: flex;
align-items: center;
padding: calc(var(--mantine-spacing-xl) * 2);
- border-radius: var(--mantine-radius-md);
- background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
- border: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-8));
@media (max-width: $mantine-breakpoint-sm) {
flex-direction: column-reverse;
@@ -58,4 +55,7 @@
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
-
\ No newline at end of file
+
+ .error:first-letter {
+ text-transform: capitalize
+ }
\ No newline at end of file
diff --git a/webapp/src/AppInit/SetupBanner.tsx b/webapp/src/AppInit/SetupBanner.tsx
index 76b99f6..2360d6a 100644
--- a/webapp/src/AppInit/SetupBanner.tsx
+++ b/webapp/src/AppInit/SetupBanner.tsx
@@ -5,9 +5,10 @@ import React from 'react';
type Props = {
onCompleted: (newType: boolean) => void;
+ cloudType: string;
};
-export function SetupBanner({onCompleted}:Props) {
+export function SetupBanner({onCompleted, cloudType}:Props) {
const [step, setStep] = useState
(0);
const [secret, setSecret] = useState("");
@@ -18,7 +19,7 @@ export function SetupBanner({onCompleted}:Props) {
}, [step]);
if(step === 0) {
- return
+ return
} else if(step === 1) {
return
}