email: push new-mail to /email via SSE (IMAP IDLE -> sidecar -> server -> EventSource)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ const RECONCILE_MS = 5 * 60_000;
|
||||
|
||||
const watchers = new Map<number, Watcher>();
|
||||
let reconcileTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let notifyNewMail: ((userEmail: string) => void) | null = null;
|
||||
|
||||
async function resolveAuth(account: Account): Promise<{ user: string; pass?: string; accessToken?: string } | null> {
|
||||
if (account.authType === 'oauth') {
|
||||
@@ -46,7 +47,10 @@ async function runSync(w: Watcher): Promise<void> {
|
||||
const user = await getUserById(w.account.userId);
|
||||
if (!user) return;
|
||||
const result = await performResync({ accountId: w.account.id, userEmail: user.email, userId: user.id });
|
||||
if (result.saved > 0) console.log(`[email-idle] ${w.account.email}: ${result.saved} new`);
|
||||
if (result.saved > 0) {
|
||||
console.log(`[email-idle] ${w.account.email}: ${result.saved} new`);
|
||||
notifyNewMail?.(user.email);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[email-idle] sync failed for ${w.account.email}:`, err instanceof Error ? err.message : err);
|
||||
} finally {
|
||||
@@ -141,8 +145,9 @@ async function reconcile(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export function initEmailIdle(): void {
|
||||
export function initEmailIdle(onNewMail?: (userEmail: string) => void): void {
|
||||
if (reconcileTimer) return;
|
||||
notifyNewMail = onNewMail ?? null;
|
||||
console.log('[email-idle] starting IMAP IDLE watchers');
|
||||
setTimeout(() => void reconcile(), 5_000);
|
||||
reconcileTimer = setInterval(() => void reconcile(), RECONCILE_MS);
|
||||
|
||||
@@ -85,8 +85,9 @@ const connection = createSidecarConnector({
|
||||
onConnected() {
|
||||
// Start email cron once connected (so queue commands can reach API server)
|
||||
initEmailCron();
|
||||
// Real-time push via IMAP IDLE; the cron above is the slow backstop.
|
||||
initEmailIdle();
|
||||
// Real-time push via IMAP IDLE; the cron above is the slow backstop. On new mail, tell the API
|
||||
// server so it can push an SSE event to that user's open /email page.
|
||||
initEmailIdle((userEmail) => connection.send({ type: 'email:new', userEmail }));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ export type SidecarEvent =
|
||||
| { type: 'vnc:stopped'; id: string }
|
||||
| { type: 'vnc:status'; id: string; session: VncSessionInfo | null }
|
||||
| { type: 'vnc:error'; id: string; error: string }
|
||||
// Email
|
||||
| { type: 'email:new'; userEmail: string }
|
||||
// Generic
|
||||
| { type: 'error'; id?: string; error: string };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user