Document the upload, proof and forecast endpoints
Ten routes shipped over the last few commits without reaching the inventory, which is the file everything else is generated from - openapi.json, the /api/docs browser, and the per-site build. An endpoint absent from it is invisible to anyone integrating against the API even though it answers requests. The auth notes matter more than the paths. API-REFERENCE tells a deployment reviewer that essentially every GET is optional-auth; two of these are not, and an exception buried in a table of 372 operations is one nobody reads. The installer download and the warranty proof are called out with why: one is licensed vendor software, the other carries pricing and a service tag. The application image is listed with the fully public endpoints, since tiles render before login.
This commit is contained in:
@@ -96,6 +96,18 @@ Two auth patterns dominate the reads:
|
||||
Every mutating endpoint (POST / PUT / PATCH / DELETE) requires a JWT and is
|
||||
gated by `require_role` or `require_permission`; none are public.
|
||||
|
||||
Two GETs deliberately break the optional-auth convention and require a
|
||||
permission, because what they return is not asset metadata but a file that
|
||||
carries value or liability of its own:
|
||||
|
||||
| Endpoint | Requires | Why |
|
||||
|---|---|---|
|
||||
| `GET /api/applications/package/<filename>` | `applications.view` | Licensed vendor installers. An anonymous URL is a redistribution channel. |
|
||||
| `GET /api/warranty/proof/<filename>` | `warranty.view` | Invoices and certificates carry pricing and a service tag. |
|
||||
|
||||
Their sibling upload/delete routes are `applications.edit` / `warranty.edit`
|
||||
like any other mutation.
|
||||
|
||||
### Fully public endpoints (auth = none)
|
||||
|
||||
| Endpoint | Purpose |
|
||||
@@ -107,6 +119,7 @@ gated by `require_role` or `require_permission`; none are public.
|
||||
| `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/applications/image/<filename>` | Serve an application image (tiles render before login). |
|
||||
| `GET /api/dashboard/navigation` | Public navigation tree. |
|
||||
| `GET /api/dashboard/health` | Liveness / health probe. |
|
||||
| `GET /api/plugins/enabled` | List enabled plugins (no claims used). |
|
||||
|
||||
@@ -1437,6 +1437,54 @@
|
||||
"auth": "permission:applications.edit",
|
||||
"params": "body: appversionid",
|
||||
"example": "curl -X PUT http://localhost:5001/api/applications/machines/8/15 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{\"appversionid\":3}'"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/applications/<app_id>/image",
|
||||
"auth": "jwt + applications.edit",
|
||||
"params": "multipart/form-data: file=<image> (png/jpg/gif/webp/svg/ico)",
|
||||
"purpose": "Upload or replace an application's image; sets application.image to the served URL",
|
||||
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@logo.png http://localhost:5001/api/applications/5/image"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/applications/image/<filename>",
|
||||
"auth": "none",
|
||||
"params": "none",
|
||||
"purpose": "Serve an uploaded application image. Public: application tiles render before login",
|
||||
"example": "curl http://localhost:5001/api/applications/image/application-5.png"
|
||||
},
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/api/applications/<app_id>/image",
|
||||
"auth": "jwt + applications.edit",
|
||||
"params": "none",
|
||||
"purpose": "Remove an application's image and clear the field",
|
||||
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/image"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/applications/<app_id>/package",
|
||||
"auth": "jwt + applications.edit",
|
||||
"params": "multipart/form-data: file=<installer> (exe/msi/msp/zip/7z/cab/iso/appx/msix/ps1/bat/txt/pdf), max 500MB",
|
||||
"purpose": "Upload or replace the installer; sets installpath to the download URL",
|
||||
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@setup.msi http://localhost:5001/api/applications/5/package"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/applications/package/<filename>",
|
||||
"auth": "jwt + applications.view",
|
||||
"params": "none",
|
||||
"purpose": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software",
|
||||
"example": "curl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/package/application-5.msi"
|
||||
},
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/api/applications/<app_id>/package",
|
||||
"auth": "jwt + applications.edit",
|
||||
"params": "none",
|
||||
"purpose": "Remove an uploaded installer; clears installpath only when it pointed at the upload",
|
||||
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/package"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2895,6 +2943,14 @@
|
||||
"params": "path: modelsupplyid",
|
||||
"purpose": "Hard-delete a model supply mapping.",
|
||||
"example": "curl -X DELETE -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/printers/supplies/44"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/printers/supplies/forecast",
|
||||
"auth": "jwt-optional",
|
||||
"params": "days (1-365, default 90)",
|
||||
"purpose": "Days-to-empty per printer from Zabbix history, soonest first, with cartridge replacement counts; printers with no honest estimate are returned separately with the reason. available=false when Zabbix is unreachable",
|
||||
"example": "curl 'http://localhost:5001/api/printers/supplies/forecast?days=90'"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2972,6 +3028,30 @@
|
||||
"auth": "jwt-optional",
|
||||
"params": "none",
|
||||
"example": "curl -H \"Authorization: Bearer $TOKEN\" http://localhost:5001/api/warranty/report"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/warranty/<warrantyid>/proof",
|
||||
"auth": "jwt + warranty.edit",
|
||||
"params": "multipart/form-data: file=<document> (pdf/png/jpg/gif/webp/tif/msg/eml/doc/docx/xls/xlsx), max 25MB",
|
||||
"purpose": "Upload or replace the proof-of-cover document (invoice, certificate)",
|
||||
"example": "curl -X POST -H 'Authorization: Bearer <token>' -F file=@invoice.pdf http://localhost:5001/api/warranty/12/proof"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/warranty/proof/<filename>",
|
||||
"auth": "jwt + warranty.view",
|
||||
"params": "none",
|
||||
"purpose": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag",
|
||||
"example": "curl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/proof/warranty-12.pdf"
|
||||
},
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/api/warranty/<warrantyid>/proof",
|
||||
"auth": "jwt + warranty.edit",
|
||||
"params": "none",
|
||||
"purpose": "Remove a proof document and clear both columns",
|
||||
"example": "curl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/12/proof"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "ShopDB Flask API",
|
||||
"version": "0.8.1",
|
||||
"version": "0.9.0",
|
||||
"description": "Asset-management API (core + plugins). Responses use a `success_response` envelope: `{status, data, meta}`. Auth: Bearer JWT (login or a managed PAT) for `jwt`/`admin`/`permission:*`; `X-API-Key` for collector/managed-token endpoints; public endpoints need neither."
|
||||
},
|
||||
"servers": [
|
||||
@@ -3271,6 +3271,112 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/applications/{app_id}/image": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Upload or replace an application's image; sets application.image to the served URL",
|
||||
"description": "Upload or replace an application's image; sets application.image to the served URL\n\n**Auth:** jwt + applications.edit\n\n**Params:** multipart/form-data: file=<image> (png/jpg/gif/webp/svg/ico)\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer <token>' -F file=@logo.png http://localhost:5001/api/applications/5/image\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Remove an application's image and clear the field",
|
||||
"description": "Remove an application's image and clear the field\n\n**Auth:** jwt + applications.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/image\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/applications/image/{filename}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Serve an uploaded application image. Public: application tiles render before login",
|
||||
"description": "Serve an uploaded application image. Public: application tiles render before login\n\n**Auth:** none\n\n**Params:** none\n\n**Example:**\n```\ncurl http://localhost:5001/api/applications/image/application-5.png\n```",
|
||||
"security": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/applications/{app_id}/package": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Upload or replace the installer; sets installpath to the download URL",
|
||||
"description": "Upload or replace the installer; sets installpath to the download URL\n\n**Auth:** jwt + applications.edit\n\n**Params:** multipart/form-data: file=<installer> (exe/msi/msp/zip/7z/cab/iso/appx/msix/ps1/bat/txt/pdf), max 500MB\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer <token>' -F file=@setup.msi http://localhost:5001/api/applications/5/package\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Remove an uploaded installer; clears installpath only when it pointed at the upload",
|
||||
"description": "Remove an uploaded installer; clears installpath only when it pointed at the upload\n\n**Auth:** jwt + applications.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/5/package\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/applications/package/{filename}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"core-catalog"
|
||||
],
|
||||
"summary": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software",
|
||||
"description": "Download an uploaded installer as an attachment. Authenticated: licensed vendor software\n\n**Auth:** jwt + applications.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/applications/package/application-5.msi\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/computers/types": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -6451,6 +6557,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/printers/supplies/forecast": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"plugin-printers"
|
||||
],
|
||||
"summary": "Days-to-empty per printer from Zabbix history, soonest first, with cartridge replacement counts; printers with no honest",
|
||||
"description": "Days-to-empty per printer from Zabbix history, soonest first, with cartridge replacement counts; printers with no honest estimate are returned separately with the reason. available=false when Zabbix is unreachable\n\n**Auth:** jwt-optional\n\n**Params:** days (1-365, default 90)\n\n**Example:**\n```\ncurl 'http://localhost:5001/api/printers/supplies/forecast?days=90'\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/warranty": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -6615,6 +6740,61 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/warranty/{warrantyid}/proof": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"plugin-warranty"
|
||||
],
|
||||
"summary": "Upload or replace the proof-of-cover document (invoice, certificate)",
|
||||
"description": "Upload or replace the proof-of-cover document (invoice, certificate)\n\n**Auth:** jwt + warranty.edit\n\n**Params:** multipart/form-data: file=<document> (pdf/png/jpg/gif/webp/tif/msg/eml/doc/docx/xls/xlsx), max 25MB\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer <token>' -F file=@invoice.pdf http://localhost:5001/api/warranty/12/proof\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"plugin-warranty"
|
||||
],
|
||||
"summary": "Remove a proof document and clear both columns",
|
||||
"description": "Remove a proof document and clear both columns\n\n**Auth:** jwt + warranty.edit\n\n**Params:** none\n\n**Example:**\n```\ncurl -X DELETE -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/12/proof\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/warranty/proof/{filename}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"plugin-warranty"
|
||||
],
|
||||
"summary": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag",
|
||||
"description": "Download a proof document under the vendor's original filename. Authenticated: carries pricing and a service tag\n\n**Auth:** jwt + warranty.view\n\n**Params:** none\n\n**Example:**\n```\ncurl -OJ -H 'Authorization: Bearer <token>' http://localhost:5001/api/warranty/proof/warranty-12.pdf\n```",
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success (success_response envelope)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user