From bfeee8673c41d9cbe276ce4ed09a8bd6956b9bd9 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 29 Jan 2025 16:03:01 +0100 Subject: [PATCH] use new mail service in dashboard --- dashboard/nuxt.config.ts | 3 +- dashboard/server/api/auth/confirm_email.ts | 12 +- .../server/api/auth/google_login.post.ts | 13 +- dashboard/server/api/auth/register.post.ts | 12 +- .../integrations/github/oauth2/callback.ts | 72 ------ dashboard/server/api/pay/webhook.post.ts | 20 +- dashboard/server/api/timeline/visits.ts | 4 +- dashboard/server/api/user/password/reset.ts | 8 +- dashboard/server/init.ts | 12 - .../server/services/EmailServiceHelper.ts | 18 ++ dashboard/shared/services/EmailService.ts | 35 +++ email/templates/PurchaseEmail.ts | 10 - package.json | 1 + scripts/dashboard/deploy.ts | 12 +- scripts/dashboard/shared.ts | 5 +- scripts/tsconfig.json | 12 + shared_global/services/EmailService.ts | 220 +++--------------- .../email_templates/AnomalyDomainEmail.ts | 159 ------------- .../email_templates/AnomalyUsageEmail.ts | 152 ------------ .../services/email_templates/ConfirmEmail.ts | 69 ------ .../services/email_templates/Limit50Email.ts | 116 --------- .../services/email_templates/Limit90Email.ts | 117 ---------- .../services/email_templates/LimitMaxEmail.ts | 138 ----------- .../services/email_templates/PurchaseEmail.ts | 45 ---- .../email_templates/ResetPasswordEmail.ts | 109 --------- .../services/email_templates/WelcomeEmail.ts | 39 ---- 26 files changed, 147 insertions(+), 1266 deletions(-) delete mode 100644 dashboard/server/api/integrations/github/oauth2/callback.ts create mode 100644 dashboard/server/services/EmailServiceHelper.ts create mode 100644 dashboard/shared/services/EmailService.ts create mode 100644 scripts/tsconfig.json delete mode 100644 shared_global/services/email_templates/AnomalyDomainEmail.ts delete mode 100644 shared_global/services/email_templates/AnomalyUsageEmail.ts delete mode 100644 shared_global/services/email_templates/ConfirmEmail.ts delete mode 100644 shared_global/services/email_templates/Limit50Email.ts delete mode 100644 shared_global/services/email_templates/Limit90Email.ts delete mode 100644 shared_global/services/email_templates/LimitMaxEmail.ts delete mode 100644 shared_global/services/email_templates/PurchaseEmail.ts delete mode 100644 shared_global/services/email_templates/ResetPasswordEmail.ts delete mode 100644 shared_global/services/email_templates/WelcomeEmail.ts diff --git a/dashboard/nuxt.config.ts b/dashboard/nuxt.config.ts index c156a4f..2fdb91f 100644 --- a/dashboard/nuxt.config.ts +++ b/dashboard/nuxt.config.ts @@ -44,8 +44,7 @@ export default defineNuxtConfig({ AI_ORG: process.env.AI_ORG, AI_PROJECT: process.env.AI_PROJECT, AI_KEY: process.env.AI_KEY, - EMAIL_SERVICE: process.env.EMAIL_SERVICE, - BREVO_API_KEY: process.env.BREVO_API_KEY, + EMAIL_SECRET: process.env.EMAIL_SECRET, AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET, GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID, GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET, diff --git a/dashboard/server/api/auth/confirm_email.ts b/dashboard/server/api/auth/confirm_email.ts index c1d1a87..4d970be 100644 --- a/dashboard/server/api/auth/confirm_email.ts +++ b/dashboard/server/api/auth/confirm_email.ts @@ -1,8 +1,9 @@ -import { createUserJwt, readRegisterJwt } from '~/server/AuthManager'; +import { createUserJwt, readRegisterJwt } from '~/server/AuthManager'; import { UserModel } from '@schema/UserSchema'; 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 => { @@ -14,9 +15,12 @@ export default defineEventHandler(async event => { try { 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() }); - // 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' }); - 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) { return setResponseStatus(event, 400, 'Error creating user'); } diff --git a/dashboard/server/api/auth/google_login.post.ts b/dashboard/server/api/auth/google_login.post.ts index 3e58383..1b784cb 100644 --- a/dashboard/server/api/auth/google_login.post.ts +++ b/dashboard/server/api/auth/google_login.post.ts @@ -2,7 +2,8 @@ import { OAuth2Client } from 'google-auth-library'; import { createUserJwt } from '~/server/AuthManager'; 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() @@ -58,10 +59,12 @@ export default defineEventHandler(async event => { const savedUser = await newUser.save(); - // setImmediate(() => { - // console.log('SENDING WELCOME EMAIL TO', payload.email); - // if (payload.email) EmailService.sendWelcomeEmail(payload.email); - // }); + setImmediate(() => { + console.log('SENDING WELCOME EMAIL TO', 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 }) } diff --git a/dashboard/server/api/auth/register.post.ts b/dashboard/server/api/auth/register.post.ts index a22b47b..c1bcaf8 100644 --- a/dashboard/server/api/auth/register.post.ts +++ b/dashboard/server/api/auth/register.post.ts @@ -1,9 +1,10 @@ -import { createRegisterJwt, createUserJwt } from '~/server/AuthManager'; +import { createRegisterJwt } from '~/server/AuthManager'; import { UserModel } from '@schema/UserSchema'; import { RegisterModel } from '@schema/RegisterSchema'; -// import EmailService from '@services/EmailService'; +import { EmailService } from '@services/EmailService'; import crypto from 'crypto'; +import { EmailServiceHelper } from '~/server/services/EmailServiceHelper'; function canRegister(email: string, password: string) { if (email.length == 0) return false; @@ -33,9 +34,10 @@ export default defineEventHandler(async event => { await RegisterModel.create({ email, password: hashedPassword }); - // setImmediate(() => { - // EmailService.sendConfirmEmail(email, `https://dashboard.litlyx.com/api/auth/confirm_email?register_code=${jwt}`); - // }); + setImmediate(() => { + const emailData = EmailService.getEmailServerInfo('confirm', { target: email, link: `https://dashboard.litlyx.com/api/auth/confirm_email?register_code=${jwt}` }); + EmailServiceHelper.sendEmail(emailData); + }); return { error: false, diff --git a/dashboard/server/api/integrations/github/oauth2/callback.ts b/dashboard/server/api/integrations/github/oauth2/callback.ts deleted file mode 100644 index fdb5eea..0000000 --- a/dashboard/server/api/integrations/github/oauth2/callback.ts +++ /dev/null @@ -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 }) } - -}); \ No newline at end of file diff --git a/dashboard/server/api/pay/webhook.post.ts b/dashboard/server/api/pay/webhook.post.ts index d78cf40..7a75e0b 100644 --- a/dashboard/server/api/pay/webhook.post.ts +++ b/dashboard/server/api/pay/webhook.post.ts @@ -4,9 +4,9 @@ import type Event from 'stripe'; import { ProjectModel } from '@schema/project/ProjectSchema'; import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM'; import { ProjectLimitModel } from '@schema/project/ProjectsLimits'; -// import EmailService from '@services/EmailService' +import { EmailService } from '@services/EmailService' 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) { @@ -93,9 +93,10 @@ async function onPaymentOnetimeSuccess(event: Event.PaymentIntentSucceededEvent) const user = await UserModel.findOne({ _id: project.owner }); if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id } - // setTimeout(() => { - // EmailService.sendPurchaseEmail(user.email, project.name); - // }, 1); + setTimeout(() => { + const emailData = EmailService.getEmailServerInfo('purchase', { target: user.email, projectName: project.name }); + EmailServiceHelper.sendEmail(emailData); + }, 1); return { ok: true }; } @@ -140,10 +141,11 @@ async function onPaymentSuccess(event: Event.InvoicePaidEvent) { const user = await UserModel.findOne({ _id: project.owner }); if (!user) return { ok: false, error: 'USER NOT EXIST FOR PROJECT' + project.id } - // setTimeout(() => { - // if (PLAN.ID == 0) return; - // if (isNewSubscription) EmailService.sendPurchaseEmail(user.email, project.name); - // }, 1); + setTimeout(() => { + if (PLAN.ID == 0) return; + const emailData = EmailService.getEmailServerInfo('purchase', { target: user.email, projectName: project.name }); + EmailServiceHelper.sendEmail(emailData); + }, 1); return { ok: true }; diff --git a/dashboard/server/api/timeline/visits.ts b/dashboard/server/api/timeline/visits.ts index 47501d2..48655a8 100644 --- a/dashboard/server/api/timeline/visits.ts +++ b/dashboard/server/api/timeline/visits.ts @@ -1,6 +1,6 @@ import { VisitModel } from "@schema/metrics/VisitSchema"; import { Redis } from "~/server/services/CacheService"; -import { executeTimelineAggregation } from "~/server/services/TimelineService"; +import { executeAdvancedTimelineAggregation } from "~/server/services/TimelineService"; export default defineEventHandler(async event => { @@ -13,7 +13,7 @@ export default defineEventHandler(async event => { const cacheExp = 60; return await Redis.useCacheV2(cacheKey, cacheExp, async () => { - const timelineData = await executeTimelineAggregation({ + const timelineData = await executeAdvancedTimelineAggregation({ projectId: project_id, model: VisitModel, from, to, slice, timeOffset, domain diff --git a/dashboard/server/api/user/password/reset.ts b/dashboard/server/api/user/password/reset.ts index cbcdbe2..9af6fbf 100644 --- a/dashboard/server/api/user/password/reset.ts +++ b/dashboard/server/api/user/password/reset.ts @@ -1,7 +1,8 @@ import crypto from 'crypto'; 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 => { @@ -19,8 +20,9 @@ export default defineEventHandler(async event => { target.password = hashedPassword; 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' } }); \ No newline at end of file diff --git a/dashboard/server/init.ts b/dashboard/server/init.ts index e3f17fa..c1a1bd5 100644 --- a/dashboard/server/init.ts +++ b/dashboard/server/init.ts @@ -1,6 +1,5 @@ import mongoose from "mongoose"; import { Redis } from "~/server/services/CacheService"; -// import EmailService from '@services/EmailService'; import StripeService from '~/server/services/StripeService'; import { logger } from "./Logger"; @@ -14,12 +13,6 @@ export default async () => { logger.info('[SERVER] Initializing'); - // if (config.EMAIL_SERVICE) { - // EmailService.init(config.BREVO_API_KEY); - // logger.info('[EMAIL] Initialized'); - // } - - if (config.STRIPE_SECRET) { StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false); logger.info('[STRIPE] Initialized'); @@ -41,13 +34,8 @@ export default async () => { logger.info('[SERVER] Completed'); - logger.warn('[ANOMALY LOOP] Disabled'); - - logger.warn(`[SELFHOSTED_SERVER] ${config.SELFHOSTED}`); logger.warn(`[SELFHOSTED_CLIENT] ${config.public.SELFHOSTED}`); logger.warn(`[AUTH] ${config.public.AUTH_MODE}`); - // anomalyLoop(); - }; \ No newline at end of file diff --git a/dashboard/server/services/EmailServiceHelper.ts b/dashboard/server/services/EmailServiceHelper.ts new file mode 100644 index 0000000..ffa2d23 --- /dev/null +++ b/dashboard/server/services/EmailServiceHelper.ts @@ -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); + } + } +} \ No newline at end of file diff --git a/dashboard/shared/services/EmailService.ts b/dashboard/shared/services/EmailService.ts new file mode 100644 index 0000000..30759f2 --- /dev/null +++ b/dashboard/shared/services/EmailService.ts @@ -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, headers: Record }; + +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(template: T, data: Extract['data']): EmailServerInfo { + return { + url: `https://mail-service.litlyx.com/send${templateMap[template]}`, + body: data, + headers: { 'Content-Type': 'application/json' } + }; + } +} \ No newline at end of file diff --git a/email/templates/PurchaseEmail.ts b/email/templates/PurchaseEmail.ts index 9d2f326..e7483be 100644 --- a/email/templates/PurchaseEmail.ts +++ b/email/templates/PurchaseEmail.ts @@ -19,16 +19,6 @@ export const PURCHASE_EMAIL = `

You can find your current plan details and download your invoices under Settings > Billing Tab.

-

What does this mean for you?

- -

With your upgraded plan, you can now enjoy more data collection, advanced features, and additional benefits to enhance your data analysis capabilities:

- -
    -
  1. Access to more storage and increased data limits.
  2. -
  3. Advanced analytics tools like IP-Company Matching, download CSV for your raw data, AI insights, and more.
  4. -
  5. Priority support to help you make the most of your Litlyx experience on Slack or Discord!
  6. -
-

If you have any questions about your new plan or need assistance, feel free to reach out to our support team at help@litlyx.com. We’re here to help you make the most out of your upgraded plan!

Thank you for using Litlyx every day as your analytics tool and for being a part of our journey.

diff --git a/package.json b/package.json index 15600db..06dc075 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dashboard:clear-logs": "ts-node scripts/dashboard/clear-logs.ts", "dashboard:shared": "ts-node scripts/dashboard/shared.ts", + "dashboard:deploy": "ts-node scripts/dashboard/deploy.ts", "producer:shared": "node scripts/producer/shared.js", "email:deploy": "ts-node scripts/email/deploy.ts" }, diff --git a/scripts/dashboard/deploy.ts b/scripts/dashboard/deploy.ts index 6e9c41e..6e9590a 100644 --- a/scripts/dashboard/deploy.ts +++ b/scripts/dashboard/deploy.ts @@ -28,8 +28,8 @@ async function main() { if (fs.existsSync(TMP_PATH)) fs.rmSync(TMP_PATH, { force: true, recursive: true }); fs.ensureDirSync(TMP_PATH); - // console.log('Building'); - // child.execSync(`cd ${LOCAL_PATH} && pnpm i && pnpm run build`) + console.log('Building'); + child.execSync(`cd ${LOCAL_PATH} && pnpm i && pnpm run build`) console.log('Creting zip file'); const archive = createZip(TMP_PATH + '/' + ZIP_NAME); @@ -71,12 +71,12 @@ async function main() { console.log('Extracting remote'); await DeployHelper.execute(`cd ${REMOTE_PATH} && unzip ${ZIP_NAME} && rm -r ${ZIP_NAME}`); - // console.log('Installing remote'); - // await DeployHelper.execute(`cd ${REMOTE_PATH} && /root/.nvm/versions/node/v21.2.0/bin/pnpm i`); + console.log('Installing remote'); + 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(); - } diff --git a/scripts/dashboard/shared.ts b/scripts/dashboard/shared.ts index 735a451..dbe7248 100644 --- a/scripts/dashboard/shared.ts +++ b/scripts/dashboard/shared.ts @@ -1,14 +1,13 @@ import { SharedHelper } from "../helpers/shared-helper"; -import path from "path"; +import path from "node:path"; const helper = new SharedHelper(path.join(__dirname, '../../dashboard/shared')) helper.clear(); -// TODO: Email service as external repo - helper.create('services'); helper.copy('services/DateService.ts'); +helper.copy('services/EmailService.ts'); helper.create('data'); diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 0000000..1fe764a --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "target": "ESNext" + }, + "include": [ + "./**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/shared_global/services/EmailService.ts b/shared_global/services/EmailService.ts index 7290c3a..30759f2 100644 --- a/shared_global/services/EmailService.ts +++ b/shared_global/services/EmailService.ts @@ -1,193 +1,35 @@ -import { TransactionalEmailsApi, SendSmtpEmail } from '@getbrevo/brevo'; -import { WELCOME_EMAIL } from './email_templates/WelcomeEmail'; -import { LIMIT_50_EMAIL } from './email_templates/Limit50Email'; -import { LIMIT_90_EMAIL } from './email_templates/Limit90Email'; -import { LIMIT_MAX_EMAIL } from './email_templates/LimitMaxEmail'; -import { PURCHASE_EMAIL } from './email_templates/PurchaseEmail'; -import { ANOMALY_VISITS_EVENTS_EMAIL } from './email_templates/AnomalyUsageEmail'; -import { ANOMALY_DOMAIN_EMAIL } from './email_templates/AnomalyDomainEmail'; -import { CONFIRM_EMAIL } from './email_templates/ConfirmEmail'; -import { RESET_PASSWORD_EMAIL } from './email_templates/ResetPasswordEmail'; +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; -class EmailService { +export type EmailTemplate = keyof typeof templateMap; +export type EmailServerInfo = { url: string, body: Record, headers: Record }; - 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) { - this.apiInstance.setApiKey(0, apiKey); +export class EmailService { + static getEmailServerInfo(template: T, data: Extract['data']): EmailServerInfo { + return { + url: `https://mail-service.litlyx.com/send${templateMap[template]}`, + body: data, + headers: { 'Content-Type': 'application/json' } + }; } - - async sendLimitEmail50(target: string, projectName: string) { - try { - 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 => (`
  • Visits in date ${new Date(e._id).toLocaleDateString('en-EN')} [ ${e.count} ]
  • `)), - ...data.events.map(e => (`
  • Events in date ${new Date(e._id).toLocaleDateString('en-EN')} [ ${e.count} ]
  • `)) - ] - .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 => (`
  • ${e}
  • `)).join('
    ') - .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; \ No newline at end of file +} \ No newline at end of file diff --git a/shared_global/services/email_templates/AnomalyDomainEmail.ts b/shared_global/services/email_templates/AnomalyDomainEmail.ts deleted file mode 100644 index 102801c..0000000 --- a/shared_global/services/email_templates/AnomalyDomainEmail.ts +++ /dev/null @@ -1,159 +0,0 @@ -export const ANOMALY_DOMAIN_EMAIL = ` - - - - - - - - - - - - - - - - -
    - - - - - - - -
    - - - - - - -
    - - - - - - -
    -

    - Dear user -

    -

    - Our AI Agent noticed a recent Anomaly on your project on Litlyx. -

    -

    - Time: [CURRENT_DATE] - -

    -

    - Project: [Project Name] -

    -

    - Suspicious DNS: [DNS_ENTRIES] -

    -

    - If this was you, there's nothing else you - need to do. -

    - -
    - - - - - - - -
    - - Go to Dashboard -
    - - - - - - - -
    -

    If this wasn't you..

    - -

    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).

    - -

    You can also use https://www.whois.com/whois/ - to get the contact details of the webmaster or domain owner.

    - -

    If webmasters don't respond or cooperate, you can file a - DMCA complaint here: https://support.google.com/legal/answer/3110420?hl=en - with Google to request the removal of the duplicate - content from their search results.

    - -

    Please refer to this for more information:

    - - - -

    Your safety is our main priority.

    - -

    Thank you for choosing Litlyx every day as your analytics tool! -

    - -

    Antonio,

    -

    CEO | Litlyx

    -
    - - -
    - - - - - - -
    - -
    -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194 -

    -
    - - - - -` \ No newline at end of file diff --git a/shared_global/services/email_templates/AnomalyUsageEmail.ts b/shared_global/services/email_templates/AnomalyUsageEmail.ts deleted file mode 100644 index d4acffe..0000000 --- a/shared_global/services/email_templates/AnomalyUsageEmail.ts +++ /dev/null @@ -1,152 +0,0 @@ -export const ANOMALY_VISITS_EVENTS_EMAIL = ` - - - - - - - - - - - - - - - -
    - - - - - - - -
    - - - - - - -
    - - - - - - -
    -

    - Dear user -

    -

    - Our AI Agent noticed a recent unexpected usage on your project - on Litlyx. -

    -

    - Project: [Project Name] -

    -

    - Info: [ENTRIES] -

    -

    If this spike in activity is expected, there’s 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 - Litlyx Dashboard. You can analyze your recent - traffic and event logs to identify any irregularities or - potential issues.

    - - -
    - - - - - - - -
    - - Go to Dashboard -
    - - - - - - - -
    - -

    What can I do?

    - -

    To better understand the situation, you can:

    - -
      -
    1. Review your traffic sources to see where the visits or - events are coming from.
    2. -
    3. Check for any unexpected patterns, such as a high number of - visits from unknown sources or abnormal event triggers.
    4. -
    5. Check your code to find bugs on a specific action that is - triggered in loops.
    6. -
    - -

    If you need help understanding this activity or have any - concerns, feel free to reach out to our support team at help@litlyx.com. - We are here to assist you!

    - -

    Your safety and data integrity are our top - priorities.

    - -

    Thank you for trusting Litlyx as your analytics tool!

    - -

    Best regards,

    - -

    Antonio,

    -

    CEO | Litlyx

    -
    - - -
    - - - - - - -
    - -
    -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194 -

    -
    - - - - -` \ No newline at end of file diff --git a/shared_global/services/email_templates/ConfirmEmail.ts b/shared_global/services/email_templates/ConfirmEmail.ts deleted file mode 100644 index 05f9a3b..0000000 --- a/shared_global/services/email_templates/ConfirmEmail.ts +++ /dev/null @@ -1,69 +0,0 @@ - -export const CONFIRM_EMAIL = ` - - - - - - Email Confirmation - - - - -
    -

    Confirm your email on Litlyx

    -

    Hello,

    -

    Thank you so much for signing up on Litlyx! Please confirm your email address by clicking the button below: -

    -

    Confirm Email

    -

    If you didn't create an account with us, you can safely ignore this email.

    -

    We hope to hear from you soon!

    - - -
    - - -` \ No newline at end of file diff --git a/shared_global/services/email_templates/Limit50Email.ts b/shared_global/services/email_templates/Limit50Email.ts deleted file mode 100644 index 496a909..0000000 --- a/shared_global/services/email_templates/Limit50Email.ts +++ /dev/null @@ -1,116 +0,0 @@ -export const LIMIT_50_EMAIL = ` - - - - - - - - - - - - - - - - - -
    - - - - - - - -
    - Founder -
    - - - - - - -
    - - - -

    - You’ve Reached 50% of Your Litlyx Project Limit on [Project Name] -

    -

    - To avoid losing precious data, please remember to monitor your usage - on the Litlyx Dashboard. You can find your current - usage details under Settings > Billing Tab -

    - -

    If you need more data collection storage, you may consider upgrading - your plan to get additional benefits and ensure uninterrupted data - collection.

    - -

    Feel free to reply to this email or contact us at help@litlyx.com - if you have any questions or need assistance.

    - -

    Thank you for choosing Litlyx every day as your analytics tool.

    - -

    Have a nice day!

    - -

    Antonio,

    -

    CEO | Litlyx

    - - - - Go to Dashboard - - -
    -
    -
    - - - - - - -
    - - - -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: - 17814721001- REA: RM-1743194 -

    - - - -
    -
    -
    - - - - -` diff --git a/shared_global/services/email_templates/Limit90Email.ts b/shared_global/services/email_templates/Limit90Email.ts deleted file mode 100644 index dc53f84..0000000 --- a/shared_global/services/email_templates/Limit90Email.ts +++ /dev/null @@ -1,117 +0,0 @@ -export const LIMIT_90_EMAIL = ` - - - - - - - - - - - - - - - - - -
    - - - - - - - -
    - Founder -
    - - - - - - -
    - - - -

    - You’ve Reached 90% of Your Litlyx Project Limit on [Project Name] -

    -

    - To avoid losing precious data, please remember to monitor your usage - on the Litlyx Dashboard. You can find your current - usage details under Settings > Billing Tab -

    - -

    If you need more data collection storage, you may consider upgrading - your plan to get additional benefits and ensure uninterrupted data - collection.

    - -

    Feel free to reply to this email or contact us at help@litlyx.com - if you have any questions or need assistance.

    - -

    Thank you for choosing Litlyx every day as your analytics tool.

    - -

    Have a nice day!

    - -

    Antonio,

    -

    CEO | Litlyx

    - - - - Go to Dashboard - - -
    -
    -
    - - - - - - -
    - - - -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: - 17814721001- REA: RM-1743194 -

    - - - -
    -
    -
    - - - - - -` diff --git a/shared_global/services/email_templates/LimitMaxEmail.ts b/shared_global/services/email_templates/LimitMaxEmail.ts deleted file mode 100644 index 8d15df3..0000000 --- a/shared_global/services/email_templates/LimitMaxEmail.ts +++ /dev/null @@ -1,138 +0,0 @@ -export const LIMIT_MAX_EMAIL = ` - - - - - - - - - - - - - - - - - -
    - - - - - - - -
    - Founder -
    - - - - - - -
    - - - -

    - You’ve Reached Your Litlyx Project Limit on [Project Name] -

    -

    - We noticed that Litlyx has stopped collecting data for your project. -

    - -

    - To help you avoid losing valuable insights, we recommend keeping an - eye on your usage via the Litlyx Dashboard. -

    - -

    - You can view your current usage details under Settings > Billing - Tab. -

    - -

    - If you need additional storage for data collection, consider - upgrading your plan to unlock more benefits and ensure uninterrupted - service. -

    -

    - As a token of appreciation, we're offering you 25% off for life at - checkout with the code LIT25. -

    - Thank you for choosing Litlyx as your trusted analytics tool. -

    - -

    - If you have any questions or need assistance, feel free to reply to - this email or contact us at help@litlyx.com -

    - -

    - Have a great day! -

    - -

    - Antonio - CEO | Litlyx -

    - - - - - Go - to Dashboard - - -
    -
    -
    - - - - - - -
    - - - -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: - 17814721001- REA: RM-1743194 -

    - - - -
    -
    -
    - - - - - -` diff --git a/shared_global/services/email_templates/PurchaseEmail.ts b/shared_global/services/email_templates/PurchaseEmail.ts deleted file mode 100644 index 9d2f326..0000000 --- a/shared_global/services/email_templates/PurchaseEmail.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export const PURCHASE_EMAIL = ` - - - - - - Thank You for Upgrading Your Litlyx Plan! - - - - - -

    Dear User,

    - -

    We are thrilled to inform you that [Project Name] on Litlyx has successfully been upgraded to a higher plan! Thank you for choosing to elevate your experience with us and for believing in our project.

    - -

    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.

    - -

    You can find your current plan details and download your invoices under Settings > Billing Tab.

    - -

    What does this mean for you?

    - -

    With your upgraded plan, you can now enjoy more data collection, advanced features, and additional benefits to enhance your data analysis capabilities:

    - -
      -
    1. Access to more storage and increased data limits.
    2. -
    3. Advanced analytics tools like IP-Company Matching, download CSV for your raw data, AI insights, and more.
    4. -
    5. Priority support to help you make the most of your Litlyx experience on Slack or Discord!
    6. -
    - -

    If you have any questions about your new plan or need assistance, feel free to reach out to our support team at help@litlyx.com. We’re here to help you make the most out of your upgraded plan!

    - -

    Thank you for using Litlyx every day as your analytics tool and for being a part of our journey.

    - -

    We look forward to continuing to support your growth and success!

    - -

    Best regards,

    - -

    Antonio,

    -

    CEO | Litlyx

    - - - -` \ No newline at end of file diff --git a/shared_global/services/email_templates/ResetPasswordEmail.ts b/shared_global/services/email_templates/ResetPasswordEmail.ts deleted file mode 100644 index cef7858..0000000 --- a/shared_global/services/email_templates/ResetPasswordEmail.ts +++ /dev/null @@ -1,109 +0,0 @@ -export const RESET_PASSWORD_EMAIL = ` - - - - - - - - - - - - - - - - -
    - - - - - - - -
    - - - - - - -
    - - - - - - -
    -

    - Dear user -

    -

    - Below is the temporary password for logging in again to the - Litlyx dashboard. -

    -

    - Temporary Password: [NEW_PASSWORD] - -

    - -

    - Please ensure that you change your password as soon as possible. - To do so, go to Settings > Account in the dashboard.
    - If you need further assistance, feel free to contact us at - help@litlyx.com. -

    - -
    - - - - - - - -
    - - Go to Dashboard -
    -
    - - - - - - -
    - -
    -

    - 2024 © Litlyx. All rights reserved. -
    - Litlyx S.R.L. - Viale Tirreno, 187 - 00141 Rome - P.IVA: 17814721001- REA: RM-1743194 -

    -
    - - - - - -` \ No newline at end of file diff --git a/shared_global/services/email_templates/WelcomeEmail.ts b/shared_global/services/email_templates/WelcomeEmail.ts deleted file mode 100644 index 803b828..0000000 --- a/shared_global/services/email_templates/WelcomeEmail.ts +++ /dev/null @@ -1,39 +0,0 @@ -export const WELCOME_EMAIL = ` - - - - - - - Welcome to Litlyx! - - - -

    We’re happy to have you onboard,

    - -

    At Litlyx, we’re committed to creating the best analytics collection experience for everybody, starting from developers.

    - -

    Here are a few things you can do to get started tracking analytics today:

    - -
      -
    1. Create a new project – by just naming it
    2. -
    3. Copy the universal Script – we provide you the snippets to copy in your index.html file and start instantly to track metrics on your website or web app.
    4. -
    5. Deploy – Litlyx is production ready.
    6. -
    - -

    If you have any questions or need support, visit docs.litlyx.com.

    - -

    Feel free to reply to this email or reach out to our team at help@litlyx.com. We’re here to help!

    - -

    Link to Discord for developer support: https://discord.com/invite/9cQykjsmWX

    - -

    Thank you for joining us, and we look forward to seeing you around.

    - -

    We want to make analytics the freshest thing on the web.

    - -

    Antonio,

    -

    CEO | Litlyx

    - - - -` \ No newline at end of file