diff --git a/src/servers/queue/engine.ts b/src/servers/queue/engine.ts index 05a004d1..77ef9ecd 100644 --- a/src/servers/queue/engine.ts +++ b/src/servers/queue/engine.ts @@ -218,6 +218,7 @@ async function runJob(job: Job) { if (final && final.status === 'running') { final.status = 'completed'; final.completedAt = Date.now(); + final.meta = { ...final.meta, ...sharedMeta }; await writeJob(final); console.log(`[queue] Job ${final.id} completed`); if (final.notify !== false) await notifyCompletion(final); diff --git a/src/servers/queue/handlers/gmail-sync.ts b/src/servers/queue/handlers/gmail-sync.ts index b58c8955..21d9ae82 100644 --- a/src/servers/queue/handlers/gmail-sync.ts +++ b/src/servers/queue/handlers/gmail-sync.ts @@ -268,19 +268,29 @@ const gmailSyncHandler: JobHandler = { await readLoop; if (exitCode !== 0) { - console.error(`[gmail-sync] mbsync failed`); const isOverquota = stderrBuf.includes('OVERQUOTA'); const isAuthFail = stderrBuf.includes('AUTHENTICATIONFAILED') || stderrBuf.includes('Invalid credentials'); - if (isOverquota || isAuthFail) { - const emailCount = await countMaildirFiles(maildirPath); - ctx.meta.gmailSyncRecoverable = true; - ctx.meta.gmailSyncEmailCount = emailCount; - ctx.meta.gmailSyncIsAuthFail = isAuthFail; - } - throw new Error(`mbsync exited with code ${exitCode}: ${stderrBuf.slice(-500)}`); - } - console.log('[gmail-sync] mbsync completed successfully'); + if (isOverquota) { + // Gmail throttled us — don't retry, just import what we have + const emailCount = await countMaildirFiles(maildirPath); + console.log(`[gmail-sync] Gmail OVERQUOTA — proceeding to import ${emailCount} downloaded emails`); + ctx.meta.gmailSyncPartial = true; + ctx.meta.gmailSyncEmailCount = emailCount; + // Fall through to import step + } else { + console.error(`[gmail-sync] mbsync failed`); + if (isAuthFail) { + const emailCount = await countMaildirFiles(maildirPath); + ctx.meta.gmailSyncRecoverable = true; + ctx.meta.gmailSyncEmailCount = emailCount; + ctx.meta.gmailSyncIsAuthFail = true; + } + throw new Error(`mbsync exited with code ${exitCode}: ${stderrBuf.slice(-500)}`); + } + } else { + console.log('[gmail-sync] mbsync completed successfully'); + } } finally { // Always clean up config (contains password) await unlink(configPath).catch(() => {}); diff --git a/src/workspaces/emailer/emails/JobCompleted.tsx b/src/workspaces/emailer/emails/JobCompleted.tsx index 57cc8f83..86c318c6 100644 --- a/src/workspaces/emailer/emails/JobCompleted.tsx +++ b/src/workspaces/emailer/emails/JobCompleted.tsx @@ -2,19 +2,39 @@ import Layout from 'emailer/emails/layouts/MainLayout.jsx'; import { Container, Text } from '@react-email/components'; type JobEmailData = { - job: { type: string; completedAt?: number }; + job: { type: string; completedAt?: number; meta?: { gmailSyncPartial?: boolean; gmailSyncEmailCount?: number } }; }; const Email = ({ job }: JobEmailData) => { + const isPartialGmail = job?.type === 'gmail-sync' && job.meta?.gmailSyncPartial; + return ( - Job Completed - - {job?.type === 'gmail-sync' - ? 'Your Google account is now completely synced.' - : `Your ${job?.type ?? 'unknown'} job has finished successfully.`} - + {isPartialGmail ? 'Gmail Sync Paused' : 'Job Completed'} + {job?.type === 'gmail-sync' ? ( + isPartialGmail ? ( + <> + + Gmail throttled the connection after downloading too many emails at once. This is normal for large + mailboxes — your emails have been imported successfully. + + {job.meta?.gmailSyncEmailCount ? ( + + {job.meta.gmailSyncEmailCount.toLocaleString()} emails downloaded so far. + + ) : null} + + Wait a few hours and run the sync again to continue downloading the rest. It will pick up where it left + off. + + + ) : ( + Your Google account is now completely synced. + ) + ) : ( + Your {job?.type ?? 'unknown'} job has finished successfully. + )} );