add brevo email

This commit is contained in:
Emily
2024-08-30 16:26:53 +02:00
parent 518b4ce6c1
commit 6d26c3c8af
17 changed files with 2636 additions and 216 deletions

View File

@@ -7,9 +7,7 @@ module.exports = {
script: './dist/producer/src/index.js', script: './dist/producer/src/index.js',
env: { env: {
EMAIL_SERVICE: "", EMAIL_SERVICE: "",
EMAIL_HOST: "", BREVO_API_KEY: "",
EMAIL_USER: "",
EMAIL_PASS: "",
PORT: "", PORT: "",
MONGO_CONNECTION_STRING: "", MONGO_CONNECTION_STRING: "",
REDIS_URL: "", REDIS_URL: "",

View File

@@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@getbrevo/brevo": "^2.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.19.2", "express": "^4.19.2",
"mongoose": "^8.3.2", "mongoose": "^8.3.2",

779
broker/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,7 @@ import { requireEnv } from "../../shared/utilts/requireEnv";
import { TProjectLimit } from "@schema/ProjectsLimits"; import { TProjectLimit } from "@schema/ProjectsLimits";
if (process.env.EMAIL_SERVICE) { if (process.env.EMAIL_SERVICE) {
EmailService.createTransport( EmailService.init(requireEnv('BREVO_API_KEY'));
requireEnv('EMAIL_SERVICE'),
requireEnv('EMAIL_HOST'),
requireEnv('EMAIL_USER'),
requireEnv('EMAIL_PASS'),
);
} }
export async function checkLimitsForEmail(projectCounts: TProjectLimit) { export async function checkLimitsForEmail(projectCounts: TProjectLimit) {

View File

@@ -33,7 +33,6 @@ export async function processStreamEvent(data: Record<string, string>) {
const eventType = data._type; const eventType = data._type;
if (!eventType) return; if (!eventType) return;
const { pid, sessionHash } = data; const { pid, sessionHash } = data;
const project = await ProjectModel.exists({ _id: pid }); const project = await ProjectModel.exists({ _id: pid });

View File

@@ -10,9 +10,7 @@ AI_PROJECT=
AI_KEY= AI_KEY=
EMAIL_SERVICE= EMAIL_SERVICE=
EMAIL_HOST= BREVO_API_KEY=
EMAIL_USER=
EMAIL_PASS=
AUTH_JWT_SECRET= AUTH_JWT_SECRET=

View File

@@ -39,9 +39,7 @@ export default defineNuxtConfig({
AI_PROJECT: process.env.AI_PROJECT, AI_PROJECT: process.env.AI_PROJECT,
AI_KEY: process.env.AI_KEY, AI_KEY: process.env.AI_KEY,
EMAIL_SERVICE: process.env.EMAIL_SERVICE, EMAIL_SERVICE: process.env.EMAIL_SERVICE,
EMAIL_HOST: process.env.EMAIL_HOST, BREVO_API_KEY: process.env.BREVO_API_KEY,
EMAIL_USER: process.env.EMAIL_USER,
EMAIL_PASS: process.env.EMAIL_PASS,
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET, AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID, GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,

View File

@@ -13,6 +13,7 @@
"docker-inspect": "docker run -it litlyx-dashboard sh" "docker-inspect": "docker run -it litlyx-dashboard sh"
}, },
"dependencies": { "dependencies": {
"@getbrevo/brevo": "^2.2.0",
"@nuxtjs/tailwindcss": "^6.12.0", "@nuxtjs/tailwindcss": "^6.12.0",
"chart.js": "^3.9.1", "chart.js": "^3.9.1",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",

710
dashboard/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,7 @@ export default defineEventHandler(async event => {
const savedUser = await newUser.save(); const savedUser = await newUser.save();
setImmediate(() => { setImmediate(() => {
console.log('SENDING WELCOME EMAIL TO', payload.email);
if (payload.email) EmailService.sendWelcomeEmail(payload.email); if (payload.email) EmailService.sendWelcomeEmail(payload.email);
}); });

View File

@@ -11,7 +11,7 @@ export default async () => {
console.log('[SERVER] Initializing'); console.log('[SERVER] Initializing');
if (config.EMAIL_SERVICE) { if (config.EMAIL_SERVICE) {
EmailService.createTransport(config.EMAIL_SERVICE, config.EMAIL_HOST, config.EMAIL_USER, config.EMAIL_PASS); EmailService.init(config.BREVO_API_KEY);
console.log('[EMAIL] Initialized') console.log('[EMAIL] Initialized')
} }

View File

@@ -40,10 +40,8 @@ services:
# Optional - Used to send welcome and quota emails # Optional - Used to send welcome and quota emails
# EMAIL_SERVICE: "" # NUXT_EMAIL_SERVICE: "Brevo"
# EMAIL_HOST: "" # NUXT_BREVO_API_KEY: ""
# EMAIL_USER: ""
# EMAIL_PASS: ""
PORT: "3999" PORT: "3999"
MONGO_CONNECTION_STRING: "mongodb://litlyx:litlyx@mongo:27017/SimpleMetrics?readPreference=primaryPreferred&authSource=admin" MONGO_CONNECTION_STRING: "mongodb://litlyx:litlyx@mongo:27017/SimpleMetrics?readPreference=primaryPreferred&authSource=admin"
@@ -77,10 +75,8 @@ services:
# Optional - Used to send welcome and quota emails # Optional - Used to send welcome and quota emails
# NUXT_EMAIL_SERVICE: "" # NUXT_EMAIL_SERVICE: "Brevo"
# NUXT_EMAIL_HOST: "" # NUXT_BREVO_API_KEY: ""
# NUXT_EMAIL_USER: ""
# NUXT_EMAIL_PASS: ""
NUXT_AUTH_JWT_SECRET: "litlyx_jwt_secret" NUXT_AUTH_JWT_SECRET: "litlyx_jwt_secret"

View File

@@ -1,12 +1,11 @@
{ {
"dependencies": { "dependencies": {
"@getbrevo/brevo": "^2.2.0",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"mongoose": "^8.4.0", "mongoose": "^8.4.0",
"nodemailer": "^6.9.13",
"redis": "^4.6.14" "redis": "^4.6.14"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.12.13", "@types/node": "^20.12.13"
"@types/nodemailer": "^6.4.15"
} }
} }

1069
shared/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,137 +1,24 @@
import nodemailer from 'nodemailer'; import { TransactionalEmailsApi, SendSmtpEmail } from '@getbrevo/brevo';
import type SMTPTransport from 'nodemailer/lib/smtp-transport';
import { WELCOME_EMAIL } from './email_templates/WelcomeEmail'; import { WELCOME_EMAIL } from './email_templates/WelcomeEmail';
import { LIMIT_50_EMAIL } from './email_templates/Limit50Email';
const TemplateEmail50 = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LitLyx Limit Reached Email</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
background-color: #0a0a0a;
color: #ffffff;
margin: 0;
padding: 0;
}
.container {
width: 100%;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
padding: 20px 0;
}
.header h1 {
font-size: 36px;
font-weight: 700;
margin: 0;
}
.step {
margin: 20px 0;
text-align: center;
}
.step h2 {
font-size: 24px;
font-weight: 600;
margin: 0 0 10px 0;
}
.step p {
font-size: 16px;
font-weight: 300;
margin: 0 0 20px 0;
}
.button {
display: inline-block;
padding: 10px 20px;
font-size: 16px;
font-weight: 600;
color: #ffffff;
background-color: #1a73e8;
text-decoration: none;
border-radius: 5px;
}
.footer {
text-align: center;
padding: 20px 0;
font-size: 14px;
font-weight: 300;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>⚠ Limit for project ⚠</h1>
</div>
<div class="header">
<p>Hey there! We found that one of your projects is at 50% of the <strong>limit of the plan.</strong> In order to continue to log visits & events, you should upgrade the plan of your project!</p>
</div>
<div class="step" style="margin-top: 4rem;">
<h2>How can I upgrade the plan?</h2>
<p>We offer different plans, each of them follows the stage of your project, so based on the reach, you should upgrade to the most appropriate one for your web platform.<strong> It takes 1 minute to upgrade the plan!</strong> You can find everything in the "Billing" section in the left menu of your dashboard.</p>
<a href="https://dashboard.litlyx.com" class="button">Visit your dashboard</a>
</div>
<div class="step" style="margin-top: 4rem;">
<h2>We are in early phases!</h2>
<p>Want to become an early adopter? Book a demo with me! I'm Antonio & I'll guide you through all the features and benefits of LitLyx.<strong> A big discount is waiting for you❗</strong></p>
<a href="https://cal.com/litlyx/30min" class="button">Book a Demo with Me!</a>
</div>
<div class="footer">
<p>Thank you for choosing LitLyx each day to keep track of your business KPIs!</p>
<p>Made with ❤️ in Italy</p>
</div>
</div>
</body>
</html>
`
class EmailService { class EmailService {
private transport: nodemailer.Transporter<SMTPTransport.SentMessageInfo>; private apiInstance = new TransactionalEmailsApi();
createTransport(service: string, host: string, user: string, pass: string) { init(apiKey: string) {
this.transport = nodemailer.createTransport({ this.apiInstance.setApiKey(0, apiKey);
host,
secure: true,
auth: { user, pass },
tls: {
minVersion: 'TLSv1',
ciphers: 'HIGH:MEDIUM:!aNULL:!eNULL:@STRENGTH:!DH:!kEDH'
}
});
} }
async sendLimitEmail50(target: string) { async sendLimitEmail50(target: string) {
try { try {
if (!this.transport) return console.error('Transport not created'); const sendSmtpEmail = new SendSmtpEmail();
await this.transport.sendMail({ sendSmtpEmail.subject = "Litlyx project limit 50%";
from: 'helplitlyx@gmail.com', sendSmtpEmail.sender = { "name": "Litlyx", "email": "no-reply@litlyx.com" };
to: target, sendSmtpEmail.to = [{ "email": target }];
subject: 'Project limit 50%', sendSmtpEmail.htmlContent = LIMIT_50_EMAIL;
html: TemplateEmail50 await this.apiInstance.sendTransacEmail(sendSmtpEmail);
});
return true; return true;
} catch (ex) { } catch (ex) {
console.error('ERROR SENDING EMAIL', ex); console.error('ERROR SENDING EMAIL', ex);
@@ -141,13 +28,13 @@ class EmailService {
async sendWelcomeEmail(target: string) { async sendWelcomeEmail(target: string) {
try { try {
if (!this.transport) return console.error('Transport not created'); console.log('SENDING WELCOME EMAIL_EMAIL SERVICE')
await this.transport.sendMail({ const sendSmtpEmail = new SendSmtpEmail();
from: 'helplitlyx@gmail.com', sendSmtpEmail.subject = "Welcome to Litlyx";
to: target, sendSmtpEmail.sender = { "name": "Litlyx", "email": "no-reply@litlyx.com" };
subject: 'Welcome to Litlyx', sendSmtpEmail.to = [{ "email": target }];
html: WELCOME_EMAIL sendSmtpEmail.htmlContent = WELCOME_EMAIL;
}); await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true; return true;
} catch (ex) { } catch (ex) {
console.error('ERROR SENDING EMAIL', ex); console.error('ERROR SENDING EMAIL', ex);

View File

@@ -17,9 +17,11 @@ export class RedisStreamService {
url: requireEnv("REDIS_URL"), url: requireEnv("REDIS_URL"),
username: requireEnv("REDIS_USERNAME"), username: requireEnv("REDIS_USERNAME"),
password: requireEnv("REDIS_PASSWORD"), password: requireEnv("REDIS_PASSWORD"),
database: process.env.DEV_MODE === 'true' ? 1 : 0
}); });
static async connect() { static async connect() {
console.log('RedisStreamService DEV_MODE=', process.env.DEV_MODE === 'true');
await this.client.connect(); await this.client.connect();
} }

View File

@@ -0,0 +1,101 @@
export const LIMIT_50_EMAIL = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LitLyx Limit Reached Email</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
background-color: #0a0a0a;
color: #ffffff;
margin: 0;
padding: 0;
}
.container {
width: 100%;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
padding: 20px 0;
}
.header h1 {
font-size: 36px;
font-weight: 700;
margin: 0;
}
.step {
margin: 20px 0;
text-align: center;
}
.step h2 {
font-size: 24px;
font-weight: 600;
margin: 0 0 10px 0;
}
.step p {
font-size: 16px;
font-weight: 300;
margin: 0 0 20px 0;
}
.button {
display: inline-block;
padding: 10px 20px;
font-size: 16px;
font-weight: 600;
color: #ffffff;
background-color: #1a73e8;
text-decoration: none;
border-radius: 5px;
}
.footer {
text-align: center;
padding: 20px 0;
font-size: 14px;
font-weight: 300;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>⚠ Limit for project ⚠</h1>
</div>
<div class="header">
<p>Hey there! We found that one of your projects is at 50% of the <strong>limit of the plan.</strong> In order to continue to log visits & events, you should upgrade the plan of your project!</p>
</div>
<div class="step" style="margin-top: 4rem;">
<h2>How can I upgrade the plan?</h2>
<p>We offer different plans, each of them follows the stage of your project, so based on the reach, you should upgrade to the most appropriate one for your web platform.<strong> It takes 1 minute to upgrade the plan!</strong> You can find everything in the "Billing" section in the left menu of your dashboard.</p>
<a href="https://dashboard.litlyx.com" class="button">Visit your dashboard</a>
</div>
<div class="step" style="margin-top: 4rem;">
<h2>We are in early phases!</h2>
<p>Want to become an early adopter? Book a demo with me! I'm Antonio & I'll guide you through all the features and benefits of LitLyx.<strong> A big discount is waiting for you❗</strong></p>
<a href="https://cal.com/litlyx/30min" class="button">Book a Demo with Me!</a>
</div>
<div class="footer">
<p>Thank you for choosing LitLyx each day to keep track of your business KPIs!</p>
<p>Made with ❤️ in Italy</p>
</div>
</div>
</body>
</html>
`