Files
parking_solution/apps/desktop/src-tauri/src/lib.rs
T
julian 5c6a21e2c3
Build & push images / images (push) Successful in 3m19s
Release desktop / bundle (push) Successful in 4m57s
feat(desktop): runtime-configurable backend server address
The desktop shell is one generic .deb/.AppImage distributed via
mca/public_releases, not built per-booth, but the backend origin was baked
in at build time (VITE_API_BASE, hardcoded to http://127.0.0.1:3000) — the
same installer could never point at a different appliance without a
rebuild.

Adds ConnectScreen (shown before Login in Tauri when no backend is saved),
backed by tauri-plugin-store persisting the operator-entered URL across
restarts. CSP's connect-src tightens to 'self' only — all backend traffic
already routes through tauri-plugin-http/websocket, which run Rust-side
and are outside connect-src's reach anyway — and the real access boundary
moves to capabilities/default.json's http:default scope, wildcarded so an
operator-chosen host is actually reachable. Adds a "Change server" control
in Setup (desktop-only) to repoint an already-configured install.

While tracing the desktop auth path for this: tauri-plugin-http's fetch()
runs through Rust's reqwest, which keeps its own cookie jar separate from
the webview, so document.cookie on tauri://localhost never sees the
parking_csrf cookie the server sets (open upstream bug,
tauri-apps/tauri#13045/#11518). This means the desktop app has likely been
silently sending no CSRF header on every mutation since the shell was
first built — pre-existing, independent of this change. Fixed by having
sessionView() (routes/auth.ts) also echo the CSRF value in the login/me
JSON body; the desktop client stashes it in memory and echoes that instead
of reading document.cookie. assertCsrf() itself is untouched.

Verified end-to-end against a real LAN-bound dev server: login returns a
csrfToken matching the cookie, a mutation using the body-sourced token in
X-CSRF-Token succeeds (200), and the same mutation without it still
correctly 403s.
2026-09-04 10:32:03 +02:00

33 lines
1.9 KiB
Rust

// Parking System desktop shell — entry point.
//
// Intentionally minimal: build the default Tauri app and run it. The window
// config (kiosk, fullscreen, which URL/assets to load) lives in tauri.conf.json.
// No custom commands are registered — the renderer (the @parking/web SPA) reaches
// the backend over HTTP to a Fastify server (address operator-configured at
// runtime, not baked in — see apps/web/src/lib/backend-config.ts), NOT through
// Tauri IPC. This keeps the shell a thin presentation wrapper with a
// deny-by-default native surface (see wiki/decisions/desktop-shell-tauri.md).
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
// Auto-update: the JS side (apps/web) checks on launch, prompts the
// operator, and installs + relaunches on confirm. These plugins expose
// the update check/install and the relaunch to that flow. The updater
// endpoint + signing pubkey live in tauri.conf.json.
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_process::init())
// Routes the SPA's fetch()/WS calls to the operator-configured Fastify
// server through Tauri's native HTTP client — see the Cargo.toml
// comment on why the webview's own fetch() can't reach it directly.
.plugin(tauri_plugin_http::init())
// Live-feed WebSocket — same mixed-content reason as the HTTP plugin
// above, but WS needs its own plugin (separate browser check).
.plugin(tauri_plugin_websocket::init())
// Persists the operator-configured backend URL across restarts (JSON
// file in the app's config dir) — see backend-config.ts.
.plugin(tauri_plugin_store::Builder::new().build())
.run(tauri::generate_context!())
.expect("error while running the Parking System desktop shell");
}