Files
documenso/packages/lib/jobs/definitions/internal/sync-email-domains.handler.ts
T
2026-05-08 16:04:22 +10:00

92 lines
2.8 KiB
TypeScript

import { reregisterEmailDomain } from '@documenso/ee/server-only/lib/reregister-email-domain';
import { verifyEmailDomain } from '@documenso/ee/server-only/lib/verify-email-domain';
import { prisma } from '@documenso/prisma';
import { DateTime } from 'luxon';
import type { JobRunIO } from '../../client/_internal/job';
import type { TSyncEmailDomainsJobDefinition } from './sync-email-domains';
const BATCH_SIZE = 10;
const AUTO_REREGISTER_AFTER_HOURS = 48;
export const run = async ({ io }: { payload: TSyncEmailDomainsJobDefinition; io: JobRunIO }) => {
const pendingDomains = await prisma.emailDomain.findMany({
where: {
status: 'PENDING',
},
select: {
id: true,
domain: true,
createdAt: true,
lastVerifiedAt: true,
},
orderBy: {
lastVerifiedAt: { sort: 'asc', nulls: 'first' },
},
});
if (pendingDomains.length === 0) {
io.logger.info('No pending email domains to sync');
return;
}
io.logger.info(`Found ${pendingDomains.length} pending email domains to sync`);
let verifiedCount = 0;
let reregisteredCount = 0;
let errorCount = 0;
const reregisterCutoff = DateTime.now().minus({ hours: AUTO_REREGISTER_AFTER_HOURS }).toJSDate();
for (let i = 0; i < pendingDomains.length; i += BATCH_SIZE) {
const batch = pendingDomains.slice(i, i + BATCH_SIZE);
const results = await Promise.allSettled(
batch.map(async (domain) => {
const shouldReregister = domain.createdAt < reregisterCutoff;
const { isVerified } = await verifyEmailDomain(domain.id);
if (isVerified) {
io.logger.info(`Domain "${domain.domain}" is verified`);
return 'verified' as const;
}
if (shouldReregister) {
io.logger.info(
`Domain "${domain.domain}" has been pending since ${domain.createdAt.toISOString()}, attempting re-registration`,
);
await reregisterEmailDomain({ emailDomainId: domain.id });
return 'reregistered' as const;
}
return 'pending' as const;
}),
);
for (const result of results) {
if (result.status === 'rejected') {
errorCount++;
io.logger.error(`Failed to process email domain: ${String(result.reason)}`);
} else if (result.value === 'verified') {
verifiedCount++;
} else if (result.value === 'reregistered') {
reregisteredCount++;
}
}
// Small delay between batches to respect SES API rate limits.
if (i + BATCH_SIZE < pendingDomains.length) {
await new Promise<void>((resolve) => {
setTimeout(resolve, 1000);
});
}
}
io.logger.info(
`Sync complete: ${verifiedCount} verified, ${reregisteredCount} re-registered, ${errorCount} errors out of ${pendingDomains.length} pending domains`,
);
};