mirror of
https://github.com/documenso/documenso.git
synced 2026-07-14 06:47:10 +10:00
92 lines
2.8 KiB
TypeScript
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`,
|
|
);
|
|
};
|