Merge pull request #1972 from automatisch/AUT-1107
feat: refactor forgot password mutation with the REST API endpoint
This commit is contained in:
@@ -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,
|
||||||
|
@@ -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;
|
|
@@ -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!
|
||||||
|
@@ -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,
|
||||||
|
@@ -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) => {
|
||||||
await forgotPassword({
|
const { email } = values;
|
||||||
variables: {
|
try {
|
||||||
input: values,
|
await forgotPassword({
|
||||||
},
|
email,
|
||||||
});
|
});
|
||||||
|
} 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 }}
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const FORGOT_PASSWORD = gql`
|
|
||||||
mutation ForgotPassword($input: ForgotPasswordInput) {
|
|
||||||
forgotPassword(input: $input)
|
|
||||||
}
|
|
||||||
`;
|
|
15
packages/web/src/hooks/useForgotPassword.js
Normal file
15
packages/web/src/hooks/useForgotPassword.js
Normal 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;
|
||||||
|
}
|
@@ -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",
|
||||||
|
Reference in New Issue
Block a user