2 Commits

Author SHA1 Message Date
julian 4a7029cea6 chore(resources): bump stage TAG to 7317042
Build & push images / images (push) Successful in 2m49s
Promotes park-buzi + park-2 to the just-merged desktop-app fixes (login,
mixed-content routing, WS origin) and the WS_ALLOWED_ORIGINS fix — none of
this was on stage before. Wait for build-images.yml to confirm the image
actually exists before syncing/deploying in Komodo.
2026-09-03 16:04:57 +02:00
julian 7317042e8d fix(desktop): WS live feed offline — native plugin sends no Origin header
Build desktop / desktop (push) Successful in 4m42s
CI / check (push) Successful in 43s
Release desktop / bundle (push) Successful in 4m43s
Build & push images / images (push) Successful in 2m46s
Login worked after the mixed-content fix, but the live feed 403'd silently:
tauri-plugin-websocket's connect() runs on Tauri's Rust side, not inside the
webview page, so it never auto-attaches Origin the way a browser WebSocket
would — routes/ws.ts's anti-CSWSH check rejects a missing Origin before
auth. platform-ws.ts now sets Origin: tauri://localhost explicitly.

Also fixes a second, independent gap the above alone wouldn't have caught:
komodo/resources.toml's booth Stacks had WS_ALLOWED_ORIGINS= empty in
production despite .env.example documenting it as required for desktop.
Needs a Komodo sync + redeploy to reach a live booth.
2026-09-03 15:35:04 +02:00
4 changed files with 46 additions and 6 deletions
+9 -2
View File
@@ -62,7 +62,13 @@ class TauriSocketAdapter implements PlatformSocket {
try {
const { default: TauriWebSocket } = await import("@tauri-apps/plugin-websocket");
if (this.#closed) return; // close() called before connect resolved
const conn = await TauriWebSocket.connect(url);
// Runs on Tauri's native (Rust) side, NOT inside the webview page — there
// is no page context to auto-attach an Origin header the way a real
// browser WebSocket would. The server's anti-CSWSH check (routes/ws.ts)
// rejects any handshake with a missing/mismatched Origin, so it must be
// set explicitly here to match what WS_ALLOWED_ORIGINS expects
// (tauri://localhost — see apps/server/.env.example).
const conn = await TauriWebSocket.connect(url, { headers: { Origin: "tauri://localhost" } });
if (this.#closed) {
void conn.disconnect();
return;
@@ -78,7 +84,8 @@ class TauriSocketAdapter implements PlatformSocket {
// routes/ws.ts) — nothing else is expected.
});
this.onopen?.();
} catch {
} catch (err) {
console.error("Tauri WebSocket connect failed:", url, err);
this.onerror?.();
this.onclose?.();
}
+10 -4
View File
@@ -49,10 +49,13 @@ REGISTRY=git.infra.msai.al/mca/parking_solution
# Staging booth: pinned immutable stage-<sha>. After each promotion (merge dev → stage, CI builds
# :stage-<sha>), bump this to the new sha and re-sync/deploy from Core. The moving `:stage` tag
# exists as the pointer; we deploy the sha, not the mover.
TAG=stage-28bd838
TAG=stage-7317042
COOKIE_SECURE=0
VISION_ENABLED=1
WS_ALLOWED_ORIGINS=
# Desktop app WS handshake: Origin is tauri://localhost (set explicitly by
# platform-ws.ts, since the native WS plugin has no page context to auto-attach
# one). Linux may also send http://tauri.localhost. See routes/ws.ts anti-CSWSH check.
WS_ALLOWED_ORIGINS=tauri://localhost,http://tauri.localhost
JWT_SECRET=[[park_buzi_jwt_secret]]
EVENT_SIGNING_KEY=[[park_buzi_event_signing_key]]
BACKUP_KEY=[[park_buzi_backup_key]]
@@ -79,10 +82,13 @@ REGISTRY=git.infra.msai.al/mca/parking_solution
# Staging booth: pinned immutable stage-<sha>. After each promotion (merge dev → stage, CI builds
# :stage-<sha>), bump this to the new sha and re-sync/deploy from Core. The moving `:stage` tag
# exists as the pointer; we deploy the sha, not the mover.
TAG=stage-28bd838
TAG=stage-7317042
COOKIE_SECURE=0
VISION_ENABLED=1
WS_ALLOWED_ORIGINS=
# Desktop app WS handshake: Origin is tauri://localhost (set explicitly by
# platform-ws.ts, since the native WS plugin has no page context to auto-attach
# one). Linux may also send http://tauri.localhost. See routes/ws.ts anti-CSWSH check.
WS_ALLOWED_ORIGINS=tauri://localhost,http://tauri.localhost
JWT_SECRET=[[park_2_jwt_secret]]
EVENT_SIGNING_KEY=[[park_2_event_signing_key]]
BACKUP_KEY=[[park_2_backup_key]]
+13
View File
@@ -164,6 +164,19 @@ Per the user's choices — the operator **keeps OS access** (no fullscreen lockd
- `logger.ts`'s `flushBeacon()` (page-hide `navigator.sendBeacon`) is a native browser API with no
Tauri equivalent — it still drops silently in the desktop shell on unload. Accepted: the regular
4s-interval flush (now fixed, routes through `platformFetch`) covers the common case.
- **Gotcha (found immediately after shipping the above): the native WS plugin sends no `Origin`
header.** `tauri-plugin-websocket`'s `connect()` runs on Tauri's Rust side, not inside the
webview page — there's no page context to auto-attach `Origin: tauri://localhost` the way a real
browser `WebSocket` would. The server's anti-CSWSH check (`routes/ws.ts`, `isAllowedOrigin`)
treats a missing Origin as untrusted and 403s the handshake before touching auth — the live feed
showed **"JASHTË LINJË"** (offline) in the desktop app while the browser showed **"LIVE"**, same
server, same moment. **Fix (two parts, both needed):** `platform-ws.ts`'s `connect()` call now
passes `{ headers: { Origin: "tauri://localhost" } }` explicitly; separately, `komodo/
resources.toml`'s booth Stacks had `WS_ALLOWED_ORIGINS=` **empty** in production (despite
`.env.example` documenting `tauri://localhost,http://tauri.localhost` as required) — even a
correct Origin header is useless if the server's allowlist doesn't include it. Both fixed
together; a `resources.toml` change still needs a Komodo sync + Stack redeploy to take effect on
a live booth, it isn't automatic from a git push alone.
- **`VITE_API_BASE` — desktop vs. browser (regression found + fixed 2026-09-03):**
`apps/web/.env.production` (committed, shared by both builds) sets `VITE_API_BASE=` (empty) — this
is correct for the **browser/booth** build (Fastify same-origin, stays relative) since commit
+14
View File
@@ -2779,3 +2779,17 @@ HTTP/WS client instead of the webview's own: tauri-plugin-http (a genuine fetch(
into api.ts/logger.ts via a new platformFetch() in origin.ts) and tauri-plugin-websocket (NOT a
drop-in — async/listener API — adapted behind a native-WebSocket-shaped interface in the new
platform-ws.ts so use-live-feed.ts needed no changes). Full detail on [[desktop-shell-tauri]].
## [2026-09-03] fix | Desktop live feed offline: native WS plugin sends no Origin, prod allowlist was empty
Login worked after the mixed-content fix, but the live feed showed offline in the desktop app while
the browser showed LIVE, same server. tauri-plugin-websocket's connect() runs on Tauri's Rust side,
not inside the webview page, so it never auto-attaches an Origin header — routes/ws.ts's anti-CSWSH
check treats a missing Origin as untrusted and 403s before auth. Compounded by a second, independent
gap: komodo/resources.toml's booth Stacks had WS_ALLOWED_ORIGINS= empty in production, despite
.env.example documenting tauri://localhost as required for the desktop app. Fixed both: platform-ws.ts
now passes Origin: tauri://localhost explicitly in connect()'s headers; resources.toml's two Stacks
get the real allowlist. Needs a Komodo sync + redeploy to reach a live booth, not just a git push.
Also confirmed the "update downloads then nothing happens" report was an older pre-fix build (v0.1.2)
self-updating — expected, not a new bug; v0.1.3 carries the error-logging fix from the mixed-content
commit and should surface a real error going forward. Full detail on [[desktop-shell-tauri]].