feat(desktop): Version, Stand und Betriebssystem im Desktop-Marker mitgeben (dv, dc, dos)

with_desktop_marker haengte bisher nur desktop=1 an die erste Navigation;
der Fehler-melden-Knopf konnte deshalb Windows- und Linux-Client nicht
unterscheiden. with_client_marker ist die neue reine Kernfunktion (dv,
dc, dos zusaetzlich zu desktop=1), with_desktop_marker bleibt als Huelle
mit den echten env!-Werten die unveraenderte Aufrufstelle an allen drei
Navigationen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh
This commit is contained in:
2026-09-18 12:36:10 +02:00
parent 716947228e
commit b03cb211b3
+82 -19
View File
@@ -46,19 +46,46 @@ fn api_url(server: &str, path: &str) -> String {
format!("{}/api-proxy{}", server.trim_end_matches('/'), path) format!("{}/api-proxy{}", server.trim_end_matches('/'), path)
} }
/// Haengt das Query-Paar `desktop=1` an einen Klon der Adresse an und gibt /// Haengt `desktop=1` sowie Version, Commit-Stempel und Betriebssystem an
/// den Klon zurueck -- das Original bleibt unveraendert. Der Parameter geht /// einen Klon der Adresse an und gibt den Klon zurueck -- das Original
/// NUR in die Navigation, niemals in den gespeicherten Server-Wert /// bleibt unveraendert. Reine Funktion (kein `env!`), darum in den Tests
/// (`server_url` im Store bleibt die reine Adresse); daraus liest die /// unten mit festen Werten geprueft; `with_desktop_marker` ist die Huelle
/// Next.js-Middleware (apps/web/src/middleware.ts) das Cookie /// mit den echten Werten der laufenden App. `commit` wird getrimmt, bleibt
/// `tessera_desktop`, an dem der Web-Client den Desktop-Client erkennt. Der /// aber auch leer als Paar erhalten (`dc=`), damit die Middleware
/// Browser-Rueckfall des Tray-Eintrags "Update installieren" /// zuverlaessig erkennt, ob alle drei Parameter vorhanden sind.
/// (`open_download_page`) bekommt den Parameter bewusst NICHT -- er oeffnet fn with_client_marker(url: &tauri::Url, version: &str, commit: &str, os: &str) -> tauri::Url {
/// im System-Browser, dort muessen die Download-Links sichtbar bleiben.
fn with_desktop_marker(url: &tauri::Url) -> tauri::Url {
let mut marked = url.clone(); let mut marked = url.clone();
marked.query_pairs_mut().append_pair("desktop", "1");
marked 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 /// Liefert (Menuetext, Benachrichtigungstext) fuer den Update-Hinweis. Der
@@ -700,26 +727,62 @@ mod tests {
use super::*; use super::*;
#[test] #[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 url = tauri::Url::parse("https://tessera.example.com").unwrap();
let marked = with_desktop_marker(&url); let marked = with_client_marker(&url, "1.2.0", "a6d1a64", "windows");
assert_eq!(marked.as_str(), "https://tessera.example.com/?desktop=1"); assert_eq!(
marked.as_str(),
"https://tessera.example.com/?desktop=1&dv=1.2.0&dc=a6d1a64&dos=windows"
);
} }
#[test] #[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 url = tauri::Url::parse("https://host/app?x=1").unwrap();
let marked = with_desktop_marker(&url); let marked = with_client_marker(&url, "1.2.0", "a6d1a64", "windows");
assert_eq!(marked.as_str(), "https://host/app?x=1&desktop=1"); assert_eq!(
marked.as_str(),
"https://host/app?x=1&desktop=1&dv=1.2.0&dc=a6d1a64&dos=windows"
);
} }
#[test] #[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 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); 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] #[test]
fn update_labels_bei_versionswechsel() { fn update_labels_bei_versionswechsel() {
let (menu_text, body) = update_labels(true, "1.2.0", "abc1234"); let (menu_text, body) = update_labels(true, "1.2.0", "abc1234");