docs(saved-filters,dashboard,system,series): Beam WO3 batch 5 — 15 endpoints + dedupe
Beam drafted 15 frontmatter'd docs (saved-filters CRUD, dashboard
sections/preferences/restore, system config/settings, series list + books);
Claude verified shapes against handlers and pinned the registry:
- saved-filters: SavedFilterResponse pinned by type (RFC3339 timestamps here,
unlike most of the API); create/update share CreateSavedFilterRequest
(resource_type oneof media-items|collections|devices)
- dashboard: preferences GET pin {hidden_collections, collection_order,
items_per_section}; PUT returns full UserDashboardPreferences row; sections
wrapper {sections}
- system: GET+PUT /api/system/config are admin-defined key/value maps — new
AnyJSONObject sentinel (gate shape-checks without key comparison; request
side too). UpdateSystemConfiguration returns {status, message} — verified,
not guessed. GET /api/system/settings returns []SettingEntry.
- series: {series[{name, library_id, book_count, total_in_series,
cover_paths, last_entry_at}], total, limit, offset}; books by
?name=&library_id= with family-lens 403
Dedupe: deleted dashboard.md, system/config.md, system/settings.md — each
covered the same endpoints as Beam's per-endpoint files (content merged by
the gate-verified shapes; old prose recoverable from git).
Beam clarifications confirmed: scanner routes are WO5 (docs exist);
progress/books-match are WO5.
This commit is contained in:
1 parent
7471ca99e8
commit
fa00d6d3cb
20 files changed
+714
-416
No files matched your search
@@ -1,77 +0,0 @@
|
||||
# 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/))
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/system/config
|
||||
auth: jwt+admin
|
||||
area: system
|
||||
---
|
||||
<!-- source: internal/handlers/sidecar.go:374 SidecarHandler.GetSystemConfiguration -->
|
||||
|
||||
# Get System Configuration
|
||||
|
||||
Returns all system-wide configuration key/value pairs (backed by the
|
||||
`system_config` table).
|
||||
|
||||
## Example response
|
||||
|
||||
```json example-response
|
||||
{
|
||||
"base_url": "https://books.example.com",
|
||||
"opds_base_url": "https://books.example.com/opds",
|
||||
"api_base_url": "https://books.example.com/api"
|
||||
}
|
||||
```
|
||||
|
||||
UNVERIFIED: exact config key set is arbitrary (admin-defined); example shows common keys only.
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Body | When |
|
||||
| ------ | ---- | ---- |
|
||||
| 500 | `{"error": "failed to fetch system config"}` | unexpected server error |
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/system/settings
|
||||
auth: jwt+admin
|
||||
area: system
|
||||
---
|
||||
<!-- source: internal/handlers/system_settings.go:44 SystemSettingsHandler.GetSettings -->
|
||||
|
||||
# Get System Settings
|
||||
|
||||
Returns metadata and current values for every known tunable system setting.
|
||||
|
||||
## Example response
|
||||
|
||||
```json example-response
|
||||
[
|
||||
{
|
||||
"key": "scan_poll_interval_seconds",
|
||||
"value": "60",
|
||||
"type": "int",
|
||||
"requires_restart": false,
|
||||
"category": "scanner",
|
||||
"group": "Scanning",
|
||||
"description": "How often to scan all libraries (seconds)",
|
||||
"is_default": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Body | When |
|
||||
| ------ | ---- | ---- |
|
||||
| 503 | `{"error": "settings registry not initialized"}` | registry not wired |
|
||||
@@ -1,196 +0,0 @@
|
||||
# System Settings API
|
||||
|
||||
## Overview
|
||||
|
||||
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/system`
|
||||
**Authentication**: Admin JWT token required
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
### List All Settings
|
||||
|
||||
Retrieve every known tunable setting with its current value and metadata.
|
||||
|
||||
**Endpoint**: `GET /api/system/settings`
|
||||
|
||||
**Authentication**: Admin role required
|
||||
|
||||
**Response**: **200 OK**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Entry fields**:
|
||||
|
||||
| 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/system/settings \
|
||||
-H "Authorization: Bearer <admin_token>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Update a Setting
|
||||
|
||||
Validate, persist, and reload a single setting.
|
||||
|
||||
**Endpoint**: `PUT /api/system/settings`
|
||||
|
||||
**Authentication**: Admin role required
|
||||
|
||||
**Request Body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "scan_poll_interval_seconds",
|
||||
"value": "30"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------- | ------ | -------- | ------------------------------- |
|
||||
| `key` | string | Yes | Setting key (from the list) |
|
||||
| `value` | string | Yes | New value, as a string |
|
||||
|
||||
**Response**: **200 OK**
|
||||
|
||||
```json
|
||||
{
|
||||
"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": ""
|
||||
}
|
||||
```
|
||||
|
||||
- `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.
|
||||
|
||||
**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/system/settings \
|
||||
-H "Authorization: Bearer <admin_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"key": "scan_poll_interval_seconds", "value": "30"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Setting Catalog
|
||||
|
||||
Current tunable settings by category:
|
||||
|
||||
**Scanner** (`scanner`)
|
||||
|
||||
| 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 |
|
||||
|
||||
**General** (`general`)
|
||||
|
||||
| Key | Default | Restart | Description |
|
||||
| ----------------- | ------- | ------- | ------------------------- |
|
||||
| `default_timezone`| `UTC` | No | System default timezone |
|
||||
|
||||
**Security** (`security`)
|
||||
|
||||
| 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 |
|
||||
|
||||
**API** (`api`)
|
||||
|
||||
| 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 |
|
||||
|
||||
**Sync** (`sync`)
|
||||
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
## Legacy Scan Settings Routes
|
||||
|
||||
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
|
||||
|
||||
- `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))
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
method: PUT
|
||||
path: /api/system/config
|
||||
auth: jwt+admin
|
||||
area: system
|
||||
---
|
||||
<!-- source: internal/handlers/sidecar.go:396 SidecarHandler.UpdateSystemConfiguration -->
|
||||
|
||||
# Update System Configuration
|
||||
|
||||
Updates one or more system-wide configuration keys. Admin only. When
|
||||
`base_url` is provided, derived keys (`opds_base_url`, `api_base_url`) are
|
||||
updated automatically. `default_timezone` is validated as a timezone.
|
||||
|
||||
## Example request
|
||||
|
||||
```json example-request
|
||||
{
|
||||
"base_url": "https://books.example.com",
|
||||
"default_timezone": "UTC"
|
||||
}
|
||||
```
|
||||
|
||||
## Example response
|
||||
|
||||
```json example-response
|
||||
{
|
||||
"status": "success",
|
||||
"message": "System configuration updated"
|
||||
}
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
| ----- | ---- | -------- | ----- |
|
||||
| (any key) | string | No | Arbitrary config key/value pairs |
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Body | When |
|
||||
| ------ | ---- | ---- |
|
||||
| 400 | `{"error": "invalid request format"}` / `{"error": "invalid timezone"}` | malformed body or bad timezone |
|
||||
| 401 | `{"error": "unauthorized"}` | no/invalid JWT |
|
||||
| 403 | `{"error": "admin access required"}` | non-admin |
|
||||
| 500 | `{"error": "failed to update config key: ..."}` / `{"error": "failed to update default timezone"}` / `{"error": "failed to update derived config key: ..."}` | unexpected server error |
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
method: PUT
|
||||
path: /api/system/settings
|
||||
auth: jwt+admin
|
||||
area: system
|
||||
---
|
||||
<!-- source: internal/handlers/system_settings.go:65 SystemSettingsHandler.UpdateSetting -->
|
||||
|
||||
# Update System Setting
|
||||
|
||||
Validates, persists, and reloads a single tunable system setting. Admin only.
|
||||
|
||||
## Example request
|
||||
|
||||
```json example-request
|
||||
{
|
||||
"key": "scan_poll_interval_seconds",
|
||||
"value": "60"
|
||||
}
|
||||
```
|
||||
|
||||
## Example response
|
||||
|
||||
```json example-response
|
||||
{
|
||||
"key": "scan_poll_interval_seconds",
|
||||
"value": "60",
|
||||
"type": "int",
|
||||
"requires_restart": false,
|
||||
"category": "scanner",
|
||||
"group": "Scanning",
|
||||
"description": "How often to scan all libraries (seconds)",
|
||||
"is_default": true,
|
||||
"reload_required": false
|
||||
}
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
| ----- | ---- | -------- | ----- |
|
||||
| key | string | Yes | Known setting key |
|
||||
| value | string | Yes | Value (validated against type/bounds) |
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Body | When |
|
||||
| ------ | ---- | ---- |
|
||||
| 400 | `{"error": "invalid request"}` / `{"error": "key is required"}` / `{"error": "unknown setting key: ..."}` / `{"error": "value must be ..."}` | malformed body or invalid value |
|
||||
| 503 | `{"error": "settings registry not initialized"}` | registry not wired |
|
||||
Reference in new issue
Block a user