Skip to content

Commit

Permalink
patch: replace resend with nodemailer
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdip-b committed Jan 3, 2024
1 parent f97adf0 commit f0b0887
Show file tree
Hide file tree
Showing 19 changed files with 1,547 additions and 1,134 deletions.
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ DATABASE_URL=postgresql://postgres:<your-project-password>@db.<your-project-name
SUPABASE_API_URL=https://<your-project-name>.supabase.co
SUPABASE_ANON_KEY=

RESEND_API_KEY=re_
SMTP_HOST=
SMTP_PORT=
SMTP_EMAIL_ADDRESS=
SMTP_PASSWORD=
FROM_EMAIL="your-name <[email protected]>"

JWT_SECRET=secret

FROM_EMAIL="your-name <[email protected]>"
WEB_FRONTEND_URL=https://keyshade.xyz
WORKSPACE_FRONTEND_URL=https://app.keyshade.xyz
1 change: 0 additions & 1 deletion apps/api/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"outputPath": "dist/apps/api",
"main": "apps/api/src/main.ts",
"tsConfig": "apps/api/tsconfig.app.json",
"assets": ["apps/api/src/assets"],
"webpackConfig": "apps/api/webpack.config.js"
},
"configurations": {
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PassportModule } from '@nestjs/passport'
import { AuthModule } from '../auth/auth.module'
import { PrismaModule } from '../prisma/prisma.module'
import { CommonModule } from '../common/common.module'
import { ResendModule } from '../resend/resend.module'
import { MailModule } from '../mail/mail.module'
import { APP_GUARD } from '@nestjs/core'
import { AuthGuard } from '../auth/guard/auth.guard'
import { UserModule } from '../user/user.module'
Expand All @@ -24,7 +24,7 @@ import { EnvironmentModule } from '../environment/environment.module'
AuthModule,
PrismaModule,
CommonModule,
ResendModule,
MailModule,
SupabaseModule,
UserModule,
ProjectModule,
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/auth/controller/auth.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Test, TestingModule } from '@nestjs/testing'
import { MockAuthRepository } from '../repository/mock.repository'
import { AUTH_REPOSITORY } from '../repository/interface.repository'
import { AuthService } from '../service/auth.service'
import { RESEND_SERVICE } from '../../resend/services/resend.service.interface'
import { MockResend } from '../../resend/services/mock.resend'
import { MAIL_SERVICE } from '../../mail/services/interface.service'
import { MockMailService } from '../../mail/services/mock.service'
import { JwtService } from '@nestjs/jwt'
import { PrismaService } from '../../prisma/prisma.service'
import { AuthController } from './auth.controller'
Expand All @@ -18,7 +18,7 @@ describe('AuthController', () => {
controllers: [AuthController],
providers: [
AuthService,
{ provide: RESEND_SERVICE, useClass: MockResend },
{ provide: MAIL_SERVICE, useClass: MockMailService },
{ provide: AUTH_REPOSITORY, useClass: MockAuthRepository },
{ provide: USER_REPOSITORY, useClass: MockUserRepository },
JwtService,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/auth/repository/auth.repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Otp, User } from '@prisma/client'
import { IAuthRepository } from './interface.repository'
import { PrismaService } from '../../prisma/prisma.service'
import { Injectable } from '@nestjs/common'

@Injectable()
export class AuthRepository implements IAuthRepository {
constructor(private readonly prisma: PrismaService) {}

Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/auth/service/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing'
import { AuthService } from './auth.service'
import { MockResend } from '../../resend/services/mock.resend'
import { RESEND_SERVICE } from '../../resend/services/resend.service.interface'
import { MockMailService } from '../../mail/services/mock.service'
import { MAIL_SERVICE } from '../../mail/services/interface.service'
import { JwtService } from '@nestjs/jwt'
import { PrismaService } from '../../prisma/prisma.service'
import { AUTH_REPOSITORY } from '../repository/interface.repository'
Expand All @@ -16,7 +16,7 @@ describe('AuthService', () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AuthService,
{ provide: RESEND_SERVICE, useClass: MockResend },
{ provide: MAIL_SERVICE, useClass: MockMailService },
{ provide: AUTH_REPOSITORY, useClass: MockAuthRepository },
{ provide: USER_REPOSITORY, useClass: MockUserRepository },
JwtService,
Expand Down
8 changes: 4 additions & 4 deletions apps/api/src/auth/service/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { randomUUID } from 'crypto'
import { JwtService } from '@nestjs/jwt'
import { UserAuthenticatedResponse } from '../auth.types'
import {
IResendService,
RESEND_SERVICE
} from '../../resend/services/resend.service.interface'
IMailService,
MAIL_SERVICE
} from '../../mail/services/interface.service'
import {
IUserRepository,
USER_REPOSITORY
Expand All @@ -30,7 +30,7 @@ export class AuthService {
constructor(
@Inject(AUTH_REPOSITORY) private readonly authRepository: IAuthRepository,
@Inject(USER_REPOSITORY) readonly userRepository: IUserRepository,
@Inject(RESEND_SERVICE) private resend: IResendService,
@Inject(MAIL_SERVICE) private resend: IMailService,
private jwt: JwtService
) {
this.logger = new Logger(AuthService.name)
Expand Down
20 changes: 20 additions & 0 deletions apps/api/src/mail/mail.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Global, Module } from '@nestjs/common'
import { MailService } from './services/mail.service'
import { MAIL_SERVICE } from './services/interface.service'

@Global()
@Module({
providers: [
{
provide: MAIL_SERVICE,
useClass: MailService
}
],
exports: [
{
provide: MAIL_SERVICE,
useClass: MailService
}
]
})
export class MailModule {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ProjectRole } from '@prisma/client'

export const RESEND_SERVICE = 'RESEND_SERVICE'
export const MAIL_SERVICE = 'MAIL_SERVICE'

export interface IResendService {
export interface IMailService {
sendOtp(email: string, otp: string): Promise<void>

projectInvitationMailForRegisteredUser(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { Injectable, Logger } from '@nestjs/common'
import { Resend } from 'resend'
import { IResendService } from './resend.service.interface'
import { $Enums } from '@prisma/client'
import { IMailService } from './interface.service'
import { ProjectRole } from '@prisma/client'
import { Transporter, createTransport } from 'nodemailer'

@Injectable()
export class MailResend implements IResendService {
private readonly resend: Resend
private readonly log = new Logger(MailResend.name)
export class MailService implements IMailService {
private readonly transporter: Transporter
private readonly log = new Logger(MailService.name)

constructor() {
this.resend = new Resend(process.env.RESEND_API_KEY)
this.transporter = createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: true,
auth: {
user: process.env.SMTP_EMAIL_ADDRESS,
pass: process.env.SMTP_PASSWORD
}
})
}
async projectInvitationMailForRegisteredUser(
email: string,
project: string,
actionUrl: string,
invitee: string,
role: $Enums.ProjectRole
role: ProjectRole
): Promise<void> {
const subject = `You have been invited to a ${project}`
const body = `<!DOCTYPE html>
Expand Down Expand Up @@ -44,7 +52,7 @@ export class MailResend implements IResendService {
project: string,
actionUrl: string,
invitee: string,
role: $Enums.ProjectRole
role: ProjectRole
): Promise<void> {
const subject = `You have been invited to a ${project}`
const body = `<!DOCTYPE html>
Expand Down Expand Up @@ -95,16 +103,17 @@ export class MailResend implements IResendService {
subject: string,
body: string
): Promise<void> {
const { error } = await this.resend.emails.send({
from: process.env.FROM_EMAIL,
to: email,
subject,
html: body
})

if (error) {
try {
await this.transporter.sendMail({
from: process.env.FROM_EMAIL,
to: email,
subject: subject,
html: body
})
this.log.log(`Email sent to ${email}`)
} catch (error) {
this.log.error(`Error sending email to ${email}: ${error.message}`)
throw new Error(error.message)
throw new Error(`Error sending email to ${email}: ${error.message}`)
}
}
}
36 changes: 36 additions & 0 deletions apps/api/src/mail/services/mock.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Logger } from '@nestjs/common'
import { IMailService } from './interface.service'
import { ProjectRole } from '@prisma/client'

export class MockMailService implements IMailService {
private readonly log = new Logger(MockMailService.name)

async projectInvitationMailForRegisteredUser(
email: string,
project: string,
actionUrl: string,
invitee: string,
role: ProjectRole
): Promise<void> {
this.log.log(
`Project Invitation Mail for Registered User: ${email}, ${project}, ${actionUrl}, ${invitee}, ${role}`
)
}

async projectInvitationMailForNonRegisteredUser(
email: string,
project: string,
actionUrl: string,
invitee: string,
role: ProjectRole
): Promise<void> {
this.log.log(
`Project Invitation Mail for Non Registered User: ${email}, ${project}, ${actionUrl}, ${invitee}, ${role}`
)
}

async sendOtp(email: string, otp: string): Promise<void> {
this.log.log(`OTP for ${email} is ${otp}`)
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Test, TestingModule } from '@nestjs/testing'
import { IResendService } from './resend.service.interface'
import { MockResend } from './mock.resend'
import { IMailService } from './interface.service'
import { MockMailService } from './mock.service'

describe('ResendService', () => {
let service: IResendService
let service: IMailService

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MockResend]
providers: [MockMailService]
}).compile()

service = module.get<MockResend>(MockResend)
service = module.get<MockMailService>(MockMailService)
})

it('should be defined', () => {
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/project/controller/project.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ENVIRONMENT_REPOSITORY } from '../../environment/repository/interface.r
import { MockEnvironmentRepository } from '../../environment/repository/mock.repository'
import { USER_REPOSITORY } from '../../user/repository/interface.repository'
import { MockUserRepository } from '../../user/repository/mock.repository'
import { RESEND_SERVICE } from '../../resend/services/resend.service.interface'
import { MockResend } from '../../resend/services/mock.resend'
import { MAIL_SERVICE } from '../../mail/services/interface.service'
import { MockMailService } from '../../mail/services/mock.service'
import { JwtService } from '@nestjs/jwt'
import { ProjectPermission } from '../misc/project.permission'

Expand All @@ -26,7 +26,7 @@ describe('ProjectController', () => {
useClass: MockEnvironmentRepository
},
{ provide: USER_REPOSITORY, useClass: MockUserRepository },
{ provide: RESEND_SERVICE, useClass: MockResend },
{ provide: MAIL_SERVICE, useClass: MockMailService },
JwtService,
ProjectPermission
]
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/project/service/project.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { ENVIRONMENT_REPOSITORY } from '../../environment/repository/interface.r
import { MockEnvironmentRepository } from '../../environment/repository/mock.repository'
import { USER_REPOSITORY } from '../../user/repository/interface.repository'
import { MockUserRepository } from '../../user/repository/mock.repository'
import { MockResend } from '../../resend/services/mock.resend'
import { RESEND_SERVICE } from '../../resend/services/resend.service.interface'
import { MockMailService } from '../../mail/services/mock.service'
import { MAIL_SERVICE } from '../../mail/services/interface.service'
import { JwtService } from '@nestjs/jwt'
import { ProjectPermission } from '../misc/project.permission'

Expand All @@ -24,7 +24,7 @@ describe('ProjectService', () => {
useClass: MockEnvironmentRepository
},
{ provide: USER_REPOSITORY, useClass: MockUserRepository },
{ provide: RESEND_SERVICE, useClass: MockResend },
{ provide: MAIL_SERVICE, useClass: MockMailService },
ProjectPermission,
JwtService
]
Expand Down
8 changes: 4 additions & 4 deletions apps/api/src/project/service/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import {
USER_REPOSITORY
} from '../../user/repository/interface.repository'
import {
IResendService,
RESEND_SERVICE
} from '../../resend/services/resend.service.interface'
IMailService,
MAIL_SERVICE
} from '../../mail/services/interface.service'
import { CurrentUser } from '../../decorators/user.decorator'
import { JwtService } from '@nestjs/jwt'
import { createKeyPair } from '../../common/create-key-pair'
Expand All @@ -43,7 +43,7 @@ export class ProjectService {
@Inject(ENVIRONMENT_REPOSITORY)
private readonly environmentRepository: IEnvironmentRepository,
@Inject(USER_REPOSITORY) private readonly userRepository: IUserRepository,
@Inject(RESEND_SERVICE) private readonly resendService: IResendService,
@Inject(MAIL_SERVICE) private readonly resendService: IMailService,
private readonly jwt: JwtService,
private readonly permission: ProjectPermission
) {}
Expand Down
16 changes: 0 additions & 16 deletions apps/api/src/resend/resend.module.ts

This file was deleted.

38 changes: 0 additions & 38 deletions apps/api/src/resend/services/mock.resend.ts

This file was deleted.

Loading

0 comments on commit f0b0887

Please sign in to comment.