Files
bigcapital/packages/server/src/services/InviteUsers/AcceptInviteUser.ts
2023-05-05 00:28:57 +02:00

129 lines
3.7 KiB
TypeScript

import { Service, Inject } from 'typedi';
import moment from 'moment';
import { ServiceError } from '@/exceptions';
import { Invite, SystemUser, Tenant } from '@/system/models';
import { hashPassword } from 'utils';
import events from '@/subscribers/events';
import {
IAcceptInviteEventPayload,
IInviteUserInput,
ICheckInviteEventPayload,
IUserInvite,
} from '@/interfaces';
import { ERRORS } from './constants';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import { IAcceptInviteUserService } from '@/interfaces';
@Service()
export default class AcceptInviteUserService
implements IAcceptInviteUserService
{
@Inject()
private eventPublisher: EventPublisher;
/**
* Accept the received invite.
* @param {string} token
* @param {IInviteUserInput} inviteUserInput
* @throws {ServiceErrors}
* @returns {Promise<void>}
*/
public async acceptInvite(
token: string,
inviteUserDTO: IInviteUserInput
): Promise<void> {
// Retrieve the invite token or throw not found error.
const inviteToken = await this.getInviteTokenOrThrowError(token);
// Hash the given password.
const hashedPassword = await hashPassword(inviteUserDTO.password);
// Retrieve the system user.
const user = await SystemUser.query().findOne('email', inviteToken.email);
// Sets the invited user details after invite accepting.
const systemUser = await SystemUser.query().updateAndFetchById(
inviteToken.userId,
{
...inviteUserDTO,
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
password: hashedPassword,
}
);
// Clear invite token by the given user id.
await this.clearInviteTokensByUserId(inviteToken.userId);
// Triggers `onUserAcceptInvite` event.
await this.eventPublisher.emitAsync(events.inviteUser.acceptInvite, {
inviteToken,
user: systemUser,
inviteUserDTO,
} as IAcceptInviteEventPayload);
}
/**
* Validate the given invite token.
* @param {string} token - the given token string.
* @throws {ServiceError}
*/
public async checkInvite(
token: string
): Promise<{ inviteToken: IUserInvite; orgName: object }> {
const inviteToken = await this.getInviteTokenOrThrowError(token);
// Find the tenant that associated to the given token.
const tenant = await Tenant.query()
.findById(inviteToken.tenantId)
.withGraphFetched('metadata');
// Triggers `onUserCheckInvite` event.
await this.eventPublisher.emitAsync(events.inviteUser.checkInvite, {
inviteToken,
tenant,
} as ICheckInviteEventPayload);
return { inviteToken, orgName: tenant.metadata.name };
}
/**
* Retrieve invite model from the given token or throw error.
* @param {string} token - Then given token string.
* @throws {ServiceError}
* @returns {Invite}
*/
private getInviteTokenOrThrowError = async (
token: string
): Promise<IUserInvite> => {
const inviteToken = await Invite.query()
.modify('notExpired')
.findOne('token', token);
if (!inviteToken) {
throw new ServiceError(ERRORS.INVITE_TOKEN_INVALID);
}
return inviteToken;
};
/**
* Validate the given user email and phone number uniquine.
* @param {IInviteUserInput} inviteUserInput
*/
private validateUserPhoneNumberNotExists = async (
phoneNumber: string
): Promise<void> => {
const foundUser = await SystemUser.query().findOne({ phoneNumber });
if (foundUser) {
throw new ServiceError(ERRORS.PHONE_NUMBER_EXISTS);
}
};
/**
* Clear invite tokens of the given user id.
* @param {number} userId - User id.
*/
private clearInviteTokensByUserId = async (userId: number) => {
await Invite.query().where('user_id', userId).delete();
};
}