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:
1 parent
00d4c17392
commit
f521d2b220
11 files changed
+757
-131
No files matched your search
+43
-7
@@ -7,7 +7,9 @@ and all data access still flows through the API Server (never PocketBase directl
|
||||
|
||||
```
|
||||
Browser ─► Web App BFF (:8090) ──/api/*──► API Server (:8080) ─► PocketBase
|
||||
└── serves embedded Vue SPA
|
||||
│ └── serves embedded Vue SPA
|
||||
└──────────────────────────────────────► API Server at another site ─► its PocketBase
|
||||
(added in the app, called straight from the browser)
|
||||
```
|
||||
|
||||
## Layout
|
||||
@@ -23,7 +25,8 @@ web/ Vue 3 + Vite + Tailwind v4 source
|
||||
main.js app bootstrap
|
||||
router.js /login, / (dashboard), /charging, /cars/:id, /settings
|
||||
api.js the only place that calls the API Server (base URL resolution)
|
||||
auth.js token/profile state, isAdmin
|
||||
servers.js the server list + a session per server; which one is active
|
||||
auth.js token/profile state for the active server, isAdmin
|
||||
prefs.js theme/locale/date/font preferences -> <html>
|
||||
i18n/ en / pl / da translation files + loader
|
||||
lib/format.js date/km formatting + next-service status badges
|
||||
@@ -34,7 +37,8 @@ web/ Vue 3 + Vite + Tailwind v4 source
|
||||
TechnicalCheckFormModal, MaintenanceFormModal, FuelFormModal,
|
||||
ChargingFormModal,
|
||||
DocumentFormModal, ReminderFormModal, PartFormModal, ShareModal,
|
||||
OrgManager, AdminUsers, Logo
|
||||
OrgManager, AdminUsers, Logo, ServerSwitcher,
|
||||
ServerConnectModal
|
||||
views/ Login, Dashboard, CarDetail, Charging, Settings
|
||||
```
|
||||
|
||||
@@ -62,9 +66,9 @@ override with `VITE_API_TARGET`), so the client uses same-origin relative URLs a
|
||||
avoids CORS. It also listens on all interfaces (`host: true`) so it's reachable on
|
||||
the LAN (e.g. `http://10.2.1.101:5173`).
|
||||
|
||||
At runtime, users can override the API base URL from the login screen's **Server
|
||||
settings** (persisted in `localStorage` as `cc_server_url`); resolution order is
|
||||
that override → `VITE_API_BASE` → `/api`.
|
||||
The base URL each call goes to comes from the active server (see **More than one
|
||||
server** below); with only the built-in one, that resolves to `VITE_API_BASE` →
|
||||
`/api`.
|
||||
|
||||
## Build & run (production-style)
|
||||
|
||||
@@ -168,11 +172,43 @@ Config (`server/.env`, copy from `.env.example`):
|
||||
- **Theming** — light/dark/system app-wide (Tailwind v4 class strategy); `prefs.js`
|
||||
toggles `.dark` on `<html>` and applies the saved theme/locale/date/font.
|
||||
|
||||
## More than one server
|
||||
|
||||
Two sites, two full DriverVault stacks — and one browser tab. The server picker
|
||||
sits at the bottom of the app rail (after signing in, not on the login screen):
|
||||
it names the server you are reading right now, and switches between them in a
|
||||
click.
|
||||
|
||||
- **The home server** is the one that served the app, reached same-origin through
|
||||
the BFF's `/api` proxy. It is always in the list and can't be removed.
|
||||
- **Any other server** is added by address — `https://garage.example.com`; the
|
||||
`/api` is appended for you if you leave the path off — and is called **straight
|
||||
from the browser**, not relayed through the BFF. That server therefore has to be
|
||||
reachable from wherever the browser is, which it already is if the phone app
|
||||
talks to it.
|
||||
- **A session per server.** Each server is its own PocketBase with its own users,
|
||||
so a token can't be carried across: you sign into each one once, and after that
|
||||
switching needs no password. Sessions live in `localStorage` under
|
||||
`cc_session_<id>`, the list under `cc_servers`, the active one under
|
||||
`cc_active_server`.
|
||||
- **Switching goes back to the garage**, because record ids belong to the server
|
||||
that issued them — a car page can't survive the change.
|
||||
- **An expiring remote session doesn't sign you out of the app**: that server's
|
||||
token is dropped, the app falls back to the home server, and the entry stays in
|
||||
the list to sign into again. Only *Log out* clears every server at once.
|
||||
|
||||
The remote server must allow the Web App's origin in **`CORS_ALLOW_ORIGINS`**
|
||||
(API Server setting, `*` by default — so this works out of the box, and only
|
||||
needs attention on a server whose list has been narrowed). Nothing needs to be
|
||||
configured on the server you are browsing *from*.
|
||||
|
||||
## Auth & access
|
||||
|
||||
Login proxies to the API Server, which relays PocketBase's own token — there is
|
||||
no JWT the server mints and no server-side session list. The token is stored
|
||||
client-side and sent as `Authorization` on every call. `auth.js` exposes
|
||||
client-side, per server, and sent as `Authorization` on every call; each request
|
||||
is pinned to the server that was active when it went out, so one server's `401`
|
||||
can never drop another's session. `auth.js` exposes
|
||||
`isAdmin` and the current profile; the router guards `public` / `admin` routes.
|
||||
Cars are per-user (owned + shared), and the UI mirrors the server's read / write
|
||||
/ owner access levels.
|
||||
Reference in new issue
Block a user