mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
add brevo email
This commit is contained in:
@@ -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: "",
|
||||||
|
|||||||
@@ -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
779
broker/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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=
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
710
dashboard/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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
1069
shared/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
shared/services/email_templates/Limit50Email.ts
Normal file
101
shared/services/email_templates/Limit50Email.ts
Normal 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>
|
||||||
|
`
|
||||||
Reference in New Issue
Block a user