docs: restructure documentation into audience-based portals
BREAKING CHANGE: Documentation URLs have changed New structure: - user/ - End-user documentation (device setup, sync guides, frontend) - developer/ - Developer documentation (API reference, protocols, specs) - operations/ - Operations documentation (deployment, troubleshooting) - contributing/ - Contribution guides Changes: - Created portal INDEX.md files for each audience section - Moved device guides to user/devices/ (kobo-setup.md, koreader-setup.md) - Moved API docs to developer/ (api-reference.md, collections-api.md) - Moved sync guide to user/sync-guide.md - Moved troubleshooting to operations/troubleshooting.md - Moved all split API docs to developer/api/ - Renamed protocol files (kobo-protocol.md, koreader-protocol.md) - Added placeholder user guides (frontend, user-areas, settings, admin) - Updated all internal links to new paths - Updated Go code (http_handler.go, navigation.go) for new paths - Updated main INDEX.md for audience-based navigation Benefits: - Clear separation of user and developer documentation - Scalable structure for future user guide expansion - Better organization and discoverability - Audience-specific landing pages Related to DOCS_IMPLEMENTATION_PLAN.md Phase 2 completion
This commit is contained in:
@@ -0,0 +1,493 @@
|
||||
# Collections API Documentation
|
||||
|
||||
Complete API reference for collection management endpoints.
|
||||
|
||||
**Base Path**: `/api/collections`
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Collections allow you to organize your books into custom categories with:
|
||||
- **Auto-assignment rules**: Automatically add books matching criteria
|
||||
- **View settings**: Per-device display preferences
|
||||
- **Shelf mappings**: Sync to device-specific shelves (Kobo, KOReader)
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
### List All Collections
|
||||
|
||||
**Endpoint**: `GET /api/collections`
|
||||
**Authentication**: Required
|
||||
**Description**: Get all collections for the authenticated user
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"collections": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "Science Fiction",
|
||||
"description": "My sci-fi collection",
|
||||
"color": "#FF5733",
|
||||
"icon": "🚀",
|
||||
"auto_assign_rules": [
|
||||
{
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Science Fiction",
|
||||
"priority": 1,
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"view_settings": {
|
||||
"kobo": {
|
||||
"view_mode": "grid",
|
||||
"sort_order": "name",
|
||||
"items_per_page": 24
|
||||
}
|
||||
},
|
||||
"created_at": "2026-02-01T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Create Collection
|
||||
|
||||
**Endpoint**: `POST /api/collections`
|
||||
**Authentication**: Required
|
||||
**Description**: Create a new collection
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"name": "To Read",
|
||||
"description": "Books I want to read soon",
|
||||
"color": "#00FF00",
|
||||
"icon": "📖",
|
||||
"auto_assign_rules": [
|
||||
{
|
||||
"field": "tags",
|
||||
"operator": "contains",
|
||||
"value": "to-read",
|
||||
"priority": 1,
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"view_settings": {
|
||||
"kobo": {
|
||||
"view_mode": "grid"
|
||||
},
|
||||
"koreader": {
|
||||
"view_mode": "list"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `name` (required): Collection name (max 255 chars)
|
||||
- `description` (optional): Collection description
|
||||
- `color` (optional): Hex color code (e.g., "#FF5733")
|
||||
- `icon` (optional): Emoji icon (e.g., "🚀", "📖")
|
||||
- `auto_assign_rules` (optional): Array of rule objects
|
||||
- `view_settings` (optional): Per-device display preferences
|
||||
|
||||
**Rule Object**:
|
||||
- `field`: Field to match on (genre, author, series, language, publisher, copyright_year, tags)
|
||||
- `operator`: Comparison operator (equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than)
|
||||
- `value`: Value to compare against
|
||||
- `priority`: Rule priority (1 = highest)
|
||||
- `enabled`: Whether rule is active
|
||||
|
||||
**Response** (201 Created): Collection object
|
||||
|
||||
### Get Collection Details
|
||||
|
||||
**Endpoint**: `GET /api/collections/{id}`
|
||||
**Authentication**: Required
|
||||
**Description**: Get single collection with all books
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "Science Fiction",
|
||||
"description": "My sci-fi collection",
|
||||
"color": "#FF5733",
|
||||
"icon": "🚀",
|
||||
"books": [
|
||||
{
|
||||
"media_item_id": "660e8400-e29b-41d4-a716-446655440000",
|
||||
"title": "Foundation",
|
||||
"author": "Isaac Asimov",
|
||||
"cover_image_path": "/covers/foundation.jpg"
|
||||
}
|
||||
],
|
||||
"auto_assign_rules": [],
|
||||
"view_settings": {},
|
||||
"created_at": "2026-02-01T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Update Collection
|
||||
|
||||
**Endpoint**: `PUT /api/collections/{id}`
|
||||
**Authentication**: Required
|
||||
**Description**: Update collection details
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Request Body**: All fields are optional
|
||||
```json
|
||||
{
|
||||
"name": "Sci-Fi Favorites",
|
||||
"description": "Updated description",
|
||||
"color": "#BLUE",
|
||||
"icon": "🌟"
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (200 OK): Updated collection object
|
||||
|
||||
### Delete Collection
|
||||
|
||||
**Endpoint**: `DELETE /api/collections/{id}`
|
||||
**Authentication**: Required
|
||||
**Description**: Delete a collection (books are NOT deleted)
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Response** (204 No Content)
|
||||
|
||||
---
|
||||
|
||||
## Collection Books
|
||||
|
||||
### Add Books to Collection
|
||||
|
||||
**Endpoint**: `POST /api/collections/{id}/books`
|
||||
**Authentication**: Required
|
||||
**Description**: Add one or more books to a collection
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"book_ids": [
|
||||
"660e8400-e29b-41d4-a716-446655440000",
|
||||
"770e8400-e29b-41d4-a716-446655440000",
|
||||
"880e8400-e29b-41d4-a716-446655440000"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (204 No Content)
|
||||
|
||||
### Remove Book from Collection
|
||||
|
||||
**Endpoint**: `DELETE /api/collections/{id}/books/{bookId}`
|
||||
**Authentication**: Required
|
||||
**Description**: Remove a single book from a collection
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
- `bookId`: Media Item UUID
|
||||
|
||||
**Response** (204 No Content)
|
||||
|
||||
### Bulk Remove Books
|
||||
|
||||
**Endpoint**: `POST /api/collections/{id}/books/bulk-remove`
|
||||
**Authentication**: Required
|
||||
**Description**: Remove multiple books at once (efficient)
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"book_ids": [
|
||||
"660e8400-e29b-41d4-a716-446655440000",
|
||||
"770e8400-e29b-41d4-a716-446655440000"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"removed": 2,
|
||||
"total": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example**: Removing 2 books, both successfully removed
|
||||
|
||||
### Get Collection Books
|
||||
|
||||
**Endpoint**: `GET /api/collections/{id}/books`
|
||||
**Authentication**: Required
|
||||
**Description**: Get all books in a collection
|
||||
|
||||
**Path Parameters**:
|
||||
- `id`: Collection UUID
|
||||
|
||||
**Query Parameters**:
|
||||
- `limit` (optional): Number of books to return (default: 50)
|
||||
- `offset` (optional): Number of books to skip (default: 0)
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"books": [
|
||||
{
|
||||
"media_item_id": "660e8400-e29b-41d4-a716-446655440000",
|
||||
"title": "Foundation",
|
||||
"author": "Isaac Asimov",
|
||||
"cover_image_path": "/covers/foundation.jpg",
|
||||
"added_at": "2026-02-01T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collection Rules
|
||||
|
||||
### Test Collection Rules
|
||||
|
||||
**Endpoint**: `POST /api/collections/test-rules`
|
||||
**Authentication**: Required
|
||||
**Description**: Test which books would match given rules (without saving)
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Science Fiction"
|
||||
},
|
||||
{
|
||||
"field": "author",
|
||||
"operator": "contains",
|
||||
"value": "Asimov"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Fields**:
|
||||
- `genre`: Book genre
|
||||
- `author`: Book author
|
||||
- `series`: Book series name
|
||||
- `language`: Book language
|
||||
- `publisher`: Publisher name
|
||||
- `copyright_year`: Publication year (numeric comparison)
|
||||
- `tags`: Book tags
|
||||
|
||||
**Supported Operators**:
|
||||
- `equals`: Exact match
|
||||
- `not_equals`: Not equal
|
||||
- `contains`: Contains substring (case-insensitive)
|
||||
- `not_contains`: Does not contain
|
||||
- `starts_with`: Starts with (case-insensitive)
|
||||
- `ends_with`: Ends with (case-insensitive)
|
||||
- `greater_than`: Greater than (numeric)
|
||||
- `less_than`: Less than (numeric)
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"matches": [
|
||||
{
|
||||
"media_item_id": "660e8400-e29b-41d4-a716-446655440000",
|
||||
"title": "Foundation",
|
||||
"author": "Isaac Asimov",
|
||||
"cover_image_path": "/covers/foundation.jpg",
|
||||
"match_reason": "Matched rule: genre equals Science Fiction"
|
||||
}
|
||||
],
|
||||
"total": 42
|
||||
}
|
||||
```
|
||||
|
||||
**Use Case**: Test rules before creating collection to verify correct book matching
|
||||
|
||||
---
|
||||
|
||||
## Device Shelf Mappings
|
||||
|
||||
### Get Device Shelf Mappings
|
||||
|
||||
**Endpoint**: `GET /api/devices/{deviceId}/collections`
|
||||
**Authentication**: Required
|
||||
**Description**: Get all collection-to-shelf mappings for a device
|
||||
|
||||
**Path Parameters**:
|
||||
- `deviceId`: Device UUID
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"mappings": [
|
||||
{
|
||||
"id": "990e8400-e29b-41d4-a716-446655440000",
|
||||
"collection_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"collection_name": "Science Fiction",
|
||||
"device_shelf_name": "Sci-Fi",
|
||||
"sync_direction": "bidirectional",
|
||||
"created_at": "2026-02-01T10:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Create Shelf Mapping
|
||||
|
||||
**Endpoint**: `POST /api/devices/{deviceId}/collections`
|
||||
**Authentication**: Required
|
||||
**Description**: Map a collection to a device shelf
|
||||
|
||||
**Path Parameters**:
|
||||
- `deviceId`: Device UUID
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"collection_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"device_shelf_name": "Sci-Fi",
|
||||
"sync_direction": "bidirectional"
|
||||
}
|
||||
```
|
||||
|
||||
**Sync Directions**:
|
||||
- `bidirectional`: Sync both ways between Bookhoard and device
|
||||
- `book_to_hoard`: Bookhoard → Device only
|
||||
- `device_to_hoard`: Device → Bookhoard only
|
||||
- `none`: No sync (mapping only)
|
||||
|
||||
**Response** (201 Created): Mapping object
|
||||
|
||||
### Update Shelf Mapping
|
||||
|
||||
**Endpoint**: `PUT /api/devices/{deviceId}/collections/{collectionId}`
|
||||
**Authentication**: Required
|
||||
**Description**: Update existing shelf mapping
|
||||
|
||||
**Path Parameters**:
|
||||
- `deviceId`: Device UUID
|
||||
- `collectionId`: Collection UUID
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"device_shelf_name": "Science Fiction",
|
||||
"sync_direction": "book_to_hoard"
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (200 OK): Updated mapping object
|
||||
|
||||
### Delete Shelf Mapping
|
||||
|
||||
**Endpoint**: `DELETE /api/devices/{deviceId}/collections/{collectionId}`
|
||||
**Authentication**: Required
|
||||
**Description**: Remove shelf mapping
|
||||
|
||||
**Path Parameters**:
|
||||
- `deviceId`: Device UUID
|
||||
- `collectionId`: Collection UUID
|
||||
|
||||
**Response** (204 No Content)
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints may return these errors:
|
||||
|
||||
**400 Bad Request**:
|
||||
```json
|
||||
{
|
||||
"error": "invalid request: validation failed"
|
||||
}
|
||||
```
|
||||
|
||||
**401 Unauthorized**:
|
||||
```json
|
||||
{
|
||||
"error": "authentication required"
|
||||
}
|
||||
```
|
||||
|
||||
**404 Not Found**:
|
||||
```json
|
||||
{
|
||||
"error": "collection not found"
|
||||
}
|
||||
```
|
||||
|
||||
**500 Internal Server Error**:
|
||||
```json
|
||||
{
|
||||
"error": "internal server error"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
- **Authenticated**: 100 requests per minute
|
||||
- **Unauthenticated**: 10 requests per minute
|
||||
|
||||
Headers included:
|
||||
```
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 95
|
||||
X-RateLimit-Reset: 1643723400
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bruno Tests
|
||||
|
||||
Complete API tests available in `bruno/collections/`:
|
||||
|
||||
- `Get Collections.bru`
|
||||
- `Create Collection.bru`
|
||||
- `Get Collection.bru`
|
||||
- `Update Collection.bru`
|
||||
- `Delete Collection.bru`
|
||||
- `Add Books to Collection.bru`
|
||||
- `Remove Book from Collection.bru`
|
||||
- `Get Book Collections.bru`
|
||||
- `Test Collection Rules.bru`
|
||||
- `Bulk Remove Books.bru`
|
||||
|
||||
Run tests:
|
||||
```bash
|
||||
bruno run bruno/collections/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2026-02-01
|
||||
**Version**: 1.0
|
||||
Reference in New Issue
Block a user