From d6d208c3d14619236134017b30244a4837f2d31b Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 31 Jan 2026 22:32:56 -0500 Subject: [PATCH] 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 --- bruno/collections/Add Books to Collection.bru | 25 ++ bruno/collections/Create Collection.bru | 37 +++ bruno/collections/Create Device Mapping.bru | 23 ++ bruno/collections/Delete Collection.bru | 14 + bruno/collections/Delete Device Mapping.bru | 14 + bruno/collections/Get Book Collections.bru | 14 + bruno/collections/Get Collection.bru | 14 + bruno/collections/Get Collections.bru | 14 + bruno/collections/Get Device Mappings.bru | 14 + .../Remove Book from Collection.bru | 14 + bruno/collections/Update Collection.bru | 37 +++ bruno/collections/Update Device Mapping.bru | 22 ++ bruno/kobo/bookmark-sync.bru | 30 +++ bruno/kobo/initialization.bru | 21 ++ bruno/kobo/markup-sync.bru | 41 +++ .../Sync Annotations (Per-Book SHA-256).bru | 39 +++ bruno/koreader/Sync Bookmarks (SHA-256).bru | 47 ++++ .../koreader/Sync Progress (SHA-256 Only).bru | 29 +++ bruno/koreader/Sync Progress (SHA-256).bru | 37 +++ bruno/opds/Download Book EPUB.bru | 11 + bruno/opds/Download Book KEPUB.bru | 11 + bruno/opds/Get Cover Image.bru | 11 + bruno/opds/Get Device Catalog.bru | 11 + bruno/opds/Get Device Navigation.bru | 11 + bruno/opds/List Formats.bru | 11 + bruno/opds/Search Device Catalog.bru | 11 + .../sidecar/Download Device Sidecar File.bru | 19 ++ bruno/sidecar/Get Device Sidecar Config.bru | 21 ++ bruno/sidecar/Get System Configuration.bru | 20 ++ bruno/sidecar/README.md | 243 ++++++++++++++++++ bruno/sidecar/Update System Configuration.bru | 23 ++ bruno/sync-kobo/get-unlinked-books.bru | 21 ++ bruno/sync-kobo/link-book.bru | 26 ++ 33 files changed, 936 insertions(+) create mode 100644 bruno/collections/Add Books to Collection.bru create mode 100644 bruno/collections/Create Collection.bru create mode 100644 bruno/collections/Create Device Mapping.bru create mode 100644 bruno/collections/Delete Collection.bru create mode 100644 bruno/collections/Delete Device Mapping.bru create mode 100644 bruno/collections/Get Book Collections.bru create mode 100644 bruno/collections/Get Collection.bru create mode 100644 bruno/collections/Get Collections.bru create mode 100644 bruno/collections/Get Device Mappings.bru create mode 100644 bruno/collections/Remove Book from Collection.bru create mode 100644 bruno/collections/Update Collection.bru create mode 100644 bruno/collections/Update Device Mapping.bru create mode 100644 bruno/kobo/bookmark-sync.bru create mode 100644 bruno/kobo/initialization.bru create mode 100644 bruno/kobo/markup-sync.bru create mode 100644 bruno/koreader/Sync Annotations (Per-Book SHA-256).bru create mode 100644 bruno/koreader/Sync Bookmarks (SHA-256).bru create mode 100644 bruno/koreader/Sync Progress (SHA-256 Only).bru create mode 100644 bruno/koreader/Sync Progress (SHA-256).bru create mode 100644 bruno/opds/Download Book EPUB.bru create mode 100644 bruno/opds/Download Book KEPUB.bru create mode 100644 bruno/opds/Get Cover Image.bru create mode 100644 bruno/opds/Get Device Catalog.bru create mode 100644 bruno/opds/Get Device Navigation.bru create mode 100644 bruno/opds/List Formats.bru create mode 100644 bruno/opds/Search Device Catalog.bru create mode 100644 bruno/sidecar/Download Device Sidecar File.bru create mode 100644 bruno/sidecar/Get Device Sidecar Config.bru create mode 100644 bruno/sidecar/Get System Configuration.bru create mode 100644 bruno/sidecar/README.md create mode 100644 bruno/sidecar/Update System Configuration.bru create mode 100644 bruno/sync-kobo/get-unlinked-books.bru create mode 100644 bruno/sync-kobo/link-book.bru diff --git a/bruno/collections/Add Books to Collection.bru b/bruno/collections/Add Books to Collection.bru new file mode 100644 index 0000000..5472789 --- /dev/null +++ b/bruno/collections/Add Books to Collection.bru @@ -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}}" + ] + } +} diff --git a/bruno/collections/Create Collection.bru b/bruno/collections/Create Collection.bru new file mode 100644 index 0000000..1b0bde7 --- /dev/null +++ b/bruno/collections/Create Collection.bru @@ -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" + } + } +} diff --git a/bruno/collections/Create Device Mapping.bru b/bruno/collections/Create Device Mapping.bru new file mode 100644 index 0000000..a3e6d8d --- /dev/null +++ b/bruno/collections/Create Device Mapping.bru @@ -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" + } +} diff --git a/bruno/collections/Delete Collection.bru b/bruno/collections/Delete Collection.bru new file mode 100644 index 0000000..2f95562 --- /dev/null +++ b/bruno/collections/Delete Collection.bru @@ -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}} +} diff --git a/bruno/collections/Delete Device Mapping.bru b/bruno/collections/Delete Device Mapping.bru new file mode 100644 index 0000000..3aca44c --- /dev/null +++ b/bruno/collections/Delete Device Mapping.bru @@ -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}} +} diff --git a/bruno/collections/Get Book Collections.bru b/bruno/collections/Get Book Collections.bru new file mode 100644 index 0000000..e02a265 --- /dev/null +++ b/bruno/collections/Get Book Collections.bru @@ -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}} +} diff --git a/bruno/collections/Get Collection.bru b/bruno/collections/Get Collection.bru new file mode 100644 index 0000000..a7af5bd --- /dev/null +++ b/bruno/collections/Get Collection.bru @@ -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}} +} diff --git a/bruno/collections/Get Collections.bru b/bruno/collections/Get Collections.bru new file mode 100644 index 0000000..246b54f --- /dev/null +++ b/bruno/collections/Get Collections.bru @@ -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}} +} diff --git a/bruno/collections/Get Device Mappings.bru b/bruno/collections/Get Device Mappings.bru new file mode 100644 index 0000000..27be071 --- /dev/null +++ b/bruno/collections/Get Device Mappings.bru @@ -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}} +} diff --git a/bruno/collections/Remove Book from Collection.bru b/bruno/collections/Remove Book from Collection.bru new file mode 100644 index 0000000..f9e7408 --- /dev/null +++ b/bruno/collections/Remove Book from Collection.bru @@ -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}} +} diff --git a/bruno/collections/Update Collection.bru b/bruno/collections/Update Collection.bru new file mode 100644 index 0000000..a5a4859 --- /dev/null +++ b/bruno/collections/Update Collection.bru @@ -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" + } + } +} diff --git a/bruno/collections/Update Device Mapping.bru b/bruno/collections/Update Device Mapping.bru new file mode 100644 index 0000000..506ecf7 --- /dev/null +++ b/bruno/collections/Update Device Mapping.bru @@ -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" + } +} diff --git a/bruno/kobo/bookmark-sync.bru b/bruno/kobo/bookmark-sync.bru new file mode 100644 index 0000000..4f51cac --- /dev/null +++ b/bruno/kobo/bookmark-sync.bru @@ -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 +} diff --git a/bruno/kobo/initialization.bru b/bruno/kobo/initialization.bru new file mode 100644 index 0000000..07d45f2 --- /dev/null +++ b/bruno/kobo/initialization.bru @@ -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() +} diff --git a/bruno/kobo/markup-sync.bru b/bruno/kobo/markup-sync.bru new file mode 100644 index 0000000..63c5e2d --- /dev/null +++ b/bruno/kobo/markup-sync.bru @@ -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 +} diff --git a/bruno/koreader/Sync Annotations (Per-Book SHA-256).bru b/bruno/koreader/Sync Annotations (Per-Book SHA-256).bru new file mode 100644 index 0000000..0535b72 --- /dev/null +++ b/bruno/koreader/Sync Annotations (Per-Book SHA-256).bru @@ -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 +} diff --git a/bruno/koreader/Sync Bookmarks (SHA-256).bru b/bruno/koreader/Sync Bookmarks (SHA-256).bru new file mode 100644 index 0000000..f6f7982 --- /dev/null +++ b/bruno/koreader/Sync Bookmarks (SHA-256).bru @@ -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 +} diff --git a/bruno/koreader/Sync Progress (SHA-256 Only).bru b/bruno/koreader/Sync Progress (SHA-256 Only).bru new file mode 100644 index 0000000..39c4467 --- /dev/null +++ b/bruno/koreader/Sync Progress (SHA-256 Only).bru @@ -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 +} diff --git a/bruno/koreader/Sync Progress (SHA-256).bru b/bruno/koreader/Sync Progress (SHA-256).bru new file mode 100644 index 0000000..ec06a75 --- /dev/null +++ b/bruno/koreader/Sync Progress (SHA-256).bru @@ -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 +} diff --git a/bruno/opds/Download Book EPUB.bru b/bruno/opds/Download Book EPUB.bru new file mode 100644 index 0000000..dbc02a8 --- /dev/null +++ b/bruno/opds/Download Book EPUB.bru @@ -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 diff --git a/bruno/opds/Download Book KEPUB.bru b/bruno/opds/Download Book KEPUB.bru new file mode 100644 index 0000000..fbb0365 --- /dev/null +++ b/bruno/opds/Download Book KEPUB.bru @@ -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 diff --git a/bruno/opds/Get Cover Image.bru b/bruno/opds/Get Cover Image.bru new file mode 100644 index 0000000..3de3ad6 --- /dev/null +++ b/bruno/opds/Get Cover Image.bru @@ -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 diff --git a/bruno/opds/Get Device Catalog.bru b/bruno/opds/Get Device Catalog.bru new file mode 100644 index 0000000..aa9ab15 --- /dev/null +++ b/bruno/opds/Get Device Catalog.bru @@ -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 diff --git a/bruno/opds/Get Device Navigation.bru b/bruno/opds/Get Device Navigation.bru new file mode 100644 index 0000000..4a7e1be --- /dev/null +++ b/bruno/opds/Get Device Navigation.bru @@ -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 diff --git a/bruno/opds/List Formats.bru b/bruno/opds/List Formats.bru new file mode 100644 index 0000000..328f2ad --- /dev/null +++ b/bruno/opds/List Formats.bru @@ -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 diff --git a/bruno/opds/Search Device Catalog.bru b/bruno/opds/Search Device Catalog.bru new file mode 100644 index 0000000..ba1a640 --- /dev/null +++ b/bruno/opds/Search Device Catalog.bru @@ -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 diff --git a/bruno/sidecar/Download Device Sidecar File.bru b/bruno/sidecar/Download Device Sidecar File.bru new file mode 100644 index 0000000..8ee9e96 --- /dev/null +++ b/bruno/sidecar/Download Device Sidecar File.bru @@ -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" +} diff --git a/bruno/sidecar/Get Device Sidecar Config.bru b/bruno/sidecar/Get Device Sidecar Config.bru new file mode 100644 index 0000000..9414d5b --- /dev/null +++ b/bruno/sidecar/Get Device Sidecar Config.bru @@ -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() +} diff --git a/bruno/sidecar/Get System Configuration.bru b/bruno/sidecar/Get System Configuration.bru new file mode 100644 index 0000000..f73f057 --- /dev/null +++ b/bruno/sidecar/Get System Configuration.bru @@ -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() +} diff --git a/bruno/sidecar/README.md b/bruno/sidecar/README.md new file mode 100644 index 0000000..64dd996 --- /dev/null +++ b/bruno/sidecar/README.md @@ -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 diff --git a/bruno/sidecar/Update System Configuration.bru b/bruno/sidecar/Update System Configuration.bru new file mode 100644 index 0000000..6337d60 --- /dev/null +++ b/bruno/sidecar/Update System Configuration.bru @@ -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" +} diff --git a/bruno/sync-kobo/get-unlinked-books.bru b/bruno/sync-kobo/get-unlinked-books.bru new file mode 100644 index 0000000..72d7fc6 --- /dev/null +++ b/bruno/sync-kobo/get-unlinked-books.bru @@ -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 +} diff --git a/bruno/sync-kobo/link-book.bru b/bruno/sync-kobo/link-book.bru new file mode 100644 index 0000000..1343ef5 --- /dev/null +++ b/bruno/sync-kobo/link-book.bru @@ -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() +}