Files
speckle-server/packages/server/modules/gatekeeper/services/trialEmails.ts
T
Kristaps Fabians Geikins 82c7814b39 feat(server): track wp trial expiratiom email sends (#3753)
* feat(server): track wp trial expiratiom email sends

* cr fix
2025-01-02 13:28:55 +02:00

139 lines
4.5 KiB
TypeScript

import { GetServerInfo } from '@/modules/core/domain/server/operations'
import { FindEmailsByUserId } from '@/modules/core/domain/userEmails/operations'
import {
EmailTemplateParams,
RenderEmail,
SendEmail,
SendEmailParams
} from '@/modules/emails/domain/operations'
import { getServerOrigin } from '@/modules/shared/helpers/envHelper'
import { mixpanel } from '@/modules/shared/utils/mixpanel'
import { GetWorkspaceCollaborators } from '@/modules/workspaces/domain/operations'
import { WorkspaceTeamMember } from '@/modules/workspaces/domain/types'
import { Workspace } from '@/modules/workspacesCore/domain/types'
import { Roles } from '@speckle/shared'
type TrialExpiresArgs = {
workspace: Workspace
expiresInDays: number
}
type TrialExpiresArgsWithAdmin = TrialExpiresArgs & {
workspaceAdmin: WorkspaceTeamMember
}
const buildMjmlBody = ({
workspace,
expiresInDays,
workspaceAdmin
}: TrialExpiresArgsWithAdmin) => {
const expireMessage =
expiresInDays === 0
? `<strong>today</strong>`
: `in <strong>${expiresInDays} days</strong>`
const bodyStart = `<mj-text>
Hi ${workspaceAdmin.name}!
<br/>
<br/>
The trial for your workspace <span style="font-variant: small-caps; font-weight: bold;">${workspace.name}</span> expires ${expireMessage}.
<br/>
<br/>
Upgrade to a paid plan before the trial expires to keep using your workspace. You can compare plans and get an overview of your estimated billing from your workspace's billing settings.
<br/>
<br/>
</mj-text>
`
const bodyEnd = `<mj-text>
<span style="font-weight: bold;">Have questions or feedback?</span> Please write us at <a href="mailto:hello@speckle.systems" target="_blank">hello@speckle.systems</a> and we'd be more than happy to talk.
</mj-text>`
return { bodyStart, bodyEnd }
}
const buildTextBody = ({
workspace,
expiresInDays,
workspaceAdmin
}: TrialExpiresArgsWithAdmin) => {
const expireMessage = expiresInDays === 0 ? `today` : `in ${expiresInDays} days`
const bodyStart = `
Hi ${workspaceAdmin.name}!
\r\n\r\n
The trial for your workspace ${workspace.name} expires ${expireMessage}.
\r\n\r\n
Upgrade to a paid plan before the trial expires to keep using your workspace. You can compare plans and get an overview of your estimated billing from your workspace's billing settings.
\r\n\r\n
`
const bodyEnd = `Have questions or feedback? Please write us at hello@speckle.systems and we'd be more than happy to talk.`
return { bodyStart, bodyEnd }
}
const buildEmailTemplateParams = (
args: TrialExpiresArgsWithAdmin
): EmailTemplateParams => {
const url = new URL(`workspaces/${args.workspace.slug}`, getServerOrigin()).toString()
return {
mjml: buildMjmlBody(args),
text: buildTextBody(args),
cta: {
title: 'Upgrade your workspace',
url
}
}
}
export const sendWorkspaceTrialExpiresEmailFactory =
({
renderEmail,
sendEmail,
getServerInfo,
getWorkspaceCollaborators,
getUserEmails
}: {
renderEmail: RenderEmail
sendEmail: SendEmail
getServerInfo: GetServerInfo
getWorkspaceCollaborators: GetWorkspaceCollaborators
getUserEmails: FindEmailsByUserId
}) =>
async (args: TrialExpiresArgs) => {
const mp = mixpanel({ userEmail: undefined, req: undefined })
const [serverInfo, workspaceAdmins] = await Promise.all([
getServerInfo(),
getWorkspaceCollaborators({
workspaceId: args.workspace.id,
limit: 100,
filter: { roles: [Roles.Workspace.Admin] }
})
])
const sendEmailParams = await Promise.all(
workspaceAdmins.map(async (admin) => {
const userEmails = await getUserEmails({ userId: admin.id })
const emailTemplateParams = buildEmailTemplateParams({
...args,
workspaceAdmin: admin
})
const { html, text } = await renderEmail(emailTemplateParams, serverInfo, null)
const subject =
args.expiresInDays === 0
? 'Your workspace trial expires today'
: `Your workspace trial expires in ${args.expiresInDays} days`
const sendEmailParams: SendEmailParams = {
html,
text,
subject,
to: userEmails.map((e) => e.email)
}
return sendEmailParams
})
)
await Promise.all(sendEmailParams.map((params) => sendEmail(params)))
await mp.track('Workspace Trial Expiration Email Sent', {
workspaceId: args.workspace.id,
// eslint-disable-next-line camelcase
workspace_id: args.workspace.id,
expiresInDays: args.expiresInDays
})
}