From b27d1708b0762e010cd9abcc30f66e4638f31d6e Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Fri, 4 Apr 2025 23:35:08 +0100 Subject: [PATCH] queue trial ended job (#992) --- .../workspace/services/workspace.service.ts | 24 +++++++++- apps/server/src/ee | 2 +- .../queue/constants/queue.constants.ts | 2 + .../emails/cloud/trial-ended-email.tsx | 46 +++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 apps/server/src/integrations/transactional/emails/cloud/trial-ended-email.tsx diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index f515c172..3ddcc5a0 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -26,6 +26,9 @@ import { DomainService } from '../../../integrations/environment/domain.service' import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { addDays } from 'date-fns'; import { DISALLOWED_HOSTNAMES, WorkspaceStatus } from '../workspace.constants'; +import { InjectQueue } from '@nestjs/bullmq'; +import { QueueJob, QueueName } from '../../../integrations/queue/constants'; +import { Queue } from 'bullmq'; @Injectable() export class WorkspaceService { @@ -39,6 +42,7 @@ export class WorkspaceService { private environmentService: EnvironmentService, private domainService: DomainService, @InjectKysely() private readonly db: KyselyDB, + @InjectQueue(QueueName.BILLING_QUEUE) private billingQueue: Queue, ) {} async findById(workspaceId: string) { @@ -91,13 +95,15 @@ export class WorkspaceService { createWorkspaceDto: CreateWorkspaceDto, trx?: KyselyTransaction, ) { - return await executeTx( + let trialEndAt = undefined; + + const createdWorkspace = await executeTx( this.db, async (trx) => { let hostname = undefined; - let trialEndAt = undefined; let status = undefined; let plan = undefined; + let billingEmail = undefined; if (this.environmentService.isCloud()) { // generate unique hostname @@ -110,6 +116,7 @@ export class WorkspaceService { ); status = WorkspaceStatus.Active; plan = 'standard'; + billingEmail = user.email; } // create workspace @@ -121,6 +128,7 @@ export class WorkspaceService { status, trialEndAt, plan, + billingEmail, }, trx, ); @@ -195,6 +203,18 @@ export class WorkspaceService { }, trx, ); + + if (this.environmentService.isCloud() && trialEndAt) { + const delay = trialEndAt.getTime() - Date.now(); + + await this.billingQueue.add( + QueueJob.TRIAL_ENDED, + { workspaceId: createdWorkspace.id }, + { delay }, + ); + } + + return createdWorkspace; } async addUserToWorkspace( diff --git a/apps/server/src/ee b/apps/server/src/ee index bcc0731b..2d9bc2b3 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit bcc0731b6a3a2d32341928164ba3243702c5513f +Subproject commit 2d9bc2b3946ea0e1cbc76ccf7ea71a3e61e93353 diff --git a/apps/server/src/integrations/queue/constants/queue.constants.ts b/apps/server/src/integrations/queue/constants/queue.constants.ts index 596feb78..3941b11d 100644 --- a/apps/server/src/integrations/queue/constants/queue.constants.ts +++ b/apps/server/src/integrations/queue/constants/queue.constants.ts @@ -14,4 +14,6 @@ export enum QueueJob { PAGE_BACKLINKS = 'page-backlinks', STRIPE_SEATS_SYNC = 'sync-stripe-seats', + + TRIAL_ENDED = 'trial-ended', } diff --git a/apps/server/src/integrations/transactional/emails/cloud/trial-ended-email.tsx b/apps/server/src/integrations/transactional/emails/cloud/trial-ended-email.tsx new file mode 100644 index 00000000..f07a54dc --- /dev/null +++ b/apps/server/src/integrations/transactional/emails/cloud/trial-ended-email.tsx @@ -0,0 +1,46 @@ +import { Section, Text, Button } from '@react-email/components'; +import * as React from 'react'; +import { button, content, paragraph } from '@docmost/transactional/css/styles'; +import { MailBody } from '@docmost/transactional/partials/partials'; + +interface Props { + billingLink: string; + workspaceName: string; +} + +export const TrialEndedEmail = ({ billingLink, workspaceName }: Props) => { + return ( + +
+ Hi there, + + Your Docmost 7-day free trial for {workspaceName} has come to an end. + + + To continue using Docmost for your wiki and documentation, please + upgrade to a premium plan. + +
+
+ +
+
+ + PS: If you would like to extend your trial, please reply this email. + +
+
+ ); +}; + +export default TrialEndedEmail;