feat(desktop): wire updater endpoint to self-hosted Gitea + document Tauri WS origin

Point the Tauri updater at the real self-hosted Gitea "latest release" path:
https://git.infra.msai.al/mca/parking_solution/releases/latest/download/latest.json
— redirects to the newest tag's latest.json published by release.yml. Verified
against tauri-plugin-updater: it GETs the endpoint (200 + manifest / 204 = up to
date) and reads platforms.linux-x86_64.{signature,url}.

Document the desktop WS origin: the Tauri window loads from tauri://localhost
(Linux may also send http://tauri.localhost), which is NOT same-origin with the
backend, so WS_ALLOWED_ORIGINS must include both or the live feed won't connect.
Added both to apps/server/.env.example.

Updated the as-built in wiki/decisions/desktop-shell-tauri.md. Also carries an
unrelated plans.namePlaceholder copy tweak already in the tree. turbo build lint
14/14 green.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-21 12:51:11 +02:00
parent ae5c122980
commit 0cbae94842
4 changed files with 15 additions and 6 deletions
+2 -1
View File
@@ -41,8 +41,9 @@
}, },
"plugins": { "plugins": {
"updater": { "updater": {
"//": "Stable 'latest release' path on Gitea — redirects to the newest tag's latest.json (published by .gitea/workflows/release.yml). The updater GETs this, gets the manifest (platforms.linux-x86_64.{signature,url}), and compares versions. The release is reachable to the appliance only when it's brought online (phone hotspot); offline-first means a failed check is a no-op.",
"endpoints": [ "endpoints": [
"https://UPDATES.EXAMPLE.invalid/parking/{{target}}/{{arch}}/{{current_version}}" "https://git.infra.msai.al/mca/parking_solution/releases/latest/download/latest.json"
], ],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDgxNzg5RUQ1QkM0Q0FDRjYKUldUMnJFeTgxWjU0Z1RlNmhneDVZQlVVTVZZdGhJTkUxTGdDeGYwQSttZmNKVVp5WEdVMWlBb1YK" "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDgxNzg5RUQ1QkM0Q0FDRjYKUldUMnJFeTgxWjU0Z1RlNmhneDVZQlVVTVZZdGhJTkUxTGdDeGYwQSttZmNKVVp5WEdVMWlBb1YK"
} }
+4 -1
View File
@@ -28,7 +28,10 @@ EVENT_SIGNING_KEY=
# Comma-separated extra origins allowed to open the booth WebSocket (/api/ws). # Comma-separated extra origins allowed to open the booth WebSocket (/api/ws).
# In dev, set the Vite SPA origin. Same-origin is always allowed without this. # In dev, set the Vite SPA origin. Same-origin is always allowed without this.
WS_ALLOWED_ORIGINS=http://localhost:5173 # The Tauri DESKTOP shell loads from tauri://localhost (Linux may also send
# http://tauri.localhost), which is NOT same-origin with the backend — add both
# so the desktop app's live feed connects. See apps/desktop.
WS_ALLOWED_ORIGINS=http://localhost:5173,tauri://localhost,http://tauri.localhost
# Vision / ANPR (optional) ------------------------------------------------- # Vision / ANPR (optional) -------------------------------------------------
# OFF by default. The Node SERVER's view of the vision microservice (apps/vision), # OFF by default. The Node SERVER's view of the vision microservice (apps/vision),
+1 -1
View File
@@ -496,7 +496,7 @@ export const sq = {
newVersionHint: "Kjo publikon një version TË RI të planit — shitjet ekzistuese ruajnë çmimin origjinal.", newVersionHint: "Kjo publikon një version TË RI të planit — shitjet ekzistuese ruajnë çmimin origjinal.",
period: "Periudha", period: "Periudha",
pricePer: "Çmimi për periudhë", pricePer: "Çmimi për periudhë",
namePlaceholder: "p.sh. Hotel ditor", namePlaceholder: "p.sh. Mujor standard, ose Mujor natën, ose Hotel ditor",
needName: "Emri i planit është i detyrueshëm.", needName: "Emri i planit është i detyrueshëm.",
needPrice: "Shkruaj një çmim më të madh se zero.", needPrice: "Shkruaj një çmim më të madh se zero.",
saved: "Plani u ruajt.", saved: "Plani u ruajt.",
+8 -3
View File
@@ -149,9 +149,14 @@ Per the user's choices — the operator **keeps OS access** (no fullscreen lockd
offline), prompts the operator (i18n `update.prompt`), then `downloadAndInstall()` + `relaunch()`. offline), prompts the operator (i18n `update.prompt`), then `downloadAndInstall()` + `relaunch()`.
Accepts that the appliance may be **offline** day-to-day and brought online (phone hotspot) only Accepts that the appliance may be **offline** day-to-day and brought online (phone hotspot) only
when an update is wanted — consistent with [[offline-first]] (no network dependency in *core* when an update is wanted — consistent with [[offline-first]] (no network dependency in *core*
operation; updates are out-of-band). Endpoint in `tauri.conf.json` is a **placeholder** operation; updates are out-of-band). Endpoint is the **self-hosted Gitea** "latest release"
(`https://UPDATES.EXAMPLE.invalid/...`) to fill in once the self-hosted update URL exists; the path — `https://git.infra.msai.al/mca/parking_solution/releases/latest/download/latest.json`
server must serve `latest.json` + the signed installer + its `.sig`. — which redirects to the newest tag's `latest.json` (published by `.gitea/workflows/release.yml`).
The updater GETs it (200 + manifest, or 204 = up-to-date), reads `platforms.linux-x86_64.
{signature,url}`, and downloads the signed installer. **WS origin:** the desktop window's origin
is `tauri://localhost` (Linux may also send `http://tauri.localhost`), so the backend's
`WS_ALLOWED_ORIGINS` must include both or the live feed won't connect (documented in
`apps/server/.env.example`).
- **Code-signing (updater):** an Ed25519 **updater keypair** was generated. The **public key is - **Code-signing (updater):** an Ed25519 **updater keypair** was generated. The **public key is
embedded** in `tauri.conf.json` (`plugins.updater.pubkey`); the **private key + password live embedded** in `tauri.conf.json` (`plugins.updater.pubkey`); the **private key + password live
OUTSIDE the repo** at `~/.parking-updater-keys/` (0600) and as the build-time secrets OUTSIDE the repo** at `~/.parking-updater-keys/` (0600) and as the build-time secrets