diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index 51acd9cfe5..114ffbff39 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -590,78 +590,4 @@ export class UsersController { await this.externalHooks.run('user.deleted', [await this.userService.toPublic(userToDelete)]); return { success: true }; } - - /** - * Resend email invite to user. - */ - @Post('/:id/reinvite') - async reinviteUser(req: UserRequest.Reinvite) { - const { id: idToReinvite } = req.params; - const isWithinUsersLimit = Container.get(License).isWithinUsersLimit(); - - if (!isWithinUsersLimit) { - this.logger.debug( - 'Request to send email invite(s) to user(s) failed because the user limit quota has been reached', - ); - throw new UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED); - } - - if (!this.mailer.isEmailSetUp) { - this.logger.error('Request to reinvite a user failed because email sending was not set up'); - throw new InternalServerError('Email sending must be set up in order to invite other users'); - } - - const reinvitee = await this.userService.findOneBy({ id: idToReinvite }); - if (!reinvitee) { - this.logger.debug( - 'Request to reinvite a user failed because the ID of the reinvitee was not found in database', - ); - throw new NotFoundError('Could not find user'); - } - - if (reinvitee.password) { - this.logger.debug( - 'Request to reinvite a user failed because the invite had already been accepted', - { userId: reinvitee.id }, - ); - throw new BadRequestError('User has already accepted the invite'); - } - - const baseUrl = getInstanceBaseUrl(); - const inviteAcceptUrl = `${baseUrl}/signup?inviterId=${req.user.id}&inviteeId=${reinvitee.id}`; - - try { - const result = await this.mailer.invite({ - email: reinvitee.email, - inviteAcceptUrl, - domain: baseUrl, - }); - if (result.emailSent) { - void this.internalHooks.onUserReinvite({ - user: req.user, - target_user_id: reinvitee.id, - public_api: false, - }); - - void this.internalHooks.onUserTransactionalEmail({ - user_id: reinvitee.id, - message_type: 'Resend invite', - public_api: false, - }); - } - } catch (error) { - void this.internalHooks.onEmailFailed({ - user: reinvitee, - message_type: 'Resend invite', - public_api: false, - }); - this.logger.error('Failed to send email', { - email: reinvitee.email, - inviteAcceptUrl, - domain: baseUrl, - }); - throw new InternalServerError(`Failed to send email to ${reinvitee.email}`); - } - return { success: true }; - } } diff --git a/packages/cli/test/integration/auth.mw.test.ts b/packages/cli/test/integration/auth.mw.test.ts index 60c9eff8fe..933f33a31b 100644 --- a/packages/cli/test/integration/auth.mw.test.ts +++ b/packages/cli/test/integration/auth.mw.test.ts @@ -19,7 +19,6 @@ describe('Auth Middleware', () => { const ROUTES_REQUIRING_AUTHORIZATION: Readonly> = [ ['POST', '/users'], ['DELETE', '/users/123'], - ['POST', '/users/123/reinvite'], ['POST', '/owner/setup'], ]; diff --git a/packages/cli/test/integration/users.api.test.ts b/packages/cli/test/integration/users.api.test.ts index cb0c6e7710..f197bdec8c 100644 --- a/packages/cli/test/integration/users.api.test.ts +++ b/packages/cli/test/integration/users.api.test.ts @@ -655,26 +655,3 @@ describe('POST /users', () => { assertInviteUserErrorResponse(invitationResponse); }); }); - -describe('POST /users/:id/reinvite', () => { - test('should send reinvite, but fail if user already accepted invite', async () => { - mailer.invite.mockImplementation(async () => ({ emailSent: true })); - - const email = randomEmail(); - const payload = [{ email }]; - const response = await authOwnerAgent.post('/users').send(payload); - - expect(response.statusCode).toBe(200); - - const { data } = response.body; - const invitedUserId = data[0].user.id; - const reinviteResponse = await authOwnerAgent.post(`/users/${invitedUserId}/reinvite`); - - expect(reinviteResponse.statusCode).toBe(200); - - const member = await createMember(); - const reinviteMemberResponse = await authOwnerAgent.post(`/users/${member.id}/reinvite`); - - expect(reinviteMemberResponse.statusCode).toBe(400); - }); -}); diff --git a/packages/editor-ui/src/stores/users.store.ts b/packages/editor-ui/src/stores/users.store.ts index d43cdf9276..b8745401fd 100644 --- a/packages/editor-ui/src/stores/users.store.ts +++ b/packages/editor-ui/src/stores/users.store.ts @@ -8,7 +8,6 @@ import { login, loginCurrentUser, logout, - reinvite, sendForgotPasswordEmail, setupOwner, signup, @@ -328,9 +327,14 @@ export const useUsersStore = defineStore(STORES.USERS, { this.addUsers(users.map(({ user }) => ({ isPending: true, ...user }))); return users; }, - async reinviteUser(params: { id: string }): Promise { + async reinviteUser(params: { email: string }): Promise { const rootStore = useRootStore(); - await reinvite(rootStore.getRestApiContext, params); + const invitationResponse = await inviteUsers(rootStore.getRestApiContext, [ + { email: params.email }, + ]); + if (!invitationResponse[0].user.emailSent) { + throw Error(invitationResponse[0].error); + } }, async getUserInviteLink(params: { id: string }): Promise<{ link: string }> { const rootStore = useRootStore(); diff --git a/packages/editor-ui/src/views/SettingsUsersView.vue b/packages/editor-ui/src/views/SettingsUsersView.vue index b51cd6f889..9f392a3ee9 100644 --- a/packages/editor-ui/src/views/SettingsUsersView.vue +++ b/packages/editor-ui/src/views/SettingsUsersView.vue @@ -144,15 +144,14 @@ export default defineComponent({ }, async onReinvite(userId: string) { const user = this.usersStore.getUserById(userId); - if (user) { + if (user?.email) { try { - await this.usersStore.reinviteUser({ id: user.id }); - + await this.usersStore.reinviteUser({ email: user.email }); this.showToast({ type: 'success', title: this.$locale.baseText('settings.users.inviteResent'), message: this.$locale.baseText('settings.users.emailSentTo', { - interpolate: { email: user.email || '' }, + interpolate: { email: user.email ?? '' }, }), }); } catch (e) {