diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cec3abe..129d0da7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,6 +137,23 @@ jobs: working-directory: Client/tauri-client/ run: cargo tauri-typegen generate + - name: Fix generated TypeScript bindings (tauri-typegen 0.5.0 workaround) + working-directory: Client/tauri-client/ + # tauri-typegen 0.5.0 cannot map serde_json::Value to a TS type — patch post-generation. + # Duplicate events are avoided at source by using one emit() call site per event name. + run: | + node -e " + const fs = require('fs'); + const tp = fs.readFileSync('src/generated/types.ts', 'utf8'); + if (!tp.includes('export type Value')) { + fs.writeFileSync('src/generated/types.ts', tp.replace( + 'export interface CredentialData', + 'export type Value = unknown;\n\nexport interface CredentialData' + )); + } + console.log('Generated bindings patched.'); + " + - name: Clippy lint (Rust) working-directory: Client/tauri-client/src-tauri/ run: cargo clippy -- -D warnings diff --git a/Client/tauri-client/src-tauri/src/ws_proxy.rs b/Client/tauri-client/src-tauri/src/ws_proxy.rs index 80bb5575..111c9be8 100644 --- a/Client/tauri-client/src-tauri/src/ws_proxy.rs +++ b/Client/tauri-client/src-tauri/src/ws_proxy.rs @@ -179,6 +179,16 @@ fn tofu_check( } } +/// Single call site for ws-state events — keeps tauri-typegen from generating duplicates. +fn emit_ws_state(app: &AppHandle, state: &str) { + let _ = app.emit("ws-state", state); +} + +/// Single call site for cert-tofu events — keeps tauri-typegen from generating duplicates. +fn emit_cert_tofu(app: &AppHandle, payload: serde_json::Value) { + let _ = app.emit("cert-tofu", payload); +} + /// Connect to a WSS server. Spawns a background task that: /// - Emits `ws-message` events for incoming server messages /// - Emits `ws-state` events for connection state changes @@ -207,7 +217,7 @@ pub async fn ws_connect( return Err("Only wss:// connections are permitted".into()); } - let _ = app.emit("ws-state", "connecting"); + emit_ws_state(&app, "connecting"); // Create TOFU verifier that captures the cert fingerprint during handshake. let (verifier, captured_fp) = TofuVerifier::new(); @@ -255,27 +265,21 @@ pub async fn ws_connect( match tofu_check(&app, &host, &fingerprint) { Ok(status) => { info!("[ws_proxy] TOFU check passed for {}: {}", host, status); - let _ = app.emit( - "cert-tofu", - serde_json::json!({ - "host": host, - "fingerprint": fingerprint, - "status": status, - }), - ); + emit_cert_tofu(&app, serde_json::json!({ + "host": host, + "fingerprint": fingerprint, + "status": status, + })); } Err(mismatch_msg) => { warn!("[ws_proxy] TOFU check FAILED for {} — certificate fingerprint mismatch", host); debug!("[ws_proxy] TOFU detail: {}", mismatch_msg); - let _ = app.emit( - "cert-tofu", - serde_json::json!({ - "host": host, - "fingerprint": fingerprint, - "status": "mismatch", - "message": mismatch_msg, - }), - ); + emit_cert_tofu(&app, serde_json::json!({ + "host": host, + "fingerprint": fingerprint, + "status": "mismatch", + "message": mismatch_msg, + })); // Reject the connection — do not proceed. return Err(mismatch_msg); } @@ -283,7 +287,7 @@ pub async fn ws_connect( // ── End TOFU check ─────────────────────────────────────────────────── info!("[ws_proxy] connected to {}", host); - let _ = app.emit("ws-state", "open"); + emit_ws_state(&app, "open"); let (mut sink, mut stream) = ws_stream.split(); @@ -340,7 +344,7 @@ pub async fn ws_connect( } } info!("[ws_proxy] connection closed"); - let _ = app_state.emit("ws-state", "closed"); + emit_ws_state(&app_state, "closed"); }); Ok(())