Add per-user Toyota integration with a settings cascade
Let each user run the Toyota Connected plugin under their own MyToyota
credentials and enable/disable it for themselves in the Web App, while a
superadmin (and, in an organization, an org admin) can impose settings
from above. Resolution is a cascade — top wins, and a lower level only
fills fields the levels above left blank:
- org user: API Server (superadmin) -> org admin -> user
- org-less user: API Server (superadmin) -> user
The MyToyota email + password resolve together as a pair from the highest
layer that supplies an email; brand resolves on its own; enablement is
strictly per-user, gated by the global master switch and the org gate.
API Server:
- plugins.Manager gains RawConfig / HealthCheckWith / InvokeWith so the
cascade can read global config and probe/invoke under a per-caller
resolved config.
- internal/api/integrations.go resolves the cascade and serves
GET/PUT /api/integrations/toyota, POST .../health, GET .../vehicles.
Secrets and inherited usernames are masked before leaving the server.
- The toyota builtin's credentials are no longer required at the global
layer, so the master switch can be enabled without global credentials.
- setup-pocketbase.mjs adds a pluginSettings JSON field to the users and
organizations collections (the user and org layers of the cascade).
Web App:
- api.js gains getToyota/saveToyota/testToyota.
- Settings grows an Integrations section: an enable toggle, credential
fields with locked / "inherited from" states, a brand select, an
org-scope switch for admins, and a live test-connection button.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5a729abd71
commit
5e435c5f77
@@ -43,6 +43,11 @@
|
||||
// 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
|
||||
//
|
||||
// # cars, service records, parts, shares
|
||||
// GET /api/cars POST /api/cars
|
||||
// GET /api/cars/{id} PATCH /api/cars/{id} DELETE /api/cars/{id}
|
||||
@@ -266,6 +271,14 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("DELETE /api/admin/plugins/{name}", s.requireSuperadminAuth(s.handleDeletePlugin))
|
||||
mux.HandleFunc("POST /api/admin/plugins/{name}/health", s.requireSuperadminAuth(s.handlePluginHealth))
|
||||
|
||||
// Integrations — per-user plugin settings under the superadmin → org admin →
|
||||
// user cascade (see integrations.go). Any authenticated user manages their
|
||||
// own layer; an org admin may also target their organization's layer.
|
||||
mux.HandleFunc("GET /api/integrations/toyota", s.handleGetToyota)
|
||||
mux.HandleFunc("PUT /api/integrations/toyota", s.handlePutToyota)
|
||||
mux.HandleFunc("POST /api/integrations/toyota/health", s.handleToyotaHealth)
|
||||
mux.HandleFunc("GET /api/integrations/toyota/vehicles", s.handleToyotaVehicles)
|
||||
|
||||
// Cars + sharing.
|
||||
mux.HandleFunc("GET /api/cars", s.listCars)
|
||||
mux.HandleFunc("POST /api/cars", s.createCar)
|
||||
|
||||
Reference in New Issue
Block a user