Files
dashback/docs/api/README.md
seb 1860f38640 Add delivery and supplier HTTP APIs with expanded response docs.
Wire store-backed CRUD for goods receipts and suppliers, and teach the API doc generator to expand $ref/allOf/array samples so route markdown shows full JSON shapes.
2026-08-06 05:32:48 +02:00

112 lines
7.0 KiB
Markdown

# Dash API
```
http://localhost:4208/api
```
## CORS & cookies
All browser requests that need auth must send cookies:
```js
fetch('http://localhost:4208/api/auth/me', {
credentials: 'include',
});
```
Sessions use an HTTP-only `token` cookie (`Secure`, `SameSite=Strict`, 24h). The browser must talk to the API over **HTTPS** for login to work — `Secure` cookies are not sent on plain HTTP.
## Auth flow
1. Call [`GET /auth/me`](routes/auth/me.md) to get `googleClientId` (included in 200 and 401 responses).
2. Sign in with Google on the frontend (Google Identity Services / One Tap).
3. POST the Google ID token to [`POST /auth/login`](routes/auth/login.md).
4. The API sets the session cookie.
5. Call protected routes with `credentials: 'include'`. The cookie is refreshed automatically when it has less than 2 hours left.
## Routes
Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/routes/). Source of truth is the code; regenerate with `npm run docs:api`.
| File | Endpoint |
|------|----------|
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
| [`routes/article/get.md`](routes/article/get.md) | `GET /article/{sku}` |
| [`routes/article/update.md`](routes/article/update.md) | `PATCH /article/{sku}` |
| [`routes/article/set-brand.md`](routes/article/set-brand.md) | `PUT /article/{sku}/brand` |
| [`routes/article/set-categories.md`](routes/article/set-categories.md) | `PUT /article/{sku}/categories` |
| [`routes/article/set-eans.md`](routes/article/set-eans.md) | `PUT /article/{sku}/eans` |
| [`routes/article/history.md`](routes/article/history.md) | `GET /article/{sku}/history` |
| [`routes/article/add-image.md`](routes/article/add-image.md) | `POST /article/{sku}/image` |
| [`routes/article/delete-image.md`](routes/article/delete-image.md) | `DELETE /article/{sku}/image/{fullHash}` |
| [`routes/article/order-images.md`](routes/article/order-images.md) | `PUT /article/{sku}/images/order` |
| [`routes/article/set-prices.md`](routes/article/set-prices.md) | `PUT /article/{sku}/prices` |
| [`routes/article/set-weight.md`](routes/article/set-weight.md) | `PUT /article/{sku}/weight` |
| [`routes/articleIndex.md`](routes/articleIndex.md) | `GET /articleIndex` |
| [`routes/auth/login.md`](routes/auth/login.md) | `POST /auth/login` |
| [`routes/auth/logout.md`](routes/auth/logout.md) | `POST /auth/logout` |
| [`routes/auth/me.md`](routes/auth/me.md) | `GET /auth/me` |
| [`routes/auth/selected-company.md`](routes/auth/selected-company.md) | `PUT /auth/selected-company` |
| [`routes/brand/list.md`](routes/brand/list.md) | `GET /brand` |
| [`routes/brand/upsert.md`](routes/brand/upsert.md) | `PUT /brand` |
| [`routes/brand/delete.md`](routes/brand/delete.md) | `DELETE /brand/{id}` |
| [`routes/category/create.md`](routes/category/create.md) | `POST /category` |
| [`routes/category/export.zip.md`](routes/category/export.zip.md) | `GET /category/export.zip` |
| [`routes/category/order.md`](routes/category/order.md) | `PUT /category/order` |
| [`routes/category/tree.md`](routes/category/tree.md) | `GET /category/tree` |
| [`routes/category/delete.md`](routes/category/delete.md) | `DELETE /category/{id}` |
| [`routes/category/update.md`](routes/category/update.md) | `PATCH /category/{id}` |
| [`routes/category/articles.md`](routes/category/articles.md) | `GET /category/{id}/articles` |
| [`routes/category/delete-image.md`](routes/category/delete-image.md) | `DELETE /category/{id}/image` |
| [`routes/category/add-image.md`](routes/category/add-image.md) | `POST /category/{id}/image` |
| [`routes/company/list.md`](routes/company/list.md) | `GET /company` |
| [`routes/company/create.md`](routes/company/create.md) | `POST /company` |
| [`routes/company/delete.md`](routes/company/delete.md) | `DELETE /company/{id}` |
| [`routes/company/update.md`](routes/company/update.md) | `PUT /company/{id}` |
| [`routes/delivery/list.md`](routes/delivery/list.md) | `GET /delivery` |
| [`routes/delivery/create.md`](routes/delivery/create.md) | `POST /delivery` |
| [`routes/delivery/delete.md`](routes/delivery/delete.md) | `DELETE /delivery/{id}` |
| [`routes/delivery/get.md`](routes/delivery/get.md) | `GET /delivery/{id}` |
| [`routes/delivery/update.md`](routes/delivery/update.md) | `PATCH /delivery/{id}` |
| [`routes/events.md`](routes/events.md) | `GET /events` |
| [`routes/health.md`](routes/health.md) | `GET /health` |
| [`routes/image/get.md`](routes/image/get.md) | `GET /image/{fullHash}` |
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
| [`routes/posterminal/setOffAll.md`](routes/posterminal/setOffAll.md) | `DELETE /posterminal/{id}/all` |
| [`routes/posterminal/setOnAll.md`](routes/posterminal/setOnAll.md) | `PUT /posterminal/{id}/all` |
| [`routes/posterminal/setOff.md`](routes/posterminal/setOff.md) | `DELETE /posterminal/{id}/category/{categoryId}` |
| [`routes/posterminal/setOn.md`](routes/posterminal/setOn.md) | `PUT /posterminal/{id}/category/{categoryId}` |
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
| [`routes/supplier/delete.md`](routes/supplier/delete.md) | `DELETE /supplier/{id}` |
| [`routes/supplier/update.md`](routes/supplier/update.md) | `PATCH /supplier/{id}` |
| [`routes/thumb/get.md`](routes/thumb/get.md) | `GET /thumb/{fullHash}` |
| [`routes/unit/delete.md`](routes/unit/delete.md) | `DELETE /unit` |
| [`routes/unit/list.md`](routes/unit/list.md) | `GET /unit` |
| [`routes/unit/upsert.md`](routes/unit/upsert.md) | `PUT /unit` |
Machine-readable spec: [`openapi.json`](openapi.json).
## Signals (SSE)
Clients subscribe via [`GET /events`](routes/events.md) (`text/event-stream`). After a successful mutation, the API emits a named SSE event (signal) to subscribers of the same selected company.
| Signal | Emitted by |
|--------|------------|
| `article-<sku>` | `PUT /article/{sku}/brand`, `PUT /article/{sku}/categories`, `PUT /article/{sku}/eans`, `POST /article/{sku}/image`, `DELETE /article/{sku}/image/{fullHash}`, `PUT /article/{sku}/images/order`, `PUT /article/{sku}/prices`, `PUT /article/{sku}/weight` |
| `articles` | `POST /article`, `DELETE /article/{sku}`, `PATCH /article/{sku}` |
| `category` | `POST /category`, `PUT /category/order`, `DELETE /category/{id}`, `PATCH /category/{id}`, `DELETE /category/{id}/image`, `POST /category/{id}/image` |
## DESIGN
- for every route we have a file in /src/api/routes
- that should loosly bound to services in /src/services
- for every service we have a CLI tester in /scripts/
- all DB SQL code shall be in /src/db
- with mssql staements in /src/db/mssql/
- API docs: `@openapi` JSDoc on handlers → `npm run docs:api` → `docs/api/`