93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
import {
|
|
buildTransactionalMailJob,
|
|
enqueueJob,
|
|
queueNames,
|
|
resolveRedisConnectionOptions,
|
|
resolveRedisConnectionOptionsFromProcessEnv,
|
|
type RedisConnectionEnv,
|
|
type TypedQueue,
|
|
} from "@toolctrl/queue";
|
|
|
|
export type QueueTransactionalEmailInput = {
|
|
to: string;
|
|
subject?: string;
|
|
body?: string;
|
|
alias?: string;
|
|
tenantId?: string;
|
|
userId?: string;
|
|
data?: Record<string, unknown>;
|
|
/**
|
|
* Explicit BullMQ priority for this mail job. Use `"critical"` for
|
|
* auth-flow mails (password-reset, magic-link, invitation, verification)
|
|
* so they preempt low-priority batch jobs regardless of tenant plan.
|
|
*/
|
|
priorityOverride?: "critical" | "high" | "normal" | "low";
|
|
};
|
|
|
|
export type QueueTransactionalEmailResult =
|
|
| {
|
|
enqueued: true;
|
|
jobId?: string;
|
|
};
|
|
|
|
type QueueTransactionalEmailDependencies = {
|
|
env?: RedisConnectionEnv;
|
|
queue?: TypedQueue<typeof queueNames.email>;
|
|
};
|
|
|
|
export async function queueTransactionalEmail(
|
|
input: QueueTransactionalEmailInput,
|
|
dependencies: QueueTransactionalEmailDependencies = {}
|
|
): Promise<QueueTransactionalEmailResult> {
|
|
let queue = dependencies.queue;
|
|
let shouldCloseQueue = false;
|
|
const connection = queue
|
|
? undefined
|
|
: dependencies.env
|
|
? resolveRedisConnectionOptions({ env: dependencies.env })
|
|
: resolveRedisConnectionOptionsFromProcessEnv();
|
|
|
|
if (!queue && !connection) {
|
|
throw new Error("Redis connection required to enqueue transactional email.");
|
|
}
|
|
|
|
if (!queue && connection) {
|
|
const queueRuntimeModule = "@toolctrl/queue/runtime";
|
|
const { createQueue } = (await import(/* @vite-ignore */ queueRuntimeModule)) as typeof import("@toolctrl/queue/runtime");
|
|
queue = createQueue(queueNames.email, {
|
|
connection,
|
|
});
|
|
shouldCloseQueue = true;
|
|
}
|
|
if (!queue) {
|
|
throw new Error("Email queue was not initialized.");
|
|
}
|
|
|
|
try {
|
|
const descriptor = buildTransactionalMailJob({
|
|
to: [input.to],
|
|
subject: input.subject,
|
|
html: input.body,
|
|
templateData: input.data,
|
|
tenantId: input.tenantId,
|
|
userId: input.userId,
|
|
metadata: {
|
|
alias: input.alias,
|
|
},
|
|
});
|
|
const job = await enqueueJob(queue, descriptor, {
|
|
...(input.tenantId !== undefined ? { tenantId: input.tenantId } : {}),
|
|
...(input.priorityOverride !== undefined ? { priorityOverride: input.priorityOverride } : {}),
|
|
});
|
|
|
|
return {
|
|
enqueued: true,
|
|
jobId: job.id,
|
|
};
|
|
} finally {
|
|
if (shouldCloseQueue) {
|
|
await queue.close();
|
|
}
|
|
}
|
|
}
|