Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
211e913501 | ||
|
|
fda9beb80a |
+19
-1
@@ -1,4 +1,4 @@
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { eq, and, inArray } from 'drizzle-orm';
|
||||
import { db } from 'officerdb/db';
|
||||
import { encryptSecret, decryptSecret } from 'officerdb/crypto';
|
||||
import { emailAccounts } from './schema';
|
||||
@@ -71,6 +71,24 @@ export async function getAllSyncedAccounts(): Promise<EmailAccount[]> {
|
||||
return rows.map(open);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a `syncing` or `queued` status left behind by a process that died mid-sync.
|
||||
*
|
||||
* `getAllSyncedAccounts` only picks up `status = 'synced'`, so an account stranded at either — the
|
||||
* resync route sets `syncing` before awaiting and only clears it after; a first sync sets `queued`
|
||||
* before handing off to the in-process job runner — silently drops out of both the cron and the IDLE
|
||||
* watcher forever. A fresh process can't have either genuinely in flight (the job runner is in-memory,
|
||||
* not persisted), so on startup that state only ever means "orphaned by the last restart." `performResync`
|
||||
* — what the cron and IDLE watcher call — already handles an account with no prior sync state by doing a
|
||||
* full fetch, so resetting to `synced` rather than replaying the dedicated first-sync job is safe.
|
||||
*/
|
||||
export async function resetStuckSyncingAccounts(): Promise<void> {
|
||||
await db
|
||||
.update(emailAccounts)
|
||||
.set({ status: 'synced' })
|
||||
.where(inArray(emailAccounts.status, ['syncing', 'queued']));
|
||||
}
|
||||
|
||||
/** Replace an account's stored secret — used when an OAuth token is refreshed. */
|
||||
export async function updateEmailAccountCredentials(id: number, credentials: Record<string, unknown>): Promise<void> {
|
||||
await db.update(emailAccounts).set({ credentials: seal(credentials) }).where(eq(emailAccounts.id, id));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUserById } from 'officerdb';
|
||||
import { getAllSyncedAccounts } from '../db/queries';
|
||||
import { getAllSyncedAccounts, resetStuckSyncingAccounts } from '../db/queries';
|
||||
import { performResync } from './resync';
|
||||
|
||||
const INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
||||
@@ -32,6 +32,9 @@ async function tick() {
|
||||
|
||||
export function initEmailCron() {
|
||||
if (timer) return;
|
||||
resetStuckSyncingAccounts().catch((err) => {
|
||||
console.error('[email-cron] Failed to reset stuck accounts:', err instanceof Error ? err.message : err);
|
||||
});
|
||||
console.log(`[email-cron] Starting email sync cron (every ${INTERVAL_MS / 60_000} min)`);
|
||||
timer = setInterval(tick, INTERVAL_MS);
|
||||
setTimeout(tick, 30_000);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: Email database
|
||||
name: email-db
|
||||
title: Email database
|
||||
description: Use when querying the user's synced email — searching messages, senders, domains, attachments or counts. Carries the SQLite schema and the queries worth knowing, so you do not have to rediscover them.
|
||||
version: 1
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user