mirror of
https://github.com/openziti/zrok.git
synced 2025-01-09 07:28:15 +01:00
revamped registration infrastructure (#143)
This commit is contained in:
parent
63f2049c2c
commit
0033021139
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link rel="stylesheet" href="bootstrap.min.css">
|
<link rel="stylesheet" href="/bootstrap.min.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet">
|
||||||
<title>zrok</title>
|
<title>zrok</title>
|
||||||
|
@ -50,7 +50,7 @@ const Login = (props) => {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<Form.Group controlId={"email"} >
|
<Form.Group controlId={"email"}>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
type={"email"}
|
type={"email"}
|
||||||
placeholder={"Email Address"}
|
placeholder={"Email Address"}
|
||||||
@ -71,9 +71,11 @@ const Login = (props) => {
|
|||||||
<Button variant={"light"} type={"submit"}>Log In</Button>
|
<Button variant={"light"} type={"submit"}>Log In</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row>
|
||||||
|
{message}
|
||||||
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -68,6 +68,7 @@ code, pre {
|
|||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ code, pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fullscreen h1 {
|
.fullscreen h1 {
|
||||||
font-size: 96pt;
|
font-size: 64pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullscreen-body {
|
.fullscreen-body {
|
||||||
@ -93,6 +94,14 @@ code, pre {
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullscreen a {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen pre {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.errorMessage {
|
.errorMessage {
|
||||||
color: deeppink;
|
color: deeppink;
|
||||||
}
|
}
|
||||||
|
16
ui/src/register/InvalidRequest.js
Normal file
16
ui/src/register/InvalidRequest.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Button, Container, Row} from "react-bootstrap";
|
||||||
|
|
||||||
|
const InvalidRequest = () => {
|
||||||
|
return (
|
||||||
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<img alt="ziggy" src={"/ziggy.svg"} width={200}/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h1>Invitation not found!</h1>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvalidRequest;
|
@ -1,105 +1,8 @@
|
|||||||
import Icon from "@mdi/react";
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {mdiContentCopy} from "@mdi/js";
|
|
||||||
import * as account from "../api/account";
|
import * as account from "../api/account";
|
||||||
|
import InvalidRequest from "./InvalidRequest";
|
||||||
const RegistrationForm = (props) => {
|
import SetPasswordForm from "./SetPasswordForm";
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
const [confirm, setConfirm] = useState('');
|
|
||||||
const [message, setMessage] = useState();
|
|
||||||
const [authToken, setAuthToken] = useState('');
|
|
||||||
const [complete, setComplete] = useState(false);
|
|
||||||
|
|
||||||
const passwordMismatchMessage = <h2 className={"errorMessage"}>Entered passwords do not match!</h2>
|
|
||||||
const registerFailed = <h2 className={"errorMessage"}>Account creation failed!</h2>
|
|
||||||
|
|
||||||
const handleSubmit = async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
console.log("handleSubmit");
|
|
||||||
if(confirm !== password) {
|
|
||||||
setMessage(passwordMismatchMessage);
|
|
||||||
} else {
|
|
||||||
account.register({body: {"token": props.token, "password": password}})
|
|
||||||
.then(resp => {
|
|
||||||
if(!resp.error) {
|
|
||||||
console.log("resp", resp)
|
|
||||||
setMessage(undefined);
|
|
||||||
setAuthToken(resp.data.token);
|
|
||||||
setComplete(true);
|
|
||||||
} else {
|
|
||||||
setMessage(registerFailed);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(resp => {
|
|
||||||
console.log("resp", resp);
|
|
||||||
setMessage(registerFailed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!complete) {
|
|
||||||
return (
|
|
||||||
<div className={"fullscreen"}>
|
|
||||||
<img src={"/ziggy.svg"} width={200}/>
|
|
||||||
<h1>A new zrok user!</h1>
|
|
||||||
<h2>{props.email}</h2>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<fieldset>
|
|
||||||
<legend>Set A Password</legend>
|
|
||||||
<p><label htmlFor={"password"}>password: </label><input type={"password"} value={password} placeholder={"Password"} onChange={({target}) => setPassword(target.value)}/></p>
|
|
||||||
<p>
|
|
||||||
<label htmlFor={"confirm"}>confirm: </label><input type={"password"} value={confirm} placeholder={"Confirm Password"} onChange={({target}) => setConfirm(target.value)}/>
|
|
||||||
<button type={"submit"}>Register</button>
|
|
||||||
</p>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return <Success email={props.email} token={authToken}/>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const NoAccountRequest = () => {
|
|
||||||
return (
|
|
||||||
<div className={"fullscreen"}>
|
|
||||||
<img src={"/ziggy.svg"} width={200}/>
|
|
||||||
<h1>No such account request!</h1>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Success = (props) => {
|
|
||||||
const handleCopy = async () => {
|
|
||||||
let copiedText = document.getElementById("zrok-enable-command").innerHTML;
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(copiedText);
|
|
||||||
console.log("copied enable command");
|
|
||||||
} catch(err) {
|
|
||||||
console.error("failed to copy", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear local storage on new account registration success.
|
|
||||||
localStorage.clear();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"fullscreen"}>
|
|
||||||
<img src={"/ziggy.svg"} width={200}/>
|
|
||||||
<h1>Welcome to zrok!</h1>
|
|
||||||
|
|
||||||
<p>You can proceed to the <a href={"/"}>zrok web portal</a> and log in with your email and password.</p>
|
|
||||||
|
|
||||||
<p>To enable your shell for zrok, use this command:</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
$ <span id={"zrok-enable-command"}>zrok enable {props.token}</span> <Icon path={mdiContentCopy} size={0.7} onClick={handleCopy}/>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let step;
|
let step;
|
||||||
|
|
||||||
@ -130,16 +33,13 @@ const Register = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if(activeRequest) {
|
if(activeRequest) {
|
||||||
step = <RegistrationForm
|
step = <SetPasswordForm email={email} token={token}/>
|
||||||
email={email}
|
|
||||||
token={token}
|
|
||||||
/>
|
|
||||||
} else {
|
} else {
|
||||||
step = <NoAccountRequest />
|
step = <InvalidRequest />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>{step}</div>
|
<div className={"fullscreen"}>{step}</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
92
ui/src/register/SetPasswordForm.js
Normal file
92
ui/src/register/SetPasswordForm.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import React, {useState} from "react";
|
||||||
|
import * as account from "../api/account";
|
||||||
|
import Success from "./Success";
|
||||||
|
import {Button, Container, Form, Row} from "react-bootstrap";
|
||||||
|
|
||||||
|
const SetPasswordForm = (props) => {
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [confirm, setConfirm] = useState('');
|
||||||
|
const [message, setMessage] = useState();
|
||||||
|
const [authToken, setAuthToken] = useState('');
|
||||||
|
const [complete, setComplete] = useState(false);
|
||||||
|
|
||||||
|
const passwordMismatchMessage = <h2 className={"errorMessage"}>Entered passwords do not match!</h2>
|
||||||
|
const passwordTooShortMessage = <h2 className={"errorMessage"}>Entered password too short! (4 characters, minimum)</h2>
|
||||||
|
const registerFailed = <h2 className={"errorMessage"}>Account creation failed!</h2>
|
||||||
|
|
||||||
|
const handleSubmit = async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
if(confirm.length < 4) {
|
||||||
|
setMessage(passwordTooShortMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(confirm !== password) {
|
||||||
|
setMessage(passwordMismatchMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
account.register({body: {"token": props.token, "password": password}})
|
||||||
|
.then(resp => {
|
||||||
|
if(!resp.error) {
|
||||||
|
console.log("resp", resp)
|
||||||
|
setMessage(undefined);
|
||||||
|
setAuthToken(resp.data.token);
|
||||||
|
setComplete(true);
|
||||||
|
} else {
|
||||||
|
setMessage(registerFailed);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(resp => {
|
||||||
|
console.log("resp", resp);
|
||||||
|
setMessage(registerFailed);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!complete) {
|
||||||
|
return (
|
||||||
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<img alt="ziggy" src={"/ziggy.svg"} width={200}/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h1>Welcome new zrok user!</h1>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h2>{props.email}</h2>
|
||||||
|
</Row>
|
||||||
|
<Row className={"fullscreen-body"}>
|
||||||
|
<Container className={"fullscreen-form"}>
|
||||||
|
<Row>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Form.Group controlId={"password"}>
|
||||||
|
<Form.Control
|
||||||
|
type={"password"}
|
||||||
|
placeholder={"Set Password"}
|
||||||
|
onChange={t => { setMessage(null); setPassword(t.target.value); }}
|
||||||
|
value={password}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group controlId={"confirm"}>
|
||||||
|
<Form.Control
|
||||||
|
type={"password"}
|
||||||
|
placeholder={"Confirm Password"}
|
||||||
|
onChange={t => { setMessage(null); setConfirm(t.target.value); }}
|
||||||
|
value={confirm}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Button variant={"light"} type={"submit"}>Register Account</Button>
|
||||||
|
</Form>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
{message}
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <Success email={props.email} token={authToken}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SetPasswordForm;
|
47
ui/src/register/Success.js
Normal file
47
ui/src/register/Success.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import Icon from "@mdi/react";
|
||||||
|
import {mdiContentCopy} from "@mdi/js";
|
||||||
|
import {Container, Row} from "react-bootstrap";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Success = (props) => {
|
||||||
|
const handleCopy = async () => {
|
||||||
|
let copiedText = document.getElementById("zrok-enable-command").innerHTML;
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(copiedText);
|
||||||
|
console.log("copied enable command");
|
||||||
|
} catch(err) {
|
||||||
|
console.error("failed to copy", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear local storage on new account registration success.
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<img alt="ziggy" src={"/ziggy.svg"} width={200}/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h1>Welcome to zrok!</h1>
|
||||||
|
</Row>
|
||||||
|
<Row className={"fullscreen-body"}>
|
||||||
|
<Container className={"fullscreen-form"}>
|
||||||
|
<Row>
|
||||||
|
<p>You can proceed to the <a href={"/"}>zrok web portal</a> and log in with your email and password.</p>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<p>To enable your shell for zrok, use this command:</p>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<pre>
|
||||||
|
$ <span id={"zrok-enable-command"}>zrok enable {props.token}</span> <Icon path={mdiContentCopy} size={0.7} onClick={handleCopy}/>
|
||||||
|
</pre>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Success;
|
Loading…
Reference in New Issue
Block a user