Files
DriverVault/README.md
T
tajniak81andClaude Opus 5 cd16d4383f Orgs: let any user create an organization and become its admin
Organization writes were superadmin-only, so standing up a tenant needed
an out-of-band superadmin. Creating one is now self-service, and an admin
manages the org they belong to.

- POST /api/orgs is open to any authenticated user. A creator who isn't a
  superadmin must have no organization yet (a single-valued membership
  relation means a second one would abandon the first), and is promoted to
  the new org's admin and first member in the same request. If that
  promotion fails the org is rolled back, so it is never left stranded
  with nobody able to administer it. Superadmins still create tenants
  without joining them.
- PATCH/DELETE are manager-gated and scope an admin to their own org. An
  admin deletes theirs only as its sole member: they are detached and
  demoted to a plain user before the record goes, so the org is empty when
  it is removed. Other members still block deletion with a 409.
- /api/me now carries organization + organizationName, which the clients
  need to tell "no org yet" from "org you administer".

The panel, Web App (new OrgManager.vue in Settings) and Phone App (new
_OrganizationSection) all mirror the server's gates rather than
re-deciding them. The Phone App cached its role at login and gates the
Users tab on it, so AuthService.adoptRole refreshes that from the profile
instead of making a freshly promoted admin sign in again.

Covered by orgs_test.go, which drives the real handler + middleware chain
against a stand-in PocketBase: promotion, the already-a-member refusal,
superadmin staying unattached, the rollback, own-org scoping, the
detach-and-demote, and the blocking-member 409.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:54:02 +02:00

140 lines
7.6 KiB
Markdown

# DriverVault
A car control & service-tracking system (originally "Car Control Project").
Built incrementally — starting with a **car maintenance tracker** (modeled on
`Car Service.xlsx`) and growing toward live integration with the car via a
cellular ESP32 device and third-party services (Toyota Connected, EV chargers).
## Architecture
All clients communicate with the database **only through the API Server**
nothing talks to PocketBase directly.
```
┌──────────────────┐
Web App (Vue) ─────▶│ │
Phone App (Flutter)▶│ API Server │────▶ PocketBase
Home Assistant ────▶│ (Go, stdlib) │ (10.2.1.10:8027)
Car Agent (ESP32) ─▶│ │
└──────────────────┘
```
| Component | Stack | Status | Docs |
|---|---|---|---|
| **API Server** | Go (stdlib) | ✅ built, running, verified | [API Server/README.md](API%20Server/README.md) |
| **Database** | PocketBase | ✅ running, schema + seed done | — |
| **Web App** | Vue 3 + Vite + Tailwind v4 | ✅ full feature set (below) | [Web App/README.md](Web%20App/README.md) |
| **Phone App** | Flutter (Android) | ✅ web parity + biometric login | [Phone App/README.md](Phone%20App/README.md) |
| **Docker** | Compose (multi-container / all-in-one) | ✅ deployment configs | [Docker/README.md](Docker/README.md) · [Docker AIO/README.md](Docker%20AIO/README.md) |
| **Car Agent Device** | ESP32 + SIM7600 (LILYGO TTGO) | 🚧 firmware in progress | [Car Agent Device](Car%20Agent%20Device) |
| **Home Assistant Plugin** | — | ⬜ later | — |
The Web and Phone apps are at feature parity (the phone omits only data
export/import).
## Features
- **Maintenance tracking** — cars, service history (date/odometer + which parts
were changed), and a per-car parts catalog, with next-due date/km status from
the spreadsheet formulas.
- **Technical checks** — the mandatory roadworthiness inspections (przegląd
techniczny / MOT / TÜV): result, cost, station and the certificate's
valid-until, which overrides the car's interval and drives the next-due date.
- **Maintenance log** — workshop visits and repairs outside the routine schedule:
type/status, workshop, parts used, labour + parts cost, invoice, warranty-until.
- **Fuel tracking** — refills with derived efficiency (average / best / worst
consumption, cost per km, price per litre). Consumption is measured between
full tanks, so partial fills roll into the next full one.
- **Documents** — insurance, registration, road tax and the rest, each with a
server-computed renewal/expiry state.
- **Reminders** — date- and/or odometer-triggered, one-off or recurring, plus
read-only reminders the server derives from documents and service records.
- **Attachments** — one optional file (PDF or image) per service record,
technical check, maintenance entry, refill, document and part; fetched back
through the API Server, never a public URL.
- **Accounts** — PocketBase-token login, profile + appearance preferences
(theme/locale/date format/currency/font), avatar, email verification, data
export/import, and an account-deletion state machine.
- **Organizations & roles** — multi-tenant `user` / `admin` / `superadmin`
roles; admins manage users within their own organization, superadmins span all.
Any user without an organization can create one and becomes its admin.
- **Per-user ownership & sharing** — each car has an owner and can be shared with
other users as read or write; the UI mirrors the server's access checks.
- **Integrations** — per-user connectors under a superadmin → org-admin → user
cascade. Built-in today: **Toyota Connected** (read-only vehicle data) and the
**Anker Solix** V1 EV charger.
- **Cars from the manufacturer's own service** — import a car straight off a
connected account (MyToyota today), choosing what to pull in, and read everything
that service knows about it from a dedicated first tab on the car. Generic over
providers: the next manufacturer is one adapter in the API Server.
- **EV charging control** — for Anker Solix chargers the API Server runs an
**OCPP 1.6J Central System**; in own/proxy mode the charger dials back in and
the owner can start/stop and set limits from the Charging screen.
- **Translated UI** — the interface reads its text from per-language files
(English, Polish, Danish today), with English as the fallback for any
untranslated string. See [TRANSLATIONS.md](TRANSLATIONS.md).
- **Phone biometric login & app lock** — fingerprint / face sign-in with an
app-lock that requires an unlock on relaunch. See the Phone App README.
## Auth model
All apps share one auth model: **authentication is PocketBase's own.**
`POST /api/auth/login` is proxied to the PocketBase users collection and the
client keeps the token PocketBase minted — the API Server does **not** issue its
own JWT. Every protected request carries `Authorization: <token>` (both
`Bearer <token>` and a raw token are accepted) and the server re-resolves it
against PocketBase on each call, so a role change or a deletion takes effect
immediately. Tokens are stateless, so there is no per-device session list;
changing an account's password rotates its token key and invalidates every token
already issued. Access to cars/records is gated by per-user ownership and shares;
user management requires the admin or superadmin role. Creating an organization
is the one management action open to a plain user — it promotes them to admin of
the organization they just created.
## Domain (from `Car Service.xlsx`)
- **Cars** — one per vehicle (was: one spreadsheet sheet), with spec fields
(engine / transmission / differential oil, brake fluid, coolant, VIN, fuel
type, build / first-registration dates, …) and configurable service intervals.
- **Service records** — date + odometer per service, plus which parts were
changed (oil & oil filter, engine air filter, cabin air filter).
- **Parts** — per-car catalog of part numbers.
Key spreadsheet formulas, reproduced by the API Server on read:
```
Next Service Date = service date + serviceIntervalDays (default 365; Excel: =A+365)
Next Service Km = service km + serviceIntervalKm (default 15 000; Excel: =B+15000)
```
Intervals are configurable per car.
## Getting started
Bring up the stack in this order — each app's README has the details:
1. **[API Server](API%20Server/README.md)** — configure `.env`, run
`setup-pocketbase.mjs`, start the server. This must be running for either app.
2. **[Web App](Web%20App/README.md)** — `npm install && npm run dev`
(proxies `/api` to the server).
3. **[Phone App](Phone%20App/README.md)** — `flutter build apk` /
`flutter run` with `--dart-define=API_BASE=http://<server-ip>:8080/api`.
Or skip all of that and bring the whole stack up with **Docker**, which runs the
schema setup itself — see [Docker](Docker/README.md) (PocketBase + API Server +
Web App as three containers) or [Docker AIO](Docker%20AIO/README.md) (all three
in a single image).
## Layout
```
DriverVault/
├── API Server/ # Go gateway to PocketBase (the only DB client)
├── Web App/ # Vue 3 + Vite + Tailwind v4 SPA + Go BFF
├── Phone App/ # Flutter (Android)
├── Car Agent Device/ # ESP32 + SIM7600 firmware (LILYGO TTGO T-SIM7600)
├── Home Assistant Plugin/ # later phase
├── Docker/ # Compose deployment (API Server + Web App)
└── Docker AIO/ # single all-in-one image
```