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)
This commit is contained in:
@@ -13,6 +13,14 @@ export interface ShowNotificationResult {
|
||||
shown: boolean;
|
||||
}
|
||||
|
||||
export interface WsMessageEvent {
|
||||
id: string;
|
||||
type: "open" | "message" | "close" | "error";
|
||||
data?: string;
|
||||
code?: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld("vnc", {
|
||||
isElectron: true,
|
||||
// Routes to Electron's own Notification API in main.ts (ipcMain.handle
|
||||
@@ -24,4 +32,26 @@ contextBridge.exposeInMainWorld("vnc", {
|
||||
options?: ShowNotificationOptions,
|
||||
): Promise<ShowNotificationResult> =>
|
||||
ipcRenderer.invoke("vnc:show-notification", title, options),
|
||||
|
||||
// WebSocket bridge for JMAP-over-WebSocket (RFC 8887). The browser
|
||||
// WebSocket constructor cannot attach Authorization headers, so
|
||||
// connections go through the main process which controls headers.
|
||||
wsConnect: (
|
||||
url: string,
|
||||
authHeader: string,
|
||||
): Promise<string> =>
|
||||
ipcRenderer.invoke("vnc:ws-connect", { url, authHeader }),
|
||||
|
||||
wsSend: (id: string, data: string): Promise<boolean> =>
|
||||
ipcRenderer.invoke("vnc:ws-send", { id, data }),
|
||||
|
||||
wsClose: (id: string): Promise<void> =>
|
||||
ipcRenderer.invoke("vnc:ws-close", { id }),
|
||||
|
||||
onWsMessage: (callback: (event: WsMessageEvent) => void): () => void => {
|
||||
const handler = (_event: Electron.IpcRendererEvent, data: WsMessageEvent) =>
|
||||
callback(data);
|
||||
ipcRenderer.on("vnc:ws-message", handler);
|
||||
return () => { ipcRenderer.removeListener("vnc:ws-message", handler); };
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user