use new mail service in dashboard

This commit is contained in:
Emily
2025-01-29 16:03:01 +01:00
parent 39b8dd84f1
commit bfeee8673c
26 changed files with 147 additions and 1266 deletions

View File

@@ -44,8 +44,7 @@ export default defineNuxtConfig({
AI_ORG: process.env.AI_ORG, AI_ORG: process.env.AI_ORG,
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_SECRET: process.env.EMAIL_SECRET,
BREVO_API_KEY: process.env.BREVO_API_KEY,
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

@@ -2,7 +2,8 @@
import { createUserJwt, readRegisterJwt } from '~/server/AuthManager'; import { createUserJwt, readRegisterJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema'; import { UserModel } from '@schema/UserSchema';
import { PasswordModel } from '@schema/PasswordSchema'; import { PasswordModel } from '@schema/PasswordSchema';
// import EmailService from '@services/EmailService'; import { EmailService } from '@services/EmailService';
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@@ -14,7 +15,10 @@ export default defineEventHandler(async event => {
try { try {
await PasswordModel.create({ email: data.email, password: data.password }) await PasswordModel.create({ email: data.email, password: data.password })
await UserModel.create({ email: data.email, given_name: '', name: 'EmailLogin', locale: '', picture: '', created_at: Date.now() }); await UserModel.create({ email: data.email, given_name: '', name: 'EmailLogin', locale: '', picture: '', created_at: Date.now() });
// setImmediate(() => { EmailService.sendWelcomeEmail(data.email); }); setImmediate(() => {
const emailData = EmailService.getEmailServerInfo('welcome', { target: data.email });
EmailServiceHelper.sendEmail(emailData);
});
const jwt = createUserJwt({ email: data.email, name: 'EmailLogin' }); const jwt = createUserJwt({ email: data.email, name: 'EmailLogin' });
return sendRedirect(event, `https://dashboard.litlyx.com/jwt_login?jwt_login=${jwt}`); return sendRedirect(event, `https://dashboard.litlyx.com/jwt_login?jwt_login=${jwt}`);
} catch (ex) { } catch (ex) {

View File

@@ -2,7 +2,8 @@
import { OAuth2Client } from 'google-auth-library'; import { OAuth2Client } from 'google-auth-library';
import { createUserJwt } from '~/server/AuthManager'; import { createUserJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema'; import { UserModel } from '@schema/UserSchema';
// import EmailService from '@services/EmailService'; import { EmailService } from '@services/EmailService';
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
const { GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_ID } = useRuntimeConfig() const { GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_ID } = useRuntimeConfig()
@@ -58,10 +59,12 @@ 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); console.log('SENDING WELCOME EMAIL TO', payload.email);
// if (payload.email) EmailService.sendWelcomeEmail(payload.email); if (!payload.email) return;
// }); const emailData = EmailService.getEmailServerInfo('welcome', { target: payload.email });
EmailServiceHelper.sendEmail(emailData);
});
return { error: false, access_token: createUserJwt({ email: savedUser.email, name: savedUser.name }) } return { error: false, access_token: createUserJwt({ email: savedUser.email, name: savedUser.name }) }

View File

@@ -1,9 +1,10 @@
import { createRegisterJwt, createUserJwt } from '~/server/AuthManager'; import { createRegisterJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema'; import { UserModel } from '@schema/UserSchema';
import { RegisterModel } from '@schema/RegisterSchema'; import { RegisterModel } from '@schema/RegisterSchema';
// import EmailService from '@services/EmailService'; import { EmailService } from '@services/EmailService';
import crypto from 'crypto'; import crypto from 'crypto';
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
function canRegister(email: string, password: string) { function canRegister(email: string, password: string) {
if (email.length == 0) return false; if (email.length == 0) return false;
@@ -33,9 +34,10 @@ export default defineEventHandler(async event => {
await RegisterModel.create({ email, password: hashedPassword }); await RegisterModel.create({ email, password: hashedPassword });
// setImmediate(() => { setImmediate(() => {
// EmailService.sendConfirmEmail(email, `https://dashboard.litlyx.com/api/auth/confirm_email?register_code=${jwt}`); const emailData = EmailService.getEmailServerInfo('confirm', { target: email, link: `https://dashboard.litlyx.com/api/auth/confirm_email?register_code=${jwt}` });
// }); EmailServiceHelper.sendEmail(emailData);
});
return { return {
error: false, error: false,

View File

@@ -1,72 +0,0 @@
import { createUserJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema';
import EmailService from '@services/EmailService';
const config = useRuntimeConfig();
export default defineEventHandler(async event => {
const { code } = getQuery(event);
console.log('CODE', code);
const redirect_uri = 'http://127.0.0.1:3000'
const res = await fetch(`https://github.com/login/oauth/access_token?client_id=${config.GITHUB_AUTH_CLIENT_ID}&client_secret=${config.GITHUB_AUTH_CLIENT_SECRET}&code=${code}&redirect_url=${redirect_uri}`, {
headers: {
"Accept": "application/json",
"Accept-Encoding": "application/json",
},
});
const data = await res.json();
const access_token = data.access_token;
console.log(data);
return sendRedirect(event,`http://127.0.0.1:3000/login?github_access_token=${access_token}`)
// const origin = event.headers.get('origin');
// const tokenResponse = await client.getToken({
// code: body.code,
// redirect_uri: origin || ''
// });
// const tokens = tokenResponse.tokens;
// const ticket = await client.verifyIdToken({
// idToken: tokens.id_token || '',
// audience: GOOGLE_AUTH_CLIENT_ID,
// });
// const payload = ticket.getPayload();
// if (!payload) return { error: true, access_token: '' };
// const user = await UserModel.findOne({ email: payload.email });
// if (user) return { error: false, access_token: createUserJwt({ email: user.email, name: user.name }) }
// const newUser = new UserModel({
// email: payload.email,
// given_name: payload.given_name,
// name: payload.name,
// locale: payload.locale,
// picture: payload.picture,
// created_at: Date.now()
// });
// const savedUser = await newUser.save();
// setImmediate(() => {
// console.log('SENDING WELCOME EMAIL TO', payload.email);
// if (payload.email) EmailService.sendWelcomeEmail(payload.email);
// });
// return { error: false, access_token: createUserJwt({ email: savedUser.email, name: savedUser.name }) }
});

View File

@@ -4,9 +4,9 @@ import type Event from 'stripe';
import { ProjectModel } from '@schema/project/ProjectSchema'; import { ProjectModel } from '@schema/project/ProjectSchema';
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM'; import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM';
import { ProjectLimitModel } from '@schema/project/ProjectsLimits'; import { ProjectLimitModel } from '@schema/project/ProjectsLimits';
// import EmailService from '@services/EmailService' import { EmailService } from '@services/EmailService'
import { UserModel } from '@schema/UserSchema'; import { UserModel } from '@schema/UserSchema';
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA, subscription_id: string, current_period_start: number, current_period_end: number) { async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA, subscription_id: string, current_period_start: number, current_period_end: number) {
@@ -93,9 +93,10 @@ async function onPaymentOnetimeSuccess(event: Event.PaymentIntentSucceededEvent)
const user = await UserModel.findOne({ _id: project.owner }); const user = await UserModel.findOne({ _id: project.owner });
if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id } if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id }
// setTimeout(() => { setTimeout(() => {
// EmailService.sendPurchaseEmail(user.email, project.name); const emailData = EmailService.getEmailServerInfo('purchase', { target: user.email, projectName: project.name });
// }, 1); EmailServiceHelper.sendEmail(emailData);
}, 1);
return { ok: true }; return { ok: true };
} }
@@ -140,10 +141,11 @@ async function onPaymentSuccess(event: Event.InvoicePaidEvent) {
const user = await UserModel.findOne({ _id: project.owner }); const user = await UserModel.findOne({ _id: project.owner });
if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id } if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id }
// setTimeout(() => { setTimeout(() => {
// if (PLAN.ID == 0) return; if (PLAN.ID == 0) return;
// if (isNewSubscription) EmailService.sendPurchaseEmail(user.email, project.name); const emailData = EmailService.getEmailServerInfo('purchase', { target: user.email, projectName: project.name });
// }, 1); EmailServiceHelper.sendEmail(emailData);
}, 1);
return { ok: true }; return { ok: true };

View File

@@ -1,6 +1,6 @@
import { VisitModel } from "@schema/metrics/VisitSchema"; import { VisitModel } from "@schema/metrics/VisitSchema";
import { Redis } from "~/server/services/CacheService"; import { Redis } from "~/server/services/CacheService";
import { executeTimelineAggregation } from "~/server/services/TimelineService"; import { executeAdvancedTimelineAggregation } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@@ -13,7 +13,7 @@ export default defineEventHandler(async event => {
const cacheExp = 60; const cacheExp = 60;
return await Redis.useCacheV2(cacheKey, cacheExp, async () => { return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
const timelineData = await executeTimelineAggregation({ const timelineData = await executeAdvancedTimelineAggregation({
projectId: project_id, projectId: project_id,
model: VisitModel, model: VisitModel,
from, to, slice, timeOffset, domain from, to, slice, timeOffset, domain

View File

@@ -1,7 +1,8 @@
import crypto from 'crypto'; import crypto from 'crypto';
import { PasswordModel } from '@schema/PasswordSchema'; import { PasswordModel } from '@schema/PasswordSchema';
// import EmailService from '@services/EmailService' import { EmailService } from '@services/EmailService'
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@@ -19,7 +20,8 @@ export default defineEventHandler(async event => {
target.password = hashedPassword; target.password = hashedPassword;
await target.save(); await target.save();
// await EmailService.sendResetPasswordEmail(email, newPass); const emailData = EmailService.getEmailServerInfo('reset_password', { target: email, newPassword: newPass });
EmailServiceHelper.sendEmail(emailData);
return { error: false, message: 'Password changed' } return { error: false, message: 'Password changed' }

View File

@@ -1,6 +1,5 @@
import mongoose from "mongoose"; import mongoose from "mongoose";
import { Redis } from "~/server/services/CacheService"; import { Redis } from "~/server/services/CacheService";
// import EmailService from '@services/EmailService';
import StripeService from '~/server/services/StripeService'; import StripeService from '~/server/services/StripeService';
import { logger } from "./Logger"; import { logger } from "./Logger";
@@ -14,12 +13,6 @@ export default async () => {
logger.info('[SERVER] Initializing'); logger.info('[SERVER] Initializing');
// if (config.EMAIL_SERVICE) {
// EmailService.init(config.BREVO_API_KEY);
// logger.info('[EMAIL] Initialized');
// }
if (config.STRIPE_SECRET) { if (config.STRIPE_SECRET) {
StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false); StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false);
logger.info('[STRIPE] Initialized'); logger.info('[STRIPE] Initialized');
@@ -41,13 +34,8 @@ export default async () => {
logger.info('[SERVER] Completed'); logger.info('[SERVER] Completed');
logger.warn('[ANOMALY LOOP] Disabled');
logger.warn(`[SELFHOSTED_SERVER] ${config.SELFHOSTED}`); logger.warn(`[SELFHOSTED_SERVER] ${config.SELFHOSTED}`);
logger.warn(`[SELFHOSTED_CLIENT] ${config.public.SELFHOSTED}`); logger.warn(`[SELFHOSTED_CLIENT] ${config.public.SELFHOSTED}`);
logger.warn(`[AUTH] ${config.public.AUTH_MODE}`); logger.warn(`[AUTH] ${config.public.AUTH_MODE}`);
// anomalyLoop();
}; };

View File

@@ -0,0 +1,18 @@
import { EmailServerInfo } from '@services/EmailService'
const { EMAIL_SECRET } = useRuntimeConfig();
export class EmailServiceHelper {
static async sendEmail(data: EmailServerInfo) {
try {
await $fetch(data.url, {
method: 'POST',
body: JSON.stringify(data.body),
headers: { ...data.headers, 'x-litlyx-token': EMAIL_SECRET }
})
} catch (ex) {
console.error(ex);
}
}
}

View File

@@ -0,0 +1,35 @@
const templateMap = {
confirm: '/confirm',
welcome: '/welcome',
purchase: '/purchase',
reset_password: '/reset_password',
anomaly_domain: '/anomaly/domain',
anomaly_visits_events: '/anomaly_visits_events',
limit_50: '/limit/50',
limit_90: '/limit/90',
limit_max: '/limit/max',
} as const;
export type EmailTemplate = keyof typeof templateMap;
export type EmailServerInfo = { url: string, body: Record<string, any>, headers: Record<string, string> };
type EmailData =
| { template: 'confirm', data: { target: string, link: string } }
| { template: 'welcome', data: { target: string } }
| { template: 'purchase', data: { target: string, projectName: string } }
| { template: 'reset_password', data: { target: string, newPassword: string } }
| { template: 'anomaly_domain', data: { target: string, projectName: string, domains: string[] } }
| { template: 'anomaly_visits_events', data: { target: string, projectName: string, data: any[] } }
| { template: 'limit_50', data: { target: string, projectName: string } }
| { template: 'limit_90', data: { target: string, projectName: string } }
| { template: 'limit_max', data: { target: string, projectName: string } };
export class EmailService {
static getEmailServerInfo<T extends EmailTemplate>(template: T, data: Extract<EmailData, { template: T }>['data']): EmailServerInfo {
return {
url: `https://mail-service.litlyx.com/send${templateMap[template]}`,
body: data,
headers: { 'Content-Type': 'application/json' }
};
}
}

View File

@@ -19,16 +19,6 @@ export const PURCHASE_EMAIL = `<!DOCTYPE html>
<p>You can find your current plan details and download your invoices under <strong>Settings > Billing Tab</strong>.</p> <p>You can find your current plan details and download your invoices under <strong>Settings > Billing Tab</strong>.</p>
<h3>What does this mean for you?</h3>
<p>With your upgraded plan, you can now enjoy more data collection, advanced features, and additional benefits to enhance your data analysis capabilities:</p>
<ol>
<li>Access to more storage and increased data limits.</li>
<li>Advanced analytics tools like IP-Company Matching, download CSV for your raw data, AI insights, and more.</li>
<li>Priority support to help you make the most of your Litlyx experience on Slack or Discord!</li>
</ol>
<p>If you have any questions about your new plan or need assistance, feel free to reach out to our support team at <a href="mailto:help@litlyx.com" style="color: #28a745; text-decoration: none;"><strong>help@litlyx.com</strong></a>. Were here to help you make the most out of your upgraded plan!</p> <p>If you have any questions about your new plan or need assistance, feel free to reach out to our support team at <a href="mailto:help@litlyx.com" style="color: #28a745; text-decoration: none;"><strong>help@litlyx.com</strong></a>. Were here to help you make the most out of your upgraded plan!</p>
<p><strong>Thank you for using Litlyx every day as your analytics tool and for being a part of our journey.</strong></p> <p><strong>Thank you for using Litlyx every day as your analytics tool and for being a part of our journey.</strong></p>

View File

@@ -6,6 +6,7 @@
"scripts": { "scripts": {
"dashboard:clear-logs": "ts-node scripts/dashboard/clear-logs.ts", "dashboard:clear-logs": "ts-node scripts/dashboard/clear-logs.ts",
"dashboard:shared": "ts-node scripts/dashboard/shared.ts", "dashboard:shared": "ts-node scripts/dashboard/shared.ts",
"dashboard:deploy": "ts-node scripts/dashboard/deploy.ts",
"producer:shared": "node scripts/producer/shared.js", "producer:shared": "node scripts/producer/shared.js",
"email:deploy": "ts-node scripts/email/deploy.ts" "email:deploy": "ts-node scripts/email/deploy.ts"
}, },

View File

@@ -28,8 +28,8 @@ async function main() {
if (fs.existsSync(TMP_PATH)) fs.rmSync(TMP_PATH, { force: true, recursive: true }); if (fs.existsSync(TMP_PATH)) fs.rmSync(TMP_PATH, { force: true, recursive: true });
fs.ensureDirSync(TMP_PATH); fs.ensureDirSync(TMP_PATH);
// console.log('Building'); console.log('Building');
// child.execSync(`cd ${LOCAL_PATH} && pnpm i && pnpm run build`) child.execSync(`cd ${LOCAL_PATH} && pnpm i && pnpm run build`)
console.log('Creting zip file'); console.log('Creting zip file');
const archive = createZip(TMP_PATH + '/' + ZIP_NAME); const archive = createZip(TMP_PATH + '/' + ZIP_NAME);
@@ -71,12 +71,12 @@ async function main() {
console.log('Extracting remote'); console.log('Extracting remote');
await DeployHelper.execute(`cd ${REMOTE_PATH} && unzip ${ZIP_NAME} && rm -r ${ZIP_NAME}`); await DeployHelper.execute(`cd ${REMOTE_PATH} && unzip ${ZIP_NAME} && rm -r ${ZIP_NAME}`);
// console.log('Installing remote'); console.log('Installing remote');
// await DeployHelper.execute(`cd ${REMOTE_PATH} && /root/.nvm/versions/node/v21.2.0/bin/pnpm i`); await DeployHelper.execute(`cd ${REMOTE_PATH}/.output/server && /root/.nvm/versions/node/v21.2.0/bin/pnpm i`);
// await DeployHelper.execute(`cd ${REMOTE_PATH} && /root/.nvm/versions/node/v21.2.0/bin/pm2 start ecosystem.config.js`); console.log('Executing remote');
await DeployHelper.execute(`cd ${REMOTE_PATH} && /root/.nvm/versions/node/v21.2.0/bin/pm2 start ecosystem.config.js`);
ssh.dispose(); ssh.dispose();
} }

View File

@@ -1,14 +1,13 @@
import { SharedHelper } from "../helpers/shared-helper"; import { SharedHelper } from "../helpers/shared-helper";
import path from "path"; import path from "node:path";
const helper = new SharedHelper(path.join(__dirname, '../../dashboard/shared')) const helper = new SharedHelper(path.join(__dirname, '../../dashboard/shared'))
helper.clear(); helper.clear();
// TODO: Email service as external repo
helper.create('services'); helper.create('services');
helper.copy('services/DateService.ts'); helper.copy('services/DateService.ts');
helper.copy('services/EmailService.ts');
helper.create('data'); helper.create('data');

12
scripts/tsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "NodeNext",
"target": "ESNext"
},
"include": [
"./**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,193 +1,35 @@
import { TransactionalEmailsApi, SendSmtpEmail } from '@getbrevo/brevo'; const templateMap = {
import { WELCOME_EMAIL } from './email_templates/WelcomeEmail'; confirm: '/confirm',
import { LIMIT_50_EMAIL } from './email_templates/Limit50Email'; welcome: '/welcome',
import { LIMIT_90_EMAIL } from './email_templates/Limit90Email'; purchase: '/purchase',
import { LIMIT_MAX_EMAIL } from './email_templates/LimitMaxEmail'; reset_password: '/reset_password',
import { PURCHASE_EMAIL } from './email_templates/PurchaseEmail'; anomaly_domain: '/anomaly/domain',
import { ANOMALY_VISITS_EVENTS_EMAIL } from './email_templates/AnomalyUsageEmail'; anomaly_visits_events: '/anomaly_visits_events',
import { ANOMALY_DOMAIN_EMAIL } from './email_templates/AnomalyDomainEmail'; limit_50: '/limit/50',
import { CONFIRM_EMAIL } from './email_templates/ConfirmEmail'; limit_90: '/limit/90',
import { RESET_PASSWORD_EMAIL } from './email_templates/ResetPasswordEmail'; limit_max: '/limit/max',
} as const;
class EmailService { export type EmailTemplate = keyof typeof templateMap;
export type EmailServerInfo = { url: string, body: Record<string, any>, headers: Record<string, string> };
private apiInstance = new TransactionalEmailsApi(); type EmailData =
| { template: 'confirm', data: { target: string, link: string } }
| { template: 'welcome', data: { target: string } }
| { template: 'purchase', data: { target: string, projectName: string } }
| { template: 'reset_password', data: { target: string, newPassword: string } }
| { template: 'anomaly_domain', data: { target: string, projectName: string, domains: string[] } }
| { template: 'anomaly_visits_events', data: { target: string, projectName: string, data: any[] } }
| { template: 'limit_50', data: { target: string, projectName: string } }
| { template: 'limit_90', data: { target: string, projectName: string } }
| { template: 'limit_max', data: { target: string, projectName: string } };
init(apiKey: string) { export class EmailService {
this.apiInstance.setApiKey(0, apiKey); static getEmailServerInfo<T extends EmailTemplate>(template: T, data: Extract<EmailData, { template: T }>['data']): EmailServerInfo {
} return {
url: `https://mail-service.litlyx.com/send${templateMap[template]}`,
async sendLimitEmail50(target: string, projectName: string) { body: data,
try { headers: { 'Content-Type': 'application/json' }
const sendSmtpEmail = new SendSmtpEmail(); };
sendSmtpEmail.subject = "⚡ You've reached 50% limit on Litlyx";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = LIMIT_50_EMAIL
.replace(/\[Project Name\]/, projectName)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
} }
} }
async sendLimitEmail90(target: string, projectName: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "⚡ You've reached 90% limit on Litlyx";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = LIMIT_90_EMAIL
.replace(/\[Project Name\]/, projectName)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendLimitEmailMax(target: string, projectName: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "🚨 You've reached your limit on Litlyx!";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = LIMIT_MAX_EMAIL
.replace(/\[Project Name\]/, projectName)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendWelcomeEmail(target: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "Welcome to Litlyx!";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = WELCOME_EMAIL;
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendPurchaseEmail(target: string, projectName: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "Thank You for Upgrading Your Litlyx Plan!";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = PURCHASE_EMAIL
.replace(/\[Project Name\]/, projectName)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendAnomalyVisitsEventsEmail(target: string, projectName: string,
data: {
visits: { _id: string, count: number }[],
events: { _id: string, count: number }[]
}) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "🔍 Unexpected Activity Detected by our AI";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = ANOMALY_VISITS_EVENTS_EMAIL
.replace(/\[Project Name\]/, projectName)
.replace(/\[ENTRIES\]/,
[
...data.visits.map(e => (`<li> Visits in date ${new Date(e._id).toLocaleDateString('en-EN')} [ ${e.count} ] </li>`)),
...data.events.map(e => (`<li> Events in date ${new Date(e._id).toLocaleDateString('en-EN')} [ ${e.count} ] </li>`))
]
.join('')
)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendAnomalyDomainEmail(target: string, projectName: string, domains: string[]) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "🔍 Suspicious dns detected by our AI";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "help@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = ANOMALY_DOMAIN_EMAIL
.replace(/\[Project Name\]/, projectName)
.replace(/\[CURRENT_DATE\]/, new Date().toLocaleDateString('en-EN'))
// .replace(/\[DNS_ENTRIES\]/,
// domains.map(e => (`<li> ${e} </li>`)).join('<br>')
.replace(/\[DNS_ENTRIES\]/, domains[0])
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendConfirmEmail(target: string, link: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "Confirm your email";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "no-reply@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = CONFIRM_EMAIL
.replace(/\[CONFIRM_LINK\]/, link)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
async sendResetPasswordEmail(target: string, newPassword: string) {
try {
const sendSmtpEmail = new SendSmtpEmail();
sendSmtpEmail.subject = "Password reset";
sendSmtpEmail.sender = { "name": "Litlyx", "email": "no-reply@litlyx.com" };
sendSmtpEmail.to = [{ "email": target }];
sendSmtpEmail.htmlContent = RESET_PASSWORD_EMAIL
.replace(/\[NEW_PASSWORD\]/, newPassword)
.toString();
await this.apiInstance.sendTransacEmail(sendSmtpEmail);
return true;
} catch (ex) {
console.error('ERROR SENDING EMAIL', ex);
return false;
}
}
}
const instance = new EmailService();
export default instance;

View File

@@ -1,159 +0,0 @@
export const ANOMALY_DOMAIN_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:37.5em">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="border:1px solid rgb(0,0,0, 0.1);border-radius:3px;overflow:hidden">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<img src="https://litlyx.com/images/locker2.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-bottom:0">
<tbody style="width:100%">
<tr style="width:100%">
<td data-id="__react-email-column">
<h1 style="font-size:32px;font-weight:bold;text-align:center">
Dear user
</h1>
<h2 style="font-size:26px;font-weight:bold;text-align:center">
Our AI Agent noticed a recent Anomaly on your project on Litlyx.
</h2>
<p style="font-size:16px;line-height:24px;margin:16px 0">
<b>Time: </b> [CURRENT_DATE]
<!-- September 7, 2022 at 10:58 AM -->
</p>
<p
style="font-size:16px;line-height:24px;margin:16px 0;margin-top:-5px">
<b>Project: </b> [Project Name]
</p>
<p
style="font-size:16px;line-height:24px;margin:16px 0;margin-top:-5px">
<b>Suspicious DNS: </b> [DNS_ENTRIES]
</p>
<p style="font-size:16px;line-height:24px;margin:16px 0">
If this was you, there&#x27;s nothing else you
need to do.
</p>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-top:0">
<tbody style="width:100%">
<tr style="width:100%">
<td colspan="2" data-id="__react-email-column"
style="display:flex;justify-content:center;width:100%">
<a style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#FFF;font-weight:bold;border:1px solid rgb(0,0,0, 0.1);cursor:pointer;padding:12px 30px 12px 30px"
target="_blank" href="https://dashboard.litlyx.com"><span></span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px">
Go to Dashboard</span><span></span></a>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-bottom:0">
<tbody style="width:100%">
<tr style="width:100%">
<td data-id="__react-email-column">
<h3>If this wasn't you..</h3>
<p>you should reach out to the webmasters of
the websites that have duplicated your content and request them
to remove it or give you proper attribution (if available).</p>
<p>You can also use <a href="https://www.whois.com/whois/"
style="color: #D32F2F; text-decoration: none;">https://www.whois.com/whois/</a>
to get the contact details of the webmaster or domain owner.</p>
<p>If webmasters don't respond or cooperate, <strong>you can file a
DMCA complaint here:</strong> <a
href="https://support.google.com/legal/answer/3110420?hl=en"
style="color: #D32F2F; text-decoration: none;">https://support.google.com/legal/answer/3110420?hl=en</a>
<strong>with Google to request the removal of the duplicate
content from their search results.</strong></p>
<h3>Please refer to this for more information:</h3>
<ul>
<li><a href="https://support.google.com/legal/answer/3110420?hl=en&sjid=14235884554806745995-AP&authuser=2"
style="color: #D32F2F; text-decoration: none;">Report
Content for Legal Reasons</a></li>
<li><a href="https://www.dmca.com/FAQ/How-can-I-get-a-webpage-removed-from-Google-search-results"
style="color: #D32F2F; text-decoration: none;">How can I
get a webpage removed from Google search results?</a>
</li>
</ul>
<p>Your safety is our main priority.</p>
<p>Thank you for choosing Litlyx every day as your analytics tool!
</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding:45px 0 0 0">
<tbody>
<tr>
<td>
<img src="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/yelp-footer.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</td>
</tr>
</tbody>
</table>
<p style="font-size:12px;line-height:24px;margin:16px 0;text-align:center;color:rgb(0,0,0, 0.7)">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194
</p>
</td>
</tr>
</tbody>
</table>
</body>
</html>
`

View File

@@ -1,152 +0,0 @@
export const ANOMALY_VISITS_EVENTS_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:37.5em">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="border:1px solid rgb(0,0,0, 0.1);border-radius:3px;overflow:hidden">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<img src="https://litlyx.com/images/locker2.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-bottom:0">
<tbody style="width:100%">
<tr style="width:100%">
<td data-id="__react-email-column">
<h1 style="font-size:32px;font-weight:bold;text-align:center">
Dear user
</h1>
<h2 style="font-size:26px;font-weight:bold;text-align:center">
Our AI Agent noticed a recent unexpected usage on your project
on Litlyx.
</h2>
<p
style="font-size:16px;line-height:24px;margin:16px 0;margin-top:-5px">
<b>Project: </b> [Project Name]
</p>
<p
style="font-size:16px;line-height:24px;margin:16px 0;margin-top:-5px">
<b>Info: </b> [ENTRIES]
</p>
<p>If this spike in activity is expected, theres no need to worry.
However, if you believe this could be unexpected or suspicious,
we recommend taking a closer look at your data on the
<strong>Litlyx Dashboard</strong>. You can analyze your recent
traffic and event logs to identify any irregularities or
potential issues.</p>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-top:0">
<tbody style="width:100%">
<tr style="width:100%">
<td colspan="2" data-id="__react-email-column"
style="display:flex;justify-content:center;width:100%">
<a style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#FFF;font-weight:bold;border:1px solid rgb(0,0,0, 0.1);cursor:pointer;padding:12px 30px 12px 30px"
target="_blank"
href="https://dashboard.litlyx.com"><span></span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px">
Go to Dashboard</span><span></span></a>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-bottom:0">
<tbody style="width:100%">
<tr style="width:100%">
<td data-id="__react-email-column">
<h3>What can I do?</h3>
<p>To better understand the situation, you can:</p>
<ol>
<li>Review your traffic sources to see where the visits or
events are coming from.</li>
<li>Check for any unexpected patterns, such as a high number of
visits from unknown sources or abnormal event triggers.</li>
<li>Check your code to find bugs on a specific action that is
triggered in loops.</li>
</ol>
<p>If you need help understanding this activity or have any
concerns, feel free to reach out to our support team at <a
href="mailto:help@litlyx.com"
style="color: #D32F2F; text-decoration: none;"><strong>help@litlyx.com</strong></a>.
We are here to assist you!</p>
<p><strong>Your safety and data integrity are our top
priorities.</strong></p>
<p>Thank you for trusting Litlyx as your analytics tool!</p>
<p>Best regards,</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding:45px 0 0 0">
<tbody>
<tr>
<td>
<img src="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/yelp-footer.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</td>
</tr>
</tbody>
</table>
<p style="font-size:12px;line-height:24px;margin:16px 0;text-align:center;color:rgb(0,0,0, 0.7)">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194
</p>
</td>
</tr>
</tbody>
</table>
</body>
</html>
`

View File

@@ -1,69 +0,0 @@
export const CONFIRM_EMAIL = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Confirmation</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
.container {
background-color: #ffffff;
padding: 20px;
max-width: 600px;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333333;
}
p {
color: #555555;
line-height: 1.5;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #007bff;
color: #ffffff;
text-decoration: none;
border-radius: 5px;
}
.footer {
margin-top: 20px;
font-size: 12px;
color: #777777;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h2>Confirm your email on Litlyx</h2>
<p>Hello,</p>
<p>Thank you so much for signing up on Litlyx! Please confirm your email address by clicking the button below:
</p>
<p><a href="[CONFIRM_LINK]" class="button">Confirm Email</a></p>
<p>If you didn't create an account with us, you can safely ignore this email.</p>
<p>We hope to hear from you soon!</p>
<div class="footer">
<p>&copy; 2024 Litlyx. All rights reserved.</p>
</div>
</div>
</body>
</html>`

View File

@@ -1,116 +0,0 @@
export const LIMIT_50_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<link rel="preload" as="image" href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-logo.png" />
<link rel="preload" as="image"
href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-review-user.jpg" />
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#ffffff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:100%;margin:0 auto;padding:20px 0 48px;width:580px">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<img alt="Founder" height="96"
src="https://litlyx.com/images/founder.jpg"
style="display:block;outline:none;border:none;text-decoration:none;margin:0 auto;margin-bottom:16px;border-radius:50%"
width="96" />
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding-bottom:20px">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:32px;line-height:1.3;margin:16px 0;font-weight:700;color:#484848">
Youve Reached 50% of Your Litlyx Project Limit on [Project Name]
</p>
<p
style="font-size:18px;line-height:1.4;margin:16px 0;color:#484848;padding:24px;background-color:#f2f3f3;border-radius:4px">
To avoid losing precious data, please remember to monitor your usage
on the <strong>Litlyx Dashboard</strong>. You can find your current
usage details under <strong>Settings > Billing Tab</strong>
</p>
<p>If you need more data collection storage, you may consider upgrading
your plan to get additional benefits and ensure uninterrupted data
collection.</p>
<p>Feel free to reply to this email or contact us at <a
href="mailto:help@litlyx.com"
style="color: #FF5733; text-decoration: none;">help@litlyx.com</a>
if you have any questions or need assistance.</p>
<p>Thank you for choosing Litlyx every day as your analytics tool.</p>
<p>Have a nice day!</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
<a href="https://dashboard.litlyx.com/"
style="line-height:100%;text-decoration:none;display:block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#fff;font-size:18px;padding-top:19px;padding-bottom:19px;text-align:center;width:100%;padding:19px 0px 19px 0px"
target="_blank"><span>
</span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:14.25px">Go to Dashboard</span><span></span></a>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<hr
style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#cccccc;margin:20px 0" />
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:14px;line-height:24px;margin:16px 0;color:#9ca299;margin-bottom:10px">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA:
17814721001- REA: RM-1743194
</p>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--/$-->
</body>
</html>
`

View File

@@ -1,117 +0,0 @@
export const LIMIT_90_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<link rel="preload" as="image" href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-logo.png" />
<link rel="preload" as="image"
href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-review-user.jpg" />
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#ffffff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:100%;margin:0 auto;padding:20px 0 48px;width:580px">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<img alt="Founder" height="96"
src="https://litlyx.com/images/founder.jpg"
style="display:block;outline:none;border:none;text-decoration:none;margin:0 auto;margin-bottom:16px;border-radius:50%"
width="96" />
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding-bottom:20px">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:32px;line-height:1.3;margin:16px 0;font-weight:700;color:#484848">
Youve Reached 90% of Your Litlyx Project Limit on [Project Name]
</p>
<p
style="font-size:18px;line-height:1.4;margin:16px 0;color:#484848;padding:24px;background-color:#f2f3f3;border-radius:4px">
To avoid losing precious data, please remember to monitor your usage
on the <strong>Litlyx Dashboard</strong>. You can find your current
usage details under <strong>Settings > Billing Tab</strong>
</p>
<p>If you need more data collection storage, you may consider upgrading
your plan to get additional benefits and ensure uninterrupted data
collection.</p>
<p>Feel free to reply to this email or contact us at <a
href="mailto:help@litlyx.com"
style="color: #FF5733; text-decoration: none;">help@litlyx.com</a>
if you have any questions or need assistance.</p>
<p>Thank you for choosing Litlyx every day as your analytics tool.</p>
<p>Have a nice day!</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
<a href="https://dashboard.litlyx.com/"
style="line-height:100%;text-decoration:none;display:block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#fff;font-size:18px;padding-top:19px;padding-bottom:19px;text-align:center;width:100%;padding:19px 0px 19px 0px"
target="_blank"><span>
</span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:14.25px">Go to Dashboard</span><span></span></a>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<hr
style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#cccccc;margin:20px 0" />
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:14px;line-height:24px;margin:16px 0;color:#9ca299;margin-bottom:10px">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA:
17814721001- REA: RM-1743194
</p>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--/$-->
</body>
</html>
`

View File

@@ -1,138 +0,0 @@
export const LIMIT_MAX_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<link rel="preload" as="image" href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-logo.png" />
<link rel="preload" as="image"
href="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/airbnb-review-user.jpg" />
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#ffffff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:100%;margin:0 auto;padding:20px 0 48px;width:580px">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<img alt="Founder" height="96"
src="https://litlyx.com/images/founder.jpg"
style="display:block;outline:none;border:none;text-decoration:none;margin:0 auto;margin-bottom:16px;border-radius:50%"
width="96" />
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding-bottom:20px">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:32px;line-height:1.3;margin:16px 0;font-weight:700;color:#484848">
Youve Reached Your Litlyx Project Limit on [Project Name]
</p>
<p
style="font-size:18px;line-height:1.4;margin:16px 0;color:#484848;padding:24px;background-color:#ffbb03;border-radius:4px">
We noticed that Litlyx has stopped collecting data for your project.
</p>
<p>
To help you avoid losing valuable insights, we recommend keeping an
eye on your usage via the Litlyx Dashboard.
</p>
<p>
You can view your current usage details under Settings > Billing
Tab.
</p>
<p>
If you need additional storage for data collection, consider
upgrading your plan to unlock more benefits and ensure uninterrupted
service.
</p>
<p style="font-weight: 700;">
As a token of appreciation, we're offering you 25% off for life at
checkout with the code LIT25.
</p>
Thank you for choosing Litlyx as your trusted analytics tool.
<p></p>
<p>
If you have any questions or need assistance, feel free to reply to
this email or contact us at <a href="mailto:help@litlyx.com"
style="color: #FF5733; text-decoration: none;">help@litlyx.com</a>
</p>
<p>
Have a great day!
</p>
<p>
Antonio
CEO | Litlyx
</p>
<a href="https://dashboard.litlyx.com/"
style="line-height:100%;text-decoration:none;display:block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#fff;font-size:18px;padding-top:19px;padding-bottom:19px;text-align:center;width:100%;padding:19px 0px 19px 0px"
target="_blank"><span>
</span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:14.25px">Go
to Dashboard</span><span></span></a>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<hr
style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#cccccc;margin:20px 0" />
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<p
style="font-size:14px;line-height:24px;margin:16px 0;color:#9ca299;margin-bottom:10px">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA:
17814721001- REA: RM-1743194
</p>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--/$-->
</body>
</html>
`

View File

@@ -1,45 +0,0 @@
export const PURCHASE_EMAIL = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thank You for Upgrading Your Litlyx Plan!</title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<!-- Email Content -->
<p>Dear User,</p>
<p>We are thrilled to inform you that <strong>[Project Name]</strong> on <strong>Litlyx</strong> has successfully been upgraded to a higher plan! Thank you for choosing to elevate your experience with us and for believing in our project.</p>
<p>We appreciate your trust in Litlyx and are committed to providing you with the best analytics experience. Your support helps us to continually improve our platform and bring new features to make your analytics journey even better.</p>
<p>You can find your current plan details and download your invoices under <strong>Settings > Billing Tab</strong>.</p>
<h3>What does this mean for you?</h3>
<p>With your upgraded plan, you can now enjoy more data collection, advanced features, and additional benefits to enhance your data analysis capabilities:</p>
<ol>
<li>Access to more storage and increased data limits.</li>
<li>Advanced analytics tools like IP-Company Matching, download CSV for your raw data, AI insights, and more.</li>
<li>Priority support to help you make the most of your Litlyx experience on Slack or Discord!</li>
</ol>
<p>If you have any questions about your new plan or need assistance, feel free to reach out to our support team at <a href="mailto:help@litlyx.com" style="color: #28a745; text-decoration: none;"><strong>help@litlyx.com</strong></a>. Were here to help you make the most out of your upgraded plan!</p>
<p><strong>Thank you for using Litlyx every day as your analytics tool and for being a part of our journey.</strong></p>
<p>We look forward to continuing to support your growth and success!</p>
<p>Best regards,</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
</body>
</html>
`

View File

@@ -1,109 +0,0 @@
export const RESET_PASSWORD_EMAIL = `<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta name="x-apple-disable-message-reformatting" />
<!--$-->
</head>
<body
style='background-color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="max-width:37.5em">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="border:1px solid rgb(0,0,0, 0.1);border-radius:3px;overflow:hidden">
<tbody>
<tr>
<td>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation">
<tbody style="width:100%">
<tr style="width:100%">
<img src="https://litlyx.com/images/locker2.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-bottom:0">
<tbody style="width:100%">
<tr style="width:100%">
<td data-id="__react-email-column">
<h1 style="font-size:32px;font-weight:bold;text-align:center">
Dear user
</h1>
<h2 style="font-size:26px;font-weight:bold;text-align:center">
Below is the temporary password for logging in again to the
Litlyx dashboard.
</h2>
<p style="font-size:16px;line-height:24px;margin:16px 0">
<b>Temporary Password: </b> [NEW_PASSWORD]
<!-- September 7, 2022 at 10:58 AM -->
</p>
<p style="font-size:16px;line-height:24px;margin:16px 0">
Please ensure that you change your password as soon as possible.
To do so, go to <b>Settings > Account</b> in the dashboard. <br>
If you need further assistance, feel free to contact us at
<a href="mailto:help@litlyx.com">help@litlyx.com</a>.
</p>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0"
role="presentation" style="padding:20px;padding-top:0">
<tbody style="width:100%">
<tr style="width:100%">
<td colspan="2" data-id="__react-email-column"
style="display:flex;justify-content:center;width:100%">
<a style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;background-color:#5680f8;border-radius:3px;color:#FFF;font-weight:bold;border:1px solid rgb(0,0,0, 0.1);cursor:pointer;padding:12px 30px 12px 30px"
target="_blank"
href="https://dashboard.litlyx.com"><span></span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px">
Go to Dashboard</span><span></span></a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="padding:45px 0 0 0">
<tbody>
<tr>
<td>
<img src="https://react-email-demo-lpdmf0ryo-resend.vercel.app/static/yelp-footer.png"
style="display:block;outline:none;border:none;text-decoration:none;max-width:100%"
width="620" />
</td>
</tr>
</tbody>
</table>
<p style="font-size:12px;line-height:24px;margin:16px 0;text-align:center;color:rgb(0,0,0, 0.7)">
2024 © Litlyx. All rights reserved.
<br>
Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194
</p>
</td>
</tr>
</tbody>
</table>
</body>
</html>
`

View File

@@ -1,39 +0,0 @@
export const WELCOME_EMAIL = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to Litlyx!</title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<p>Were happy to have you onboard,</p>
<p>At Litlyx, were committed to creating the best analytics collection experience for everybody, starting from developers.</p>
<p>Here are a few things you can do to get started tracking analytics today:</p>
<ol>
<li><strong><a href="https://dashboard.litlyx.com" style="color: #007BFF; text-decoration: none;">Create a new project</a></strong> by just naming it</li>
<li><strong><a style="color: #0a0a0a; text-decoration: none;">Copy the universal Script</a></strong> we provide you the snippets to copy in your index.html file and start instantly to track metrics on your website or web app.</li>
<li><strong><a style="color: #0a0a0a; text-decoration: none;">Deploy</a></strong> Litlyx is production ready.</li>
</ol>
<p>If you have any questions or need support, visit <a href="http://docs.litlyx.com" style="color: #007BFF;">docs.litlyx.com</a>.</p>
<p>Feel free to reply to this email or reach out to our team at <a href="mailto:help@litlyx.com" style="color: #007BFF;">help@litlyx.com</a>. Were here to help!</p>
<p>Link to Discord for developer support: <a href="https://discord.com/invite/9cQykjsmWX" style="color: #007BFF;">https://discord.com/invite/9cQykjsmWX</a></p>
<p>Thank you for joining us, and we look forward to seeing you around.</p>
<p>We want to make analytics the freshest thing on the web.</p>
<p>Antonio,</p>
<p>CEO | Litlyx</p>
</body>
</html>
`