@@ -66,6 +66,10 @@ export const env = createEnv({
|
|||||||
SMTP_HOST: z.string().default("smtp.usesend.com"),
|
SMTP_HOST: z.string().default("smtp.usesend.com"),
|
||||||
SMTP_USER: z.string().default("usesend"),
|
SMTP_USER: z.string().default("usesend"),
|
||||||
CONTACT_BOOK_ID: z.string().optional(),
|
CONTACT_BOOK_ID: z.string().optional(),
|
||||||
|
EMAIL_CLEANUP_DAYS: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((str) => (str ? parseInt(str, 10) : undefined)),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,6 +126,7 @@ export const env = createEnv({
|
|||||||
SMTP_HOST: process.env.SMTP_HOST,
|
SMTP_HOST: process.env.SMTP_HOST,
|
||||||
SMTP_USER: process.env.SMTP_USER,
|
SMTP_USER: process.env.SMTP_USER,
|
||||||
CONTACT_BOOK_ID: process.env.CONTACT_BOOK_ID,
|
CONTACT_BOOK_ID: process.env.CONTACT_BOOK_ID,
|
||||||
|
EMAIL_CLEANUP_DAYS: process.env.EMAIL_CLEANUP_DAYS,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { env } from "./env";
|
import { env } from "./env";
|
||||||
import { isCloud } from "./utils/common";
|
import { isCloud , isEmailCleanupEnabled } from "./utils/common";
|
||||||
|
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
|
||||||
@@ -25,6 +25,10 @@ export async function register() {
|
|||||||
await import("~/server/jobs/usage-job");
|
await import("~/server/jobs/usage-job");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isEmailCleanupEnabled()) {
|
||||||
|
await import("~/server/jobs/cleanup-email-bodies");
|
||||||
|
}
|
||||||
|
|
||||||
const { CampaignSchedulerService } = await import(
|
const { CampaignSchedulerService } = await import(
|
||||||
"~/server/jobs/campaign-scheduler-job"
|
"~/server/jobs/campaign-scheduler-job"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import {Queue, Worker} from "bullmq";
|
||||||
|
import {db} from "~/server/db";
|
||||||
|
import {getRedis} from "~/server/redis";
|
||||||
|
import {logger} from "../logger/log";
|
||||||
|
import {DEFAULT_QUEUE_OPTIONS} from "../queue/queue-constants";
|
||||||
|
import {env} from "~/env";
|
||||||
|
import {isSelfHosted, isEmailCleanupEnabled} from "~/utils/common";
|
||||||
|
|
||||||
|
const CLEANUP_QUEUE_NAME = "cleanup-email-bodies";
|
||||||
|
|
||||||
|
const CLEANUP_CRON = "0 0 * * *"; // default: midnight UTC
|
||||||
|
|
||||||
|
// Only initialize if self hosted and cleanup enabled
|
||||||
|
if (isSelfHosted() && isEmailCleanupEnabled()) {
|
||||||
|
const CLEANUP_DAYS = env.EMAIL_CLEANUP_DAYS!;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Queue
|
||||||
|
*/
|
||||||
|
const cleanupQueue = new Queue(CLEANUP_QUEUE_NAME, {
|
||||||
|
connection: getRedis(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const worker = new Worker(
|
||||||
|
CLEANUP_QUEUE_NAME,
|
||||||
|
async () => {
|
||||||
|
logger.info(`[Cleanup] Starting cleanup for emails older than ${CLEANUP_DAYS} days...`);
|
||||||
|
|
||||||
|
const cutoffDate = new Date();
|
||||||
|
cutoffDate.setDate(cutoffDate.getDate() - CLEANUP_DAYS);
|
||||||
|
|
||||||
|
const result = await db.email.updateMany({
|
||||||
|
where: {
|
||||||
|
createdAt: {lt: cutoffDate},
|
||||||
|
OR: [
|
||||||
|
{text: {not: null}},
|
||||||
|
{html: {not: null}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
text: null,
|
||||||
|
html: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`[Cleanup] Emails cleaned: ${result.count}`);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
connection: getRedis(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await cleanupQueue.upsertJobScheduler(
|
||||||
|
"scheduled-email-cleanup",
|
||||||
|
{
|
||||||
|
pattern: CLEANUP_CRON,
|
||||||
|
tz: "UTC",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opts: {
|
||||||
|
...DEFAULT_QUEUE_OPTIONS,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
worker.on("completed", (job) => {
|
||||||
|
logger.info({jobId: job.id}, ` Email Body cleanup job completed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on("failed", (job, err) => {
|
||||||
|
logger.error({err, jobId: job?.id}, `Email Body cleanup job failed`);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -7,3 +7,11 @@ export function isCloud() {
|
|||||||
export function isSelfHosted() {
|
export function isSelfHosted() {
|
||||||
return !isCloud();
|
return !isCloud();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isEmailCleanupEnabled() {
|
||||||
|
const days = env.EMAIL_CLEANUP_DAYS;
|
||||||
|
if (days === undefined || isNaN(days) || days <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user