Almost Add Verify Email

This commit is contained in:
ADAMJR 2021-09-28 17:31:51 +01:00
parent 760a9311d3
commit ce47aaf3c1
6 changed files with 50 additions and 25 deletions

View File

@ -6,6 +6,8 @@ import Users from '../../data/users';
import { Verification } from '../../email/verification';
import { EmailFunctions } from '../../email/email-functions';
import { APIError } from '../modules/api-error';
import patterns from '../../types/patterns';
import { updateUser, validateUser } from '../modules/middleware';
export const router = Router();
@ -75,15 +77,28 @@ router.get('/verify', async (req, res) => {
res.status(200).json({ message, token } as REST.From.Get['/auth/verify']);
});
router.get('/email/verify-email', updateUser, validateUser, async (req, res) => {
const user = res.locals.user as SelfUserDocument;
await sendEmail.verifyEmail(user.email, user);
res.json({ message: 'Email sent' } as REST.From.Get['/email/verify-email']);
});
router.get('/email/forgot-password', async (req, res) => {
const email = req.query.email?.toString();
if (!email)
throw new APIError(400, 'Email not provided');
const user = await users.getByEmail(email);
await sendEmail.forgotPassword(email, user);
const isValid = patterns.email.test(email);
if (!isValid)
throw new APIError(400, 'Email is not in a valid format');
return res.status(200).json({ message: 'Email sent' });
try {
const user = await users.getByEmail(email);
await sendEmail.forgotPassword(email, user);
} finally {
return res.status(200).json({ message: 'Email sent' });
}
});
router.post('/change-password', async (req, res) => {

View File

@ -8,19 +8,18 @@ import { useSnackbar } from 'notistack';
import PageWrapper from '../page-wrapper';
import Input from '../../utils/input/input';
import NormalButton from '../../utils/buttons/normal-button';
import { loginUser, forgotPasswordEmail, verifyCode } from '../../../store/auth';
import { useState } from 'react';
import { loginUser, forgotPasswordEmail, sendVerifyCode } from '../../../store/auth';
import { actions as ui } from '../../../store/ui';
const LoginPage: React.FunctionComponent = () => {
const dispatch = useDispatch();
const { register, handleSubmit, getValues } = useForm();
const user = useSelector((s: Store.AppState) => s.auth.user);
const { enqueueSnackbar } = useSnackbar();
const shouldVerify = useSelector((s: Store.AppState) => s.auth.shouldVerify);
const VerifyCodeInput = () => {
const verifyForm = useForm();
const onVerify = () => dispatch(verifyCode(verifyForm.getValues().code));
const onVerify = () => dispatch(sendVerifyCode(verifyForm.getValues().code));
return (
<div>
@ -42,15 +41,7 @@ const LoginPage: React.FunctionComponent = () => {
}
const onLogin = (data) => dispatch(loginUser(data));
const resetPassword = () => {
dispatch(forgotPasswordEmail(getValues().email));
enqueueSnackbar('Reset password instructions sent to email.', {
anchorOrigin: { vertical: 'bottom', horizontal: 'left' },
key: 'saveChanges',
variant: 'info',
});
}
const resetPassword = () => dispatch(forgotPasswordEmail(getValues().email));
return (user)
? <Redirect to="/channels/@me" />

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { Redirect, useLocation } from 'react-router';
import { verifyCode } from '../../../store/auth';
import { sendVerifyCode } from '../../../store/auth';
import LoadingPage from '../loading-page';
const VerifyPage: React.FunctionComponent = () => {
@ -11,7 +11,7 @@ const VerifyPage: React.FunctionComponent = () => {
useEffect(() => {
if (code)
dispatch(verifyCode(code));
dispatch(sendVerifyCode(code));
}, []);
return (code) ? <LoadingPage /> : <Redirect to="/" />;

View File

@ -14,7 +14,7 @@ export interface APIArgs {
data?: object;
headers?: object;
method?: 'get' | 'post' | 'put' | 'patch' | 'delete';
onSuccess: string[];
onSuccess?: string[];
url: string;
/** Callback to handle side effects. */
callback?: (payload: any) => any;

View File

@ -1,8 +1,8 @@
import { createSlice } from '@reduxjs/toolkit';
import { State } from 'react-select/src/Select';
import { WS } from '../types/ws';
import { actions as api } from './api';
import { token } from './utils/rest-headers';
import { openDialog } from './ui';
import { headers, token } from './utils/rest-headers';
const slice = createSlice({
name: 'auth',
@ -64,7 +64,10 @@ export const forgotPasswordEmail = (email: string) => (dispatch) => {
if (!email) return;
dispatch(api.restCallBegan({
onSuccess: [],
callback: () => dispatch(openDialog({
variant: 'info',
content: 'Sent reset password instructions to email, if email is registered.',
})),
url: `/auth/email/forgot-password?email=${email}`,
}));
}
@ -87,13 +90,26 @@ export const registerUser = (data: REST.To.Post['/auth/register']) => (dispatch)
}));
}
export const verifyCode = (code: string) => (dispatch) => {
export const sendVerifyEmail = (code: string) => (dispatch) => {
dispatch(api.restCallBegan({
onSuccess: [],
url: `/auth/email/verify-email`,
headers,
callback: ({ message }: REST.From.Get['/auth/email/verify-email']) => {
// FIXME: creates black hole that kills React
// if (message)
// dispatch(openDialog({ content: message, variant: 'info' }));
},
}))
}
export const sendVerifyCode = (code: string) => (dispatch) => {
dispatch(api.restCallBegan({
onSuccess: [],
url: `/auth/verify?code=${code}`,
callback: ({ message, token }: REST.From.Get['/auth/verify']) => {
// TODO: add REST snackbar
if (message) alert(message);
// FIXME: creates black hole that kills React
// if (message)
// dispatch(openDialog({ content: message, variant: 'info' }));
if (!token) return;
localStorage.setItem('token', token);

3
types/rest.d.ts vendored
View File

@ -27,6 +27,9 @@ declare namespace REST {
roles: Entity.Role[];
users: Entity.User[];
}
'/auth/email/verify-email': {
message?: 'Email sent';
}
'/auth/verify': {
token?: string;
message?: 'Email verified' | 'Password reset';