feat: redirect to register page if no users exist

fixes #130
This commit is contained in:
Jordan Knott 2021-10-06 14:20:36 -05:00
parent eab33bfd9a
commit 8b1de30204
8 changed files with 73 additions and 41 deletions

View File

@ -32,7 +32,6 @@ type ValidateTokenResponse = {
const UserRequiredRoute: React.FC<any> = ({ children }) => { const UserRequiredRoute: React.FC<any> = ({ children }) => {
const { user } = useCurrentUser(); const { user } = useCurrentUser();
const location = useLocation(); const location = useLocation();
console.log('user required', user);
if (user) { if (user) {
return children; return children;
} }

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import Confirm from 'shared/components/Confirm'; import Confirm from 'shared/components/Confirm';
import { useHistory, useLocation } from 'react-router'; import { useHistory, useLocation } from 'react-router';
import * as QueryString from 'query-string'; import * as QueryString from 'query-string';
@ -9,35 +9,34 @@ const UsersConfirm = () => {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
const params = QueryString.parse(location.search); const params = QueryString.parse(location.search);
const [hasFailed, setFailed] = useState(false);
const { setUser } = useCurrentUser(); const { setUser } = useCurrentUser();
useEffect(() => {
fetch('/auth/confirm', {
method: 'POST',
body: JSON.stringify({
confirmToken: params.confirmToken,
}),
})
.then(async (x) => {
const { status } = x;
if (status === 200) {
const response = await x.json();
const { userID } = response;
setUser(userID);
history.push('/');
} else {
setFailed(true);
}
})
.catch(() => {
setFailed(false);
});
}, []);
return ( return (
<Container> <Container>
<LoginWrapper> <LoginWrapper>
<Confirm <Confirm hasConfirmToken={params.confirmToken !== undefined} hasFailed={hasFailed} />
hasConfirmToken={params.confirmToken !== undefined}
onConfirmUser={setFailed => {
fetch('/auth/confirm', {
method: 'POST',
body: JSON.stringify({
confirmToken: params.confirmToken,
}),
})
.then(async x => {
const { status } = x;
if (status === 200) {
const response = await x.json();
const { userID } = response;
setUser(userID);
history.push('/');
} else {
setFailed();
}
})
.catch(() => {
setFailed();
});
}}
/>
</LoginWrapper> </LoginWrapper>
</Container> </Container>
); );

View File

@ -17,6 +17,7 @@ const UsersRegister = () => {
<Register <Register
registered={registered} registered={registered}
onSubmit={(data, setComplete, setError) => { onSubmit={(data, setComplete, setError) => {
let isRedirected = false;
if (data.password !== data.password_confirm) { if (data.password !== data.password_confirm) {
setError('password', { type: 'error', message: 'Passwords must match' }); setError('password', { type: 'error', message: 'Passwords must match' });
setError('password_confirm', { type: 'error', message: 'Passwords must match' }); setError('password_confirm', { type: 'error', message: 'Passwords must match' });
@ -41,17 +42,21 @@ const UsersRegister = () => {
console.log(response); console.log(response);
if (setup) { if (setup) {
history.replace(`/confirm?confirmToken=xxxx`); history.replace(`/confirm?confirmToken=xxxx`);
isRedirected = true;
} else if (params.confirmToken) { } else if (params.confirmToken) {
history.replace(`/confirm?confirmToken=${params.confirmToken}`); history.replace(`/confirm?confirmToken=${params.confirmToken}`);
isRedirected = true;
} else { } else {
setRegistered(true); setRegistered(true);
} }
}) })
.catch(() => { .catch((e) => {
toast('There was an issue trying to register'); toast('There was an issue trying to register');
}); });
} }
setComplete(true); if (!isRedirected) {
setComplete(true);
}
}} }}
/> />
</LoginWrapper> </LoginWrapper>

View File

@ -21,14 +21,7 @@ import {
SubTitle, SubTitle,
} from './Styles'; } from './Styles';
const Confirm = ({ onConfirmUser, hasConfirmToken }: ConfirmProps) => { const Confirm = ({ hasFailed, hasConfirmToken }: ConfirmProps) => {
const [hasFailed, setFailed] = useState(false);
const setHasFailed = () => {
setFailed(true);
};
useEffect(() => {
onConfirmUser(setHasFailed);
});
return ( return (
<Wrapper> <Wrapper>
<Column> <Column>

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import AccessAccount from 'shared/undraw/AccessAccount'; import AccessAccount from 'shared/undraw/AccessAccount';
import { User, Lock, Taskcafe } from 'shared/icons'; import { User, Lock, Taskcafe } from 'shared/icons';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router';
import LoadingSpinner from 'shared/components/LoadingSpinner'; import LoadingSpinner from 'shared/components/LoadingSpinner';
import { import {
Form, Form,
@ -25,6 +25,7 @@ import {
const Login = ({ onSubmit }: LoginProps) => { const Login = ({ onSubmit }: LoginProps) => {
const [isComplete, setComplete] = useState(true); const [isComplete, setComplete] = useState(true);
const [showRegistration, setShowRegistration] = useState(false);
const { const {
register, register,
handleSubmit, handleSubmit,
@ -35,6 +36,17 @@ const Login = ({ onSubmit }: LoginProps) => {
setComplete(false); setComplete(false);
onSubmit(data, setComplete, setError); onSubmit(data, setComplete, setError);
}; };
const history = useHistory();
useEffect(() => {
fetch('/settings').then(async (x) => {
const { isConfigured, allowPublicRegistration } = await x.json();
if (!isConfigured) {
history.push('/register');
} else if (allowPublicRegistration) {
setShowRegistration(true);
}
});
}, []);
return ( return (
<Wrapper> <Wrapper>
<Column> <Column>
@ -68,7 +80,7 @@ const Login = ({ onSubmit }: LoginProps) => {
{errors.password && <FormError>{errors.password.message}</FormError>} {errors.password && <FormError>{errors.password.message}</FormError>}
<ActionButtons> <ActionButtons>
<RegisterButton variant="outline">Register</RegisterButton> {showRegistration ? <RegisterButton variant="outline">Register</RegisterButton> : <div />}
{!isComplete && <LoadingSpinner size="32px" thickness="2px" borderSize="48px" />} {!isComplete && <LoadingSpinner size="32px" thickness="2px" borderSize="48px" />}
<LoginButton type="submit" disabled={!isComplete}> <LoginButton type="submit" disabled={!isComplete}>
Login Login

View File

@ -89,7 +89,7 @@ type ErrorOption =
type SetFailedFn = () => void; type SetFailedFn = () => void;
type ConfirmProps = { type ConfirmProps = {
hasConfirmToken: boolean; hasConfirmToken: boolean;
onConfirmUser: (setFailed: SetFailedFn) => void; hasFailed: boolean;
}; };
type RegisterProps = { type RegisterProps = {

View File

@ -102,6 +102,7 @@ func NewRouter(dbConnection *sqlx.DB, emailConfig utils.EmailConfig, securityCon
mux.Mount("/uploads/", http.StripPrefix("/uploads/", imgServer)) mux.Mount("/uploads/", http.StripPrefix("/uploads/", imgServer))
mux.Post("/auth/confirm", taskcafeHandler.ConfirmUser) mux.Post("/auth/confirm", taskcafeHandler.ConfirmUser)
mux.Post("/auth/register", taskcafeHandler.RegisterUser) mux.Post("/auth/register", taskcafeHandler.RegisterUser)
mux.Get("/settings", taskcafeHandler.PublicSettings)
}) })
auth := AuthenticationMiddleware{*repository} auth := AuthenticationMiddleware{*repository}
r.Group(func(mux chi.Router) { r.Group(func(mux chi.Router) {

View File

@ -0,0 +1,23 @@
package route
import (
"encoding/json"
"net/http"
log "github.com/sirupsen/logrus"
)
type PublicSettingsResponse struct {
IsConfigured bool `json:"isConfigured"`
AllowPublicRegistration bool `json:"allowPublicRegistration"`
}
func (h *TaskcafeHandler) PublicSettings(w http.ResponseWriter, r *http.Request) {
userExists, err := h.repo.HasAnyUser(r.Context())
if err != nil {
log.WithError(err).Error("issue checking if user accounts exist")
w.WriteHeader(http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(PublicSettingsResponse{IsConfigured: userExists, AllowPublicRegistration: false})
}