From 6a931a907ee4df86d43e7c3e406f966a90731d10 Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munt@gmail.com> Date: Thu, 5 Jul 2018 10:13:38 +0300 Subject: [PATCH] feat(author-signup): author signup form and confirm api route --- packages/component-email/src/helpers/Email.js | 33 ++- .../component-email/src/routes/emails/post.js | 33 ++- packages/component-mail-service/src/Mail.js | 18 +- packages/component-user-manager/src/Users.js | 5 + .../src/routes/users/confirm.js | 32 +++ .../src/components/SignUp/AuthorSignup.js | 197 ++++++++++++++++++ .../src/components/SignUp/ConfirmAccount.js | 19 ++ .../components/SignUp/SignUpInvitationForm.js | 18 +- .../components/SignUp/SignUpInvitationPage.js | 37 +++- .../src/components/SignUp/SignUpStep0.js | 8 +- .../src/components/SignUp/SignUpStep1.js | 29 ++- .../src/components/SignUp/index.js | 2 + .../src/components/UIComponents/FormItems.js | 2 + .../src/components/utils.js | 28 +++ .../components-faraday/src/redux/users.js | 7 + packages/xpub-faraday/app/routes.js | 22 +- packages/xpub-faraday/config/default.js | 3 + packages/xpub-faraday/config/validations.js | 1 + 18 files changed, 453 insertions(+), 41 deletions(-) create mode 100644 packages/component-user-manager/src/routes/users/confirm.js create mode 100644 packages/components-faraday/src/components/SignUp/AuthorSignup.js create mode 100644 packages/components-faraday/src/components/SignUp/ConfirmAccount.js diff --git a/packages/component-email/src/helpers/Email.js b/packages/component-email/src/helpers/Email.js index 292e4a637..02a9816e0 100644 --- a/packages/component-email/src/helpers/Email.js +++ b/packages/component-email/src/helpers/Email.js @@ -3,6 +3,37 @@ const logger = require('@pubsweet/logger') const helpers = require('./helpers') module.exports = { + sendSignupEmail: async ({ dashboardUrl, res, email, UserModel }) => { + let user + try { + user = await UserModel.findByEmail(email) + } catch (e) { + const notFoundError = await helpers.handleNotFoundError(e, 'User') + return res.status(notFoundError.status).json({ + error: notFoundError.message, + }) + } + if (!user.confirmationToken) { + return res + .status(400) + .json({ error: 'User does not have a confirmation token.' }) + } + try { + await mailService.sendSimpleEmail({ + toEmail: user.email, + user, + emailType: 'signup', + dashboardUrl, + meta: { + confirmationToken: user.confirmationToken, + }, + }) + return res.status(200).json({}) + } catch (e) { + logger.error(e) + return res.status(500).json({ error: 'Email could not be sent.' }) + } + }, setupNewUserEmail: async ({ dashboardUrl, res, email, role, UserModel }) => { let user try { @@ -13,7 +44,7 @@ module.exports = { error: notFoundError.message, }) } - if (user.passwordResetToken === undefined) { + if (!user.passwordResetToken) { return res .status(400) .json({ error: 'User does not have a password reset token.' }) diff --git a/packages/component-email/src/routes/emails/post.js b/packages/component-email/src/routes/emails/post.js index 9ff44cc05..33a149738 100644 --- a/packages/component-email/src/routes/emails/post.js +++ b/packages/component-email/src/routes/emails/post.js @@ -3,21 +3,34 @@ const helpers = require('../../helpers/helpers') const emailHelper = require('../../helpers/Email') module.exports = models => async (req, res) => { - const { email, type, role } = req.body + const { email, type, role = 'author' } = req.body if (!helpers.checkForUndefinedParams(email, type, role)) { res.status(400).json({ error: 'Email and type are required.' }) logger.error('User ID and role are missing') return } - if (type !== 'invite') - return res.status(400).json({ error: `Email type ${type} is not defined.` }) + // if (type !== 'invite') + // return res.status(400).json({ error: `Email type ${type} is not defined.` }) - return emailHelper.setupNewUserEmail({ - dashboardUrl: `${req.protocol}://${req.get('host')}`, - res, - email, - role, - UserModel: models.User, - }) + if (type === 'signup') { + return emailHelper.sendSignupEmail({ + res, + email, + UserModel: models.User, + dashboardUrl: `${req.protocol}://${req.get('host')}`, + }) + } + + if (type === 'invite') { + return emailHelper.setupNewUserEmail({ + dashboardUrl: `${req.protocol}://${req.get('host')}`, + res, + email, + role, + UserModel: models.User, + }) + } + + return res.end() } diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js index 67d85243b..b8028ff25 100644 --- a/packages/component-mail-service/src/Mail.js +++ b/packages/component-mail-service/src/Mail.js @@ -2,8 +2,9 @@ const Email = require('@pubsweet/component-send-email') const config = require('config') const helpers = require('./helpers/helpers') -const resetPasswordPath = config.get('invite-reviewer.url') +const confirmSignUp = config.get('confirm-signup.url') const resetPath = config.get('invite-reset-password.url') +const resetPasswordPath = config.get('invite-reviewer.url') module.exports = { sendSimpleEmail: async ({ @@ -43,6 +44,21 @@ module.exports = { replacements.url } ${replacements.buttonText}` break + case 'signup': + subject = 'Confirm your email address' + replacements.headline = '' + replacements.paragraph = + 'Please confirm your account by clicking on the link below.' + replacements.previewText = 'Hindawi account confirmation' + replacements.buttonText = 'CONFIRM' + replacements.url = helpers.createUrl(dashboardUrl, confirmSignUp, { + userId: user.id, + confirmationToken: meta.confirmationToken, + }) + textBody = `${replacements.headline} ${replacements.paragraph} ${ + replacements.url + } ${replacements.buttonText}` + break case 'invite-author': subject = 'Author Invitation' replacements.headline = diff --git a/packages/component-user-manager/src/Users.js b/packages/component-user-manager/src/Users.js index 3fdfb1b60..64f74bf44 100644 --- a/packages/component-user-manager/src/Users.js +++ b/packages/component-user-manager/src/Users.js @@ -38,6 +38,11 @@ const Invite = app => { '/api/users/reset-password', require('./routes/users/resetPassword')(app.locals.models), ) + + app.post( + '/api/users/confirm', + require('./routes/users/confirm')(app.locals.models), + ) } module.exports = Invite diff --git a/packages/component-user-manager/src/routes/users/confirm.js b/packages/component-user-manager/src/routes/users/confirm.js new file mode 100644 index 000000000..2675e160e --- /dev/null +++ b/packages/component-user-manager/src/routes/users/confirm.js @@ -0,0 +1,32 @@ +const { token } = require('pubsweet-server/src/authentication') +const { services } = require('pubsweet-component-helper-service') + +module.exports = ({ User }) => async (req, res) => { + const { userId, confirmationToken } = req.body + + if (!services.checkForUndefinedParams(userId, confirmationToken)) + return res.status(400).json({ error: 'Missing required params' }) + + let user + try { + user = await User.find(userId) + + if (user.confirmationToken !== confirmationToken) { + return res.status(400).json({ error: 'Wrong confirmation token.' }) + } + + if (user.isConfirmed) + return res.status(400).json({ error: 'User is already confirmed' }) + + user.isConfirmed = true + delete user.confirmationToken + await user.save() + + return res.status(200).json({ + ...user, + token: token.create(user), + }) + } catch (e) { + throw e + } +} diff --git a/packages/components-faraday/src/components/SignUp/AuthorSignup.js b/packages/components-faraday/src/components/SignUp/AuthorSignup.js new file mode 100644 index 000000000..40f08b2fa --- /dev/null +++ b/packages/components-faraday/src/components/SignUp/AuthorSignup.js @@ -0,0 +1,197 @@ +import React, { Fragment } from 'react' +import { reduxForm } from 'redux-form' +import { th } from '@pubsweet/ui-toolkit' +import { required } from 'xpub-validators' +import { compose, withState } from 'recompose' +import styled, { css } from 'styled-components' +import { Icon, Button, TextField, ValidatedField } from '@pubsweet/ui' + +import { FormItems } from '../UIComponents' + +const { Row, RowItem, Label, RootContainer, FormContainer } = FormItems + +const Step1 = ({ handleSubmit }) => ( + <CustomFormContainer onSubmit={handleSubmit}> + <Fragment> + <CustomRow noMargin> + <CustomRowItem vertical> + <Label>Email</Label> + <ValidatedField + component={TextField} + name="email" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + <CustomRow> + <CustomRowItem vertical> + <Label>Password</Label> + <ValidatedField + component={TextField} + name="password" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + <CustomRow> + <CustomRowItem vertical> + <Label>Confirm password</Label> + <ValidatedField + component={TextField} + name="confirmPassword" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + </Fragment> + <Button primary type="submit"> + Next + </Button> + </CustomFormContainer> +) + +const AuthorSignupStep1 = reduxForm({ + form: 'authorSignup', + destroyOnUnmount: false, + enableReinitialize: true, + forceUnregisterOnUnmount: true, +})(Step1) + +const Step2 = ({ handleSubmit }) => ( + <CustomFormContainer onSubmit={handleSubmit}> + <Fragment> + <CustomRow noMargin> + <CustomRowItem vertical> + <Label>First name</Label> + <ValidatedField + component={TextField} + name="firstName" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + <CustomRow noMargin> + <CustomRowItem vertical> + <Label>Last name</Label> + <ValidatedField + component={TextField} + name="lastName" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + <CustomRow noMargin> + <CustomRowItem vertical> + <Label>Affiliation</Label> + <ValidatedField + component={TextField} + name="affiliation" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + <CustomRow noMargin> + <CustomRowItem vertical> + <Label>Title</Label> + <ValidatedField + component={TextField} + name="title" + validate={[required]} + /> + </CustomRowItem> + </CustomRow> + </Fragment> + <Button primary type="submit"> + Submit + </Button> + </CustomFormContainer> +) + +const AuthorSignupStep2 = reduxForm({ + form: 'authorSignup', + destroyOnUnmount: false, + forceUnregisterOnUnmount: true, + onSubmit: null, +})(Step2) + +const AuthorWizard = ({ step, changeStep, history }) => ( + <CustomRootContainer> + <IconButton onClick={history.goBack}> + <Icon primary size={3}> + x + </Icon> + </IconButton> + <Title>Author Signup</Title> + {step === 0 && <AuthorSignupStep1 onSubmit={() => changeStep(1)} />} + {step === 1 && <AuthorSignupStep2 />} + </CustomRootContainer> +) + +export default compose(withState('step', 'changeStep', 0))(AuthorWizard) + +// #region styled-components +const verticalPadding = css` + padding: ${th('subGridUnit')} 0; +` + +const CustomRow = Row.extend` + div[role='alert'] { + margin-top: 0; + } +` + +const CustomRowItem = RowItem.extend` + & > div { + flex: 1; + + & > div { + max-width: 400px; + width: 400px; + } + } +` + +const CustomRootContainer = RootContainer.extend` + align-items: center; + border: ${th('borderDefault')}; + position: relative; +` + +const CustomFormContainer = FormContainer.extend` + align-items: center; + display: flex; + flex-direction: column; + justify-content: flex-start; +` + +const Title = styled.span` + font-family: ${th('fontHeading')}; + font-size: ${th('fontSizeHeading5')}; + ${verticalPadding}; +` + +const IconButton = styled.button` + align-items: center; + background-color: ${th('backgroundColorReverse')}; + border: none; + color: ${th('colorPrimary')}; + cursor: ${({ hide }) => (hide ? 'auto' : 'pointer')}; + display: flex; + font-family: ${th('fontInterface')}; + font-size: ${th('fontSizeBaseSmall')}; + opacity: ${({ hide }) => (hide ? 0 : 1)}; + text-align: left; + + position: absolute; + top: ${th('subGridUnit')}; + right: ${th('subGridUnit')}; + + &:active, + &:focus { + outline: none; + } + &:hover { + opacity: 0.7; + } +` +// #endregion diff --git a/packages/components-faraday/src/components/SignUp/ConfirmAccount.js b/packages/components-faraday/src/components/SignUp/ConfirmAccount.js new file mode 100644 index 000000000..979c221a0 --- /dev/null +++ b/packages/components-faraday/src/components/SignUp/ConfirmAccount.js @@ -0,0 +1,19 @@ +import React from 'react' +import { compose, lifecycle } from 'recompose' + +import { parseSearchParams } from '../utils' +import { confirmUser } from '../../redux/users' + +const ConfirmAccount = ({ location }) => <div>confirm</div> + +export default compose( + lifecycle({ + componentDidMount() { + const { location } = this.props + const { confirmationToken, userId } = parseSearchParams(location.search) + if (userId) { + confirmUser(userId, confirmationToken) + } + }, + }), +)(ConfirmAccount) diff --git a/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js b/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js index f7b21c178..4a3f99957 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js +++ b/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js @@ -6,22 +6,25 @@ import { FormItems } from '../UIComponents' const { RootContainer, Title, Subtitle, Email, Err } = FormItems +const defaultSubtitle = `Your details have been pre-filled, please review and confirm before set +your password.` + const SignUpInvitation = ({ step, email, token, error, journal, + onSubmit, nextStep, initialValues, - submitConfirmation, + type = 'invitation', + subtitle = defaultSubtitle, + title = 'Add New Account Details', }) => ( <RootContainer bordered> - <Title>Add New Account Details</Title> - <Subtitle> - Your details have been pre-filled, please review and confirm before set - your password. - </Subtitle> + <Title>{title}</Title> + <Subtitle>{subtitle}</Subtitle> <Email>{initialValues.email}</Email> {error && <Err>Token expired or Something went wrong.</Err>} {step === 0 && ( @@ -37,7 +40,8 @@ const SignUpInvitation = ({ error={error} initialValues={initialValues} journal={journal} - onSubmit={submitConfirmation} + onSubmit={onSubmit} + type={type} /> )} </RootContainer> diff --git a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js index 0ad1a8f2a..9c8b24d62 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js +++ b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js @@ -6,6 +6,7 @@ import { loginUser } from 'pubsweet-component-login/actions' import { compose, withState, withProps, withHandlers } from 'recompose' import SignUpInvitation from './SignUpInvitationForm' +import { handleError, parseSignupAuthor } from '../utils' const login = (dispatch, values, history) => dispatch(loginUser(values)) @@ -43,10 +44,34 @@ const confirmUser = (email, token, history) => (values, dispatch) => { } } +const signUpUser = history => (values, dispatch) => + create('/users', parseSignupAuthor(values)) + .then(r => { + const { username } = r + const { password } = values + login(dispatch, { username, password }, history).then(() => { + create('/emails', { + email: values.email, + type: 'signup', + }) + }) + }) + .catch(handleError) + export default compose( withJournal, withState('step', 'changeStep', 0), - withProps(({ location }) => { + withHandlers({ + nextStep: ({ changeStep }) => () => changeStep(step => step + 1), + prevStep: ({ changeStep }) => () => changeStep(step => step - 1), + submitConfirmation: ({ + initialValues: { email, token }, + history, + ...rest + }) => confirmUser(email, token, history), + signUp: ({ history }) => signUpUser(history), + }), + withProps(({ location, type, signUp, submitConfirmation }) => { const params = new URLSearchParams(location.search) const email = params.get('email') const token = params.get('token') @@ -64,15 +89,7 @@ export default compose( firstName, affiliation, }, + onSubmit: type === 'signup' ? signUp : submitConfirmation, } }), - withHandlers({ - nextStep: ({ changeStep }) => () => changeStep(step => step + 1), - prevStep: ({ changeStep }) => () => changeStep(step => step - 1), - submitConfirmation: ({ - initialValues: { email, token }, - history, - ...rest - }) => confirmUser(email, token, history), - }), )(SignUpInvitation) diff --git a/packages/components-faraday/src/components/SignUp/SignUpStep0.js b/packages/components-faraday/src/components/SignUp/SignUpStep0.js index fb342d2ad..237bba74d 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpStep0.js +++ b/packages/components-faraday/src/components/SignUp/SignUpStep0.js @@ -12,7 +12,7 @@ const Step0 = ({ journal, handleSubmit, initialValues, error }) => !isUndefined(initialValues) ? ( <FormContainer onSubmit={handleSubmit}> <Row> - <RowItem vertical> + <RowItem vertical withRightMargin> <Label>First name*</Label> <ValidatedField component={TextField} @@ -20,7 +20,7 @@ const Step0 = ({ journal, handleSubmit, initialValues, error }) => validate={[required]} /> </RowItem> - <RowItem vertical> + <RowItem vertical withRightMargin> <Label>Last name*</Label> <ValidatedField component={TextField} @@ -30,7 +30,7 @@ const Step0 = ({ journal, handleSubmit, initialValues, error }) => </RowItem> </Row> <Row> - <RowItem vertical> + <RowItem vertical withRightMargin> <Label>Affiliation*</Label> <ValidatedField component={TextField} @@ -39,7 +39,7 @@ const Step0 = ({ journal, handleSubmit, initialValues, error }) => /> </RowItem> - <RowItem vertical> + <RowItem vertical withRightMargin> <Label>Title*</Label> <ValidatedField component={input => <Menu {...input} options={journal.title} />} diff --git a/packages/components-faraday/src/components/SignUp/SignUpStep1.js b/packages/components-faraday/src/components/SignUp/SignUpStep1.js index b3e5317e0..ae97bc2a1 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpStep1.js +++ b/packages/components-faraday/src/components/SignUp/SignUpStep1.js @@ -7,20 +7,43 @@ import { FormItems } from '../UIComponents' const { Row, Err, Label, RowItem, FormContainer } = FormItems -const TextFormField = input => <TextField {...input} type="password" /> +const PasswordField = input => <TextField {...input} type="password" /> +const EmailField = input => <TextField {...input} type="email" /> -const Step1 = ({ journal, handleSubmit, error }) => ( +const Step1 = ({ journal, handleSubmit, error, type }) => ( <FormContainer onSubmit={handleSubmit}> + {type === 'signup' && ( + <Row> + <RowItem vertical> + <Label>Email</Label> + <ValidatedField + component={EmailField} + name="email" + validate={[required]} + /> + </RowItem> + </Row> + )} <Row> <RowItem vertical> <Label>Password</Label> <ValidatedField - component={TextFormField} + component={PasswordField} name="password" validate={[required]} /> </RowItem> </Row> + <Row> + <RowItem vertical> + <Label>Confirm password</Label> + <ValidatedField + component={PasswordField} + name="confirmPassword" + validate={[required]} + /> + </RowItem> + </Row> {error && ( <Row> <RowItem> diff --git a/packages/components-faraday/src/components/SignUp/index.js b/packages/components-faraday/src/components/SignUp/index.js index 8fc6a0dd3..d06b74c2b 100644 --- a/packages/components-faraday/src/components/SignUp/index.js +++ b/packages/components-faraday/src/components/SignUp/index.js @@ -1,3 +1,5 @@ +export { default as AuthorSignup } from './AuthorSignup' +export { default as ConfirmAccount } from './ConfirmAccount' export { default as ReviewerSignUp } from './ReviewerSignUp' export { default as ReviewerDecline } from './ReviewerDecline' export { default as SignUpInvitationPage } from './SignUpInvitationPage' diff --git a/packages/components-faraday/src/components/UIComponents/FormItems.js b/packages/components-faraday/src/components/UIComponents/FormItems.js index fc91cd922..275aea4be 100644 --- a/packages/components-faraday/src/components/UIComponents/FormItems.js +++ b/packages/components-faraday/src/components/UIComponents/FormItems.js @@ -63,6 +63,8 @@ export const RowItem = styled.div` flex: ${({ flex }) => flex || 1}; flex-direction: ${({ vertical }) => (vertical ? 'column' : 'row')}; justify-content: ${({ centered }) => (centered ? 'center' : 'initial')}; + margin-right: ${({ withRightMargin }) => + withRightMargin ? th('gridUnit') : 0}; & > div { flex: 1; diff --git a/packages/components-faraday/src/components/utils.js b/packages/components-faraday/src/components/utils.js index 1780bd7a3..3050fa966 100644 --- a/packages/components-faraday/src/components/utils.js +++ b/packages/components-faraday/src/components/utils.js @@ -66,3 +66,31 @@ export const redirectToError = redirectFn => err => { const errorText = get(JSON.parse(err.response), 'error') redirectFn('/error-page', errorText || 'Oops! Something went wrong.') } + +const generatePasswordHash = () => + Array.from({ length: 4 }, () => + Math.random() + .toString(36) + .slice(4), + ).join('') + +export const parseSignupAuthor = ({ token, confirmPassword, ...values }) => ({ + ...values, + admin: false, + isConfirmed: false, + editorInChief: false, + handlingEditor: false, + username: values.email, + confirmationToken: generatePasswordHash(), +}) + +export const parseSearchParams = url => { + const params = new URLSearchParams(url) + const parsedObject = {} + /* eslint-disable */ + for ([key, value] of params) { + parsedObject[key] = value + } + /* eslint-enable */ + return parsedObject +} diff --git a/packages/components-faraday/src/redux/users.js b/packages/components-faraday/src/redux/users.js index dd9cd57ab..e7cfc67be 100644 --- a/packages/components-faraday/src/redux/users.js +++ b/packages/components-faraday/src/redux/users.js @@ -1,4 +1,11 @@ import { get } from 'lodash' +import { create } from 'pubsweet-client/src/helpers/api' export const currentUserIs = (state, role) => get(state, `currentUser.user.${role}`) + +export const confirmUser = (userId, confirmationToken) => + create(`/users/confirm`, { + userId, + confirmationToken, + }) diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js index bf161391a..4de1459d8 100644 --- a/packages/xpub-faraday/app/routes.js +++ b/packages/xpub-faraday/app/routes.js @@ -3,25 +3,25 @@ import { withProps } from 'recompose' import { Route, Switch } from 'react-router-dom' import { AuthenticatedComponent } from 'pubsweet-client' import Login from 'pubsweet-component-login/LoginContainer' -import Signup from 'pubsweet-component-signup/SignupContainer' import { Wizard } from 'pubsweet-component-wizard/src/components' import { ManuscriptPage } from 'pubsweet-component-manuscript/src/components' import DashboardPage from 'pubsweet-components-faraday/src/components/Dashboard' import { NotFound, - ConfirmationPage, ErrorPage, + ConfirmationPage, } from 'pubsweet-components-faraday/src/components/UIComponents/' import { - AdminDashboard, AdminUsers, AdminRoute, + AdminDashboard, } from 'pubsweet-components-faraday/src/components/Admin' import AddEditUser from 'pubsweet-components-faraday/src/components/Admin/AddEditUser' import { - SignUpInvitationPage, + ConfirmAccount, ReviewerSignUp, + SignUpInvitationPage, } from 'pubsweet-components-faraday/src/components/SignUp' import FaradayApp from './FaradayApp' @@ -43,7 +43,19 @@ const Routes = () => ( <FaradayApp> <Switch> <Route component={LoginPage} exact path="/login" /> - <Route component={Signup} exact path="/signup" /> + <Route + component={routeParams => ( + <SignUpInvitationPage + subtitle={null} + title="Author signup" + type="signup" + {...routeParams} + /> + )} + exact + path="/signup" + /> + <Route component={ConfirmAccount} exact path="/confirm-signup" /> <PrivateRoute component={DashboardPage} exact path="/" /> <PrivateRoute component={ConfirmationPage} diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index 6cb6dc5d3..b04435400 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -69,6 +69,9 @@ module.exports = { 'invite-reviewer': { url: process.env.PUBSWEET_INVITE_REVIEWER_URL || '/invite-reviewer', }, + 'confirm-signup': { + url: process.env.PUBSWEET_CONFIRM_SIGNUP_URL || '/confirm-signup', + }, roles: { global: ['admin', 'editorInChief', 'author', 'handlingEditor'], collection: ['handlingEditor', 'reviewer', 'author'], diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index 3f8642489..591ba26b0 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -124,6 +124,7 @@ module.exports = { editorInChief: Joi.boolean(), handlingEditor: Joi.boolean(), invitationToken: Joi.string().allow(''), + confirmationToken: Joi.string().allow(''), }, team: { group: Joi.string(), -- GitLab