inline email resync instead of job queue, refresh list on completion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,74 +1,28 @@
|
||||
import type { Job, EnqueueParams } from '../../queue/types';
|
||||
import { getAllSyncedAccounts, getUserById, getUserIntegration } from 'officerdb';
|
||||
import { getAllSyncedAccounts, getUserById } from 'officerdb';
|
||||
import { performResync } from '../../api/email/resync';
|
||||
|
||||
const INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
let enqueueFn: ((params: EnqueueParams) => Promise<Job>) | null = null;
|
||||
let listJobsFn: (() => Promise<Job[]>) | null = null;
|
||||
|
||||
async function tick() {
|
||||
if (!enqueueFn || !listJobsFn) return;
|
||||
|
||||
try {
|
||||
const accounts = await getAllSyncedAccounts();
|
||||
if (accounts.length === 0) return;
|
||||
|
||||
const allJobs = await listJobsFn();
|
||||
const activeEmailSyncIds = new Set(
|
||||
allJobs
|
||||
.filter((j) => j.type === 'email-sync' && (j.status === 'queued' || j.status === 'running'))
|
||||
.map((j) => (j.meta as Record<string, unknown> | undefined)?.emailAccountId),
|
||||
);
|
||||
|
||||
for (const account of accounts) {
|
||||
if (activeEmailSyncIds.has(account.id)) continue;
|
||||
|
||||
const user = await getUserById(account.userId);
|
||||
if (!user) continue;
|
||||
|
||||
// Resolve IMAP auth
|
||||
const imapAuth: Record<string, unknown> = { user: account.email };
|
||||
if (account.authType === 'oauth') {
|
||||
const integration = await getUserIntegration(account.userId, 'google');
|
||||
const config = integration?.config as Record<string, unknown> | undefined;
|
||||
const accessToken = config?.accessToken as string | undefined;
|
||||
if (!accessToken) {
|
||||
console.log(`[email-cron] Skipping ${account.email}: no OAuth access token`);
|
||||
continue;
|
||||
}
|
||||
imapAuth.accessToken = accessToken;
|
||||
} else {
|
||||
const creds = account.credentials as Record<string, unknown>;
|
||||
imapAuth.pass = creds.password;
|
||||
}
|
||||
|
||||
try {
|
||||
await enqueueFn({
|
||||
lane: 'email',
|
||||
type: 'email-sync',
|
||||
userId: user.email,
|
||||
meta: {
|
||||
emailAccountId: account.id,
|
||||
userEmail: user.email,
|
||||
account: {
|
||||
id: account.id,
|
||||
userId: account.userId,
|
||||
email: account.email,
|
||||
imapHost: account.imapHost,
|
||||
imapPort: account.imapPort,
|
||||
imapSecure: account.imapSecure,
|
||||
provider: account.provider,
|
||||
authType: account.authType,
|
||||
credentials: account.credentials,
|
||||
},
|
||||
imapAuth,
|
||||
},
|
||||
});
|
||||
console.log(`[email-cron] Enqueued incremental sync for ${account.email}`);
|
||||
console.log(`[email-cron] Resyncing ${account.email}`);
|
||||
const result = await performResync({ accountId: account.id, userEmail: user.email, userId: user.id });
|
||||
if (result.saved > 0) {
|
||||
console.log(`[email-cron] ${account.email}: ${result.saved} new emails`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[email-cron] Failed to enqueue sync for ${account.email}:`,
|
||||
`[email-cron] Failed to resync ${account.email}:`,
|
||||
err instanceof Error ? err.message : err,
|
||||
);
|
||||
}
|
||||
@@ -78,18 +32,10 @@ async function tick() {
|
||||
}
|
||||
}
|
||||
|
||||
type EmailCronDeps = {
|
||||
enqueue: (params: EnqueueParams) => Promise<Job>;
|
||||
listJobs: () => Promise<Job[]>;
|
||||
};
|
||||
|
||||
export function initEmailCron(deps: EmailCronDeps) {
|
||||
export function initEmailCron() {
|
||||
if (timer) return;
|
||||
enqueueFn = deps.enqueue;
|
||||
listJobsFn = deps.listJobs;
|
||||
console.log(`[email-cron] Starting email sync cron (every ${INTERVAL_MS / 60_000} min)`);
|
||||
timer = setInterval(tick, INTERVAL_MS);
|
||||
// Run first tick after a short delay
|
||||
setTimeout(tick, 30_000);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user