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
This commit is contained in:
2026-08-20 14:40:45 -04:00
parent 8ca95db08a
commit 44a0f8c7a4
2 changed files with 193 additions and 109 deletions
+77
View File
@@ -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 <admin_token>"
```
---
### 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 <admin_token>" \
-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/))
+116 -109
View File
@@ -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 <admin_token>"
```
---
### 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 <admin_token>" \
-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))