feat(email): send trial ending soon mails

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-03-30 20:58:17 +02:00
parent 0f0bb13107
commit a9c664dcfb
12 changed files with 1143 additions and 123 deletions

View File

@@ -4,6 +4,7 @@ import { Worker } from 'bullmq';
import {
cronQueue,
eventsQueue,
miscQueue,
notificationQueue,
sessionsQueue,
} from '@openpanel/queue';
@@ -13,6 +14,7 @@ import { performance } from 'node:perf_hooks';
import { setTimeout as sleep } from 'node:timers/promises';
import { cronJob } from './jobs/cron';
import { eventsJob } from './jobs/events';
import { miscJob } from './jobs/misc';
import { notificationJob } from './jobs/notification';
import { sessionsJob } from './jobs/sessions';
import { logger } from './utils/logger';
@@ -35,12 +37,14 @@ export async function bootWorkers() {
notificationJob,
workerOptions,
);
const miscWorker = new Worker(miscQueue.name, miscJob, workerOptions);
const workers = [
sessionsWorker,
eventsWorker,
cronWorker,
notificationWorker,
miscWorker,
];
workers.forEach((worker) => {
@@ -105,12 +109,7 @@ export async function bootWorkers() {
try {
const time = performance.now();
await waitForQueueToEmpty(cronQueue);
await Promise.all([
cronWorker.close(),
eventsWorker.close(),
sessionsWorker.close(),
notificationWorker.close(),
]);
await Promise.all(workers.map((worker) => worker.close()));
logger.info('workers closed successfully', {
elapsed: performance.now() - time,
});

View File

@@ -7,6 +7,7 @@ import { createInitialSalts } from '@openpanel/db';
import {
cronQueue,
eventsQueue,
miscQueue,
notificationQueue,
sessionsQueue,
} from '@openpanel/queue';
@@ -36,6 +37,7 @@ async function start() {
new BullMQAdapter(sessionsQueue),
new BullMQAdapter(cronQueue),
new BullMQAdapter(notificationQueue),
new BullMQAdapter(miscQueue),
],
serverAdapter: serverAdapter,
});

View File

@@ -0,0 +1,50 @@
import { db } from '@openpanel/db';
import { sendEmail } from '@openpanel/email';
import type { MiscQueuePayloadTrialEndingSoon } from '@openpanel/queue';
import type { Job } from 'bullmq';
export async function trialEndingSoonJob(
job: Job<MiscQueuePayloadTrialEndingSoon>,
) {
const { organizationId } = job.data.payload;
const organization = await db.organization.findUnique({
where: {
id: organizationId,
},
include: {
createdBy: {
select: {
email: true,
},
},
projects: {
select: {
id: true,
},
},
},
});
if (!organization) {
return;
}
const project = organization.projects[0];
if (!organization.createdBy?.email) {
return;
}
if (!project) {
return;
}
return sendEmail('trial-ending-soon', {
to: organization.createdBy?.email,
data: {
organizationName: organization.name,
url: `https://dashboard.openpanel.dev/${organization.id}/${project.id}/settings/organization?tab=billing`,
},
});
}

View File

@@ -0,0 +1,13 @@
import type { Job } from 'bullmq';
import type { MiscQueuePayloadTrialEndingSoon } from '@openpanel/queue';
import { trialEndingSoonJob } from './misc.trail-ending-soon';
export async function miscJob(job: Job<MiscQueuePayloadTrialEndingSoon>) {
switch (job.data.type) {
case 'trialEndingSoon': {
return await trialEndingSoonJob(job);
}
}
}