test(bruno): Add API tests for Kobo, Koreader, OPDS, collections
- Add Kobo bookmark/markup sync tests - Add Koreader progress/bookmark sync tests - Add OPDS feed tests - Add collection tests - Add sync tests for book linking
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
meta {
|
||||
name: Add Books to Collection
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}/books
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": [
|
||||
"{{book_id_1}}",
|
||||
"{{book_id_2}}",
|
||||
"{{book_id_3}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
meta {
|
||||
name: Create Collection
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/collections
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"name": "Science Fiction",
|
||||
"description": "My favorite sci-fi books",
|
||||
"color": "#ff0000",
|
||||
"icon": "🚀",
|
||||
"auto_assign_rules": [
|
||||
{
|
||||
"id": "rule-1",
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Science Fiction",
|
||||
"priority": 8
|
||||
}
|
||||
],
|
||||
"view_settings": {
|
||||
"sort_by": "title",
|
||||
"view_mode": "grid"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
meta {
|
||||
name: Create Device Mapping
|
||||
type: http
|
||||
seq: 9
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"collection_id": "{{collection_id}}",
|
||||
"device_shelf_name": "Sci-Fi",
|
||||
"sync_direction": "bidirectional"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Delete Collection
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Delete Device Mapping
|
||||
type: http
|
||||
seq: 11
|
||||
}
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Get Book Collections
|
||||
type: http
|
||||
seq: 12
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections/books/{{book_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Get Collection
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Get Collections
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections?include_auto=true&sort_by=name
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Get Device Mappings
|
||||
type: http
|
||||
seq: 8
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
meta {
|
||||
name: Remove Book from Collection
|
||||
type: http
|
||||
seq: 7
|
||||
}
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}/books/{{book_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
meta {
|
||||
name: Update Collection
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
put {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"name": "Sci-Fi Favorites",
|
||||
"description": "Updated description",
|
||||
"color": "#00ff00",
|
||||
"icon": "⭐",
|
||||
"auto_assign_rules": [
|
||||
{
|
||||
"id": "rule-2",
|
||||
"field": "series",
|
||||
"operator": "equals",
|
||||
"value": "Foundation",
|
||||
"priority": 9
|
||||
}
|
||||
],
|
||||
"view_settings": {
|
||||
"sort_by": "author",
|
||||
"view_mode": "list"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
meta {
|
||||
name: Update Device Mapping
|
||||
type: http
|
||||
seq: 10
|
||||
}
|
||||
|
||||
put {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"device_shelf_name": "Science Fiction",
|
||||
"sync_direction": "book_to_device"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
meta {
|
||||
name: "Kobo Bookmark Sync - Enhanced with ContentId Mapping"
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/kobo/bookmark
|
||||
body: json({
|
||||
"BookmarkSync": [
|
||||
{
|
||||
"BookmarkId": "bookmark_2",
|
||||
"ContentId": "kobo_xyz789",
|
||||
"BookmarkText": "Important note",
|
||||
"BookmarkType": "bookmark",
|
||||
"DateCreated": "2026-01-31T12:00:00Z"
|
||||
}
|
||||
]
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.Status == "Success"
|
||||
response.body.BookmarksSynced >= 0
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: "Kobo Initialization - Enhanced with ContentId Mapping"
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/v1/kobo/initialization
|
||||
body: none
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.ContentId exists()
|
||||
response.body.Categories exists()
|
||||
response.body.BookmannUUID exists()
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
meta {
|
||||
name: "Kobo Markup Sync - Enhanced with ContentId Mapping"
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/kobo/markup
|
||||
body: json({
|
||||
"ReadingSync": [
|
||||
{
|
||||
"ContentId": "kobo_abc123def456",
|
||||
"PercentRead": 60.0,
|
||||
"RemainingTimeMin": 120,
|
||||
"ReadingEvent": "BookRead",
|
||||
"LastModified": "2026-01-31T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"BookmarkSync": [
|
||||
{
|
||||
"BookmarkId": "bookmark_1",
|
||||
"ContentId": "kobo_abc123def456",
|
||||
"BookmarkText": "Great quote",
|
||||
"BookmarkType": "annotation",
|
||||
"DateCreated": "2026-01-31T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"Metadata": true
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.Status == "Success" || response.body.Status == "Partial"
|
||||
response.body.MarkupsSynced >= 0
|
||||
response.body.BookmarksSynced >= 0
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
meta {
|
||||
name: "KOReader Sync Annotations - Per-Annotation SHA-256"
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/koreader/sync/bookmarks
|
||||
body: json({
|
||||
"book_uuid": "{{book_uuid}}",
|
||||
"highlights": [
|
||||
{
|
||||
"text": "Quote from book 1",
|
||||
"pos0": "/6/4[chap1ref]!/4/2/1:0",
|
||||
"pos1": "/6/4[chap1ref]!/4/2/1:50",
|
||||
"color": "#ffff00",
|
||||
"page": 10,
|
||||
"book_sha256": "{{book_sha256}}"
|
||||
},
|
||||
{
|
||||
"text": "Quote from book 2 (different book)",
|
||||
"pos0": "/6/4[chap1ref]!/4/2/1:0",
|
||||
"pos1": "/6/4[chap1ref]!/4/2/1:50",
|
||||
"color": "#00ff00",
|
||||
"page": 15,
|
||||
"book_sha256": "{{another_book_sha256}}"
|
||||
}
|
||||
]
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{koreader_device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.highlights_synced >= 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
meta {
|
||||
name: "KOReader Sync Bookmarks - Enhanced with SHA-256"
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/koreader/sync/bookmarks
|
||||
body: json({
|
||||
"book_sha256": "{{book_sha256}}",
|
||||
"bookmarks": [
|
||||
{
|
||||
"text": "Important passage about chapter 3",
|
||||
"pos0": "/6/4[chap3ref]!/4/2/1:0",
|
||||
"page": 45,
|
||||
"type": "bookmark"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
{
|
||||
"notes": "My note about this section",
|
||||
"pos0": "/6/4[chap3ref]!/4/2/1:100",
|
||||
"page": 47,
|
||||
"text": "Quoted text from the book"
|
||||
}
|
||||
],
|
||||
"highlights": [
|
||||
{
|
||||
"text": "This is highlighted text",
|
||||
"pos0": "/6/4[chap3ref]!/4/2/1:50",
|
||||
"pos1": "/6/4[chap3ref]!/4/2/1:100",
|
||||
"color": "#ffff00",
|
||||
"page": 50
|
||||
}
|
||||
]
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{koreader_device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.sync_status == "completed"
|
||||
response.body.total_synced >= 0
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
meta {
|
||||
name: "KOReader Sync Progress - Priority Matching (SHA-256 only)"
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/koreader/sync/progress
|
||||
body: json({
|
||||
"sync_mode": "immediate",
|
||||
"books": [
|
||||
{
|
||||
"sha256": "{{book_sha256}}",
|
||||
"file_path": "/mnt/onboard/Unknown%20Book.epub",
|
||||
"percentage": 0.45,
|
||||
"page": 89,
|
||||
"total_pages": 200
|
||||
}
|
||||
]
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{koreader_device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200 || response.status == 202
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
meta {
|
||||
name: "KOReader Sync Progress - Enhanced with SHA-256"
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/v1/koreader/sync/progress
|
||||
body: json({
|
||||
"sync_mode": "immediate",
|
||||
"books": [
|
||||
{
|
||||
"uuid": "{{book_uuid}}",
|
||||
"sha256": "{{book_sha256}}",
|
||||
"file_path": "/mnt/onboard/The%20Hobbit.epub",
|
||||
"percentage": 0.65,
|
||||
"chapter": 5,
|
||||
"page": 142,
|
||||
"total_pages": 310,
|
||||
"epubcfi": "/6/4[chap1ref]!/4/2/1:0",
|
||||
"last_read": "2026-01-31T12:00:00Z",
|
||||
"title": "The Hobbit",
|
||||
"authors": ["J.R.R. Tolkien"]
|
||||
}
|
||||
]
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{koreader_device_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200 || response.status == 202
|
||||
response.body.sync_status exists()
|
||||
response.body.books_synced >= 0
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Download Book (EPUB)
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}
|
||||
}
|
||||
|
||||
vars:device_id, book_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Download Book (KEPUB)
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}?format=kepub
|
||||
}
|
||||
|
||||
vars:device_id, book_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Get Cover Image
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/cover/{{book_id}}
|
||||
}
|
||||
|
||||
vars:device_id, book_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Get Device Catalog
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/catalog?page=1&per_page=50
|
||||
}
|
||||
|
||||
vars:device_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Get Device Navigation
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/nav
|
||||
}
|
||||
|
||||
vars:device_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: List Formats
|
||||
type: http
|
||||
seq: 7
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/formats/{{book_id}}
|
||||
}
|
||||
|
||||
vars:device_id, book_id, opds_base_url
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Search Device Catalog
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{opds_base_url}}/devices/{{device_id}}/search?q=hobbit
|
||||
}
|
||||
|
||||
vars:device_id, opds_base_url
|
||||
@@ -0,0 +1,19 @@
|
||||
meta {
|
||||
name: "Download Device Sidecar File"
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/sidecar/download
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{user_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.headers["Content-Type"] contains "application/json"
|
||||
response.headers["Content-Disposition"] contains ".bookmann.json"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: "Get Device Sidecar Config"
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/sidecar
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{user_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.version == "1.0"
|
||||
response.body.bookmann exists()
|
||||
response.body.books exists()
|
||||
response.body.collections exists()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: "Get System Configuration"
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/system/config
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{admin_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.base_url exists()
|
||||
response.body.opds_base_url exists()
|
||||
response.body.api_base_url exists()
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
# Phase 8: Sidecar Configuration System - API Documentation
|
||||
|
||||
## Overview
|
||||
This document describes the sidecar configuration system that enables easy device setup for Kobo and KOReader devices.
|
||||
|
||||
## What is a Sidecar File?
|
||||
|
||||
A sidecar file (`.bookmann.json`) is a configuration file that contains all the information a device needs to connect to Bookmann, including:
|
||||
- OPDS catalog URL for wireless book browsing
|
||||
- Sync API endpoints for progress sync
|
||||
- Book inventory with SHA-256 hashes
|
||||
- Collection metadata with shelf mappings
|
||||
|
||||
## Authentication
|
||||
All endpoints require authentication:
|
||||
- User endpoints: Use user JWT token (Authorization: Bearer {{user_token}})
|
||||
- Admin endpoints: Use admin JWT token (Authorization: Bearer {{admin_token}})
|
||||
|
||||
## Endpoints
|
||||
|
||||
### 1. Get Device Sidecar Config
|
||||
**Endpoint**: `GET /api/devices/:device_id/sidecar`
|
||||
|
||||
**Description**: Returns sidecar configuration for a device as JSON.
|
||||
|
||||
**Request Headers**:
|
||||
```
|
||||
Authorization: Bearer {{user_token}}
|
||||
```
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"bookmann": {
|
||||
"opds_catalog": "http://192.168.1.100:8765/opds/devices/kobo-id/catalog",
|
||||
"sync_api": "http://192.168.1.100:8765/api/sync/kobo",
|
||||
"opds_base_url": "http://192.168.1.100:8765/opds",
|
||||
"api_base_url": "http://192.168.1.100:8765/api",
|
||||
"device_id": "kobo-device-uuid",
|
||||
"device_token": "dev_xxxxx..."
|
||||
},
|
||||
"books": {
|
||||
"sha256:abc123...": {
|
||||
"bookmann_uuid": "uuid-123",
|
||||
"title": "The Hobbit",
|
||||
"author": "J.R.R. Tolkien",
|
||||
"available_formats": ["epub", "kepub"],
|
||||
"sha256": "abc123...",
|
||||
"file_path": "/path/to/book.epub"
|
||||
}
|
||||
},
|
||||
"collections": [
|
||||
{
|
||||
"name": "Sci-Fi",
|
||||
"shelf_mapping": "Science Fiction",
|
||||
"book_ids": ["uuid-1", "uuid-2", "uuid-3"]
|
||||
}
|
||||
],
|
||||
"opds_enabled": true,
|
||||
"sidecar_enabled": true,
|
||||
"last_updated": "2026-01-31T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Download Device Sidecar File
|
||||
**Endpoint**: `GET /api/devices/:device_id/sidecar/download`
|
||||
|
||||
**Description**: Downloads a `.bookmann.json` configuration file for device setup.
|
||||
|
||||
**Request Headers**:
|
||||
```
|
||||
Authorization: Bearer {{user_token}}
|
||||
```
|
||||
|
||||
**Response** (200 OK):
|
||||
```
|
||||
Content-Type: application/json
|
||||
Content-Disposition: attachment; filename="MyKoboClara.bookmann.json"
|
||||
```
|
||||
|
||||
File contains formatted JSON (pretty-printed) suitable for:
|
||||
- Manual device configuration
|
||||
- Backup and restore
|
||||
- Transfer via USB
|
||||
|
||||
### 3. Get System Configuration
|
||||
**Endpoint**: `GET /api/system/config`
|
||||
|
||||
**Description**: Returns system-wide configuration settings (admin only).
|
||||
|
||||
**Request Headers**:
|
||||
```
|
||||
Authorization: Bearer {{admin_token}}
|
||||
```
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"base_url": "https://bookmann.example.com",
|
||||
"opds_base_url": "https://bookmann.example.com/opds",
|
||||
"api_base_url": "https://bookmann.example.com/api"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Update System Configuration
|
||||
**Endpoint**: `PUT /api/system/config`
|
||||
|
||||
**Description**: Updates system-wide configuration settings (admin only).
|
||||
|
||||
**Request Headers**:
|
||||
```
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"base_url": "https://bookmann.example.com",
|
||||
"opds_base_url": "https://bookmann.example.com/opds",
|
||||
"api_base_url": "https://bookmann.example.com/api"
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "System configuration updated"
|
||||
}
|
||||
```
|
||||
|
||||
## Sidecar File Format
|
||||
|
||||
### Version
|
||||
Always "1.0" - enables future format changes
|
||||
|
||||
### Bookmann Section
|
||||
Contains device connection information:
|
||||
- **opds_catalog**: Full URL to device's OPDS catalog
|
||||
- **sync_api**: Sync API endpoint
|
||||
- **opds_base_url**: Base URL for all OPDS operations
|
||||
- **api_base_url**: Base URL for all API operations
|
||||
- **device_id**: Device's unique identifier
|
||||
- **device_token**: Device authentication token
|
||||
|
||||
### Books Section
|
||||
Map of book identifiers to book metadata:
|
||||
- **Key**: SHA-256 hash (preferred) or Bookmann UUID
|
||||
- **bookmann_uuid**: Canonical Bookmann UUID
|
||||
- **title**: Book title
|
||||
- **author**: Book author
|
||||
- **available_formats**: Array of formats ("epub", "kepub")
|
||||
- **sha256**: SHA-256 hash of book file
|
||||
- **file_path**: Original file path
|
||||
|
||||
### Collections Section
|
||||
Array of collection definitions:
|
||||
- **name**: Collection name in Bookmann
|
||||
- **shelf_mapping**: Device-specific shelf name (e.g., "Science Fiction")
|
||||
- **book_ids**: Array of Bookmann UUIDs in collection
|
||||
|
||||
## Device Setup Workflow
|
||||
|
||||
### Kobo E-Reader
|
||||
|
||||
1. **Download Configuration**
|
||||
- Log into Bookmann web UI
|
||||
- Navigate to Device Management
|
||||
- Click "Download Configuration" for your Kobo device
|
||||
- File saves as `MyKoboClara.bookmann.json`
|
||||
|
||||
2. **Manual Configuration** (if needed)
|
||||
- Copy `.bookmann.json` to Kobo device
|
||||
- Kobo can import configuration automatically
|
||||
|
||||
3. **OPDS Setup** (Recommended)
|
||||
- Use `opds_catalog` URL from sidecar
|
||||
- Add as new content catalog in Kobo settings
|
||||
- Browse and download books wirelessly
|
||||
|
||||
### KOReader
|
||||
|
||||
1. **Download Configuration**
|
||||
- Same process as Kobo above
|
||||
|
||||
2. **Configure Wireless Sync**
|
||||
- In KOReader, set Calibre wireless URL to `sync_api` from sidecar
|
||||
- Enable password and use `device_token`
|
||||
- Set sync frequency to desired interval
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set these in your Bruno collection:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://localhost:8765/api",
|
||||
"user_token": "your-user-jwt-token",
|
||||
"admin_token": "your-admin-jwt-token",
|
||||
"device_id": "uuid-of-device"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
- `200`: Success
|
||||
- `400`: Bad Request (invalid device ID, invalid JSON)
|
||||
- `401`: Unauthorized (missing or invalid token)
|
||||
- `403`: Forbidden (admin access required)
|
||||
- `404`: Not Found (device not found)
|
||||
- `500`: Internal Server Error (database error, generation failure)
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Easy Setup**: One file contains all configuration
|
||||
2. **No Manual Entry**: URLs and tokens pre-populated
|
||||
3. **SHA-256 Matching**: Reliable book identification
|
||||
4. **Collection Sync**: Shelf mappings included
|
||||
5. **Backup/Restore**: Save and transfer device configs
|
||||
6. **Offline Configuration**: Configure devices without network access initially
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Scenario 1: New Kobo Device
|
||||
1. Register Kobo device in Bookmann
|
||||
2. Download sidecar configuration
|
||||
3. Add OPDS catalog URL from sidecar to Kobo
|
||||
4. Browse and download books wirelessly
|
||||
5. Progress syncs automatically
|
||||
|
||||
### Scenario 2: Device Re-configuration
|
||||
1. Download current sidecar file
|
||||
2. Update system configuration if needed
|
||||
3. Re-download sidecar with new settings
|
||||
4. Re-configure device with updated file
|
||||
|
||||
### Scenario 3: Collection Management
|
||||
1. Create collections in Bookmann
|
||||
2. Set up device-specific shelf mappings
|
||||
3. Sidecar automatically includes collection info
|
||||
4. Device shelves reflect collection structure
|
||||
@@ -0,0 +1,23 @@
|
||||
meta {
|
||||
name: "Update System Configuration"
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
put {
|
||||
url: {{base_url}}/api/system/config
|
||||
body: json({
|
||||
"base_url": "https://bookmann.example.com",
|
||||
"opds_base_url": "https://bookmann.example.com/opds",
|
||||
"api_base_url": "https://bookmann.example.com/api"
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{admin_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.status == "success"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: "Get Unlinked Books - User View"
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/sync/unlinked-books
|
||||
body: none
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{user_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.unlinked exists()
|
||||
response.body.total exists()
|
||||
response.body.total >= 0
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
meta {
|
||||
name: "Link Unlinked Book - Manual Resolution"
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{base_url}}/api/sync/link-book
|
||||
body: json({
|
||||
"unlinked_book_id": "{{unlinked_book_id}}",
|
||||
"media_item_id": "{{media_item_id}}",
|
||||
"confidence_score": 1.0
|
||||
})
|
||||
auth: {
|
||||
type: bearer
|
||||
bearer: {{user_token}}
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
response.status == 200
|
||||
response.body.status == "linked"
|
||||
response.body.unlinked_book_id exists()
|
||||
response.body.media_item_id exists()
|
||||
response.body.message exists()
|
||||
}
|
||||
Reference in New Issue
Block a user