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 { db } from 'officerdb/db';
|
||||||
import { encryptSecret, decryptSecret } from 'officerdb/crypto';
|
import { encryptSecret, decryptSecret } from 'officerdb/crypto';
|
||||||
import { emailAccounts } from './schema';
|
import { emailAccounts } from './schema';
|
||||||
@@ -71,6 +71,24 @@ export async function getAllSyncedAccounts(): Promise<EmailAccount[]> {
|
|||||||
return rows.map(open);
|
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. */
|
/** 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> {
|
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));
|
await db.update(emailAccounts).set({ credentials: seal(credentials) }).where(eq(emailAccounts.id, id));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getUserById } from 'officerdb';
|
import { getUserById } from 'officerdb';
|
||||||
import { getAllSyncedAccounts } from '../db/queries';
|
import { getAllSyncedAccounts, resetStuckSyncingAccounts } from '../db/queries';
|
||||||
import { performResync } from './resync';
|
import { performResync } from './resync';
|
||||||
|
|
||||||
const INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
const INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
||||||
@@ -32,6 +32,9 @@ async function tick() {
|
|||||||
|
|
||||||
export function initEmailCron() {
|
export function initEmailCron() {
|
||||||
if (timer) return;
|
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)`);
|
console.log(`[email-cron] Starting email sync cron (every ${INTERVAL_MS / 60_000} min)`);
|
||||||
timer = setInterval(tick, INTERVAL_MS);
|
timer = setInterval(tick, INTERVAL_MS);
|
||||||
setTimeout(tick, 30_000);
|
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.
|
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
|
version: 1
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user