mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
refactor: Use POST /users to re-invite users (no-changelog) (#7714)
This commit is contained in:
parent
3460eb5eeb
commit
4020c14d59
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ describe('Auth Middleware', () => {
|
|||
const ROUTES_REQUIRING_AUTHORIZATION: Readonly<Array<[string, string]>> = [
|
||||
['POST', '/users'],
|
||||
['DELETE', '/users/123'],
|
||||
['POST', '/users/123/reinvite'],
|
||||
['POST', '/owner/setup'],
|
||||
];
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<void> {
|
||||
async reinviteUser(params: { email: string }): Promise<void> {
|
||||
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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue