From a01abd516433b0375b82d84f3d15200b4eec4e01 Mon Sep 17 00:00:00 2001 From: Juan Cobo Betancourt Date: Mon, 9 Nov 2026 09:18:56 -0800 Subject: [PATCH] docs(api): calibrate answers 501; .env.example lists every setting --- content/en/docs/developers/api-reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/en/docs/developers/api-reference.md b/content/en/docs/developers/api-reference.md index 5c877f1..f122669 100644 --- a/content/en/docs/developers/api-reference.md +++ b/content/en/docs/developers/api-reference.md @@ -104,7 +104,7 @@ grep -rhoE 'HTTPException\(\s*(status_code\s*=\s*)?(status\.HTTP_)?[0-9]{3}|stat Then drop the success codes the decorators declare — 200, 201, 202, 204. The grep does not see a status taken from an exception: `HTTPException(status_code=exc.status, …)` passes on the status of `KeepRefused` (`app/core/kept_volumes.py`, raised from `POST /collections/`, `PATCH /collections/{id}`, `POST /collections/{id}/move-records`, `PATCH /records/{id}`, `POST /projects/{id}/add_record/{rec_id}`, `POST /imports/digitisation-list`, `POST /collections/{id}/keep-on-unit` and `POST /collections/{id}/release-from-unit`), of `FreeRefused` (`app/core/offsite_free.py`), of `CodeRefused` (`_check_codes_with_fisqua`), and of `SearchRefused` on `GET /fisqua/descriptions` (`search_descriptions`, `app/core/unit_mode.py`: `400` `query_too_short`, `409` `off_in_this_mode` or `fisqua_not_configured`, `429` `rate_limited` with `Retry-After` when Fisqua gives one, `503` `fisqua_unreachable`). One raiser sits outside `backend/app/api/`: `app/core/db_errors.py`, which turns any `IntegrityError` — not only a unique-constraint one — into a 409 carrying a message and, when one could be recovered, the constraint name. For any single route, read it: the `HTTPException` calls are explicit, and twenty callables in `backend/app/api/` raise on a route's behalf — `_require_csrf_header` (reached through `_session_token`, and from `POST /auth/logout`), `_authorize_bootstrap`, `get_current_user` and `RoleChecker.__call__` in `auth.py`; `_zone` in `activity.py`; `_check_codes_with_fisqua` in `collections.py`, which raises the lookup's `422` or `409` with the status the refusal carries; `_refuse_capture_without_volume_or_stem`, `_refuse_retake_out_of_the_operators_hands`, `_refuse_a_new_volume_out_of_turn`, `_refuse_resolution_in_filename`, `_resolve_capture_target` and `_take_filename_index` in `cameras.py`; `_read_capped` in `imports.py`; `_refuse_a_name_that_is_a_stem` and `_update_project_held` in `projects.py`; `_stored_master`, `_image_row`, `assert_movable`, `_assert_promotable` and `_apply_status_change` in `records.py` — so a route can return a code its own body never names. -Capture endpoints report a failed capture as `{success: false, error: ...}` at HTTP 200, so a client must read the body as well as the status. `POST /cameras/calibrate` does the same with its own `501` for a backend without autofocus calibration: the route raises it inside its `try` block, and the `except Exception` there returns it as `{success: false, error: "501: …"}` at 200 (`calibrate_camera`, `cameras.py`). +Capture endpoints report a failed capture as `{success: false, error: ...}` at HTTP 200, so a client must read the body as well as the status. Calibration runs report a failure the same way, except on a backend that cannot calibrate: there `POST /cameras/calibrate` and `POST /cameras/calibrate/white-balance` answer `501` (`calibrate_camera` and `calibrate_white_balance`, `cameras.py`). **A volume's identity — on `/collections` requests and responses.** A volume (a row in `collections`) carries the identity defined by exchange contract 1.1: a file name stem and the catalogue item's reference code. Records have no catalogue identifier: `records.external_id` was removed in migration `a7c1e9d2b4f6`. The schema gives the pattern and the lengths; the rules below are the ones it cannot state. @@ -1101,7 +1101,7 @@ BOOTSTRAP_TOKEN= ACCESS_TOKEN_EXPIRE_SECONDS=28800 ``` -The settings the backend reads are declared in `Settings`, `backend/app/core/config.py`, which is the authoritative list. The repository root `.env.example` lists most of them; it leaves out `CAPTUA_LOG_LEVEL`, `CAPTUA_OFFSITE_HOLD_FILE`, `CAPTUA_FISQUA_BASE_URL`, `CAPTUA_FISQUA_RETRY_BASE`, `CAPTUA_FISQUA_RETRY_MAX`, `CAPTUA_FISQUA_POLL_INTERVAL` and `CAPTUA_FISQUA_TIMEOUT`, which keep their defaults unless set. The block above shows only the settings relevant to the API. +The settings the backend reads are declared in `Settings`, `backend/app/core/config.py`, which is the authoritative list. The repository root `.env.example` lists every one of them, the prefixed ones under their `DTK_` names; `scripts/tests/env-example.test.sh` checks the two against each other. The block above shows only the settings relevant to the API. The server host and port are not configurable by environment: the pixi `start` and `dev` tasks hardcode `--host 0.0.0.0 --port 8000`, so the native backend listens on every interface.