Files
filezzy-staging/backend/scripts/run-email-completed-check.ts
2026-02-04 14:16:04 +01:00

99 lines
3.4 KiB
TypeScript

/**
* Diagnose and run job-completion email job.
* Run from backend: npx ts-node scripts/run-email-completed-check.ts
* Or: npm run script -- scripts/run-email-completed-check.ts (if you add a script entry)
*
* Checks:
* 1. Email config (ENABLED, JOB_NOTIFICATION_ENABLED)
* 2. Recent completed jobs (last 24h) and which have email sent
* 3. Runs email-completed job (2h lookback by default) and prints sent/skipped/errors
*/
import { config } from '../src/config';
import { connectDatabase, disconnectDatabase, prisma } from '../src/config/database';
import { initializeMinio } from '../src/config/minio';
import { emailCompletedJob } from '../src/jobs/email-completed.job';
import { JobStatus, EmailType } from '@prisma/client';
const LOOKBACK_24H_MS = 24 * 60 * 60 * 1000;
async function main() {
console.log('\n=== Job completion email diagnostic ===\n');
// 1. Email config
const emailCfg = config.email;
console.log('Email config:');
console.log(' EMAIL_ENABLED:', emailCfg.featureFlags.enabled);
console.log(' EMAIL_JOB_NOTIFICATION_ENABLED:', emailCfg.featureFlags.jobNotificationEnabled);
console.log(' RESEND configured:', !!emailCfg.resend.apiKey);
console.log(' ENABLE_SCHEDULED_CLEANUP:', process.env.ENABLE_SCHEDULED_CLEANUP !== 'false');
console.log('');
await connectDatabase();
// 2. Recent completed jobs (last 24h)
const since24h = new Date(Date.now() - LOOKBACK_24H_MS);
const recentJobs = await prisma.job.findMany({
where: {
status: JobStatus.COMPLETED,
userId: { not: null },
outputFileId: { not: null },
updatedAt: { gte: since24h },
},
select: {
id: true,
userId: true,
outputFileId: true,
updatedAt: true,
tool: { select: { name: true, slug: true } },
},
orderBy: { updatedAt: 'desc' },
take: 20,
});
console.log('Recent completed jobs (last 24h, up to 20):', recentJobs.length);
for (const j of recentJobs) {
const emailSent = await prisma.emailLog.findFirst({
where: {
emailType: EmailType.JOB_COMPLETED,
metadata: { path: ['jobId'], equals: j.id },
},
select: { id: true, sentAt: true },
});
console.log(
' -',
j.id,
'| updated:',
j.updatedAt?.toISOString(),
'| tool:',
j.tool?.name ?? j.tool?.slug,
'| email sent:',
emailSent ? emailSent.sentAt?.toISOString() : 'NO'
);
}
console.log('');
// 3. Initialize Minio (required for presigned URLs in email job)
await initializeMinio();
// 4. Run the email-completed job (uses 2h lookback inside the job)
console.log('Running email-completed job (lookback: 2 hours)...');
const result = await emailCompletedJob();
console.log('Result:', { sent: result.sent, skipped: result.skipped, errors: result.errors });
console.log('');
if (recentJobs.length > 0 && result.sent === 0 && result.errors > 0) {
console.log('Tip: Errors often mean presigned URL failed (storage) or Resend API failed. Check backend logs when scheduler runs.');
}
if (recentJobs.length > 0 && result.sent === 0 && result.skipped === 0 && result.errors === 0) {
console.log('Tip: Job only processes jobs updated in the last 2 hours. Older jobs are ignored. Consider increasing LOOKBACK_MS in email-completed.job.ts if needed.');
}
await disconnectDatabase();
console.log('\nDone.\n');
}
main().catch((err) => {
console.error(err);
process.exit(1);
});