Docs: refresh every README against the current code

Verified each documented command, path, port and env var against what the
code actually does, and corrected the drift.

Phone App. Was still titled Car Control. The navigation description was
also stale: the app moved to a RootShell bottom nav (Garage, Charging,
Settings, and Users for admins), so the Settings gear and admin action
the dashboard bullet described no longer exist. Adds the Charging screen,
noting its public tab is placeholder data and only the Home tab's OCPP
control is real, and rebuilds the lib/ tree, which had lost i18n.dart,
theme.dart, widgets/ and three screens.

Web App. Node 18+ was wrong. The installed Vite is 8.1.2, whose engines
field is ^20.19.0 || >=22.12.0 - Node 18 is EOL and cannot build this.

API Server. The config table gained OCPP_REQUIRE_TLS, OCPP_PUBLIC_URL,
PB_BOOTSTRAP and DRIVERVAULT_SUPERADMIN_*, plus a note that PLUGINS_FILE
and the panel-written .env resolve against the working directory (a
volume, in Docker).

Plugins. Per-tenant credentials sat under "not yet implemented", but
/api/integrations/* has done exactly that for both built-ins for a while.
Narrowed the roadmap item to the genuinely missing generic version.

New Docker/README.md and Docker AIO/README.md: the root README's
component table linked those directories as documentation but neither had
any. The root README now points at them.

All 8 markdown files pass a relative-link check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-21 22:46:21 +02:00
co-authored by Claude Opus 4.8
parent 01d82ebda9
commit a0eb5e4e9d
7 changed files with 244 additions and 21 deletions
+92
View File
@@ -0,0 +1,92 @@
# DriverVault — Docker (multi-container stack)
Three containers — **PocketBase**, **API Server**, **Web App** — on one compose
network. This is the deployment to use unless you specifically want everything
in a single image; for that see [`../Docker AIO`](../Docker%20AIO).
```
Browser ─► Web App BFF (:8090) ──/api/*──► API Server (:8080) ─► PocketBase (:8070)
```
Only the Web App port is meant to be public. The API Server and the PocketBase
admin UI are published for convenience and, in the prod file, bound to
`127.0.0.1` by default.
| File | Use |
|---|---|
| `docker-compose.yml` | **builds from source** in this repo — for development and local testing |
| `docker-compose.prod.yml` | **pulls prebuilt images** from the registry — for deployment |
| `.env.example` / `.env.prod.example` | copy to `.env` for the matching compose file |
| `pocketbase/` | the PocketBase image (official release binary on alpine) |
## Run it
```bash
cd Docker
cp .env.example .env # then edit — PB_ADMIN_* have no safe defaults
docker compose up -d --build
```
Production, from the registry:
```bash
cp .env.prod.example .env # then edit
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
```
Then: web app on `http://host:8090/`, the API Server's superadmin panel on
`http://host:8080/`, PocketBase admin on `http://host:8070/_/`.
## First boot
Both steps are idempotent, so restarts and upgrades are safe:
1. **PocketBase** upserts its superuser from `PB_ADMIN_EMAIL` / `PB_ADMIN_PASSWORD`.
This is the only way to create the first superuser — the REST API cannot
bootstrap it. The API Server then authenticates with the same credentials.
2. **The API Server** creates any missing collections and reconciles existing
ones, then creates the first app `superadmin` from
`DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD` if no such user exists. Set
`PB_BOOTSTRAP=false` to skip once the database is established.
No manual `setup-pocketbase.mjs` step is needed here — the server runs the same
schema reconcile itself.
## Volumes
| Volume | Holds |
|---|---|
| `pb_data` | the PocketBase SQLite database and uploaded files |
| `api_data` | the API Server's `plugins.json`, and the `.env` the panel rewrites when a superadmin retargets the PocketBase connection |
Both default to Docker-managed named volumes. In the prod file, set `PB_DATA` /
`API_DATA` to absolute host paths for bind mounts instead.
> The API Server container runs as an unprivileged user, and a **named** volume
> inherits that ownership from the image. A **bind mount** does not — the host
> directory's ownership wins, so `chown` it to the container's `app` user (or
> `chmod` it writable) before setting `API_DATA` to a host path, otherwise the
> server cannot write `plugins.json`.
## Charger control (OCPP)
Chargers in own/proxy mode dial in to `/ocpp/{serial}` **on the API Server
port**, authenticating with a per-charger control token in an OCPP Basic-auth
header. A plaintext `ws://` would put that token on the wire in the clear, so
`OCPP_REQUIRE_TLS` defaults to `true` and non-TLS connections are rejected.
This stack serves plain HTTP, so to actually use charger control you need to
terminate TLS in a reverse proxy in front of it and set `OCPP_PUBLIC_URL` to the
public `wss://` base (behind a proxy, deriving it from request headers is
unreliable). `OCPP_REQUIRE_TLS=false` is for trusted networks only. You will
also need `API_BIND` set so the proxy can reach the port.
## Notes
- `docker-compose.yml` builds the API Server and Web App from `../API Server`
and `../Web App`, so run it from this directory with the repo checked out.
- The Web App's Vue bundle is built with an empty `VITE_API_BASE`, so the
browser uses same-origin `/api` and the BFF proxies it — no CORS in play.
- `CORS_ALLOW_ORIGINS` therefore only matters if a browser calls the API Server
directly. Native mobile apps are not subject to CORS at all.