diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 8558a4e..2d1d020 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -46,19 +46,46 @@ fn api_url(server: &str, path: &str) -> String { format!("{}/api-proxy{}", server.trim_end_matches('/'), path) } -/// Haengt das Query-Paar `desktop=1` an einen Klon der Adresse an und gibt -/// den Klon zurueck -- das Original bleibt unveraendert. Der Parameter geht -/// NUR in die Navigation, niemals in den gespeicherten Server-Wert -/// (`server_url` im Store bleibt die reine Adresse); daraus liest die -/// Next.js-Middleware (apps/web/src/middleware.ts) das Cookie -/// `tessera_desktop`, an dem der Web-Client den Desktop-Client erkennt. Der -/// Browser-Rueckfall des Tray-Eintrags "Update installieren" -/// (`open_download_page`) bekommt den Parameter bewusst NICHT -- er oeffnet -/// im System-Browser, dort muessen die Download-Links sichtbar bleiben. -fn with_desktop_marker(url: &tauri::Url) -> tauri::Url { +/// Haengt `desktop=1` sowie Version, Commit-Stempel und Betriebssystem an +/// einen Klon der Adresse an und gibt den Klon zurueck -- das Original +/// bleibt unveraendert. Reine Funktion (kein `env!`), darum in den Tests +/// unten mit festen Werten geprueft; `with_desktop_marker` ist die Huelle +/// mit den echten Werten der laufenden App. `commit` wird getrimmt, bleibt +/// aber auch leer als Paar erhalten (`dc=`), damit die Middleware +/// zuverlaessig erkennt, ob alle drei Parameter vorhanden sind. +fn with_client_marker(url: &tauri::Url, version: &str, commit: &str, os: &str) -> tauri::Url { let mut marked = url.clone(); - marked.query_pairs_mut().append_pair("desktop", "1"); marked + .query_pairs_mut() + .append_pair("desktop", "1") + .append_pair("dv", version) + .append_pair("dc", commit.trim()) + .append_pair("dos", os); + marked +} + +/// Huelle um `with_client_marker` mit den Werten der laufenden App. Seit +/// quick-260918-gza wandern Version, Commit-Stempel und Betriebssystem +/// (`dv`, `dc`, `dos`) mit -- die Next.js-Middleware +/// (apps/web/src/middleware.ts) legt daraus das Cookie +/// `tessera_desktop_client` an, aus dem der Fehler-melden-Knopf die +/// Herkunft der Meldung fuellt (Betreff-Kuerzel `[Desktop/Windows]` bzw. +/// `[Desktop/Linux]` statt `[Browser]`, weil WebView2 und WebKitGTK im +/// User-Agent sonst wie ein gewoehnlicher Browser aussehen). `desktop=1` +/// bleibt unveraendert an erster Stelle, damit ein neuer Client gegen eine +/// aeltere Middleware weiterhin als Desktop-App erkannt wird. Die Werte +/// gehen NUR in die Navigation, niemals in den gespeicherten Server-Wert +/// (`server_url` im Store bleibt die reine Adresse). Der Browser-Rueckfall +/// des Tray-Eintrags "Update installieren" (`open_download_page`) bekommt +/// den Marker bewusst NICHT -- er oeffnet im System-Browser, dort muessen +/// die Download-Links sichtbar bleiben. +fn with_desktop_marker(url: &tauri::Url) -> tauri::Url { + with_client_marker( + url, + env!("CARGO_PKG_VERSION"), + env!("APP_COMMIT"), + std::env::consts::OS, + ) } /// Liefert (Menuetext, Benachrichtigungstext) fuer den Update-Hinweis. Der @@ -700,26 +727,62 @@ mod tests { use super::*; #[test] - fn with_desktop_marker_ohne_pfad_haengt_query_an() { + fn with_client_marker_ohne_pfad_haengt_query_an() { let url = tauri::Url::parse("https://tessera.example.com").unwrap(); - let marked = with_desktop_marker(&url); - assert_eq!(marked.as_str(), "https://tessera.example.com/?desktop=1"); + let marked = with_client_marker(&url, "1.2.0", "a6d1a64", "windows"); + assert_eq!( + marked.as_str(), + "https://tessera.example.com/?desktop=1&dv=1.2.0&dc=a6d1a64&dos=windows" + ); } #[test] - fn with_desktop_marker_mit_vorhandenem_query_haengt_an() { + fn with_client_marker_mit_vorhandenem_query_haengt_an() { let url = tauri::Url::parse("https://host/app?x=1").unwrap(); - let marked = with_desktop_marker(&url); - assert_eq!(marked.as_str(), "https://host/app?x=1&desktop=1"); + let marked = with_client_marker(&url, "1.2.0", "a6d1a64", "windows"); + assert_eq!( + marked.as_str(), + "https://host/app?x=1&desktop=1&dv=1.2.0&dc=a6d1a64&dos=windows" + ); } #[test] - fn with_desktop_marker_laesst_original_unveraendert() { + fn with_client_marker_laesst_original_unveraendert() { let url = tauri::Url::parse("https://host/app").unwrap(); - let _ = with_desktop_marker(&url); + let _ = with_client_marker(&url, "1.2.0", "a6d1a64", "windows"); assert_eq!(url.query(), None); } + #[test] + fn with_client_marker_leerer_commit_bleibt_als_leeres_paar_erhalten() { + let url = tauri::Url::parse("https://host/app").unwrap(); + let marked = with_client_marker(&url, "1.2.0", "", "linux"); + assert_eq!( + marked.as_str(), + "https://host/app?desktop=1&dv=1.2.0&dc=&dos=linux" + ); + + let marked_whitespace = with_client_marker(&url, "1.2.0", " ", "linux"); + assert_eq!( + marked_whitespace.as_str(), + "https://host/app?desktop=1&dv=1.2.0&dc=&dos=linux" + ); + } + + #[test] + fn with_desktop_marker_haengt_alle_vier_paare_an() { + let url = tauri::Url::parse("https://host/app").unwrap(); + let marked = with_desktop_marker(&url); + let pairs: Vec<(String, String)> = marked + .query_pairs() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + assert!(pairs.contains(&("desktop".to_string(), "1".to_string()))); + assert!(pairs.contains(&("dv".to_string(), env!("CARGO_PKG_VERSION").to_string()))); + assert!(pairs.contains(&("dos".to_string(), std::env::consts::OS.to_string()))); + assert!(pairs.iter().any(|(k, _)| k == "dc")); + } + #[test] fn update_labels_bei_versionswechsel() { let (menu_text, body) = update_labels(true, "1.2.0", "abc1234");