Merge pull request #1972 from automatisch/AUT-1107

feat: refactor forgot password mutation with the REST API endpoint
This commit is contained in:
Ali BARIN
2024-07-17 15:32:37 +02:00
committed by GitHub
8 changed files with 39 additions and 70 deletions

View File

@@ -32,7 +32,6 @@ import verifyConnection from './mutations/verify-connection.js';
// Converted mutations // Converted mutations
import deleteUser from './mutations/delete-user.ee.js'; import deleteUser from './mutations/delete-user.ee.js';
import login from './mutations/login.js'; import login from './mutations/login.js';
import forgotPassword from './mutations/forgot-password.ee.js';
import resetPassword from './mutations/reset-password.ee.js'; import resetPassword from './mutations/reset-password.ee.js';
const mutationResolvers = { const mutationResolvers = {
@@ -51,7 +50,6 @@ const mutationResolvers = {
deleteUser, deleteUser,
duplicateFlow, duplicateFlow,
executeFlow, executeFlow,
forgotPassword,
generateAuthUrl, generateAuthUrl,
login, login,
registerUser, registerUser,

View File

@@ -1,43 +0,0 @@
import appConfig from '../../config/app.js';
import User from '../../models/user.js';
import emailQueue from '../../queues/email.js';
import {
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
} from '../../helpers/remove-job-configuration.js';
const forgotPassword = async (_parent, params) => {
const { email } = params.input;
const user = await User.query().findOne({ email: email.toLowerCase() });
if (!user) {
throw new Error('Email address not found!');
}
await user.generateResetPasswordToken();
const jobName = `Reset Password Email - ${user.id}`;
const jobPayload = {
email: user.email,
subject: 'Reset Password',
template: 'reset-password-instructions.ee',
params: {
token: user.resetPasswordToken,
webAppUrl: appConfig.webAppUrl,
fullName: user.fullName,
},
};
const jobOptions = {
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
};
await emailQueue.add(jobName, jobPayload, jobOptions);
return true;
};
export default forgotPassword;

View File

@@ -17,7 +17,6 @@ type Mutation {
deleteUser(input: DeleteUserInput): Boolean deleteUser(input: DeleteUserInput): Boolean
duplicateFlow(input: DuplicateFlowInput): Flow duplicateFlow(input: DuplicateFlowInput): Flow
executeFlow(input: ExecuteFlowInput): executeFlowType executeFlow(input: ExecuteFlowInput): executeFlowType
forgotPassword(input: ForgotPasswordInput): Boolean
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
login(input: LoginInput): Auth login(input: LoginInput): Auth
registerUser(input: RegisterUserInput): User registerUser(input: RegisterUserInput): User
@@ -405,10 +404,6 @@ input UpdateCurrentUserInput {
fullName: String fullName: String
} }
input ForgotPasswordInput {
email: String!
}
input ResetPasswordInput { input ResetPasswordInput {
token: String! token: String!
password: String! password: String!

View File

@@ -53,7 +53,6 @@ const isAuthenticatedRule = rule()(isAuthenticated);
export const authenticationRules = { export const authenticationRules = {
Mutation: { Mutation: {
'*': isAuthenticatedRule, '*': isAuthenticatedRule,
forgotPassword: allow,
login: allow, login: allow,
registerUser: allow, registerUser: allow,
resetPassword: allow, resetPassword: allow,

View File

@@ -1,23 +1,36 @@
import * as React from 'react'; import * as React from 'react';
import { useMutation } from '@apollo/client';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import LoadingButton from '@mui/lab/LoadingButton'; import LoadingButton from '@mui/lab/LoadingButton';
import { FORGOT_PASSWORD } from 'graphql/mutations/forgot-password.ee'; import { enqueueSnackbar } from 'notistack';
import useForgotPassword from 'hooks/useForgotPassword';
import Form from 'components/Form'; import Form from 'components/Form';
import TextField from 'components/TextField'; import TextField from 'components/TextField';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
export default function ForgotPasswordForm() { export default function ForgotPasswordForm() {
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const [forgotPassword, { data, loading }] = useMutation(FORGOT_PASSWORD); const {
mutateAsync: forgotPassword,
isPending: loading,
isSuccess,
} = useForgotPassword();
const handleSubmit = async (values) => { const handleSubmit = async (values) => {
const { email } = values;
try {
await forgotPassword({ await forgotPassword({
variables: { email,
input: values,
},
}); });
} catch (error) {
enqueueSnackbar(
error?.message || formatMessage('forgotPasswordForm.error'),
{
variant: 'error',
},
);
}
}; };
return ( return (
@@ -35,7 +48,6 @@ export default function ForgotPasswordForm() {
> >
{formatMessage('forgotPasswordForm.title')} {formatMessage('forgotPasswordForm.title')}
</Typography> </Typography>
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
<TextField <TextField
label={formatMessage('forgotPasswordForm.emailFieldLabel')} label={formatMessage('forgotPasswordForm.emailFieldLabel')}
@@ -45,20 +57,18 @@ export default function ForgotPasswordForm() {
margin="dense" margin="dense"
autoComplete="username" autoComplete="username"
/> />
<LoadingButton <LoadingButton
type="submit" type="submit"
variant="contained" variant="contained"
color="primary" color="primary"
sx={{ boxShadow: 2, my: 3 }} sx={{ boxShadow: 2, my: 3 }}
loading={loading} loading={loading}
disabled={data} disabled={isSuccess}
fullWidth fullWidth
> >
{formatMessage('forgotPasswordForm.submit')} {formatMessage('forgotPasswordForm.submit')}
</LoadingButton> </LoadingButton>
{isSuccess && (
{data && (
<Typography <Typography
variant="body1" variant="body1"
sx={{ color: (theme) => theme.palette.success.main }} sx={{ color: (theme) => theme.palette.success.main }}

View File

@@ -1,6 +0,0 @@
import { gql } from '@apollo/client';
export const FORGOT_PASSWORD = gql`
mutation ForgotPassword($input: ForgotPasswordInput) {
forgotPassword(input: $input)
}
`;

View File

@@ -0,0 +1,15 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useForgotPassword() {
const mutation = useMutation({
mutationFn: async (payload) => {
const { data } = await api.post('/v1/users/forgot-password', payload);
return data;
},
});
return mutation;
}

View File

@@ -157,6 +157,7 @@
"forgotPasswordForm.submit": "Send reset instructions", "forgotPasswordForm.submit": "Send reset instructions",
"forgotPasswordForm.instructionsSent": "The instructions have been sent!", "forgotPasswordForm.instructionsSent": "The instructions have been sent!",
"forgotPasswordForm.emailFieldLabel": "Email", "forgotPasswordForm.emailFieldLabel": "Email",
"forgotPasswordForm.error": "Something went wrong. Please try again.",
"resetPasswordForm.passwordsMustMatch": "Passwords must match.", "resetPasswordForm.passwordsMustMatch": "Passwords must match.",
"resetPasswordForm.mandatoryInput": "{inputName} is required.", "resetPasswordForm.mandatoryInput": "{inputName} is required.",
"resetPasswordForm.title": "Reset password", "resetPasswordForm.title": "Reset password",