Refresh docs and fix Docker builds for current layout

READMEs: correct the auth model (PocketBase token relay, not JWT/sessions),
document the full feature set (technical checks, fuel, maintenance, documents,
reminders, attachments, integrations, OCPP charging control), the shipping
built-in connectors (toyota, anker-solix), and the current endpoint surface.

Docker: build against the current repo layout — Go 1.26, cmd/server entry
point, Web App source under web/. Add the missing Web App Dockerfile (Go BFF)
and .dockerignore, drop the obsolete AUTH_SECRET, modernise CORS var naming,
and standardise on drivervault-* naming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-19 11:05:46 +02:00
co-authored by Claude Opus 4.8
parent f9b1bcc520
commit 1e76c2b7f9
15 changed files with 301 additions and 138 deletions
+66 -22
View File
@@ -23,12 +23,17 @@ internal/
│ ├── status.go # upstream health probes
│ ├── health.go respond.go panel.go
│ ├── cars.go records.go services.go parts.go shares.go me.go
── dist/ # built panel, embedded via go:embed
── technical.go fuel.go maintenance.go documents.go reminders.go
│ ├── attachments.go # one optional file per record (shared handlers)
│ ├── integrations*.go # per-user Toyota / Anker Solix settings + OCPP control
│ └── dist/ # built panel, embedded via go:embed
├── config/config.go # env + .env load, .env write-back
├── models/models.go # domain types + derived-field computation
├── ocpp/ # OCPP 1.6J Central System (Anker Solix charging control)
├── pb/client.go # PocketBase superuser client (runtime-retargetable)
└── plugins/ # plugin system — see plugins/README.md
├── plugin.go manager.go external.go doc.go
└── builtin/ # built-in connectors (none yet)
└── builtin/ # built-in connectors: toyota, ankersolix
panel/ # Vue 3 + Tailwind panel source
scripts/ # Node/Python maintenance scripts
bin/api-server.exe # prebuilt binary the deployment runs
@@ -85,14 +90,23 @@ other users `read` or `write` access. Every car/service/part handler is gated by
| Collection | Purpose | Key fields |
|---|---|---|
| `cars` | one per car | name, make, model, year, registration, vin, `currentKm`, `serviceIntervalDays` (365), `serviceIntervalKm` (15000), `technicalCheckIntervalDays` (365), `oilSpec`, `transmissionOilSpec`, `differentialOilSpec`, `brakeFluidSpec`, `coolantSpec`, `owner` |
| `cars` | one per car | name, make, model, year, registration, `registrationCountry`, vin, `fuelType`, `buildDate`, `firstRegistrationDate`, `currentKm`, `serviceIntervalDays` (365), `serviceIntervalKm` (15000), `technicalCheckIntervalDays` (365), `oilSpec`, `transmissionOilSpec`, `differentialOilSpec`, `brakeFluidSpec`, `coolantSpec`, `owner` |
| `service_records` | the service log | car, date, km, changed_oil, changed_engine_air_filter, changed_cabin_air_filter, notes |
| `technical_checks` | roadworthiness inspections (przegląd techniczny / MOT / TÜV) | car, date, `result` (passed \| failed), cost, station, `valid_until`, notes |
| `parts` | per-car parts catalog | car, name, part_number, category |
| `parts` | per-car parts catalog | car, name, part_number, category, notes |
| `fuel_entries` | refuelling log (efficiency derived on read) | car, date, km, liters, cost, `full_tank`, `missed_fill`, station, notes |
| `maintenance_entries` | workshop visits & repairs (outside routine service) | car, date, km, type, status, workshop, parts_used, labor_cost, parts_cost, invoice_number, warranty_until, notes |
| `car_documents` | paperwork (insurance, registration, road tax, …) | car, type, title, provider, reference, issue_date, expiry_date, cost, notes |
| `reminders` | date/odometer reminders (some auto-derived) | car, title, type, due_date, due_km, repeat_days, repeat_km, done, done_at, notes |
| `car_shares` | grants another user access to a car | car, user, `permission` (read \| write) |
| `organizations` | tenants | name (unique) |
| `control_audit` | OCPP control-command audit trail | user, charger, action, result, timestamp |
| `users` | login + profile (built-in auth collection) | name, email, avatar, `role` (user \| admin \| superadmin), `organization`, bio, theme, locale, date_format, currency, font_size, deletion_requested_at |
Every record collection except `car_shares` / `organizations` / `control_audit`
carries **one optional file attachment**, served only through the API Server
(`GET /api/{records}/{id}/file`) — never a public PocketBase URL.
**Spreadsheet formulas** (from the original `Car Service.xlsx`), reproduced by
the API on read:
@@ -118,12 +132,12 @@ GET /api/auth/me
GET /api/identity
# current user (profile / appearance / avatar / data / account lifecycle)
GET /api/me PATCH /api/me
GET /api/me PATCH /api/me DELETE /api/me
POST /api/me/password
POST /api/me/avatar GET /api/me/avatar DELETE /api/me/avatar
POST /api/me/verify/request
GET /api/me/export POST /api/me/import
POST /api/me/delete POST /api/me/delete/cancel DELETE /api/me
POST /api/me/delete POST /api/me/delete/cancel
# users + organizations (admin or superadmin; org writes are superadmin-only)
GET /api/users POST /api/users
@@ -131,31 +145,47 @@ PATCH /api/users/{id} DELETE /api/users/{id}
GET /api/orgs POST /api/orgs
PATCH /api/orgs/{id} DELETE /api/orgs/{id}
# superadmin
GET /api/admin/pb-config PUT /api/admin/pb-config
POST /api/admin/pb-config/test
# superadmin — connection + plugin management
GET /api/admin/pb-config PUT /api/admin/pb-config POST /api/admin/pb-config/test
GET /api/admin/webapp-config PUT /api/admin/webapp-config POST /api/admin/webapp-config/test
GET /api/admin/plugins POST /api/admin/plugins
GET /api/admin/plugins/{name} PUT /api/admin/plugins/{name}
DELETE /api/admin/plugins/{name} POST /api/admin/plugins/{name}/health
# integrations (per-user plugin settings; superadmin → org admin → user cascade)
GET /api/integrations/toyota PUT /api/integrations/toyota POST /api/integrations/toyota/health
GET /api/integrations/toyota/vehicles
GET /api/integrations/anker-solix PUT /api/integrations/anker-solix POST /api/integrations/anker-solix/health
GET /api/integrations/anker-solix/chargers
# Anker Solix OCPP charging control (own/proxy mode + a live CSMS session)
GET /api/integrations/anker-solix/chargers/{sn}/control
POST /api/integrations/anker-solix/chargers/{sn}/control/token
DELETE /api/integrations/anker-solix/chargers/{sn}/control/token
POST /api/integrations/anker-solix/chargers/{sn}/{action}
GET /ocpp/{serial} # charger dials in here (OCPP Basic auth, not bearer)
# cars + sharing
GET /api/cars POST /api/cars
GET /api/cars/{id} PATCH /api/cars/{id} DELETE /api/cars/{id}
GET /api/cars/{id}/service-records
GET /api/cars/{id}/parts
GET /api/cars/{id}/shares POST /api/cars/{id}/shares
DELETE /api/cars/{id}/shares/{userId}
GET /api/cars/{id}/service-records GET /api/cars/{id}/technical-checks
GET /api/cars/{id}/parts GET /api/cars/{id}/fuel-entries GET /api/cars/{id}/fuel-stats
GET /api/cars/{id}/maintenance GET /api/cars/{id}/documents GET /api/cars/{id}/reminders
GET /api/cars/{id}/shares POST /api/cars/{id}/shares DELETE /api/cars/{id}/shares/{userId}
# service records + parts
GET /api/service-records POST /api/service-records
GET /api/service-records/{id} PATCH /api/service-records/{id} DELETE /api/service-records/{id}
GET /api/parts POST /api/parts
GET /api/parts/{id} PATCH /api/parts/{id} DELETE /api/parts/{id}
# per-record collections — each is GET(list) POST / GET PATCH DELETE {id}
/api/service-records /api/technical-checks /api/parts
/api/fuel-entries /api/maintenance /api/car-documents
/api/reminders (+ POST /api/reminders/{id}/complete)
# attachments — one optional file per record, on every collection that takes one.
# {records} = car-documents | service-records | technical-checks | maintenance | fuel-entries | parts
POST /api/{records}/{id}/file GET /api/{records}/{id}/file DELETE /api/{records}/{id}/file
```
`GET /api/cars` returns the caller's owned cars plus any shared with them, each
annotated with an `access` field. `GET /api/service-records?car={id}` and
`GET /api/parts?car={id}` filter by car.
annotated with an `access` field. The per-record list endpoints also accept a
`?car={id}` filter (e.g. `GET /api/service-records?car={id}`).
> **Gotcha:** `updateCar` rewrites **all** car columns from the payload, so a
> `PATCH /api/cars/{id}` must send the **full** car object — omitted spec fields
@@ -198,8 +228,22 @@ superadmin. Two kinds share one contract: **built-in** (Go, compiled in) and
persists to `plugins.json`.
See **[`internal/plugins/README.md`](internal/plugins/README.md)** for the full
guide. DriverVault ships no built-in connectors yet; the external kind is the
place to start.
guide. Two built-in connectors ship today — **Toyota Connected** (`toyota`,
read-only MyToyota vehicle data) and the **Anker Solix** V1 EV charger
(`anker-solix`) — and any number of external HTTP plugins can be registered at
runtime with no rebuild.
### Integrations & charging control
Beyond the superadmin plugin registry, the built-in connectors are exposed
per-user through `/api/integrations/*` under a **superadmin → org admin → user**
cascade (each layer supplies defaults the next can override). For Anker Solix
chargers the server additionally runs an **OCPP 1.6J Central System**
(`internal/ocpp`): when the owner sets a control mode of own/proxy, the charger
dials back in at `GET /ocpp/{serial}` (authenticated with OCPP Basic auth using a
per-charger control token, not a bearer token) and the owner can start/stop and
set charge limits, with every command rate-limited and written to a
`control_audit` trail.
## Configuration