Three defects, all found on printedparts_label_prefix, all one root cause: nothing in the framework knew that setting existed. The parts kiosk runs logged out. An unauthenticated read of a setting is limited to an allowlist, the key was not on it, so the kiosk got a 404 and fell back to no prefix. An admin previewing the same page while logged in saw the prefix, which is why it looked like it worked. The same setting also looked like it would not save. The row did not exist on a site that installed the plugin before the setting was added, so the first save created it - under the placeholder category the settings API uses for keys it does not recognise, where the plugin's settings page, which lists by category, could no longer see it. The value was in the database the whole time. And the row was missing in the first place because seeding ran from on_install / on_enable, which fire only on a state transition. Neither runs again on an upgrade, so a setting added in a later plugin version never reached a site that installed an earlier one. The comment claiming enable ran every upgrade cycle was simply wrong. A plugin now declares the settings it owns in get_settings_defaults(): key, default, type, category, description, and whether a logged-out page may read it. The framework seeds declared keys at install, at enable, and on every flask plugin upgrade-all; files a first-time write under the declared category; re-homes any row left in the placeholder category, value untouched; and answers an anonymous read for keys marked public. Core carries no list of any plugin's keys. Contract 0.16.0 (additive optional hook). printedparts and printers move to the hook and floor their core_version at 0.16.0. The dev database had two rows in the misfiled state (printedparts_alert_email, employee_db_host); the first repairs itself on the next upgrade pass.
135 lines
8.8 KiB
Markdown
135 lines
8.8 KiB
Markdown
# API reference (index)
|
|
|
|
This page is an index and a pointer, not a full specification. It answers three
|
|
questions: what endpoints exist, who calls them, and what auth they require. The
|
|
detailed request and response shapes live in the live generated docs and in the
|
|
per-surface contract docs linked from each table below.
|
|
|
|
## Live and generated docs
|
|
|
|
The repo ships hosted, generated API docs. Start there:
|
|
|
|
- **Interactive spec:** `GET /api/docs` - a self-hosted Redoc page over the
|
|
generated OpenAPI spec. The Redoc bundle is vendored under
|
|
`shopdb/core/api/staticdocs/`, so it renders fully offline on the air-gapped
|
|
prod box (no CDN).
|
|
- **Raw spec:** `GET /api/docs/openapi.json` - OpenAPI 3.1, roughly 238 paths and
|
|
362 operations. Generated by `scripts/gen_openapi.py` from
|
|
`docs/api-inventory.json`; regenerate after any API change.
|
|
- **LLM / agent entry point:** `GET /api/docs/llms.txt` - a concise API guide
|
|
following the llms.txt convention, plus a read-only MCP server
|
|
(`mcp/shopdb_mcp.py`, built with `FastMCP.from_openapi` over the same spec).
|
|
The MCP server exposes a curated set of GET endpoints as tools for an agent to
|
|
query the asset database over HTTPS with a scoped read token; it never runs on
|
|
the prod box. Set it up on a work PC with
|
|
`pxe-images/github/setup-mcp.cmd`.
|
|
|
|
The docs blueprint is `shopdb/core/api/docs.py` (a core blueprint, always
|
|
mounted regardless of which plugins are staged into a site build).
|
|
|
|
Contract docs (linked per table below) hold the deep semantics: field mappings,
|
|
idempotency rules, rotation, error envelopes, staged rollout. This page only
|
|
routes you to the right one.
|
|
|
|
---
|
|
|
|
## 1. Fleet and client contracts (unauthenticated or token)
|
|
|
|
These are the endpoints the shopfloor PC fleet, kiosks, displays, and printer
|
|
installers call. They are consumed by machines, not by the interactive UI, and
|
|
they authenticate with a scoped service token or nothing at all.
|
|
|
|
| Endpoint | Auth | Purpose | Contract doc |
|
|
|---|---|---|---|
|
|
| `GET /api/geenforce/manifest?pctype=<scope>` | `geenforce.fetch` service token (`X-API-Key` or Bearer PAT) | Serve the current published manifest for a PC-type scope. ETag / 304 supported. | GE-ENFORCE-CLIENT.md, geenforce-api-cutover.md |
|
|
| `GET /api/geenforce/payload/<sha256>` | `geenforce.fetch` service token | Serve a payload blob (installer) by content hash so share-less PCs pull over HTTPS instead of SMB. Rate limited and size capped; the sha256 is the integrity guarantee. | GE-ENFORCE-CLIENT.md, geenforce-api-cutover.md |
|
|
| `POST /api/geenforce/report` | `geenforce.report` service token | Record one PC's enforcement cycle: applied manifest version plus per-entry self-heal outcomes. | GE-ENFORCE-CLIENT.md, geenforce-api-cutover.md |
|
|
| `POST /api/collector/<plugin>` | `X-API-Key` env key or a `collector.ingest` managed token | Generic idempotent inventory upsert; the PC fleet targets `/api/collector/computers`. | COLLECTOR-INTEGRATION.md |
|
|
| `POST /api/collector/pc`, `/apps`, `/heartbeat`, `/bulk` | `X-API-Key` or `collector.ingest` token | Legacy computers-only collector paths (predate ADR-006); deprecated in favor of `/api/collector/computers`. | COLLECTOR-INTEGRATION.md |
|
|
| `GET /api/collector/status` | `X-API-Key` or `collector.ingest` token | Collector liveness and endpoint list. | COLLECTOR-INTEGRATION.md |
|
|
| `GET /api/printers/install-list` | optional JWT (anonymous fleet or logged-in browser) | Flat list of network printers with floor-map positions for the signed installer. `?format=text` returns a pipe-delimited variant. | PRINTER-INSTALLER.md |
|
|
| `GET /api/printers/pc-default?machine=NNNN` | optional JWT | The PC's default printer by machine (asset) number, via the `defaultprinter` relationship. `?format=text` supported. | PRINTER-INSTALLER.md |
|
|
| `GET /api/printers/install-batch?printerids=1,2,3` | optional JWT | Generate a self-deleting Windows `.bat` that installs the selected printers. | PRINTER-INSTALLER.md |
|
|
| `GET /api/dashboarddefaults/display-role?fqdn=<fqdn>` | public (none) | Resolve what a display PC should show (role `dashboard`/`lobby`/`partskiosk`, frontend path, business unit). FQDN-first, IP fallback. | GE-ENFORCE-DISPLAY.md |
|
|
| `GET /api/dashboarddefaults/visitor-location?fqdn=<fqdn>` | public (none) | Resolve the business unit for a lobby display by FQDN (IP fallback). | GE-ENFORCE-DISPLAY.md |
|
|
|
|
The `geenforce.fetch` and `geenforce.report` scopes accept both `X-API-Key` and
|
|
`Authorization: Bearer` transports, the same managed-token pattern the collector
|
|
uses (see COLLECTOR-INTEGRATION.md for how to mint, deploy, and rotate a scoped
|
|
token). A fetch token may be further resource-bound to specific scopes; a bound
|
|
token is denied (403 on manifest, 404 on payload) anything outside its scopes.
|
|
|
|
---
|
|
|
|
## 2. Import API
|
|
|
|
The import surface (an admin PAT plus `X-Import-Mode` to preserve legacy
|
|
timestamps) lets a script load an entire legacy database through the same
|
|
endpoints the UI uses. It is documented in full, per resource, in **IMPORT-API.md**
|
|
and is not duplicated here. The dashboarddefaults import fields (FQDN-preferred
|
|
keying) are covered there as well.
|
|
|
|
---
|
|
|
|
## 3. Core UI API
|
|
|
|
Everything else is the core UI API: the endpoints the Vue frontend calls. As a
|
|
rule these are JWT-authenticated (a login token or a managed Personal Access
|
|
Token) and versioned by the plugin contract (`__contract_version__`, currently
|
|
0.16.0). Behavior and stability guarantees are in **CONTRACT-STABILITY.md**;
|
|
sister sites should pin tight `core_version` ranges until the contract reaches
|
|
1.0.
|
|
|
|
Two auth patterns dominate the reads:
|
|
|
|
- **Public (no token ever).** The endpoints below are reachable with no
|
|
credential at all. This is the surface a firewall or deployment reviewer asks
|
|
about, so it is enumerated in full.
|
|
- **Optional JWT (`jwt_required(optional=True)`).** Nearly every core and plugin
|
|
GET (list / detail / report / dashboard-summary) is optional-auth: it serves
|
|
reads anonymously and only requires a JWT for writes. There are well over a
|
|
hundred of these; rather than reprint them, enumerate them from the live spec
|
|
at `/api/docs` (filter to the `GET` operations). All product reports
|
|
(`/api/reports/*` and every plugin `.../report*`) are optional-auth by the same
|
|
convention.
|
|
|
|
Every mutating endpoint (POST / PUT / PATCH / DELETE) requires a JWT and is
|
|
gated by `require_role` or `require_permission`; none are public.
|
|
|
|
### Fully public endpoints (auth = none)
|
|
|
|
| Endpoint | Purpose |
|
|
|---|---|
|
|
| `POST /api/auth/login` | Obtain a JWT. |
|
|
| `GET /api/setup/needs-admin` | First-run check: does the instance have zero users. |
|
|
| `POST /api/setup/create-admin` | First-run only; creates the first admin, then 403s forever. |
|
|
| `GET /api/settings/map-blueprint/<filename>` | Serve the floor-map blueprint image. |
|
|
| `GET /api/settings/branding/<filename>` | Serve site branding assets (logo, etc.). |
|
|
| `GET /api/settings` and `GET /api/settings/<key>` | Read-only, and only the public allowlist: the `branding` and `map` categories, a few named site keys, plus any key a plugin declares `public` in `get_settings_defaults` (e.g. `printedparts_label_prefix`, which the logged-out parts kiosk renders). Every other key answers 404 to an anonymous caller. |
|
|
| `GET /api/models/image/<filename>` | Serve a model image. |
|
|
| `GET /api/dashboard/navigation` | Public navigation tree. |
|
|
| `GET /api/dashboard/health` | Liveness / health probe. |
|
|
| `GET /api/plugins/enabled` | List enabled plugins (no claims used). |
|
|
| `GET /api/dashboarddefaults/display-role` | Display role resolution (see section 1). |
|
|
| `GET /api/dashboarddefaults/visitor-location` | Lobby business-unit resolution (see section 1). |
|
|
| `GET /api/employees/search`, `/lookup/<sso>`, `/lookup` | Employee directory lookups (kiosk / sign-in flows). |
|
|
| `GET /api/employees/photo/<filename>` | Serve an employee photo. |
|
|
| `GET /api/notifications` (and `/types`, `/<id>`, `/active`, `/calendar`, `/dashboard/summary`, `/employee/<sso>`, `/shopfloor`) | Read shop-floor notifications for the public display and kiosks. |
|
|
| `GET /api/slides/feed` | Slide feed for the lobby display and screensaver. |
|
|
| `GET /api/slides/img/<surface>/<filename>` | Serve a slide image. |
|
|
| `GET /api/printedparts/image/<filename>` | Serve a printed-part image. |
|
|
| `GET /api/printedparts/kiosk/item/<itemcode>` | Kiosk part lookup (deliberately open; a kiosk carries no JWT). |
|
|
| `POST /api/printedparts/kiosk/take` | Kiosk part checkout (deliberately open, per decision record). |
|
|
|
|
---
|
|
|
|
## See also
|
|
|
|
- `docs/adr/README.md` - architecture decision records index.
|
|
- `docs/DEPLOY.md` - deployment; the public-endpoint inventory in section 3 above
|
|
is the site-exposure surface a deploy reviewer needs.
|
|
- `docs/PLUGINS.md` - the plugin catalog.
|
|
- Contract docs: GE-ENFORCE-CLIENT.md, geenforce-api-cutover.md,
|
|
COLLECTOR-INTEGRATION.md, PRINTER-INSTALLER.md, GE-ENFORCE-DISPLAY.md,
|
|
IMPORT-API.md, CONTRACT-STABILITY.md.
|