Docs: the USB kiosk, its endpoints and why the list costs a badge
Some checks failed
CI / naming (push) Successful in 1m7s
CI / frontend (push) Successful in 1m7s
CI / migrations-mysql (push) Failing after 3m22s
CI / backend (push) Has been cancelled

Changelog, the plugin table, the three kiosk endpoints in the API inventory,
and regenerated openapi.json / PROJECT-MAP.
This commit is contained in:
cproudlock
2026-08-27 08:13:36 -04:00
parent 7c47ae1f7c
commit 95ad4f1ef3
5 changed files with 277 additions and 3 deletions

View File

@@ -12,7 +12,7 @@ These plugins are in `plugins/` in this repo. Enable per site with `flask plugin
| `computers` | Shop-floor PCs and engineering workstations | Fed by the PXE pipeline collector per [ADR-006](adr/ADR-006-collector-contract.md). |
| `printers` | Network and shop-floor printers | Public installer map page + fleet install contract (`/api/printers/install-list`, `/pc-default`, `/install-batch`; see [PRINTER-INSTALLER.md](PRINTER-INSTALLER.md)). Optional Zabbix integration for supply tracking. Legacy `PrinterData` retiring per ADR-001. |
| `network` | Switches, routers, access points, IDFs as locations | Asset-only; cleanest of the bundled set. |
| `usb` | USB devices issued to shop-floor users | Lightweight checkout / check-in. |
| `usb` | USB devices issued to shop-floor users | Lightweight checkout / check-in, self-hosted tables or an existing `cmmc_usb` database. Optional logged-out kiosk (`/usb-kiosk`, off by default) so somebody at the drive cabinet can take and return a drive without a ShopDB login, attributed to a scanned badge. |
| `notifications` | Shop-floor notifications, recognitions, kiosk feed | Used by `ShopfloorDashboard.vue`. |
| `measuringtools` | Metrology and inspection instruments: calipers, micrometers, thread/bore/height gages, indicators | Per [ADR-005](adr/ADR-005-equipment-vs-measuringtools.md). Calibration lifecycle with derived status. First plugin built on the matured scaffold; its walkthrough is [PLUGIN-GUIDE.md](PLUGIN-GUIDE.md). Ships `default_enabled: false`. |
| `employees` | Read-only employee directory lookup | Backed by a separate HR database. Ships `default_enabled: false`. |

View File

@@ -88,6 +88,6 @@ Manifest-less directories under `plugins/` are core frontend surface and always
## Size
- test functions defined: **1333** (parametrised cases collect higher)
- documented API paths: **299** (`docs/openapi.json`, regenerate with `scripts/gen_openapi.py`)
- test functions defined: **1355** (parametrised cases collect higher)
- documented API paths: **304** (`docs/openapi.json`, regenerate with `scripts/gen_openapi.py`)

View File

@@ -2802,6 +2802,46 @@
"auth": "jwt-optional",
"params": "query: badge (filter by badge_number)",
"example": "curl 'http://localhost:5001/api/usb/checkouts/active?badge=212345678' -H \"Authorization: Bearer $JWT\""
},
{
"method": "GET",
"path": "/api/usb/kiosk/device/<device_id>",
"auth": "none (kiosk; requires usb_kiosk_enabled)",
"params": "path: device_id (the code on the drive label)",
"purpose": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of the current holder (needed for the return-for-someone-else confirm; the holder's badge is never returned). 404 when the kiosk setting is off, so a disabled kiosk does not advertise the route",
"example": "curl http://localhost:5001/api/usb/kiosk/device/USB-1"
},
{
"method": "POST",
"path": "/api/usb/kiosk/checkout",
"auth": "none (kiosk; requires usb_kiosk_enabled)",
"params": "body: device_id, badge",
"purpose": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with 422 (unlike the signed-in route, which creates the user row). 409 when the drive is already out",
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'"
},
{
"method": "POST",
"path": "/api/usb/kiosk/checkin",
"auth": "none (kiosk; requires usb_kiosk_enabled)",
"params": "body: device_id, badge, forsomeoneelse (optional bool)",
"purpose": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with details.needsconfirm and the holder's name, and the caller re-posts with forsomeoneelse=true. The badge RECORDED is the one scanned, so the log shows who handed it back. Set usb_kiosk_allow_return_for_others=false to refuse a mismatch with 403 instead",
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'"
},
{
"method": "POST",
"path": "/api/usb/kiosk/session",
"auth": "none (kiosk; requires usb_kiosk_enabled)",
"params": "body: badge",
"purpose": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a COUNT of everything else. The drives carry no barcode so the kiosk must offer a list; requiring a badge that resolves to a real person keeps that list from being anonymous, and a POST keeps the badge out of proxy access logs. 422 for an unresolvable badge",
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'"
},
{
"method": "POST",
"path": "/api/usb/kiosk/others",
"auth": "none (kiosk; requires usb_kiosk_enabled)",
"params": "body: badge",
"purpose": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step rather than on the main kiosk screen, and 404 when usb_kiosk_allow_return_for_others is off - there is no point naming holders of drives the kiosk will not let anyone return",
"example": "curl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'"
}
]
},

View File

@@ -16272,6 +16272,207 @@
}
}
},
"/api/usb/kiosk/device/{device_id}": {
"get": {
"tags": [
"plugin-usb"
],
"summary": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of...",
"description": "What the logged-out kiosk shows after a drive is scanned: description, checked-in/out status, locker, and the NAME of the current holder (needed for the return-for-someone-else confirm; the holder's badge is never returned). 404 when the kiosk setting is off, so a disabled kiosk does not advertise the route\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** path: device_id (the code on the drive label)\n\n**Example:**\n```\ncurl http://localhost:5001/api/usb/kiosk/device/USB-1\n```",
"security": [],
"responses": {
"200": {
"description": "Success. Body is the success_response envelope: {status, data, meta}.",
"content": {
"application/json": {
"$ref": "#/components/schemas/SuccessEnvelope"
}
}
},
"default": {
"description": "Error. Body is the error envelope; the code and message are nested under data.error.",
"content": {
"application/json": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
},
"404": {
"description": "No such record."
}
},
"parameters": [
{
"name": "device_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]
}
},
"/api/usb/kiosk/checkout": {
"post": {
"tags": [
"plugin-usb"
],
"summary": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with...",
"description": "Check a drive out at the kiosk, attributed to the scanned badge. A badge that resolves to no employee is refused with 422 (unlike the signed-in route, which creates the user row). 409 when the drive is already out\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: device_id, badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkout -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'\n```",
"security": [],
"responses": {
"200": {
"description": "Success. Body is the success_response envelope: {status, data, meta}.",
"content": {
"application/json": {
"$ref": "#/components/schemas/SuccessEnvelope"
}
}
},
"default": {
"description": "Error. Body is the error envelope; the code and message are nested under data.error.",
"content": {
"application/json": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true,
"description": "body: device_id, badge"
}
}
}
}
}
},
"/api/usb/kiosk/checkin": {
"post": {
"tags": [
"plugin-usb"
],
"summary": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with...",
"description": "Return a drive at the kiosk. Holder by default: a badge that does not match the current holder gets 409 with details.needsconfirm and the holder's name, and the caller re-posts with forsomeoneelse=true. The badge RECORDED is the one scanned, so the log shows who handed it back. Set usb_kiosk_allow_return_for_others=false to refuse a mismatch with 403 instead\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: device_id, badge, forsomeoneelse (optional bool)\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/checkin -H 'Content-Type: application/json' -d '{\"device_id\":\"USB-1\",\"badge\":\"270015376\"}'\n```",
"security": [],
"responses": {
"200": {
"description": "Success. Body is the success_response envelope: {status, data, meta}.",
"content": {
"application/json": {
"$ref": "#/components/schemas/SuccessEnvelope"
}
}
},
"default": {
"description": "Error. Body is the error envelope; the code and message are nested under data.error.",
"content": {
"application/json": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true,
"description": "body: device_id, badge, forsomeoneelse (optional bool)"
}
}
}
}
}
},
"/api/usb/kiosk/session": {
"post": {
"tags": [
"plugin-usb"
],
"summary": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a...",
"description": "What one badge may do at the kiosk: the caller's own drives (to return), the drives that are free (to take), and a COUNT of everything else. The drives carry no barcode so the kiosk must offer a list; requiring a badge that resolves to a real person keeps that list from being anonymous, and a POST keeps the badge out of proxy access logs. 422 for an unresolvable badge\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/session -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'\n```",
"security": [],
"responses": {
"200": {
"description": "Success. Body is the success_response envelope: {status, data, meta}.",
"content": {
"application/json": {
"$ref": "#/components/schemas/SuccessEnvelope"
}
}
},
"default": {
"description": "Error. Body is the error envelope; the code and message are nested under data.error.",
"content": {
"application/json": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true,
"description": "body: badge"
}
}
}
}
}
},
"/api/usb/kiosk/others": {
"post": {
"tags": [
"plugin-usb"
],
"summary": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step...",
"description": "Drives checked out to OTHER people, with holder names, so somebody can return one left behind. Behind its own step rather than on the main kiosk screen, and 404 when usb_kiosk_allow_return_for_others is off - there is no point naming holders of drives the kiosk will not let anyone return\n\n**Auth:** none (kiosk; requires usb_kiosk_enabled)\n\n**Params:** body: badge\n\n**Example:**\n```\ncurl -X POST http://localhost:5001/api/usb/kiosk/others -H 'Content-Type: application/json' -d '{\"badge\":\"270015376\"}'\n```",
"security": [],
"responses": {
"200": {
"description": "Success. Body is the success_response envelope: {status, data, meta}.",
"content": {
"application/json": {
"$ref": "#/components/schemas/SuccessEnvelope"
}
}
},
"default": {
"description": "Error. Body is the error envelope; the code and message are nested under data.error.",
"content": {
"application/json": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true,
"description": "body: badge"
}
}
}
}
}
},
"/api/printers/types": {
"get": {
"tags": [