From 44a0f8c7a4b3005d0cc0d1d3078b35c963a5d70c Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 20 Aug 2026 14:40:45 -0400 Subject: [PATCH] docs(api): document unified tunable system settings endpoints The scattered scan-settings JSON routes are superseded by the new admin-only /api/system/settings pair backed by the SettingsRegistry (introduced in 885f6d8 / bc47450). - Rewrite system/settings.md around GET/PUT /api/system/settings: SettingEntry metadata shape (type, min/max, requires_restart, category, group, is_default), type-aware validation rules, and the full tunable-setting catalog (scanner, general, security, api, sync, performance) with defaults, ranges, and restart requirements - Note the legacy /api/libraries/scan-settings routes as back-compat only (they now refresh the registry cache on write) - Add system/config.md for GET/PUT /api/system/config: raw key/value system configuration (e.g. base_url), including validation notes and guidance to prefer the typed settings endpoint for registry keys --- docs/developer/api/system/config.md | 77 +++++++++ docs/developer/api/system/settings.md | 225 +++++++++++++------------- 2 files changed, 193 insertions(+), 109 deletions(-) create mode 100644 docs/developer/api/system/config.md diff --git a/docs/developer/api/system/config.md b/docs/developer/api/system/config.md new file mode 100644 index 0000000..9ba8fe9 --- /dev/null +++ b/docs/developer/api/system/config.md @@ -0,0 +1,77 @@ +# System Config API + +## Overview + +Raw key/value system configuration storage (backed by the `system_config` table). Unlike the typed [System Settings API](settings.md), this endpoint reads and writes arbitrary config keys as plain strings — including keys without registry metadata, such as `base_url`. + +**Base URL**: `/api/system` +**Authentication**: Admin JWT token required +**Content-Type**: `application/json` + +--- + +## Endpoints + +### Get System Configuration + +Retrieve all system configuration entries as a flat key/value map. + +**Endpoint**: `GET /api/system/config` + +**Authentication**: Admin role required + +**Response**: **200 OK** + +```json +{ + "base_url": "http://192.168.1.100:8765", + "default_timezone": "America/New_York" +} +``` + +**Example**: + +```bash +curl -X GET https://bookhoard.example.com/api/system/config \ + -H "Authorization: Bearer " +``` + +--- + +### Update System Configuration + +Update one or more config values. + +**Endpoint**: `PUT /api/system/config` + +**Authentication**: Admin role required + +**Request Body**: a flat map of keys to string values. Only the supplied keys are updated. + +```json +{ + "base_url": "https://bookhoard.example.com" +} +``` + +**Validation**: values for known keys are validated where applicable — for example, `default_timezone` must be a valid IANA timezone (`time.LoadLocation`); invalid values return `400` without persisting. + +**Response**: **200 OK** on success; `400` (invalid value/format), `401`, `403`, `500` on failure. + +**Example**: + +```bash +curl -X PUT https://bookhoard.example.com/api/system/config \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"base_url": "https://bookhoard.example.com"}' +``` + +> **Note:** settings that appear in the typed settings registry (e.g. `default_timezone`) are better managed through [`PUT /api/system/settings`](settings.md), which also returns metadata and reload hints. Writes through either endpoint refresh the shared registry cache. + +--- + +## Related Endpoints + +- [System Settings API](settings.md) — typed, validated tunable settings with metadata +- `GET /api/devices/:id/sidecar` — device setup config derived from system config (see [Devices API](../devices/)) diff --git a/docs/developer/api/system/settings.md b/docs/developer/api/system/settings.md index 13e24a7..f39169a 100644 --- a/docs/developer/api/system/settings.md +++ b/docs/developer/api/system/settings.md @@ -1,10 +1,10 @@ -# System Scan Settings API +# System Settings API ## Overview -The System Scan Settings API allows administrators to configure system-wide scan settings that apply to all libraries. These settings control the automatic scanning behavior for the entire Bookhoard system. +The System Settings API is the canonical way to read and write Bookhoard's tunable system settings (scanning, security, rate limits, sync, performance, and defaults). Every setting carries full metadata — type, range, category, description, and whether a restart is required — so the admin UI (and API clients) can render and validate settings generically. -**Base URL**: `/api/libraries` +**Base URL**: `/api/system` **Authentication**: Admin JWT token required **Content-Type**: `application/json` @@ -12,49 +12,61 @@ The System Scan Settings API allows administrators to configure system-wide scan ## Endpoints -### Get System Scan Settings +### List All Settings -Retrieve the current system-wide scan settings. +Retrieve every known tunable setting with its current value and metadata. -**Endpoint**: `GET /api/libraries/scan-settings` +**Endpoint**: `GET /api/system/settings` **Authentication**: Admin role required -**Response**: - -- **200 OK**: Returns current scan settings -- **401 Unauthorized**: Invalid or missing authentication -- **403 Forbidden**: User does not have admin role -- **500 Internal Server Error**: Server error - -**Response Body**: +**Response**: **200 OK** ```json -{ - "scan_poll_interval_seconds": 60, - "auto_scan_enabled": true -} +[ + { + "key": "scan_poll_interval_seconds", + "value": "60", + "type": "int", + "min": "1", + "max": "3600", + "requires_restart": false, + "category": "scanner", + "group": "Scanning", + "description": "How often to scan all libraries (seconds)", + "is_default": true + } +] ``` -**Fields**: +**Entry fields**: -- `scan_poll_interval_seconds` (integer): How often to poll for file changes in seconds (1-3600) -- `auto_scan_enabled` (boolean): Whether auto-scanning is enabled system-wide +| Field | Type | Description | +| ------------------ | ------- | -------------------------------------------------------- | +| `key` | string | Setting identifier (stable API name) | +| `value` | string | Current value (validated/clamped by the registry) | +| `type` | string | `int`, `bool`, or `string` | +| `min` / `max` | string | Range bounds for `int` settings (omitted otherwise) | +| `requires_restart` | boolean | Change takes effect only after a server restart | +| `category` | string | Coarse area: `scanner`, `security`, `api`, `sync`, `performance`, `general` | +| `group` | string | Sub-section shown in the admin UI | +| `description` | string | Human-readable description | +| `is_default` | boolean | True when the current value equals the compiled default | **Example**: ```bash -curl -X GET https://bookhoard.example.com/api/libraries/scan-settings \ +curl -X GET https://bookhoard.example.com/api/system/settings \ -H "Authorization: Bearer " ``` --- -### Update System Scan Settings +### Update a Setting -Update the system-wide scan settings. +Validate, persist, and reload a single setting. -**Endpoint**: `PUT /api/libraries/scan-settings` +**Endpoint**: `PUT /api/system/settings` **Authentication**: Admin role required @@ -62,128 +74,123 @@ Update the system-wide scan settings. ```json { - "scan_poll_interval_seconds": 30, - "auto_scan_enabled": true + "key": "scan_poll_interval_seconds", + "value": "30" } ``` -**Fields**: +| Field | Type | Required | Description | +| ------- | ------ | -------- | ------------------------------- | +| `key` | string | Yes | Setting key (from the list) | +| `value` | string | Yes | New value, as a string | -- `scan_poll_interval_seconds` (integer, required): How often to poll for file changes in seconds - - Minimum: 1 (1 second) - - Maximum: 3600 (1 hour) - - Default: 60 -- `auto_scan_enabled` (boolean, required): Whether auto-scanning is enabled system-wide - - Default: true - -**Response**: - -- **200 OK**: Settings updated successfully -- **400 Bad Request**: Invalid request parameters -- **401 Unauthorized**: Invalid or missing authentication -- **403 Forbidden**: User does not have admin role -- **500 Internal Server Error**: Server error - -**Success Response Body**: +**Response**: **200 OK** ```json { - "scan_poll_interval_seconds": 30, - "auto_scan_enabled": true, - "message": "scan settings updated successfully" + "key": "scan_poll_interval_seconds", + "value": "30", + "type": "int", + "min": "1", + "max": "3600", + "requires_restart": false, + "category": "scanner", + "group": "Scanning", + "description": "How often to scan all libraries (seconds)", + "is_default": false, + "reload_required": false, + "message": "" } ``` -**Error Response Body**: +- `reload_required: true` means the change takes effect only after a restart (e.g. rate limits, worker pool, lockout settings). +- Validation is type-aware: `int` values are checked against `min`/`max`, `bool` values must parse, `default_timezone` must be a valid IANA timezone via `time.LoadLocation`, and strings must be non-empty. -```json -{ - "error": "error message" -} -``` - -**Validation Rules**: - -- `scan_poll_interval_seconds` must be between 1 and 3600 seconds (1 second to 1 hour) -- Both fields are required +**Errors**: `400` (unknown key, invalid value, out of range), `401`, `403`, `503` (settings registry not initialized). **Example**: ```bash -curl -X PUT https://bookhoard.example.com/api/libraries/scan-settings \ +curl -X PUT https://bookhoard.example.com/api/system/settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ - -d '{ - "scan_poll_interval_seconds": 30, - "auto_scan_enabled": true - }' + -d '{"key": "scan_poll_interval_seconds", "value": "30"}' ``` --- -## Behavior +## Setting Catalog -### Poll Interval +Current tunable settings by category: -The `scan_poll_interval_seconds` setting determines how often the system will poll library folders for file changes as a fallback to real-time file watching. +**Scanner** (`scanner`) -**Constraints**: +| Key | Default | Range | Restart | Description | +| ------------------------------ | ------- | -------- | ------- | ----------------------------------------- | +| `scan_poll_interval_seconds` | `60` | 1-3600 | No | How often to scan all libraries (seconds) | +| `auto_scan_enabled` | `true` | - | No | Whether auto-scanning is enabled | -- Minimum: 1 second -- Maximum: 3600 seconds (1 hour) -- Default: 60 seconds +**General** (`general`) -### Auto-Scan Toggle +| Key | Default | Restart | Description | +| ----------------- | ------- | ------- | ------------------------- | +| `default_timezone`| `UTC` | No | System default timezone | -The `auto_scan_enabled` setting acts as a master switch for automatic scanning: +**Security** (`security`) -- When `true`: File watching and polling fallback are active for all libraries -- When `false`: No automatic file monitoring occurs (manual scans still available) +| Key | Default | Range | Restart | Description | +| ---------------------------- | --------- | ------------ | ------- | ---------------------------------------------- | +| `session_duration_seconds` | `604800` | 300-31536000 | No | How long a login session stays valid | +| `password_min_length` | `8` | 1-128 | No | Minimum password length | +| `password_require_upper` | `true` | - | No | Require at least one uppercase letter | +| `password_require_lower` | `true` | - | No | Require at least one lowercase letter | +| `password_require_number` | `true` | - | No | Require at least one number | +| `password_require_special` | `true` | - | No | Require at least one special character | +| `auth_rate_limit_per_min` | `10` | 1-10000 | **Yes** | Global auth API rate limit (req/min) | +| `login_max_attempts` | `5` | 1-100 | **Yes** | Failed login attempts before lockout | +| `login_lockout_minutes` | `15` | 1-10080 | **Yes** | Lockout duration after failed logins | -### File Watching System +**API** (`api`) -The scan settings control the file watching system which consists of: +| Key | Default | Range | Restart | Description | +| ------------------------------- | ------- | --------- | ------- | ------------------------------------ | +| `opds_default_page_size` | `50` | 1-500 | No | Default OPDS page size | +| `opds_max_page_size` | `200` | 1-1000 | No | Maximum OPDS page size | +| `device_rate_sync_per_min` | `60` | 1-10000 | No | Device sync requests per minute | +| `device_rate_progress_per_min` | `120` | 1-10000 | No | Device progress requests per minute | +| `device_rate_metadata_per_min` | `30` | 1-10000 | No | Device metadata requests per minute | -1. **Real-time file watching**: Uses fsnotify to detect file changes immediately -2. **Polling fallback**: If file watching fails or is unavailable, polls folders at the configured interval +**Sync** (`sync`) -The system applies these settings to all configured libraries automatically on startup. +| Key | Default | Range | Restart | Description | +| ------------------------------- | ------- | -------- | ------- | -------------------------------------------------- | +| `annotation_tombstone_ttl_days` | `30` | 1-3650 | No | How long deleted annotations are kept before purge | +| `sync_queue_interval_seconds` | `5` | 1-3600 | **Yes** | How often the sync queue flushes | +| `sync_queue_batch_size` | `50` | 1-10000 | **Yes** | Max items processed per sync queue flush | + +**Performance** (`performance`) + +| Key | Default | Range | Restart | Description | +| ------------------------ | ------- | --------- | ------- | ------------------------------------------- | +| `conversion_cache_ttl_hours` | `24` | 1-720 | No | How long converted (KEPUB) files are cached | +| `worker_pool_size` | `3` | 1-100 | **Yes** | Number of background worker goroutines | +| `worker_queue_cap` | `100` | 1-10000 | **Yes** | Background worker job queue capacity | --- -## Error Codes +## Legacy Scan Settings Routes -| Status Code | Error Description | -| ----------- | ---------------------------------------------------------- | -| 400 | Invalid request parameters (e.g., frequency outside range) | -| 401 | Missing or invalid JWT token | -| 403 | User lacks admin role | -| 500 | Internal server error (e.g., database connection issue) | +The older JSON routes still work for backward compatibility and now refresh the settings registry cache on write, but they are **superseded** by `GET/PUT /api/system/settings`: + +- `GET /api/libraries/scan-settings` — returns only `scan_poll_interval_seconds` and `auto_scan_enabled` +- `PUT /api/libraries/scan-settings` — accepts `{ "scan_poll_interval_seconds": int, "auto_scan_enabled": bool }` + +Both fields are backed by the same registry entries documented above. --- ## Related Endpoints -- `POST /api/libraries/{id}/scan` - Manually trigger a scan for a specific library (admin only) -- `GET /api/libraries` - List all libraries -- `GET /api/libraries/{id}` - Get details for a specific library - ---- - -## Migration Notes - -This API has been updated to use a new polling-based scanning system. The following changes were made: - -- **Changed**: `scan_frequency_minutes` renamed to `scan_poll_interval_seconds` -- **Changed**: Unit changed from minutes to seconds (15-1440 minutes → 1-3600 seconds) -- **Removed**: Old scheduler-based scanning system -- **Added**: Real-time file watching with polling fallback -- **Preserved**: API endpoint paths remain the same - -The new system ensures that: - -1. File changes are detected in real-time when possible (via fsnotify) -2. Polling fallback catches missed events at the configured interval -3. Settings apply to all libraries system-wide -4. Only administrators can modify scan settings -5. The `auto_scan_enabled` setting controls both file watching and polling +- `GET/PUT /api/system/config` — raw key/value system configuration (see [System Config API](config.md)) +- `POST /api/scanner/scan` — trigger a manual scan (see [Scanner API](../scanner/)) +- `GET /api/admin/hash-conflicts` — duplicates found during hashing (see [Hash Conflicts API](../admin/hash-conflicts.md))