fix: image processing
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-10-21 16:29:32 +02:00
parent e826e98c89
commit 4dae99786f
5 changed files with 219 additions and 46 deletions
+32 -10
View File
@@ -33,6 +33,8 @@ Swagger UI: `http://localhost:3001/api/docs`
### Devices
`POST /devices/register` - Pair a terminal; every pairing adds one
`POST /devices/:deviceId/sync` - Sync device state (device auth)
`POST /devices/:deviceId/images` - Upload a picture taken on the terminal (device auth, multipart field `file`):
`{ imageId }`. Processed like a dashboard upload (see Images); the terminal links it with a `product_updated` event
`POST /devices/:deviceId/commands/:commandId/ack` - Acknowledge command (device auth)
`GET /devices/generation-code` - Generate registration code (admin)
@@ -49,6 +51,12 @@ and `docs/server-stock-source-of-truth.md`.
- `settled`: `amountInCents`, `paymentMethod` (`cash`, `payconiq`, `payment_pending` or `other`), optional
`commandId` and `items` (`[{ productId?, productName, quantity, unitPriceInCents }]`)
- `stock_set`: `productId`, `quantity` (a count ≥ 0, or `null` to stop tracking); no `tabId`
- `product_created` / `product_updated` / `product_archived`: `productId` (the terminal makes the id of a new
product) and `product` with the fields that changed: any of `name`, `category`, `priceInCents`,
`lowStockThreshold`, `archived`, `imageId`. `product_created` needs `name` and `priceInCents`; no `tabId`.
Every field keeps the time it last changed, and an event only changes a field it changed later (by
`occurredAt`), so an offline rename and a dashboard price change both survive. An `imageId` the server doesn't
have is ignored; an update of a product the venue doesn't have is acknowledged and dropped
Each id is applied once (tab state and stock in the same transaction). `acknowledgedEventIds` lists the applied
ones, including repeats; `rejectedEvents` (`[{ id, reason }]`) were invalid and never applied. The terminal drops
@@ -62,10 +70,12 @@ and `docs/server-stock-source-of-truth.md`.
first, at most 500; the response's `settlementsCursor` is sent back next time.
- `openTabs` is only used once per venue, on the first sync after the server started holding the tabs: the terminal's
own tabs (and catalog stock) are taken over and the events of that request are stored without applying them again.
- The dashboard manages the products. `products` in the request (at most 2000) is only used on that same first sync,
to take over the terminal's catalog with its stock; after that it is ignored. The response's `catalog` lists every
product of the venue: `[{ id, name, category, priceInCents, lowStockThreshold, archived }]`. The terminal adds and
updates its products from it and hides archived products and products the catalog doesn't list.
- Products are changed on the dashboard and, through the product events above, on terminals. `products` in the
request (at most 2000) is only used on that same first sync, to take over the terminal's catalog with its stock;
after that it is ignored. The response's `catalog` lists every product of the venue:
`[{ id, name, category, priceInCents, lowStockThreshold, archived, imageId }]`. The terminal adds and updates its
products from it (replaying its unsent product events on top), hides archived products and products the catalog
doesn't list, and downloads pictures it doesn't have yet from `GET /images/:imageId`.
- Planned customers are opened as tabs by the server, once; `plannedTabNames` is always empty.
- Dashboard stock adjustments are applied by the server right away; `stockAdjustments` is always empty.
`stockAdjustmentResults` from older app versions are still recorded and acknowledged.
@@ -95,15 +105,27 @@ Names match case-insensitively and keep their existing spelling; `replace` also
`GET /venues/:venueId/stock` - The venue's products (archived ones too, with `archivedAt`) with stock, low stock
level and pending adjustments
`GET /venues/:venueId/stock/adjustments?productId=&limit=&offset=` - Stock adjustment history, newest first
`POST /venues/:venueId/stock/adjustments` - Request stock changes (admins and viewers): `{ adjustments: [{ productId, type: 'add' | 'set', quantity, note? }] }`.
`POST /venues/:venueId/stock/adjustments` - Request stock changes (admins and managers): `{ adjustments: [{ productId, type: 'add' | 'set', quantity, note? }] }`.
`add` takes a non-zero quantity (negative for breakage) and needs a tracked product; `set` takes 0 or more.
`POST /venues/:venueId/stock/adjustments/:id/cancel` - Cancel an adjustment the terminal hasn't received yet (admins and viewers)
`POST /venues/:venueId/stock/products` - Add a product (admin): `{ name, category?, priceInCents, stock?, lowStockThreshold? }`.
`stock` is the starting count (recorded as an applied `set` adjustment); null or left out: not tracked.
`product_name_exists` when an active product has the same name (ignoring case).
`PATCH /venues/:venueId/stock/products/:productId` - Change a product (admin): any of `{ name, category, priceInCents,
`POST /venues/:venueId/stock/adjustments/:id/cancel` - Cancel an adjustment the terminal hasn't received yet (admins and managers)
`POST /venues/:venueId/stock/products` - Add a product (admins and managers): `{ name, category?, priceInCents, stock?,
lowStockThreshold? }`. `stock` is the starting count (recorded as an applied `set` adjustment); null or left out:
not tracked. `product_name_exists` when an active product has the same name (ignoring case). Terminals may still
create products with the same name; the products page marks those as duplicates.
`PATCH /venues/:venueId/stock/products/:productId` - Change a product (admins and managers): any of `{ name, category, priceInCents,
lowStockThreshold, archived }`. `archived: true` deletes it for the terminals (kept for tabs and history),
`archived: false` brings it back. The stock itself changes through adjustments.
`POST /venues/:venueId/stock/products/:productId/image` - Set a product's picture (admins and managers; multipart
field `file`, see Images): the product with its new `imageId`
`DELETE /venues/:venueId/stock/products/:productId/image` - Remove a product's picture (admins and managers)
### Images
`GET /images/:imageId` - A product picture (public, `image/webp`, cached for a year: an id never changes content).
Uploads (JPEG, PNG, WebP, GIF or AVIF, at most 10 MB) are turned upright and stored as a 512×512 WebP without
metadata. A product on a plain background (all four edges one colour, or transparent) is trimmed and centred
with a 40 px margin on that background. Any other photo is cropped around its centre when roughly square, or
fitted whole on a transparent background when not (bottles, cans). The id is the SHA-256 of the stored file.
`invalid_image` when the upload can't be read.
### Commands
`POST /venues/:venueId/commands` - Settle a tab: `{ tabId, type: "close_tab", expectedVersion, paymentMethod }`. The