99 lines
3.4 KiB
TypeScript
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);
|
|
});
|