Skip to content

Commit 19cd3c5

Browse files
committed
feat: use mailjet in production
1 parent d7746bf commit 19cd3c5

File tree

8 files changed

+162
-105
lines changed

8 files changed

+162
-105
lines changed

.github/workflows/deploy-api.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,8 @@ jobs:
3939
sed -i "s|MYSQL_HOST=.*|MYSQL_HOST=${{ secrets.DATABASE_HOST }}|" .env
4040
sed -i "s|MYSQL_USER=.*|MYSQL_USER=${{ secrets.DATABASE_USERNAME }}|" .env
4141
sed -i "s|MYSQL_PASSWORD=.*|MYSQL_PASSWORD=${{ secrets.DATABASE_PASSWORD }}|" .env
42-
sed -i "s|SMTP_HOST=.*|SMTP_HOST=${{ secrets.SMTP_HOST }}|" .env
43-
sed -i "s|SMTP_PORT=.*|SMTP_PORT=${{ secrets.SMTP_PORT }}|" .env
44-
sed -i "s|SMTP_USERNAME=.*|SMTP_USERNAME=${{ secrets.SMTP_USERNAME }}|" .env
45-
sed -i "s|SMTP_PASSWORD=.*|SMTP_PASSWORD=${{ secrets.SMTP_PASSWORD }}|" .env
42+
sed -i "s|MAILJET_API_SECRET_KEY=.*|MAILJET_API_SECRET_KEY=${{ secrets.MAILJET_API_SECRET_KEY }}|" .env
43+
sed -i "s|MAILJET_API_KEY=.*|MAILJET_API_KEY=${{ secrets.MAILJET_API_KEY }}|" .env
4644
sed -i "s|GOOGLE_API_KEY=.*|GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }}|" .env
4745
sed -i "s|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}|" .env
4846
sed -i "s|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}|" .env

api/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ APP_PRIMARY_COLOR="#db0a61"
3131
APP_EMAIL_NO_REPLY=[email protected]
3232
APP_EMAIL_SUPPORT=[email protected]
3333
SESSION_DRIVER=cookie
34+
MAILJET_API_SECRET_KEY=none
35+
MAILJET_API_KEY=none

api/.env.production

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ MYSQL_DATABASE=
1616
MYSQL_HOST=
1717
MYSQL_PASSWORD=
1818
MYSQL_USER=
19-
SMTP_HOST=
20-
SMTP_PASSWORD=
21-
SMTP_PORT=
22-
SMTP_USERNAME=
19+
MAILJET_API_SECRET_KEY=
20+
MAILJET_API_KEY=
2321
GOOGLE_API_KEY=
2422
GOOGLE_CALLBACK_URL=
2523
GOOGLE_CLIENT_ID=

api/app/Services/MailService.ts

Lines changed: 90 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
1-
import Mail from '@ioc:Adonis/Addons/Mail'
2-
import type { MailMessage, MailsMessages } from '@ioc:Adonis/Addons/Mail'
1+
import Mail, { BaseMailer } from '@ioc:Adonis/Addons/Mail'
2+
import type { MessageContract } from '@ioc:Adonis/Addons/Mail'
3+
import Env from '@ioc:Adonis/Core/Env'
34
import View from '@ioc:Adonis/Core/View'
45
import mjml from 'mjml'
6+
import Mailjet from 'node-mailjet'
7+
import type { LibraryResponse } from 'node-mailjet'
8+
import Logger from '@ioc:Adonis/Core/Logger'
59
import appInfos from 'Config/app-infos'
10+
import type { RequestData } from 'node-mailjet/declarations/request/Request'
611

7-
export default class MailService {
8-
protected static replyTo = {
12+
type MailReplayTo = {
13+
email: string
14+
name: string
15+
}
16+
17+
interface BaseMailPayload {
18+
payload?: Record<string, unknown>
19+
subject?: string
20+
viewPath?: string
21+
}
22+
23+
interface MailPayload extends BaseMailPayload {
24+
email: string
25+
}
26+
27+
interface MailManyPayload extends BaseMailPayload {
28+
emails: string[]
29+
}
30+
31+
export default class MailService extends BaseMailer {
32+
protected static replyTo: MailReplayTo = {
933
email: appInfos.emails.support,
1034
name: `${appInfos.name} Team`,
1135
}
@@ -15,70 +39,80 @@ export default class MailService {
1539
payload,
1640
subject,
1741
viewPath = 'emails/test-email',
18-
}: {
19-
email: string
20-
payload?: Record<string, unknown>
21-
subject?: string
22-
viewPath?: string
23-
}): Promise<void> {
24-
const mail: MailMessage = {
25-
viewPath,
26-
from: appInfos.emails.noReply,
27-
to: email,
28-
subject: subject,
29-
replyTo: this.replyTo,
30-
payload,
31-
}
42+
}: MailPayload): Promise<void> {
43+
const html: string = this.getHtml(viewPath, payload)
3244

33-
const html = this.getHtml(mail.viewPath, mail.payload)
34-
await Mail.sendLater((message) => {
35-
message
36-
.from(mail.from)
37-
.to(mail.to)
38-
.subject(mail.subject || '')
39-
.replyTo(mail.replyTo.email || mail.to, mail.replyTo.name)
40-
.html(html)
41-
})
45+
const isProduction: boolean = Env.get('NODE_ENV') === 'production'
46+
47+
if (isProduction) {
48+
// Use Mailjet in production
49+
this.sendMailWithMailjet({ email, payload, subject, viewPath })
50+
} else {
51+
// Use the classic operation of Adonis Mail in development
52+
await Mail.sendLater((message: MessageContract): void => {
53+
message
54+
.from(appInfos.emails.noReply)
55+
.to(email)
56+
.subject(subject || '')
57+
.replyTo(this.replyTo.email, this.replyTo.name)
58+
.html(html)
59+
})
60+
}
4261
}
4362

4463
public static async sendMany({
4564
emails,
4665
payload,
4766
subject,
4867
viewPath = 'emails/test-email',
49-
}: {
50-
emails: string[]
51-
payload?: Record<string, unknown>
52-
subject?: string
53-
viewPath?: string
54-
}) {
55-
const mails: MailsMessages = {
56-
viewPath,
57-
from: appInfos.emails.noReply,
58-
to: emails,
59-
subject: subject,
60-
replyTo: {
61-
email: appInfos.emails.support,
62-
name: `${appInfos.name} Team`,
63-
},
64-
payload,
68+
}: MailManyPayload): Promise<void> {
69+
const uniqueEmails: string[] = [...new Set(emails)]
70+
71+
for (const email of uniqueEmails) {
72+
await this.send({ email, payload, subject, viewPath })
6573
}
74+
}
6675

67-
const receivers: string[] = []
68-
const html = this.getHtml(mails.viewPath, mails.payload)
69-
mails.to.map(async (mail) => {
70-
if (receivers.includes(mail)) return
71-
receivers.push(mail)
76+
public static sendMailWithMailjet({
77+
email,
78+
payload,
79+
subject,
80+
viewPath = 'emails/test-email',
81+
}: MailPayload): void {
82+
const html: string = this.getHtml(viewPath, payload)
7283

73-
await Mail.sendLater((message) => {
74-
message
75-
.from(mails.from)
76-
.to(mail)
77-
.subject(mails.subject || '')
78-
.replyTo(mails.replyTo.email || mail, mails.replyTo.name)
79-
.html(html)
80-
})
84+
const mailjet: Mailjet = new Mailjet({
85+
apiKey: Env.get('MAILJET_API_KEY'),
86+
apiSecret: Env.get('MAILJET_API_SECRET_KEY'),
8187
})
88+
89+
const request: Promise<LibraryResponse<RequestData>> = mailjet
90+
.post('send', { version: 'v3.1' })
91+
.request({
92+
Messages: [
93+
{
94+
From: {
95+
Email: appInfos.emails.noReply,
96+
Name: this.replyTo.name,
97+
},
98+
To: [{ Email: email }],
99+
Subject: subject,
100+
HTMLPart: html,
101+
ReplyTo: {
102+
Email: this.replyTo.email,
103+
Name: this.replyTo.name,
104+
},
105+
},
106+
],
107+
})
108+
109+
request
110+
.then((result: LibraryResponse<RequestData>): void => {
111+
Logger.info('Mail sent successfully with Mailjet:', JSON.stringify(result.body))
112+
})
113+
.catch((error): void => {
114+
Logger.error('Error sending mail with Mailjet:', JSON.stringify(error.statusCode))
115+
})
82116
}
83117

84118
protected static getHtml(viewPath: string, payload?: Record<string, unknown>): string {

api/app/Services/SocialAuthService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default class SocialAuthService extends BaseService {
2424

2525
if (!user) {
2626
user = await this.createUser(socialUser, provider)
27+
await super.sendPrivateSocketEvent({ user }, 'new:user')
2728
}
2829

2930
if (!user?.oauthProviderName) {

api/config/mail.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,9 @@
1-
/**
2-
* Config source: https://git.io/JvgAf
3-
*
4-
* Feel free to let us know via PR, if you find something broken in this contract
5-
* file.
6-
*/
7-
81
import Env from '@ioc:Adonis/Core/Env'
92
import { mailConfig } from '@adonisjs/mail/build/config'
103

114
export default mailConfig({
12-
/*
13-
|--------------------------------------------------------------------------
14-
| Default mailer
15-
|--------------------------------------------------------------------------
16-
|
17-
| The following mailer will be used to send emails, when you don't specify
18-
| a mailer
19-
|
20-
*/
215
mailer: 'smtp',
22-
23-
/*
24-
|--------------------------------------------------------------------------
25-
| Mailers
26-
|--------------------------------------------------------------------------
27-
|
28-
| You can define or more mailers to send emails from your application. A
29-
| single `driver` can be used to define multiple mailers with different
30-
| config.
31-
|
32-
| For example: Postmark driver can be used to have different mailers for
33-
| sending transactional and promotional emails
34-
|
35-
*/
366
mailers: {
37-
/*
38-
|--------------------------------------------------------------------------
39-
| Smtp
40-
|--------------------------------------------------------------------------
41-
|
42-
| Uses SMTP protocol for sending email
43-
|
44-
*/
457
smtp: {
468
driver: 'smtp',
479
pool: true,

api/package-lock.json

Lines changed: 62 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)