Web App: two sites, one tab — a server you can switch

Two locations means two full stacks, and until now the app could only ever
see the one that served it. The login screen's "Server settings" could point
it elsewhere, but as a global swap: it replaced the server rather than
adding one, and forgot the session you already had.

There is now a server picker at the bottom of the app rail, after signing
in rather than on the login screen. It names the server you are reading —
which matters most when both sites look identical — and switches in a click.

Each server is its own PocketBase with its own users, so nothing is
federated: you sign into each one once and a token is kept per server. The
home server is the one that served the app, reached same-origin through the
BFF proxy; any other is added by address and called straight from the
browser, which asks nothing new of the network — an API Server the phone app
can reach is internet-facing already.

Two things the switch turned out to need:

Views load on mount, so swapping the session alone left B's user looking at
A's cars. The RouterView is keyed on the active server and a switch returns
to the garage, since record ids belong to the server that issued them.

A request now carries the id of the server it went out to, and a 401 clears
that session rather than whatever is active when the answer lands. A page
fires half a dozen calls at once: the first rejection used to switch away
and the rest then cleared the session of the server it had switched to,
turning one stale remote token into a full sign-out.

A remote session expiring falls back to the home server with the entry left
in place to sign into again. Only Log out clears them all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 5 committed 2026-08-21 11:55:50 +02:00
1 parent 00d4c17392
commit f521d2b220
11 files changed
+757 -131

No files matched your search

+22 -5
View File
@@ -78,11 +78,28 @@
"hidePassword": "Skjul adgangskode",
"submit": "Log ind",
"submitting": "Logger ind…",
"failed": "Login mislykkedes",
"serverSettings": "Serverindstillinger",
"apiServerUrl": "API-serverens adresse",
"leaveBlank": "Lad feltet stå tomt for at bruge standarden ({url}).",
"resetToDefault": "Nulstil til standard"
"failed": "Login mislykkedes"
},
"servers": {
"title": "Servere",
"home": "Denne server",
"sameOrigin": "Leveret sammen med appen",
"switchHint": "Skift hvilken server appen læser fra",
"add": "Tilføj server",
"addTitle": "Tilføj en server",
"name": "Navn",
"namePlaceholder": "Hjemmegarage",
"url": "Adresse",
"urlHint": "API-serverens offentlige adresse, f.eks. https://garage.example.com — /api tilføjes automatisk, hvis du udelader stien.",
"urlHomeHint": "Lad feltet stå tomt for at bruge den server, der kører denne app.",
"connect": "Forbind",
"connecting": "Forbinder…",
"connected": "Forbundet",
"notConnected": "Ikke forbundet — log ind for at skifte",
"signOut": "Log ud af denne server",
"removeConfirm": "Fjern {name}? Den gemte session glemmes også.",
"unknown": "Ukendt server"
},
"dashboard": {
+22 -5
View File
@@ -96,11 +96,28 @@
"hidePassword": "Hide password",
"submit": "Sign in",
"submitting": "Signing in…",
"failed": "Login failed",
"serverSettings": "Server settings",
"apiServerUrl": "API server URL",
"leaveBlank": "Leave blank to use the default ({url}).",
"resetToDefault": "Reset to default"
"failed": "Login failed"
},
"servers": {
"title": "Servers",
"home": "This server",
"sameOrigin": "Served with this app",
"switchHint": "Switch which server this app reads from",
"add": "Add server",
"addTitle": "Add a server",
"name": "Name",
"namePlaceholder": "Home garage",
"url": "Address",
"urlHint": "The API Server's public address, e.g. https://garage.example.com — /api is added for you if you leave the path off.",
"urlHomeHint": "Leave blank to use the server that runs this app.",
"connect": "Connect",
"connecting": "Connecting…",
"connected": "Connected",
"notConnected": "Not connected — sign in to switch",
"signOut": "Sign out of this server",
"removeConfirm": "Remove {name}? The session held for it is forgotten too.",
"unknown": "Unknown server"
},
"dashboard": {
+22 -5
View File
@@ -80,11 +80,28 @@
"hidePassword": "Ukryj hasło",
"submit": "Zaloguj się",
"submitting": "Logowanie…",
"failed": "Logowanie nie powiodło się",
"serverSettings": "Ustawienia serwera",
"apiServerUrl": "Adres serwera API",
"leaveBlank": "Pozostaw puste, aby użyć domyślnego ({url}).",
"resetToDefault": "Przywróć domyślny"
"failed": "Logowanie nie powiodło się"
},
"servers": {
"title": "Serwery",
"home": "Ten serwer",
"sameOrigin": "Udostępniany razem z aplikacją",
"switchHint": "Przełącz serwer, z którego korzysta aplikacja",
"add": "Dodaj serwer",
"addTitle": "Dodaj serwer",
"name": "Nazwa",
"namePlaceholder": "Garaż domowy",
"url": "Adres",
"urlHint": "Publiczny adres API Servera, np. https://garage.example.com — /api zostanie dodane, jeśli pominiesz ścieżkę.",
"urlHomeHint": "Zostaw puste, aby użyć serwera, na którym działa ta aplikacja.",
"connect": "Połącz",
"connecting": "Łączenie…",
"connected": "Połączono",
"notConnected": "Brak połączenia — zaloguj się, aby przełączyć",
"signOut": "Wyloguj z tego serwera",
"removeConfirm": "Usunąć {name}? Zapisana sesja również zostanie zapomniana.",
"unknown": "Nieznany serwer"
},
"dashboard": {