feat: event emitter added
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
"@nestjs/common": "10.2.7",
|
"@nestjs/common": "10.2.7",
|
||||||
"@nestjs/config": "3.1.1",
|
"@nestjs/config": "3.1.1",
|
||||||
"@nestjs/core": "10.2.7",
|
"@nestjs/core": "10.2.7",
|
||||||
|
"@nestjs/event-emitter": "2.0.4",
|
||||||
"@nestjs/graphql": "12.0.9",
|
"@nestjs/graphql": "12.0.9",
|
||||||
"@nestjs/jwt": "10.1.1",
|
"@nestjs/jwt": "10.1.1",
|
||||||
"@nestjs/passport": "10.0.2",
|
"@nestjs/passport": "10.0.2",
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
USER_IS_ADMIN,
|
USER_IS_ADMIN,
|
||||||
USER_NOT_FOUND,
|
USER_NOT_FOUND,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import { MailerService } from '../mailer/mailer.service';
|
|
||||||
import { InvitedUser } from './invited-user.model';
|
import { InvitedUser } from './invited-user.model';
|
||||||
import { TeamService } from '../team/team.service';
|
import { TeamService } from '../team/team.service';
|
||||||
import { TeamCollectionService } from '../team-collection/team-collection.service';
|
import { TeamCollectionService } from '../team-collection/team-collection.service';
|
||||||
@@ -31,6 +30,8 @@ import { ShortcodeService } from 'src/shortcode/shortcode.service';
|
|||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
||||||
import { UserDeletionResult } from 'src/user/user.model';
|
import { UserDeletionResult } from 'src/user/user.model';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Events } from 'src/types/EventEmitter';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
@@ -43,9 +44,9 @@ export class AdminService {
|
|||||||
private readonly teamInvitationService: TeamInvitationService,
|
private readonly teamInvitationService: TeamInvitationService,
|
||||||
private readonly pubsub: PubSubService,
|
private readonly pubsub: PubSubService,
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly mailerService: MailerService,
|
|
||||||
private readonly shortcodeService: ShortcodeService,
|
private readonly shortcodeService: ShortcodeService,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,17 +100,16 @@ export class AdminService {
|
|||||||
});
|
});
|
||||||
if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED);
|
if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED);
|
||||||
|
|
||||||
try {
|
this.eventEmitter.emit(Events.MAILER_SEND_USER_INVITATION_EMAIL, {
|
||||||
await this.mailerService.sendUserInvitationEmail(inviteeEmail, {
|
to: inviteeEmail,
|
||||||
|
mailDesc: {
|
||||||
template: 'user-invitation',
|
template: 'user-invitation',
|
||||||
variables: {
|
variables: {
|
||||||
inviteeEmail: inviteeEmail,
|
inviteeEmail: inviteeEmail,
|
||||||
magicLink: `${this.configService.get('VITE_BASE_URL')}`,
|
magicLink: `${this.configService.get('VITE_BASE_URL')}`,
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
} catch (e) {
|
});
|
||||||
return E.left(EMAIL_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add invitee email to the list of invited users by admin
|
// Add invitee email to the list of invited users by admin
|
||||||
const dbInvitedUser = await this.prisma.invitedUsers.create({
|
const dbInvitedUser = await this.prisma.invitedUsers.create({
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ import { MailerModule } from './mailer/mailer.module';
|
|||||||
import { PosthogModule } from './posthog/posthog.module';
|
import { PosthogModule } from './posthog/posthog.module';
|
||||||
import { ScheduleModule } from '@nestjs/schedule';
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
import { HealthModule } from './health/health.module';
|
import { HealthModule } from './health/health.module';
|
||||||
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
EventEmitterModule.forRoot(),
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [async () => loadInfraConfiguration()],
|
load: [async () => loadInfraConfiguration()],
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { MailerService } from 'src/mailer/mailer.service';
|
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
import { UserService } from 'src/user/user.service';
|
import { UserService } from 'src/user/user.service';
|
||||||
import { VerifyMagicDto } from './dto/verify-magic.dto';
|
import { VerifyMagicDto } from './dto/verify-magic.dto';
|
||||||
@@ -30,6 +29,8 @@ import { VerificationToken } from '@prisma/client';
|
|||||||
import { Origin } from './helper';
|
import { Origin } from './helper';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { InfraConfigService } from 'src/infra-config/infra-config.service';
|
import { InfraConfigService } from 'src/infra-config/infra-config.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Events } from 'src/types/EventEmitter';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
@@ -37,9 +38,9 @@ export class AuthService {
|
|||||||
private usersService: UserService,
|
private usersService: UserService,
|
||||||
private prismaService: PrismaService,
|
private prismaService: PrismaService,
|
||||||
private jwtService: JwtService,
|
private jwtService: JwtService,
|
||||||
private readonly mailerService: MailerService,
|
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
private infraConfigService: InfraConfigService,
|
private infraConfigService: InfraConfigService,
|
||||||
|
private eventEmitter: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,11 +235,14 @@ export class AuthService {
|
|||||||
url = this.configService.get('VITE_BASE_URL');
|
url = this.configService.get('VITE_BASE_URL');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.mailerService.sendEmail(email, {
|
this.eventEmitter.emit(Events.MAILER_SEND_EMAIL, {
|
||||||
template: 'user-invitation',
|
to: email,
|
||||||
variables: {
|
mailDesc: {
|
||||||
inviteeEmail: email,
|
template: 'user-invitation',
|
||||||
magicLink: `${url}/enter?token=${generatedTokens.token}`,
|
variables: {
|
||||||
|
inviteeEmail: email,
|
||||||
|
magicLink: `${url}/enter?token=${generatedTokens.token}`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
30
packages/hoppscotch-backend/src/mailer/mailer.listener.ts
Normal file
30
packages/hoppscotch-backend/src/mailer/mailer.listener.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { Events } from 'src/types/EventEmitter';
|
||||||
|
import {
|
||||||
|
AdminUserInvitationMailDescription,
|
||||||
|
MailDescription,
|
||||||
|
UserMagicLinkMailDescription,
|
||||||
|
} from './MailDescriptions';
|
||||||
|
import { MailerService } from './mailer.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MailerEventListener {
|
||||||
|
constructor(private mailerService: MailerService) {}
|
||||||
|
|
||||||
|
@OnEvent(Events.MAILER_SEND_EMAIL, { async: true })
|
||||||
|
async handleSendEmailEvent(data: {
|
||||||
|
to: string;
|
||||||
|
mailDesc: MailDescription | UserMagicLinkMailDescription;
|
||||||
|
}) {
|
||||||
|
await this.mailerService.sendEmail(data.to, data.mailDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent(Events.MAILER_SEND_USER_INVITATION_EMAIL, { async: true })
|
||||||
|
async handleSendUserInvitationEmailEvent(data: {
|
||||||
|
to: string;
|
||||||
|
mailDesc: AdminUserInvitationMailDescription;
|
||||||
|
}) {
|
||||||
|
await this.mailerService.sendUserInvitationEmail(data.to, data.mailDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,17 +9,21 @@ import {
|
|||||||
} from 'src/errors';
|
} from 'src/errors';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { loadInfraConfiguration } from 'src/infra-config/helper';
|
import { loadInfraConfiguration } from 'src/infra-config/helper';
|
||||||
|
import { MailerEventListener } from './mailer.listener';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({})
|
||||||
imports: [],
|
|
||||||
providers: [MailerService],
|
|
||||||
exports: [MailerService],
|
|
||||||
})
|
|
||||||
export class MailerModule {
|
export class MailerModule {
|
||||||
static async register() {
|
static async register() {
|
||||||
const env = await loadInfraConfiguration();
|
const env = await loadInfraConfiguration();
|
||||||
|
|
||||||
|
// If mailer SMTP is DISABLED, return the module without any configuration
|
||||||
|
if (env.INFRA.MAILER_SMTP_ENABLE !== 'true') {
|
||||||
|
console.log('Mailer SMTP is disabled');
|
||||||
|
return { module: MailerModule };
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mailer is ENABLED, return the module with configuration
|
||||||
let mailerSmtpUrl = env.INFRA.MAILER_SMTP_URL;
|
let mailerSmtpUrl = env.INFRA.MAILER_SMTP_URL;
|
||||||
let mailerAddressFrom = env.INFRA.MAILER_ADDRESS_FROM;
|
let mailerAddressFrom = env.INFRA.MAILER_ADDRESS_FROM;
|
||||||
|
|
||||||
@@ -31,6 +35,7 @@ export class MailerModule {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
module: MailerModule,
|
module: MailerModule,
|
||||||
|
providers: [MailerService, MailerEventListener],
|
||||||
imports: [
|
imports: [
|
||||||
NestMailerModule.forRoot({
|
NestMailerModule.forRoot({
|
||||||
transport: mailerSmtpUrl ?? throwErr(MAILER_SMTP_URL_UNDEFINED),
|
transport: mailerSmtpUrl ?? throwErr(MAILER_SMTP_URL_UNDEFINED),
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ import {
|
|||||||
import { throwErr } from 'src/utils';
|
import { throwErr } from 'src/utils';
|
||||||
import { EMAIL_FAILED } from 'src/errors';
|
import { EMAIL_FAILED } from 'src/errors';
|
||||||
import { MailerService as NestMailerService } from '@nestjs-modules/mailer';
|
import { MailerService as NestMailerService } from '@nestjs-modules/mailer';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MailerService {
|
export class MailerService {
|
||||||
constructor(private readonly nestMailerService: NestMailerService) {}
|
constructor(
|
||||||
|
private readonly nestMailerService: NestMailerService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes an input mail description and spits out the Email subject required for it
|
* Takes an input mail description and spits out the Email subject required for it
|
||||||
@@ -42,6 +46,8 @@ export class MailerService {
|
|||||||
to: string,
|
to: string,
|
||||||
mailDesc: MailDescription | UserMagicLinkMailDescription,
|
mailDesc: MailDescription | UserMagicLinkMailDescription,
|
||||||
) {
|
) {
|
||||||
|
if (this.configService.get('INFRA.MAILER_SMTP_ENABLE') !== 'true') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.nestMailerService.sendMail({
|
await this.nestMailerService.sendMail({
|
||||||
to,
|
to,
|
||||||
@@ -64,6 +70,8 @@ export class MailerService {
|
|||||||
to: string,
|
to: string,
|
||||||
mailDesc: AdminUserInvitationMailDescription,
|
mailDesc: AdminUserInvitationMailDescription,
|
||||||
) {
|
) {
|
||||||
|
if (this.configService.get('INFRA.MAILER_SMTP_ENABLE') !== 'true') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await this.nestMailerService.sendMail({
|
const res = await this.nestMailerService.sendMail({
|
||||||
to,
|
to,
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ import {
|
|||||||
TEAM_MEMBER_NOT_FOUND,
|
TEAM_MEMBER_NOT_FOUND,
|
||||||
} from 'src/errors';
|
} from 'src/errors';
|
||||||
import { TeamInvitation } from './team-invitation.model';
|
import { TeamInvitation } from './team-invitation.model';
|
||||||
import { MailerService } from 'src/mailer/mailer.service';
|
|
||||||
import { UserService } from 'src/user/user.service';
|
import { UserService } from 'src/user/user.service';
|
||||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||||
import { validateEmail } from '../utils';
|
import { validateEmail } from '../utils';
|
||||||
import { AuthUser } from 'src/types/AuthUser';
|
import { AuthUser } from 'src/types/AuthUser';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Events } from 'src/types/EventEmitter';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TeamInvitationService {
|
export class TeamInvitationService {
|
||||||
@@ -28,9 +29,9 @@ export class TeamInvitationService {
|
|||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly teamService: TeamService,
|
private readonly teamService: TeamService,
|
||||||
private readonly mailerService: MailerService,
|
|
||||||
private readonly pubsub: PubSubService,
|
private readonly pubsub: PubSubService,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
|
private eventEmitter: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,14 +148,17 @@ export class TeamInvitationService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.mailerService.sendEmail(inviteeEmail, {
|
this.eventEmitter.emit(Events.MAILER_SEND_EMAIL, {
|
||||||
template: 'team-invitation',
|
to: inviteeEmail,
|
||||||
variables: {
|
mailDesc: {
|
||||||
invitee: creator.displayName ?? 'A Hoppscotch User',
|
template: 'team-invitation',
|
||||||
action_url: `${this.configService.get('VITE_BASE_URL')}/join-team?id=${
|
variables: {
|
||||||
dbInvitation.id
|
invitee: creator.displayName ?? 'A Hoppscotch User',
|
||||||
}`,
|
action_url: `${this.configService.get(
|
||||||
invite_team_name: team.name,
|
'VITE_BASE_URL',
|
||||||
|
)}/join-team?id=${dbInvitation.id}`,
|
||||||
|
invite_team_name: team.name,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
4
packages/hoppscotch-backend/src/types/EventEmitter.ts
Normal file
4
packages/hoppscotch-backend/src/types/EventEmitter.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum Events {
|
||||||
|
MAILER_SEND_EMAIL = 'mailer.sendEmail',
|
||||||
|
MAILER_SEND_USER_INVITATION_EMAIL = 'mailer.sendUserInvitationEmail',
|
||||||
|
}
|
||||||
34235
pnpm-lock.yaml
generated
34235
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user