Files
SRCmail/lib/push-event-bus.ts
T
Bernd Rodler cfdd091d22 feat: Phase 3+4 — security hardening + polish + offline + Electron push
Phase 3 (security):
- P3.1: Feature gate server-side enforcement (403 on disabled features)
- P3.2: Unified auth error interceptor (401→logout)
- P3.3: Store-level state isolation via StoreSnapshot contract
  (added message-list-tabs + task stores to snapshot/restore cycle)
- P3.4: Push event bus extraction — email-store no longer imports
  calendar/contact/filter/file stores directly
- P1.3: Auth localStorage AES-GCM encryption via custom Zustand adapter

Phase 4 (polish):
- P4.1: Offline write queue — pending operations in localStorage,
  auto-retry on reconnect, offline-queue-indicator banner
- P4.2: Identity spoofing — fromOverrideEmail domain validation
- P4.3: WebSocket push for Electron via main-process IPC bridge
  (ws package with Authorization headers)
2026-08-07 22:10:26 +02:00

39 lines
1.2 KiB
TypeScript

import type { IJMAPClient } from '@/lib/jmap/client-interface';
type JmapTypeHandler = (client: IJMAPClient, accountChanges: Record<string, string>) => Promise<void>;
const handlers = new Map<string, JmapTypeHandler[]>();
export function registerPushHandler(jmapType: string, handler: JmapTypeHandler): () => void {
const list = handlers.get(jmapType) ?? [];
list.push(handler);
handlers.set(jmapType, list);
return () => {
const current = handlers.get(jmapType);
if (!current) return;
const idx = current.indexOf(handler);
if (idx >= 0) current.splice(idx, 1);
if (current.length === 0) handlers.delete(jmapType);
};
}
export async function dispatchPushEvent(
client: IJMAPClient,
changed: Record<string, Record<string, string>>,
accountId: string,
): Promise<void> {
const accountChanges = changed[accountId];
for (const [jmapType, typeHandlers] of handlers) {
if (accountChanges?.[jmapType]) {
for (const handler of typeHandlers) {
try {
await handler(client, accountChanges);
} catch (error) {
console.error(`Push handler for ${jmapType} failed:`, error);
}
}
}
}
}