From acd82fff0a65ff447913a7189c45ac6b752a26cd Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 24 Oct 2026 18:35:28 -0400 Subject: [PATCH] =?UTF-8?q?bruno:=20regenerate=20collection=20from=20canon?= =?UTF-8?q?ical=20docs=20+=20permanent=20docs=E2=86=94Bruno=20parity=20gat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WO4. The old collection was the drifted artifact AGENTS.md warned about: bodies pre-truncated at their first {{var}}, routes renamed server-side but not here (/api/notes/:media_id, /api/book-matching/search, /api/collections/:id/rules/test), an orphan universal-progress suite for routes that no longer exist. scripts/gen_bruno_from_docs.py emits one OpenCollection request per frontmatter'd endpoint doc (191 requests): gate-verified example-request JSON as body, {{env}} params per the environment file, device-token routes get an explicit Bearer {{device_token}} header, login/register/ refresh keep the token-capture after-response scripts that chain the collection. Old 1:1 mirrors deleted (128); hand-authored NewDevDBSetup, environments, and edge-case suites kept. 30 scenario files with truncated bodies (the documented corruption) deleted — they could not run. internal/docs/drift_test.go gains TestBrunoMirrorsDocs: every frontmatter'd doc must have a Bruno request with matching method+URL shape — the collection can never silently drift from the docs again. opencollection.yml README block replaced with the real run instructions. make test green (9 pkgs, parity test included in internal/docs). --- bruno/admin/Get Admin Library.yml | 30 --- bruno/admin/hash_conflicts_api.yml | 21 ++ bruno/admin/list_users.yml | 21 ++ bruno/admin/update_user_max_devices.yml | 27 ++ bruno/analytics/Get Device Usage.yml | 47 ---- bruno/analytics/Get Popular Books.yml | 57 ---- .../Get Reading Stats Date Range.yml | 58 ---- bruno/analytics/Get Reading Stats.yml | 58 ---- bruno/analytics/get_analytics.yml | 21 ++ bruno/analytics/get_device_usage.yml | 21 ++ bruno/analytics/get_popular_books.yml | 21 ++ bruno/authentication/login.yml | 51 ++++ bruno/authentication/logout.yml | 21 ++ bruno/authentication/password_rules.yml | 21 ++ bruno/authentication/refresh_token.yml | 50 ++++ bruno/authentication/register.yml | 54 ++++ bruno/book-matching/auto_link_books.yml | 28 ++ bruno/book-matching/bulk_link_books.yml | 38 +++ .../create_device_file_alias.yml | 29 ++ .../delete_device_file_alias.yml | 21 ++ bruno/book-matching/get_book_matches.yml | 21 ++ .../book-matching/get_device_file_aliases.yml | 21 ++ .../get_unlinked_book_suggestions.yml | 21 ++ bruno/book-matching/get_unlinked_books.yml | 21 ++ bruno/book-matching/link_book.yml | 33 +++ bruno/book-matching/search_books.yml | 31 +++ .../update_device_file_alias.yml | 28 ++ bruno/books/Bulk Delete Books.yml | 15 -- bruno/books/Bulk Update Books.yml | 16 -- bruno/collections/Add Books to Collection.yml | 12 - bruno/collections/Create Collection.yml | 13 - bruno/collections/Create Device Mapping.yml | 12 - bruno/collections/Delete Collection.yml | 30 --- bruno/collections/Delete Device Mapping.yml | 31 --- bruno/collections/Get Book Collections.yml | 44 --- bruno/collections/Get Collection.yml | 30 --- bruno/collections/Get Collections.yml | 31 --- bruno/collections/Get Device Mappings.yml | 30 --- .../Remove Book from Collection.yml | 31 --- bruno/collections/Update Collection.yml | 12 - bruno/collections/Update Device Mapping.yml | 12 - bruno/collections/add_auto_assign_rule.yml | 31 +++ bruno/collections/bulk_add_books.yml | 34 +++ bruno/collections/bulk_assign.yml | 31 +++ bruno/collections/bulk_remove_books.yml | 30 +++ .../create-custom-section-manual.yml | 16 -- .../create-custom-section-missing-fields.yml | 19 -- .../create-custom-section-rules.yml | 17 -- bruno/collections/create_collection.yml | 33 +++ bruno/collections/create_shelf_mapping.yml | 29 ++ bruno/collections/delete_collection.yml | 21 ++ bruno/collections/delete_shelf_mapping.yml | 21 ++ bruno/collections/get_collection.yml | 21 ++ bruno/collections/get_collection_books.yml | 21 ++ bruno/collections/list_collections.yml | 21 ++ bruno/collections/preview-collection.yml | 16 -- bruno/collections/preview_collection.yml | 30 +++ bruno/collections/remove_auto_assign_rule.yml | 21 ++ bruno/collections/reorder_items.yml | 30 +++ .../Bulk Add Books to Collections.yml | 18 -- .../Bulk Remove Books - All Books.yml | 15 -- .../Bulk Remove Books - Empty List.yml | 17 -- .../Bulk Remove Books - Invalid IDs.yml | 17 -- .../Bulk Remove Books - Single Book.yml | 17 -- ...est Collection Rules - Author Contains.yml | 17 -- ...on Rules - Copyright Year Greater Than.yml | 17 -- ...t Collection Rules - Empty Rules Array.yml | 17 -- .../Test Collection Rules - No Matches.yml | 17 -- .../scenarios/Test Collection Rules.yml | 18 -- bruno/collections/test_rule.yml | 38 +++ bruno/collections/update_collection.yml | 34 +++ bruno/collections/update_rule.yml | 27 ++ bruno/conflicts/Delete Conflict.yml | 22 -- bruno/conflicts/Dismiss All Resolved.yml | 37 --- bruno/conflicts/Get Conflict Details.yml | 22 -- bruno/conflicts/List Conflicts.yml | 74 ----- bruno/conflicts/Resolve Conflict.yml | 15 -- bruno/conflicts/bulk_dismiss_conflicts.yml | 30 +++ bruno/conflicts/bulk_resolve_conflicts.yml | 31 +++ bruno/conflicts/delete_conflict.yml | 21 ++ bruno/conflicts/dismiss_all_resolved.yml | 21 ++ bruno/conflicts/get_conflict.yml | 21 ++ bruno/conflicts/list_conflicts.yml | 21 ++ bruno/conflicts/resolve_conflict.yml | 27 ++ .../scenarios/Bulk Dismiss Conflicts.yml | 17 -- .../scenarios/Bulk Resolve Conflicts.yml | 18 -- .../Bulk Resolve Highest Progress.yml | 18 -- .../create-collection-with-dashboard.yml | 38 --- bruno/dashboard/get-dashboard-sections.yml | 73 ----- bruno/dashboard/get-sections-by-library.yml | 30 --- bruno/dashboard/get_preferences.yml | 21 ++ bruno/dashboard/get_sections.yml | 21 ++ bruno/dashboard/restore-system-collection.yml | 16 -- bruno/dashboard/restore_system_collection.yml | 28 ++ .../update-collection-visibility.yml | 17 -- bruno/dashboard/update-preferences.yml | 16 -- bruno/dashboard/update_preferences.yml | 30 +++ bruno/devices/add_to_shelf.yml | 29 ++ bruno/devices/approve_device_registration.yml | 21 ++ bruno/devices/clear_shelf.yml | 21 ++ bruno/devices/delete_device.yml | 21 ++ bruno/devices/download_sidecar_config.yml | 21 ++ bruno/devices/get_device.yml | 21 ++ bruno/devices/get_device_mappings.yml | 21 ++ bruno/devices/get_devices.yml | 27 ++ bruno/devices/get_shelf.yml | 21 ++ bruno/devices/get_sidecar_config.yml | 21 ++ bruno/devices/kobo/Get Analytics Tests.yml | 17 -- bruno/devices/kobo/Get Kobo Library.yml | 81 ------ .../kobo/Get Unlinked Book Suggestions.yml | 22 -- .../kobo/scenarios/Auto Link Books.yml | 17 -- .../kobo/scenarios/Bulk Link Books.yml | 17 -- .../kobo/scenarios/Server Sync to Kobo.yml | 16 -- .../devices/kobo/scenarios/bookmark-sync.yml | 15 -- .../kobo/scenarios/get-unlinked-books.yml | 39 --- bruno/devices/kobo/scenarios/link-book.yml | 15 -- .../scenarios/sync-bookmark-url-token.yml | 15 -- .../kobo/scenarios/sync-markup-url-token.yml | 15 -- bruno/devices/koreader/Get Book Metadata.yml | 14 - bruno/devices/koreader/Get Library.yml | 14 - .../Get User Library for KOReader.yml | 15 -- bruno/devices/link_book.yml | 33 +++ bruno/devices/list_devices.yml | 21 ++ bruno/devices/list_pending_registrations.yml | 21 ++ bruno/devices/regenerate_device_token.yml | 21 ++ bruno/devices/register_device.yml | 29 ++ bruno/devices/reject_device_registration.yml | 21 ++ bruno/devices/remove_from_shelf.yml | 21 ++ .../scenarios/Add Books to Kobo Shelf.yml | 15 -- .../scenarios/Check Registration Status.yml | 12 - bruno/devices/scenarios/Clear Kobo Shelf.yml | 22 -- .../scenarios/Create Device File Alias.yml | 19 -- bruno/devices/scenarios/Delete Device.yml | 22 -- bruno/devices/scenarios/Get Kobo Shelf.yml | 22 -- .../Initiate Device Registration.yml | 12 - .../scenarios/Remove Book from Kobo Shelf.yml | 22 -- bruno/devices/scenarios/Update Device.yml | 12 - bruno/devices/update_device.yml | 30 +++ bruno/devices/update_device_mapping.yml | 28 ++ bruno/environments/Bookhoard.yml | 16 ++ bruno/highlights/Create Media Highlight.yml | 46 ---- bruno/highlights/Delete Media Highlight.yml | 31 --- bruno/highlights/Get Media Highlights.yml | 40 --- .../highlights/Get Single Media Highlight.yml | 31 --- bruno/highlights/Update Media Highlight.yml | 47 ---- bruno/highlights/create_highlight.yml | 32 +++ bruno/highlights/delete_highlight.yml | 21 ++ bruno/highlights/get_highlights.yml | 21 ++ bruno/highlights/update_highlight.yml | 28 ++ bruno/jobs/create_job.yml | 28 ++ bruno/jobs/get_job_status.yml | 21 ++ bruno/kobo/analytics_gettests.yml | 30 +++ bruno/kobo/bookmark_sync.yml | 38 +++ bruno/kobo/initialization.yml | 24 ++ bruno/kobo/markup_sync.yml | 42 +++ bruno/kobo/sync_from_server.yml | 31 +++ bruno/koreader/get_library.yml | 24 ++ bruno/koreader/get_metadata.yml | 24 ++ bruno/koreader/get_rating.yml | 24 ++ bruno/koreader/get_review.yml | 24 ++ bruno/koreader/push_stats.yml | 30 +++ bruno/koreader/resolve_book.yml | 24 ++ bruno/koreader/set_rating.yml | 30 +++ bruno/koreader/set_review.yml | 30 +++ bruno/koreader/sync_bookmarks.yml | 53 ++++ bruno/koreader/sync_progress.yml | 47 ++++ bruno/libraries/add_library_folder.yml | 27 ++ bruno/libraries/browse-folders.yml | 21 ++ bruno/libraries/create_library.yml | 29 ++ bruno/libraries/delete_library.yml | 21 ++ bruno/libraries/delete_library_folder.yml | 27 ++ bruno/libraries/delete_processing_issue.yml | 21 ++ bruno/libraries/get_library.yml | 21 ++ bruno/libraries/get_library_folders.yml | 21 ++ bruno/libraries/get_library_media_items.yml | 21 ++ bruno/libraries/get_library_stats.yml | 21 ++ bruno/libraries/get_library_types.yml | 21 ++ .../libraries/get_processing_issue_stats.yml | 21 ++ bruno/libraries/get_scan_settings.yml | 21 ++ bruno/libraries/get_visible_libraries.yml | 21 ++ bruno/libraries/list_libraries.yml | 21 ++ bruno/libraries/list_processing_issues.yml | 21 ++ bruno/libraries/resolve_processing_issue.yml | 21 ++ bruno/libraries/scan_library.yml | 21 ++ bruno/libraries/set_library_visibility.yml | 28 ++ bruno/libraries/update_library.yml | 28 ++ bruno/libraries/update_scan_settings.yml | 28 ++ bruno/library/Add Library Folder.yml | 53 ---- bruno/library/Create Library.yml | 83 ------ bruno/library/Delete Library Folder.yml | 38 --- bruno/library/Delete Library.yml | 27 -- bruno/library/Get Libraries (Admin).yml | 95 ------- bruno/library/Get Library Folders.yml | 27 -- bruno/library/Get Library Stats.yml | 35 --- bruno/library/Get Library Types.yml | 39 --- bruno/library/Get Library.yml | 35 --- bruno/library/Get Scan Settings.yml | 42 --- bruno/library/Get User Visible Libraries.yml | 43 --- bruno/library/Set Library Visibility.yml | 55 ---- bruno/library/Update Library.yml | 38 --- bruno/library/Update Scan Settings.yml | 48 ---- bruno/media-items/Create Media Item.yml | 52 ---- bruno/media-items/Create Media Rating.yml | 33 --- bruno/media-items/Delete Media Item.yml | 35 --- bruno/media-items/Delete Media Rating.yml | 35 --- bruno/media-items/EPUB Download.yml | 23 -- bruno/media-items/Get Audio Chapters.yml | 31 --- bruno/media-items/Get Audio Track.yml | 31 --- bruno/media-items/Get Media Item.yml | 35 --- bruno/media-items/Get Media Rating.yml | 35 --- bruno/media-items/List Media Items Sorted.yml | 81 ------ bruno/media-items/List Media Items.yml | 61 ----- bruno/media-items/Rescan Media Item.yml | 35 --- bruno/media-items/Update Media Item.yml | 43 --- bruno/media-items/Update Media Rating.yml | 66 ----- bruno/media-items/bulk_delete_media_items.yml | 31 +++ bruno/media-items/bulk_update_media_items.yml | 43 +++ bruno/media-items/create_media_bookmark.yml | 35 +++ bruno/media-items/create_media_item.yml | 39 +++ bruno/media-items/delete_media_bookmark.yml | 21 ++ bruno/media-items/delete_media_item.yml | 21 ++ bruno/media-items/delete_media_review.yml | 21 ++ bruno/media-items/deleted_annotations.yml | 21 ++ .../media-items/download_media_item_file.yml | 21 ++ bruno/media-items/filter_count.yml | 21 ++ bruno/media-items/get_audio_chapters.yml | 21 ++ bruno/media-items/get_audio_track.yml | 21 ++ bruno/media-items/get_facets.yml | 21 ++ bruno/media-items/get_media_bookmarks.yml | 21 ++ bruno/media-items/get_media_highlight.yml | 21 ++ bruno/media-items/get_media_item.yml | 21 ++ bruno/media-items/get_media_note.yml | 21 ++ bruno/media-items/get_media_review.yml | 21 ++ bruno/media-items/list_media_items.yml | 21 ++ .../media-items/merge_archived_media_item.yml | 27 ++ .../purge_archived_media_items.yml | 21 ++ .../media-items/purge_deleted_annotation.yml | 21 ++ bruno/media-items/rescan_media_item.yml | 21 ++ .../restore_deleted_annotation.yml | 21 ++ bruno/media-items/search_media_items.yml | 21 ++ bruno/media-items/set_audio_speed.yml | 27 ++ bruno/media-items/unarchive_media_item.yml | 21 ++ bruno/media-items/update_media_bookmark.yml | 29 ++ bruno/media-items/update_media_item.yml | 31 +++ bruno/media-items/upsert_media_review.yml | 27 ++ bruno/notes/Create Media Note.yml | 40 --- bruno/notes/Delete Media Note.yml | 31 --- bruno/notes/Get Media Notes.yml | 37 --- bruno/notes/Get Single Media Note.yml | 31 --- bruno/notes/Update Media Note.yml | 41 --- bruno/notes/create_note.yml | 29 ++ bruno/notes/delete_note.yml | 21 ++ bruno/notes/get_notes.yml | 21 ++ bruno/notes/update_note.yml | 28 ++ bruno/opds/Download Book EPUB.yml | 34 --- ...oad Book KEPUB (On-the-fly Conversion).yml | 19 -- bruno/opds/Download Book KEPUB.yml | 34 --- bruno/opds/Get Cover Image.yml | 35 --- bruno/opds/Get Device Catalog.yml | 35 --- bruno/opds/Get Device Navigation.yml | 34 --- bruno/opds/List Formats.yml | 34 --- bruno/opds/Search Device Catalog.yml | 35 --- bruno/opds/acquisition.yml | 24 ++ bruno/opds/feeds.yml | 24 ++ bruno/opds/publication.yml | 24 ++ bruno/opencollection.yml | 196 ++------------ bruno/progress/Delete Reading Progress.yml | 42 --- bruno/progress/Get Reading Progress.yml | 30 --- bruno/progress/Update Reading Progress.yml | 35 --- bruno/progress/delete_media_progress.yml | 21 ++ bruno/progress/get_media_progress.yml | 21 ++ bruno/progress/get_progress_history.yml | 21 ++ bruno/progress/update_media_progress.yml | 29 ++ bruno/queue/Clear Device Queue.yml | 39 --- bruno/queue/Delete Queue Item.yml | 38 --- bruno/queue/List All Queue Items (Admin).yml | 39 --- bruno/queue/List Device Queue Items.yml | 42 --- bruno/queue/List Queue Items (Pagination).yml | 53 ---- bruno/queue/Retry Queue Item.yml | 39 --- bruno/queue/clear_device_queue.yml | 21 ++ bruno/queue/delete_queue_item.yml | 21 ++ bruno/queue/get_device_queue_stats.yml | 21 ++ bruno/queue/list_all_queue_items.yml | 21 ++ bruno/queue/list_device_queue_items.yml | 21 ++ bruno/queue/retry_queue_item.yml | 21 ++ bruno/ratings/create_rating.yml | 27 ++ bruno/ratings/delete_rating.yml | 21 ++ bruno/ratings/get_ratings.yml | 21 ++ bruno/ratings/update_rating.yml | 28 ++ bruno/saved-filters/Create Saved Filter.yml | 14 - bruno/saved-filters/Delete Saved Filter.yml | 95 ------- .../saved-filters/Get Saved Filter By ID.yml | 64 ----- bruno/saved-filters/List Saved Filters.yml | 63 ----- bruno/saved-filters/Update Saved Filter.yml | 14 - bruno/saved-filters/create_saved_filter.yml | 29 ++ bruno/saved-filters/delete_saved_filter.yml | 21 ++ bruno/saved-filters/get_saved_filter.yml | 21 ++ bruno/saved-filters/list_saved_filters.yml | 21 ++ bruno/saved-filters/update_saved_filter.yml | 29 ++ bruno/scanner/Get Scan Status.yml | 33 --- bruno/scanner/Get Watch Mode Status.yml | 28 -- bruno/scanner/Scan Media Items.yml | 74 ----- bruno/scanner/Start Scanner.yml | 32 --- bruno/scanner/Start Watch Mode.yml | 12 - bruno/scanner/Stop Scanner.yml | 32 --- bruno/scanner/Stop Watch Mode.yml | 12 - bruno/scanner/cancel_scan.yml | 21 ++ bruno/scanner/get_active_scans.yml | 21 ++ bruno/scanner/get_scan_status.yml | 21 ++ bruno/scanner/get_scan_summary.yml | 21 ++ bruno/scanner/get_watch_mode_status.yml | 21 ++ bruno/scanner/scan_library.yml | 28 ++ bruno/scanner/start_scanner.yml | 27 ++ bruno/scanner/start_watch_mode.yml | 27 ++ bruno/scanner/stop_scanner.yml | 21 ++ bruno/scanner/stop_watch_mode.yml | 27 ++ bruno/series/get_series_books.yml | 21 ++ bruno/series/list_series.yml | 21 ++ .../sidecar/Download Device Sidecar File.yml | 22 -- bruno/sidecar/Get Device Sidecar Config.yml | 22 -- bruno/sidecar/Get System Configuration.yml | 36 --- bruno/sidecar/Update System Configuration.yml | 15 -- bruno/stats/get_book_stats.yml | 21 ++ bruno/stats/get_library_usage.yml | 21 ++ bruno/stats/get_summary.yml | 21 ++ bruno/stats/get_this_week.yml | 21 ++ bruno/system/get-scan-settings.yml | 19 -- bruno/system/get_config.yml | 21 ++ bruno/system/get_settings.yml | 21 ++ bruno/system/update-scan-settings.yml | 15 -- bruno/system/update_config.yml | 28 ++ bruno/system/update_setting.yml | 28 ++ .../Get Progress History.yml | 22 -- bruno/user/admin/Delete User.yml | 25 -- bruno/user/admin/List Users.yml | 45 ---- bruno/user/admin/Register Admin User.yml | 64 ----- bruno/user/admin/Reset User Password.yml | 12 - bruno/user/admin/Update User Max Devices.yml | 27 -- bruno/user/admin/Update User.yml | 12 - bruno/user/auth/Login User.yml | 77 ------ bruno/user/auth/Logout User.yml | 43 --- bruno/user/auth/Refresh Token.yml | 83 ------ bruno/user/auth/Register User.yml | 98 ------- bruno/user/profile/Get Profile.yml | 33 --- bruno/user/profile/Update Password.yml | 12 - bruno/user/profile/Update Profile.yml | 12 - bruno/user/profile/Update Theme.yml | 12 - bruno/users/change_password.yml | 29 ++ bruno/users/delete_user.yml | 21 ++ bruno/users/get_profile.yml | 21 ++ bruno/users/update_profile.yml | 31 +++ internal/docs/drift_test.go | 57 ++++ scripts/gen_bruno_from_docs.py | 253 ++++++++++++++++++ 353 files changed, 5165 insertions(+), 5293 deletions(-) delete mode 100644 bruno/admin/Get Admin Library.yml create mode 100644 bruno/admin/hash_conflicts_api.yml create mode 100644 bruno/admin/list_users.yml create mode 100644 bruno/admin/update_user_max_devices.yml delete mode 100644 bruno/analytics/Get Device Usage.yml delete mode 100644 bruno/analytics/Get Popular Books.yml delete mode 100644 bruno/analytics/Get Reading Stats Date Range.yml delete mode 100644 bruno/analytics/Get Reading Stats.yml create mode 100644 bruno/analytics/get_analytics.yml create mode 100644 bruno/analytics/get_device_usage.yml create mode 100644 bruno/analytics/get_popular_books.yml create mode 100644 bruno/authentication/login.yml create mode 100644 bruno/authentication/logout.yml create mode 100644 bruno/authentication/password_rules.yml create mode 100644 bruno/authentication/refresh_token.yml create mode 100644 bruno/authentication/register.yml create mode 100644 bruno/book-matching/auto_link_books.yml create mode 100644 bruno/book-matching/bulk_link_books.yml create mode 100644 bruno/book-matching/create_device_file_alias.yml create mode 100644 bruno/book-matching/delete_device_file_alias.yml create mode 100644 bruno/book-matching/get_book_matches.yml create mode 100644 bruno/book-matching/get_device_file_aliases.yml create mode 100644 bruno/book-matching/get_unlinked_book_suggestions.yml create mode 100644 bruno/book-matching/get_unlinked_books.yml create mode 100644 bruno/book-matching/link_book.yml create mode 100644 bruno/book-matching/search_books.yml create mode 100644 bruno/book-matching/update_device_file_alias.yml delete mode 100644 bruno/books/Bulk Delete Books.yml delete mode 100644 bruno/books/Bulk Update Books.yml delete mode 100644 bruno/collections/Add Books to Collection.yml delete mode 100644 bruno/collections/Create Collection.yml delete mode 100644 bruno/collections/Create Device Mapping.yml delete mode 100644 bruno/collections/Delete Collection.yml delete mode 100644 bruno/collections/Delete Device Mapping.yml delete mode 100644 bruno/collections/Get Book Collections.yml delete mode 100644 bruno/collections/Get Collection.yml delete mode 100644 bruno/collections/Get Collections.yml delete mode 100644 bruno/collections/Get Device Mappings.yml delete mode 100644 bruno/collections/Remove Book from Collection.yml delete mode 100644 bruno/collections/Update Collection.yml delete mode 100644 bruno/collections/Update Device Mapping.yml create mode 100644 bruno/collections/add_auto_assign_rule.yml create mode 100644 bruno/collections/bulk_add_books.yml create mode 100644 bruno/collections/bulk_assign.yml create mode 100644 bruno/collections/bulk_remove_books.yml delete mode 100644 bruno/collections/create-custom-section-manual.yml delete mode 100644 bruno/collections/create-custom-section-missing-fields.yml delete mode 100644 bruno/collections/create-custom-section-rules.yml create mode 100644 bruno/collections/create_collection.yml create mode 100644 bruno/collections/create_shelf_mapping.yml create mode 100644 bruno/collections/delete_collection.yml create mode 100644 bruno/collections/delete_shelf_mapping.yml create mode 100644 bruno/collections/get_collection.yml create mode 100644 bruno/collections/get_collection_books.yml create mode 100644 bruno/collections/list_collections.yml delete mode 100644 bruno/collections/preview-collection.yml create mode 100644 bruno/collections/preview_collection.yml create mode 100644 bruno/collections/remove_auto_assign_rule.yml create mode 100644 bruno/collections/reorder_items.yml delete mode 100644 bruno/collections/scenarios/Bulk Add Books to Collections.yml delete mode 100644 bruno/collections/scenarios/Bulk Remove Books - All Books.yml delete mode 100644 bruno/collections/scenarios/Bulk Remove Books - Empty List.yml delete mode 100644 bruno/collections/scenarios/Bulk Remove Books - Invalid IDs.yml delete mode 100644 bruno/collections/scenarios/Bulk Remove Books - Single Book.yml delete mode 100644 bruno/collections/scenarios/Test Collection Rules - Author Contains.yml delete mode 100644 bruno/collections/scenarios/Test Collection Rules - Copyright Year Greater Than.yml delete mode 100644 bruno/collections/scenarios/Test Collection Rules - Empty Rules Array.yml delete mode 100644 bruno/collections/scenarios/Test Collection Rules - No Matches.yml delete mode 100644 bruno/collections/scenarios/Test Collection Rules.yml create mode 100644 bruno/collections/test_rule.yml create mode 100644 bruno/collections/update_collection.yml create mode 100644 bruno/collections/update_rule.yml delete mode 100644 bruno/conflicts/Delete Conflict.yml delete mode 100644 bruno/conflicts/Dismiss All Resolved.yml delete mode 100644 bruno/conflicts/Get Conflict Details.yml delete mode 100644 bruno/conflicts/List Conflicts.yml delete mode 100644 bruno/conflicts/Resolve Conflict.yml create mode 100644 bruno/conflicts/bulk_dismiss_conflicts.yml create mode 100644 bruno/conflicts/bulk_resolve_conflicts.yml create mode 100644 bruno/conflicts/delete_conflict.yml create mode 100644 bruno/conflicts/dismiss_all_resolved.yml create mode 100644 bruno/conflicts/get_conflict.yml create mode 100644 bruno/conflicts/list_conflicts.yml create mode 100644 bruno/conflicts/resolve_conflict.yml delete mode 100644 bruno/conflicts/scenarios/Bulk Dismiss Conflicts.yml delete mode 100644 bruno/conflicts/scenarios/Bulk Resolve Conflicts.yml delete mode 100644 bruno/conflicts/scenarios/Bulk Resolve Highest Progress.yml delete mode 100644 bruno/dashboard/create-collection-with-dashboard.yml delete mode 100644 bruno/dashboard/get-dashboard-sections.yml delete mode 100644 bruno/dashboard/get-sections-by-library.yml create mode 100644 bruno/dashboard/get_preferences.yml create mode 100644 bruno/dashboard/get_sections.yml delete mode 100644 bruno/dashboard/restore-system-collection.yml create mode 100644 bruno/dashboard/restore_system_collection.yml delete mode 100644 bruno/dashboard/update-collection-visibility.yml delete mode 100644 bruno/dashboard/update-preferences.yml create mode 100644 bruno/dashboard/update_preferences.yml create mode 100644 bruno/devices/add_to_shelf.yml create mode 100644 bruno/devices/approve_device_registration.yml create mode 100644 bruno/devices/clear_shelf.yml create mode 100644 bruno/devices/delete_device.yml create mode 100644 bruno/devices/download_sidecar_config.yml create mode 100644 bruno/devices/get_device.yml create mode 100644 bruno/devices/get_device_mappings.yml create mode 100644 bruno/devices/get_devices.yml create mode 100644 bruno/devices/get_shelf.yml create mode 100644 bruno/devices/get_sidecar_config.yml delete mode 100644 bruno/devices/kobo/Get Analytics Tests.yml delete mode 100644 bruno/devices/kobo/Get Kobo Library.yml delete mode 100644 bruno/devices/kobo/Get Unlinked Book Suggestions.yml delete mode 100644 bruno/devices/kobo/scenarios/Auto Link Books.yml delete mode 100644 bruno/devices/kobo/scenarios/Bulk Link Books.yml delete mode 100644 bruno/devices/kobo/scenarios/Server Sync to Kobo.yml delete mode 100644 bruno/devices/kobo/scenarios/bookmark-sync.yml delete mode 100644 bruno/devices/kobo/scenarios/get-unlinked-books.yml delete mode 100644 bruno/devices/kobo/scenarios/link-book.yml delete mode 100644 bruno/devices/kobo/scenarios/sync-bookmark-url-token.yml delete mode 100644 bruno/devices/kobo/scenarios/sync-markup-url-token.yml delete mode 100644 bruno/devices/koreader/Get Book Metadata.yml delete mode 100644 bruno/devices/koreader/Get Library.yml delete mode 100644 bruno/devices/koreader/Get User Library for KOReader.yml create mode 100644 bruno/devices/link_book.yml create mode 100644 bruno/devices/list_devices.yml create mode 100644 bruno/devices/list_pending_registrations.yml create mode 100644 bruno/devices/regenerate_device_token.yml create mode 100644 bruno/devices/register_device.yml create mode 100644 bruno/devices/reject_device_registration.yml create mode 100644 bruno/devices/remove_from_shelf.yml delete mode 100644 bruno/devices/scenarios/Add Books to Kobo Shelf.yml delete mode 100644 bruno/devices/scenarios/Check Registration Status.yml delete mode 100644 bruno/devices/scenarios/Clear Kobo Shelf.yml delete mode 100644 bruno/devices/scenarios/Create Device File Alias.yml delete mode 100644 bruno/devices/scenarios/Delete Device.yml delete mode 100644 bruno/devices/scenarios/Get Kobo Shelf.yml delete mode 100644 bruno/devices/scenarios/Initiate Device Registration.yml delete mode 100644 bruno/devices/scenarios/Remove Book from Kobo Shelf.yml delete mode 100644 bruno/devices/scenarios/Update Device.yml create mode 100644 bruno/devices/update_device.yml create mode 100644 bruno/devices/update_device_mapping.yml delete mode 100644 bruno/highlights/Create Media Highlight.yml delete mode 100644 bruno/highlights/Delete Media Highlight.yml delete mode 100644 bruno/highlights/Get Media Highlights.yml delete mode 100644 bruno/highlights/Get Single Media Highlight.yml delete mode 100644 bruno/highlights/Update Media Highlight.yml create mode 100644 bruno/highlights/create_highlight.yml create mode 100644 bruno/highlights/delete_highlight.yml create mode 100644 bruno/highlights/get_highlights.yml create mode 100644 bruno/highlights/update_highlight.yml create mode 100644 bruno/jobs/create_job.yml create mode 100644 bruno/jobs/get_job_status.yml create mode 100644 bruno/kobo/analytics_gettests.yml create mode 100644 bruno/kobo/bookmark_sync.yml create mode 100644 bruno/kobo/initialization.yml create mode 100644 bruno/kobo/markup_sync.yml create mode 100644 bruno/kobo/sync_from_server.yml create mode 100644 bruno/koreader/get_library.yml create mode 100644 bruno/koreader/get_metadata.yml create mode 100644 bruno/koreader/get_rating.yml create mode 100644 bruno/koreader/get_review.yml create mode 100644 bruno/koreader/push_stats.yml create mode 100644 bruno/koreader/resolve_book.yml create mode 100644 bruno/koreader/set_rating.yml create mode 100644 bruno/koreader/set_review.yml create mode 100644 bruno/koreader/sync_bookmarks.yml create mode 100644 bruno/koreader/sync_progress.yml create mode 100644 bruno/libraries/add_library_folder.yml create mode 100644 bruno/libraries/browse-folders.yml create mode 100644 bruno/libraries/create_library.yml create mode 100644 bruno/libraries/delete_library.yml create mode 100644 bruno/libraries/delete_library_folder.yml create mode 100644 bruno/libraries/delete_processing_issue.yml create mode 100644 bruno/libraries/get_library.yml create mode 100644 bruno/libraries/get_library_folders.yml create mode 100644 bruno/libraries/get_library_media_items.yml create mode 100644 bruno/libraries/get_library_stats.yml create mode 100644 bruno/libraries/get_library_types.yml create mode 100644 bruno/libraries/get_processing_issue_stats.yml create mode 100644 bruno/libraries/get_scan_settings.yml create mode 100644 bruno/libraries/get_visible_libraries.yml create mode 100644 bruno/libraries/list_libraries.yml create mode 100644 bruno/libraries/list_processing_issues.yml create mode 100644 bruno/libraries/resolve_processing_issue.yml create mode 100644 bruno/libraries/scan_library.yml create mode 100644 bruno/libraries/set_library_visibility.yml create mode 100644 bruno/libraries/update_library.yml create mode 100644 bruno/libraries/update_scan_settings.yml delete mode 100644 bruno/library/Add Library Folder.yml delete mode 100644 bruno/library/Create Library.yml delete mode 100644 bruno/library/Delete Library Folder.yml delete mode 100644 bruno/library/Delete Library.yml delete mode 100644 bruno/library/Get Libraries (Admin).yml delete mode 100644 bruno/library/Get Library Folders.yml delete mode 100644 bruno/library/Get Library Stats.yml delete mode 100644 bruno/library/Get Library Types.yml delete mode 100644 bruno/library/Get Library.yml delete mode 100644 bruno/library/Get Scan Settings.yml delete mode 100644 bruno/library/Get User Visible Libraries.yml delete mode 100644 bruno/library/Set Library Visibility.yml delete mode 100644 bruno/library/Update Library.yml delete mode 100644 bruno/library/Update Scan Settings.yml delete mode 100644 bruno/media-items/Create Media Item.yml delete mode 100644 bruno/media-items/Create Media Rating.yml delete mode 100644 bruno/media-items/Delete Media Item.yml delete mode 100644 bruno/media-items/Delete Media Rating.yml delete mode 100644 bruno/media-items/EPUB Download.yml delete mode 100644 bruno/media-items/Get Audio Chapters.yml delete mode 100644 bruno/media-items/Get Audio Track.yml delete mode 100644 bruno/media-items/Get Media Item.yml delete mode 100644 bruno/media-items/Get Media Rating.yml delete mode 100644 bruno/media-items/List Media Items Sorted.yml delete mode 100644 bruno/media-items/List Media Items.yml delete mode 100644 bruno/media-items/Rescan Media Item.yml delete mode 100644 bruno/media-items/Update Media Item.yml delete mode 100644 bruno/media-items/Update Media Rating.yml create mode 100644 bruno/media-items/bulk_delete_media_items.yml create mode 100644 bruno/media-items/bulk_update_media_items.yml create mode 100644 bruno/media-items/create_media_bookmark.yml create mode 100644 bruno/media-items/create_media_item.yml create mode 100644 bruno/media-items/delete_media_bookmark.yml create mode 100644 bruno/media-items/delete_media_item.yml create mode 100644 bruno/media-items/delete_media_review.yml create mode 100644 bruno/media-items/deleted_annotations.yml create mode 100644 bruno/media-items/download_media_item_file.yml create mode 100644 bruno/media-items/filter_count.yml create mode 100644 bruno/media-items/get_audio_chapters.yml create mode 100644 bruno/media-items/get_audio_track.yml create mode 100644 bruno/media-items/get_facets.yml create mode 100644 bruno/media-items/get_media_bookmarks.yml create mode 100644 bruno/media-items/get_media_highlight.yml create mode 100644 bruno/media-items/get_media_item.yml create mode 100644 bruno/media-items/get_media_note.yml create mode 100644 bruno/media-items/get_media_review.yml create mode 100644 bruno/media-items/list_media_items.yml create mode 100644 bruno/media-items/merge_archived_media_item.yml create mode 100644 bruno/media-items/purge_archived_media_items.yml create mode 100644 bruno/media-items/purge_deleted_annotation.yml create mode 100644 bruno/media-items/rescan_media_item.yml create mode 100644 bruno/media-items/restore_deleted_annotation.yml create mode 100644 bruno/media-items/search_media_items.yml create mode 100644 bruno/media-items/set_audio_speed.yml create mode 100644 bruno/media-items/unarchive_media_item.yml create mode 100644 bruno/media-items/update_media_bookmark.yml create mode 100644 bruno/media-items/update_media_item.yml create mode 100644 bruno/media-items/upsert_media_review.yml delete mode 100644 bruno/notes/Create Media Note.yml delete mode 100644 bruno/notes/Delete Media Note.yml delete mode 100644 bruno/notes/Get Media Notes.yml delete mode 100644 bruno/notes/Get Single Media Note.yml delete mode 100644 bruno/notes/Update Media Note.yml create mode 100644 bruno/notes/create_note.yml create mode 100644 bruno/notes/delete_note.yml create mode 100644 bruno/notes/get_notes.yml create mode 100644 bruno/notes/update_note.yml delete mode 100644 bruno/opds/Download Book EPUB.yml delete mode 100644 bruno/opds/Download Book KEPUB (On-the-fly Conversion).yml delete mode 100644 bruno/opds/Download Book KEPUB.yml delete mode 100644 bruno/opds/Get Cover Image.yml delete mode 100644 bruno/opds/Get Device Catalog.yml delete mode 100644 bruno/opds/Get Device Navigation.yml delete mode 100644 bruno/opds/List Formats.yml delete mode 100644 bruno/opds/Search Device Catalog.yml create mode 100644 bruno/opds/acquisition.yml create mode 100644 bruno/opds/feeds.yml create mode 100644 bruno/opds/publication.yml delete mode 100644 bruno/progress/Delete Reading Progress.yml delete mode 100644 bruno/progress/Get Reading Progress.yml delete mode 100644 bruno/progress/Update Reading Progress.yml create mode 100644 bruno/progress/delete_media_progress.yml create mode 100644 bruno/progress/get_media_progress.yml create mode 100644 bruno/progress/get_progress_history.yml create mode 100644 bruno/progress/update_media_progress.yml delete mode 100644 bruno/queue/Clear Device Queue.yml delete mode 100644 bruno/queue/Delete Queue Item.yml delete mode 100644 bruno/queue/List All Queue Items (Admin).yml delete mode 100644 bruno/queue/List Device Queue Items.yml delete mode 100644 bruno/queue/List Queue Items (Pagination).yml delete mode 100644 bruno/queue/Retry Queue Item.yml create mode 100644 bruno/queue/clear_device_queue.yml create mode 100644 bruno/queue/delete_queue_item.yml create mode 100644 bruno/queue/get_device_queue_stats.yml create mode 100644 bruno/queue/list_all_queue_items.yml create mode 100644 bruno/queue/list_device_queue_items.yml create mode 100644 bruno/queue/retry_queue_item.yml create mode 100644 bruno/ratings/create_rating.yml create mode 100644 bruno/ratings/delete_rating.yml create mode 100644 bruno/ratings/get_ratings.yml create mode 100644 bruno/ratings/update_rating.yml delete mode 100644 bruno/saved-filters/Create Saved Filter.yml delete mode 100644 bruno/saved-filters/Delete Saved Filter.yml delete mode 100644 bruno/saved-filters/Get Saved Filter By ID.yml delete mode 100644 bruno/saved-filters/List Saved Filters.yml delete mode 100644 bruno/saved-filters/Update Saved Filter.yml create mode 100644 bruno/saved-filters/create_saved_filter.yml create mode 100644 bruno/saved-filters/delete_saved_filter.yml create mode 100644 bruno/saved-filters/get_saved_filter.yml create mode 100644 bruno/saved-filters/list_saved_filters.yml create mode 100644 bruno/saved-filters/update_saved_filter.yml delete mode 100644 bruno/scanner/Get Scan Status.yml delete mode 100644 bruno/scanner/Get Watch Mode Status.yml delete mode 100644 bruno/scanner/Scan Media Items.yml delete mode 100644 bruno/scanner/Start Scanner.yml delete mode 100644 bruno/scanner/Start Watch Mode.yml delete mode 100644 bruno/scanner/Stop Scanner.yml delete mode 100644 bruno/scanner/Stop Watch Mode.yml create mode 100644 bruno/scanner/cancel_scan.yml create mode 100644 bruno/scanner/get_active_scans.yml create mode 100644 bruno/scanner/get_scan_status.yml create mode 100644 bruno/scanner/get_scan_summary.yml create mode 100644 bruno/scanner/get_watch_mode_status.yml create mode 100644 bruno/scanner/scan_library.yml create mode 100644 bruno/scanner/start_scanner.yml create mode 100644 bruno/scanner/start_watch_mode.yml create mode 100644 bruno/scanner/stop_scanner.yml create mode 100644 bruno/scanner/stop_watch_mode.yml create mode 100644 bruno/series/get_series_books.yml create mode 100644 bruno/series/list_series.yml delete mode 100644 bruno/sidecar/Download Device Sidecar File.yml delete mode 100644 bruno/sidecar/Get Device Sidecar Config.yml delete mode 100644 bruno/sidecar/Get System Configuration.yml delete mode 100644 bruno/sidecar/Update System Configuration.yml create mode 100644 bruno/stats/get_book_stats.yml create mode 100644 bruno/stats/get_library_usage.yml create mode 100644 bruno/stats/get_summary.yml create mode 100644 bruno/stats/get_this_week.yml delete mode 100644 bruno/system/get-scan-settings.yml create mode 100644 bruno/system/get_config.yml create mode 100644 bruno/system/get_settings.yml delete mode 100644 bruno/system/update-scan-settings.yml create mode 100644 bruno/system/update_config.yml create mode 100644 bruno/system/update_setting.yml delete mode 100644 bruno/universal-progress/Get Progress History.yml delete mode 100644 bruno/user/admin/Delete User.yml delete mode 100644 bruno/user/admin/List Users.yml delete mode 100644 bruno/user/admin/Register Admin User.yml delete mode 100644 bruno/user/admin/Reset User Password.yml delete mode 100644 bruno/user/admin/Update User Max Devices.yml delete mode 100644 bruno/user/admin/Update User.yml delete mode 100644 bruno/user/auth/Login User.yml delete mode 100644 bruno/user/auth/Logout User.yml delete mode 100644 bruno/user/auth/Refresh Token.yml delete mode 100644 bruno/user/auth/Register User.yml delete mode 100644 bruno/user/profile/Get Profile.yml delete mode 100644 bruno/user/profile/Update Password.yml delete mode 100644 bruno/user/profile/Update Profile.yml delete mode 100644 bruno/user/profile/Update Theme.yml create mode 100644 bruno/users/change_password.yml create mode 100644 bruno/users/delete_user.yml create mode 100644 bruno/users/get_profile.yml create mode 100644 bruno/users/update_profile.yml create mode 100644 scripts/gen_bruno_from_docs.py diff --git a/bruno/admin/Get Admin Library.yml b/bruno/admin/Get Admin Library.yml deleted file mode 100644 index b13a946f..00000000 --- a/bruno/admin/Get Admin Library.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Get Admin Library - type: http - seq: 5 -http: - method: GET - url: '{{base_url}}/admin/library' - auth: inherit - body: - type: none - -docs: |- - ## Get Admin Library Page - - Retrieves the admin library page for administrative access. - - **Method:** GET - - **Endpoint:** /admin/library - - **Headers:** - - `Authorization` (string): Bearer token - - **Response:** - - HTML content for the admin library page - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden diff --git a/bruno/admin/hash_conflicts_api.yml b/bruno/admin/hash_conflicts_api.yml new file mode 100644 index 00000000..1f034d91 --- /dev/null +++ b/bruno/admin/hash_conflicts_api.yml @@ -0,0 +1,21 @@ +info: + name: Hash Conflicts (API) + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/admin/hash-conflicts' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Hash Conflicts (API) + + `GET /api/admin/hash-conflicts` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/admin/hash_conflicts_api.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/admin/list_users.yml b/bruno/admin/list_users.yml new file mode 100644 index 00000000..7a356936 --- /dev/null +++ b/bruno/admin/list_users.yml @@ -0,0 +1,21 @@ +info: + name: List Users + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/auth/users' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Users + + `GET /api/auth/users` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/admin/list_users.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/admin/update_user_max_devices.yml b/bruno/admin/update_user_max_devices.yml new file mode 100644 index 00000000..e79c8e89 --- /dev/null +++ b/bruno/admin/update_user_max_devices.yml @@ -0,0 +1,27 @@ +info: + name: Update User Max Devices + type: http + seq: 3 + +http: + method: PUT + url: '{{base_url}}/api/auth/users/{{media_item_id}}/max-devices' + auth: inherit + body: + type: json + data: |- + { + "max_devices": 15 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update User Max Devices + + `PUT /api/auth/users/:id/max-devices` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/admin/update_user_max_devices.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/analytics/Get Device Usage.yml b/bruno/analytics/Get Device Usage.yml deleted file mode 100644 index 4dd78a0f..00000000 --- a/bruno/analytics/Get Device Usage.yml +++ /dev/null @@ -1,47 +0,0 @@ -info: - name: Get Device Usage - type: http - seq: 2 -http: - method: GET - url: '{{base_url}}/api/analytics/device-usage' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Get usage statistics for all devices. - - **Endpoint**: GET /api/analytics/device-usage - **Auth**: Required (Bearer token) - - ## Response Fields - - | Field | Type | Description | - |-------|------|-------------| - | devices | array | List of device usage statistics | - | devices[].id | string | Device ID | - | devices[].device_name | string | Device name | - | devices[].device_type | string | Device type (kobo, kindle, koreader) | - | devices[].sync_count | int | Number of sync operations | - | devices[].last_sync | string | Last sync timestamp | - | devices[].total_reading_minutes | int | Total reading time on device | - | devices[].books_read | int | Number of books completed on device | - - ## Example Response - - ```json - { - "devices": [ - { - "id": "789e4567-e89b-12d3-a456-426614174001", - "device_name": "My Kobo Clara", - "device_type": "kobo", - "sync_count": 45, - "last_sync": "2026-02-08T17:25:00Z", - "total_reading_minutes": 1250, - "books_read": 3 diff --git a/bruno/analytics/Get Popular Books.yml b/bruno/analytics/Get Popular Books.yml deleted file mode 100644 index 21ecf50b..00000000 --- a/bruno/analytics/Get Popular Books.yml +++ /dev/null @@ -1,57 +0,0 @@ -info: - name: Get Popular Books - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/analytics/popular-books?limit=10' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Get popular books sorted by read count. - - **Endpoint**: GET /api/analytics/popular-books - **Auth**: Required (Bearer token) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|-----------|-------------| - | limit | int | No | Maximum number of books to return (default: 10) | - - ## Response Fields - - | Field | Type | Description | - |-------|------|-------------| - | books | array | List of popular books | - | books[].media_item_id | string | Book ID | - | books[].title | string | Book title | - | books[].author | string | Book author | - | books[].read_count | int | Number of times read | - | books[].avg_completion | float | Average completion rate (0-1) | - | books[].cover_url | string | Cover image URL | - - ## Example Request - - ``` - GET /api/analytics/popular-books?limit=10 - ``` - - ## Example Response - - ```json - { - "books": [ - { - "media_item_id": "323e4567-e89b-12d3-a456-426614174002", - "title": "The Great Gatsby", - "author": "F. Scott Fitzgerald", - "read_count": 5, - "avg_completion": 0.85, - "cover_url": "/api/books/323e4567-e89b-12d3-a456-426614174002/cover" diff --git a/bruno/analytics/Get Reading Stats Date Range.yml b/bruno/analytics/Get Reading Stats Date Range.yml deleted file mode 100644 index 14d4ba09..00000000 --- a/bruno/analytics/Get Reading Stats Date Range.yml +++ /dev/null @@ -1,58 +0,0 @@ -info: - name: Get Reading Stats Date Range - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/analytics/reading-stats?start_date=2024-01-01&end_date=2024-01-31' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Get reading statistics for a specific date range. - - **Endpoint**: GET /api/analytics/reading-stats - **Auth**: Required (Bearer token) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|-----------|-------------| - | start_date | string | No | Start date (ISO 8601 format, default: 30 days ago) | - | end_date | string | No | End date (ISO 8601 format, default: today) | - - ## Response Fields - - | Field | Type | Description | - |-------|------|-------------| - | total_books_read | int | Total books completed in range | - | total_pages_read | int | Total pages read in range | - | total_reading_time_minutes | int | Total reading time in minutes | - | completion_rate | float | Percentage of books completed (0-1) | - | daily_reading_minutes | array | Daily reading time per day | - | daily_reading_minutes[].date | string | Date (ISO 8601) | - | daily_reading_minutes[].minutes | int | Minutes read on that date | - - ## Example Request - - ``` - GET /api/analytics/reading-stats?start_date=2024-01-01&end_date=2024-01-31 - ``` - - ## Example Response - - ```json - { - "total_books_read": 2, - "total_pages_read": 450, - "total_reading_time_minutes": 720, - "completion_rate": 0.85, - "daily_reading_minutes": [ - { - "date": "2024-01-01", - "minutes": 30 diff --git a/bruno/analytics/Get Reading Stats.yml b/bruno/analytics/Get Reading Stats.yml deleted file mode 100644 index 639d10d9..00000000 --- a/bruno/analytics/Get Reading Stats.yml +++ /dev/null @@ -1,58 +0,0 @@ -info: - name: Get Reading Stats - type: http - seq: 4 -http: - method: GET - url: '{{base_url}}/api/analytics/reading-stats' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Get overall reading statistics for the authenticated user. - - **Endpoint**: GET /api/analytics/reading-stats - **Auth**: Required (Bearer token) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|-----------|-------------| - | start_date | string | No | Start date (ISO 8601 format) | - | end_date | string | No | End date (ISO 8601 format) | - - ## Response Fields - - | Field | Type | Description | - |-------|------|-------------| - | total_books_read | int | Total books completed | - | total_pages_read | int | Total pages read | - | total_reading_time_minutes | int | Total reading time in minutes | - | completion_rate | float | Average book completion rate (0-1) | - | daily_reading_minutes | array | Daily reading time breakdown | - | daily_reading_minutes[].date | string | Date (ISO 8601) | - | daily_reading_minutes[].minutes | int | Minutes read on that date | - - ## Example Request - - ``` - GET /api/analytics/reading-stats - ``` - - ## Example Response - - ```json - { - "total_books_read": 12, - "total_pages_read": 3450, - "total_reading_time_minutes": 5400, - "completion_rate": 0.78, - "daily_reading_minutes": [ - { - "date": "2026-01-15", - "minutes": 45 diff --git a/bruno/analytics/get_analytics.yml b/bruno/analytics/get_analytics.yml new file mode 100644 index 00000000..bbbb7fdd --- /dev/null +++ b/bruno/analytics/get_analytics.yml @@ -0,0 +1,21 @@ +info: + name: Get Reading Statistics + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/analytics/reading-stats' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Reading Statistics + + `GET /api/analytics/reading-stats` — auth: jwt. + + Canonical reference: `docs/developer/api/analytics/get_analytics.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/analytics/get_device_usage.yml b/bruno/analytics/get_device_usage.yml new file mode 100644 index 00000000..d7d4793f --- /dev/null +++ b/bruno/analytics/get_device_usage.yml @@ -0,0 +1,21 @@ +info: + name: Get Device Usage + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/analytics/device-usage' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device Usage + + `GET /api/analytics/device-usage` — auth: jwt. + + Canonical reference: `docs/developer/api/analytics/get_device_usage.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/analytics/get_popular_books.yml b/bruno/analytics/get_popular_books.yml new file mode 100644 index 00000000..bc860288 --- /dev/null +++ b/bruno/analytics/get_popular_books.yml @@ -0,0 +1,21 @@ +info: + name: Get Popular Books + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/analytics/popular-books' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Popular Books + + `GET /api/analytics/popular-books` — auth: jwt. + + Canonical reference: `docs/developer/api/analytics/get_popular_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/authentication/login.yml b/bruno/authentication/login.yml new file mode 100644 index 00000000..c6969947 --- /dev/null +++ b/bruno/authentication/login.yml @@ -0,0 +1,51 @@ +info: + name: Login User + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/auth/login' + auth: inherit + body: + type: json + data: |- + { + "login": "user@example.com", + "password": "SecureP@ss123!" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +runtime: + scripts: + - type: after-response + code: |- + function onResponse(res) { + try { + const responseBody = res.getBody(); + const token = responseBody.access_token; + const refreshToken = responseBody.refresh_token; + if (token) { + bru.setEnvVar("token", token, { persist: true }); + console.log("Access token saved"); + } + if (refreshToken) { + bru.setEnvVar("refresh_token", refreshToken, { persist: true }); + console.log("Refresh token saved"); + } + } catch (error) { + console.error("Token capture failed:", error.message); + } + } + onResponse(); + +docs: |- + # Login User + + `POST /api/auth/login` — auth: none. + + Canonical reference: `docs/developer/api/authentication/login.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/authentication/logout.yml b/bruno/authentication/logout.yml new file mode 100644 index 00000000..4adcfa46 --- /dev/null +++ b/bruno/authentication/logout.yml @@ -0,0 +1,21 @@ +info: + name: Logout User + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/auth/logout' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Logout User + + `POST /api/auth/logout` — auth: none. + + Canonical reference: `docs/developer/api/authentication/logout.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/authentication/password_rules.yml b/bruno/authentication/password_rules.yml new file mode 100644 index 00000000..9129e1bb --- /dev/null +++ b/bruno/authentication/password_rules.yml @@ -0,0 +1,21 @@ +info: + name: Password Rules + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/auth/password-rules' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Password Rules + + `GET /api/auth/password-rules` — auth: none. + + Canonical reference: `docs/developer/api/authentication/password_rules.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/authentication/refresh_token.yml b/bruno/authentication/refresh_token.yml new file mode 100644 index 00000000..df3f8b0e --- /dev/null +++ b/bruno/authentication/refresh_token.yml @@ -0,0 +1,50 @@ +info: + name: Refresh Token + type: http + seq: 4 + +http: + method: POST + url: '{{base_url}}/api/auth/refresh' + auth: inherit + body: + type: json + data: |- + { + "refresh_token": "d4f5g6h7..." + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +runtime: + scripts: + - type: after-response + code: |- + function onResponse(res) { + try { + const responseBody = res.getBody(); + const token = responseBody.access_token; + const refreshToken = responseBody.refresh_token; + if (token) { + bru.setEnvVar("token", token, { persist: true }); + console.log("Access token saved"); + } + if (refreshToken) { + bru.setEnvVar("refresh_token", refreshToken, { persist: true }); + console.log("Refresh token saved"); + } + } catch (error) { + console.error("Token capture failed:", error.message); + } + } + onResponse(); + +docs: |- + # Refresh Token + + `POST /api/auth/refresh` — auth: none. + + Canonical reference: `docs/developer/api/authentication/refresh_token.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/authentication/register.yml b/bruno/authentication/register.yml new file mode 100644 index 00000000..a0dd1255 --- /dev/null +++ b/bruno/authentication/register.yml @@ -0,0 +1,54 @@ +info: + name: Register User + type: http + seq: 5 + +http: + method: POST + url: '{{base_url}}/api/auth/register' + auth: inherit + body: + type: json + data: |- + { + "email": "user@example.com", + "username": "john", + "password": "SecureP@ss123!", + "first_name": "John", + "last_name": "Doe" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +runtime: + scripts: + - type: after-response + code: |- + function onResponse(res) { + try { + const responseBody = res.getBody(); + const token = responseBody.access_token; + const refreshToken = responseBody.refresh_token; + if (token) { + bru.setEnvVar("token", token, { persist: true }); + console.log("Access token saved"); + } + if (refreshToken) { + bru.setEnvVar("refresh_token", refreshToken, { persist: true }); + console.log("Refresh token saved"); + } + } catch (error) { + console.error("Token capture failed:", error.message); + } + } + onResponse(); + +docs: |- + # Register User + + `POST /api/auth/register` — auth: none. + + Canonical reference: `docs/developer/api/authentication/register.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/auto_link_books.yml b/bruno/book-matching/auto_link_books.yml new file mode 100644 index 00000000..39ec5540 --- /dev/null +++ b/bruno/book-matching/auto_link_books.yml @@ -0,0 +1,28 @@ +info: + name: Auto Link Books + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/sync/auto-link-books' + auth: inherit + body: + type: json + data: |- + { + "confidence_threshold": 0.8, + "limit": 100 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Auto Link Books + + `POST /api/sync/auto-link-books` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/auto_link_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/bulk_link_books.yml b/bruno/book-matching/bulk_link_books.yml new file mode 100644 index 00000000..a2a12e11 --- /dev/null +++ b/bruno/book-matching/bulk_link_books.yml @@ -0,0 +1,38 @@ +info: + name: Bulk Link Books + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/sync/bulk-link-books' + auth: inherit + body: + type: json + data: |- + { + "links": [ + { + "unlinked_book_id": "770e8400-e29b-41d4-a716-446655440002", + "media_item_id": "660e8400-e29b-41d4-a716-446655440001", + "confidence_score": 0.95 + }, + { + "unlinked_book_id": "880e8400-e29b-41d4-a716-446655440003", + "media_item_id": "990e8400-e29b-41d4-a716-446655440004", + "confidence_score": 0.87 + } + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Link Books + + `POST /api/sync/bulk-link-books` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/bulk_link_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/create_device_file_alias.yml b/bruno/book-matching/create_device_file_alias.yml new file mode 100644 index 00000000..0d619dd9 --- /dev/null +++ b/bruno/book-matching/create_device_file_alias.yml @@ -0,0 +1,29 @@ +info: + name: Create Device File Alias + type: http + seq: 3 + +http: + method: POST + url: '{{base_url}}/api/devices/{{device_id}}/file-aliases' + auth: inherit + body: + type: json + data: |- + { + "media_item_id": "550e8400-e29b-41d4-a716-446655440001", + "file_name": "book.epub", + "file_hash": "abc123..." + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Device File Alias + + `POST /api/devices/:id/file-aliases` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/create_device_file_alias.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/delete_device_file_alias.yml b/bruno/book-matching/delete_device_file_alias.yml new file mode 100644 index 00000000..6529c221 --- /dev/null +++ b/bruno/book-matching/delete_device_file_alias.yml @@ -0,0 +1,21 @@ +info: + name: Delete Device File Alias + type: http + seq: 4 + +http: + method: DELETE + url: '{{base_url}}/api/devices/{{device_id}}/file-aliases/{{alias_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Device File Alias + + `DELETE /api/devices/:id/file-aliases/:aliasId` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/delete_device_file_alias.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/get_book_matches.yml b/bruno/book-matching/get_book_matches.yml new file mode 100644 index 00000000..457588f2 --- /dev/null +++ b/bruno/book-matching/get_book_matches.yml @@ -0,0 +1,21 @@ +info: + name: Get Book Matches + type: http + seq: 5 + +http: + method: GET + url: '{{base_url}}/api/books/match' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Book Matches + + `GET /api/books/match` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/get_book_matches.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/get_device_file_aliases.yml b/bruno/book-matching/get_device_file_aliases.yml new file mode 100644 index 00000000..ecda5a8c --- /dev/null +++ b/bruno/book-matching/get_device_file_aliases.yml @@ -0,0 +1,21 @@ +info: + name: Get Device File Aliases + type: http + seq: 6 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/file-aliases' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device File Aliases + + `GET /api/devices/:id/file-aliases` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/get_device_file_aliases.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/get_unlinked_book_suggestions.yml b/bruno/book-matching/get_unlinked_book_suggestions.yml new file mode 100644 index 00000000..b377215a --- /dev/null +++ b/bruno/book-matching/get_unlinked_book_suggestions.yml @@ -0,0 +1,21 @@ +info: + name: Get Unlinked Book Suggestions + type: http + seq: 7 + +http: + method: GET + url: '{{base_url}}/api/sync/unlinked-books/{{unlinked_book_id}}/suggestions' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Unlinked Book Suggestions + + `GET /api/sync/unlinked-books/:id/suggestions` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/get_unlinked_book_suggestions.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/get_unlinked_books.yml b/bruno/book-matching/get_unlinked_books.yml new file mode 100644 index 00000000..a999c9e8 --- /dev/null +++ b/bruno/book-matching/get_unlinked_books.yml @@ -0,0 +1,21 @@ +info: + name: Get Unlinked Books + type: http + seq: 8 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/sync/unlinked-books' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Unlinked Books + + `GET /api/devices/:deviceId/sync/unlinked-books` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/get_unlinked_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/link_book.yml b/bruno/book-matching/link_book.yml new file mode 100644 index 00000000..190cd9f6 --- /dev/null +++ b/bruno/book-matching/link_book.yml @@ -0,0 +1,33 @@ +info: + name: Link Book + type: http + seq: 9 + +http: + method: POST + url: '{{base_url}}/api/devices/{{device_id}}/sync/link-book' + auth: inherit + body: + type: json + data: |- + { + "device_file": { + "file_path": "/books/hobbit.epub", + "sha256": "abc123...", + "title": "The Hobbit" + }, + "media_item_id": "550e8400-e29b-41d4-a716-446655440000", + "confidence_score": 1.0 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Link Book + + `POST /api/devices/:deviceId/sync/link-book` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/link_book.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/search_books.yml b/bruno/book-matching/search_books.yml new file mode 100644 index 00000000..bf898d37 --- /dev/null +++ b/bruno/book-matching/search_books.yml @@ -0,0 +1,31 @@ +info: + name: Search Books for Matching + type: http + seq: 10 + +http: + method: POST + url: '{{base_url}}/api/sync/books/query' + auth: inherit + body: + type: json + data: |- + { + "identifiers": ["isbn:978-0345391802", "uuid:abc-123"], + "sha256": "a1b2c3d4e5f6abc123...", + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "file_size": 2456789 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Search Books for Matching + + `POST /api/sync/books/query` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/search_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/book-matching/update_device_file_alias.yml b/bruno/book-matching/update_device_file_alias.yml new file mode 100644 index 00000000..96780b68 --- /dev/null +++ b/bruno/book-matching/update_device_file_alias.yml @@ -0,0 +1,28 @@ +info: + name: Update Device File Alias + type: http + seq: 11 + +http: + method: PUT + url: '{{base_url}}/api/devices/{{device_id}}/file-aliases/{{alias_id}}' + auth: inherit + body: + type: json + data: |- + { + "file_name": "updated-book.epub", + "file_hash": "newhash123..." + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Device File Alias + + `PUT /api/devices/:id/file-aliases/:aliasId` — auth: jwt. + + Canonical reference: `docs/developer/api/book-matching/update_device_file_alias.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/books/Bulk Delete Books.yml b/bruno/books/Bulk Delete Books.yml deleted file mode 100644 index cd7d3d8a..00000000 --- a/bruno/books/Bulk Delete Books.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Bulk Delete Media Items - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/media-items/bulk-delete' - auth: inherit - body: - type: json - data: "{\n \"media_item_ids\": [\n \"{{bookId1" - headers: - - key: Content-Type - value: application/json -docs: "## Bulk Delete Media Items\n\n Deletes multiple media items in a single request.\n\n **Method:** POST\n\n **Endpoint:** /api/media-items/bulk-delete\n\n **Authentication:** Required (Bearer token)\n\n **Request Body:**\n - `media_item_ids` (array of strings): Array of media item UUIDs to delete\n\n **Response:**\n - `results` (array): Results for each deletion attempt\n - `total` (number): Total number of media items processed\n - `deleted` (number): Number of successfully deleted media items\n - `failed` (number): Number of failed deletions\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"media_item_ids\": [\n \"uuid-1\",\n \"uuid-2\",\n \"uuid-3\"\n ]" diff --git a/bruno/books/Bulk Update Books.yml b/bruno/books/Bulk Update Books.yml deleted file mode 100644 index 64e0a02b..00000000 --- a/bruno/books/Bulk Update Books.yml +++ /dev/null @@ -1,16 +0,0 @@ -info: - name: Bulk Update Media Items - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/media-items/bulk-update' - auth: inherit - body: - type: json - data: "{\n \"media_item_updates\": [\n {\n \"media_item_id\": \"{{bookId1" - headers: - - key: Content-Type - value: application/json -docs: "## Bulk Update Media Items\n\n Updates multiple media items in a single request with different fields for each item.\n\n **Method:** POST\n\n **Endpoint:** /api/media-items/bulk-update\n\n **Authentication:** Required (Bearer token)\n\n **Request Body:**\n - `media_item_updates` (array): Array of update objects\n - `media_item_id` (string): Media item UUID to update\n - `updates` (object): Fields to update (can include title, author, genre, tags, contributors, etc.)\n\n **Response:**\n - `results` (array): Results for each update attempt\n - `total` (number): Total number of media items processed\n - `updated` (number): Number of successfully updated media items\n - `failed` (number): Number of failed updates\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"media_item_updates\": [\n {\n\ - \ \"media_item_id\": \"uuid-1\",\n \"updates\": {\n \"title\": \"New Title\",\n \"genre\": \"Fiction\",\n \"tags\": [\"fiction\", \"adventure\"]" diff --git a/bruno/collections/Add Books to Collection.yml b/bruno/collections/Add Books to Collection.yml deleted file mode 100644 index 73e26d8f..00000000 --- a/bruno/collections/Add Books to Collection.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Add Books to Collection - type: http - seq: 6 -http: - method: POST - url: '{{base_url}}/api/collections/{{collection_id}}/books' - auth: inherit - body: - type: json - data: "{\n \"book_ids\": [\n \"{{book_id_1}}" -docs: "## Add Books to Collection\n\n Adds multiple books to an existing collection.\n \n **Method:** POST\n \n **Endpoint:** /api/collections/{collection_id}/books\n \n **Authentication:** Required (Bearer token)\n \n **Path Parameters:**\n - `collection_id` (string): Collection ID\n \n **Request Body:**\n - `book_ids` (array of strings, required): List of book IDs to add\n \n **Response:**\n - `message` (string): Success message\n - `added_count` (integer): Number of books added\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid request\n - 401: Unauthorized\n - 404: Collection not found" diff --git a/bruno/collections/Create Collection.yml b/bruno/collections/Create Collection.yml deleted file mode 100644 index 59b51f63..00000000 --- a/bruno/collections/Create Collection.yml +++ /dev/null @@ -1,13 +0,0 @@ -info: - name: Create Collection - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/collections' - auth: inherit - body: - type: json - data: "{\n \"name\": \"Science Fiction\",\n \"description\": \"My favorite sci-fi books\",\n \"color\": \"#ff0000\",\n \"icon\": \"\U0001F680\",\n \"auto_assign_rules\": [\n {\n \"id\": \"rule-1\",\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"Science Fiction\",\n \"priority\": 8" -docs: "## Create Collection\n\n Creates a new collection for organizing books.\n \n **Method:** POST\n \n **Endpoint:** /api/collections\n \n **Authentication:** Required (Bearer token)\n \n **Request Body:**\n - `name` (string, required): Collection name (max 100 chars)\n - `description` (string, optional): Collection description (max 500 chars)\n - `color` (string, optional): Hex color code (default #3498db)\n - `icon` (string, optional): Emoji icon (default \U0001F4DA)\n - `auto_assign_rules` (array, optional): Auto-assignment rules\n - `id` (string): Rule ID\n - `field` (string): Field to match (genre, author, series, etc.)\n - `operator` (string): Comparison operator (equals, contains, startsWith)\n - `value` (string): Value to match\n - `priority` (integer): Rule priority (1-10)\n \n **Response:**\n - Collection object with generated ID\n \n **Status Codes:**\n - 201: Created\n - 400: Invalid request\n - 401: Unauthorized\n - 409: Collection name\ - \ already exists" diff --git a/bruno/collections/Create Device Mapping.yml b/bruno/collections/Create Device Mapping.yml deleted file mode 100644 index 56ee66a7..00000000 --- a/bruno/collections/Create Device Mapping.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Create Device Mapping - type: http - seq: 9 -http: - method: POST - url: '{{base_url}}/api/devices/{{device_id}}/collections' - auth: inherit - body: - type: json - data: "{\n \"collection_id\": \"{{collection_id}}" -docs: "## Create Device Mapping\n\n Maps a collection to a device shelf for synchronization.\n \n **Method:** POST\n \n **Endpoint:** /api/devices/{device_id}/collections\n \n **Authentication:** Required (Bearer token)\n \n **Path Parameters:**\n - `device_id` (string): Device ID\n \n **Request Body:**\n - `collection_id` (string, required): Collection ID to map\n - `device_shelf_name` (string, optional): Custom shelf name on device\n - `sync_direction` (string, optional): sync direction (book_to_device, device_to_book, bidirectional)\n \n **Response:**\n - Mapping object with generated ID\n \n **Status Codes:**\n - 201: Created\n - 400: Invalid request\n - 401: Unauthorized\n - 404: Device or collection not found" diff --git a/bruno/collections/Delete Collection.yml b/bruno/collections/Delete Collection.yml deleted file mode 100644 index 67d7451d..00000000 --- a/bruno/collections/Delete Collection.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Delete Collection - type: http - seq: 5 -http: - method: DELETE - url: '{{base_url}}/api/collections/{{collection_id}}' - auth: inherit - -docs: |- - ## Delete Collection - - Permanently deletes a collection and all its mappings. - - **Method:** DELETE - - **Endpoint:** /api/collections/{collection_id} - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `collection_id` (string): Collection ID - - **Response:** - - `message` (string): Success message - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Collection not found diff --git a/bruno/collections/Delete Device Mapping.yml b/bruno/collections/Delete Device Mapping.yml deleted file mode 100644 index d15dd74b..00000000 --- a/bruno/collections/Delete Device Mapping.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Delete Device Mapping - type: http - seq: 11 -http: - method: DELETE - url: '{{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}' - auth: inherit - -docs: |- - ## Delete Device Mapping - - Removes a collection-to-device mapping. - - **Method:** DELETE - - **Endpoint:** /api/devices/{device_id}/collections/{mapping_id} - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - `mapping_id` (string): Mapping ID - - **Response:** - - `message` (string): Success message - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device or mapping not found diff --git a/bruno/collections/Get Book Collections.yml b/bruno/collections/Get Book Collections.yml deleted file mode 100644 index 0c879284..00000000 --- a/bruno/collections/Get Book Collections.yml +++ /dev/null @@ -1,44 +0,0 @@ -info: - name: Get Book Collections - type: http - seq: 12 - -http: - method: GET - url: "{{base_url}}/api/collections/{{collection_id}}/books" - body: - type: json - data: |- - { - "collection_id": "{{collection_id}}" - } - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Book Collections - - Retrieves all collections that a specific book belongs to. - - **Method:** GET - - **Endpoint:** /api/collections/{{collection_id}}/books - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `book_id` (string, required): Book ID - - **Response:** - - Array of collection objects containing the book - - **Status Codes:** - - 200: Success - - 400: Invalid request - - 401: Unauthorized - - 404: Book not found diff --git a/bruno/collections/Get Collection.yml b/bruno/collections/Get Collection.yml deleted file mode 100644 index a865cbc9..00000000 --- a/bruno/collections/Get Collection.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Get Collection - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/collections/{{collection_id}}' - auth: inherit - -docs: |- - ## Get Collection - - Retrieves detailed information about a specific collection. - - **Method:** GET - - **Endpoint:** /api/collections/{collection_id} - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `collection_id` (string): Collection ID - - **Response:** - - Collection object with all fields including books, rules, and mappings - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Collection not found diff --git a/bruno/collections/Get Collections.yml b/bruno/collections/Get Collections.yml deleted file mode 100644 index 894677fe..00000000 --- a/bruno/collections/Get Collections.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Get Collections - type: http - seq: 2 -http: - method: GET - url: '{{base_url}}/api/collections?include_auto=true&sort_by=name' - auth: inherit - -docs: |- - ## Get Collections - - Retrieves all collections for the authenticated user. - - **Method:** GET - - **Endpoint:** /api/collections - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `include_auto` (boolean, optional): Include auto-created collections (default false) - - `sort_by` (string, optional): Sort field (name, created_at, book_count) - - `order` (string, optional): Sort order (asc, desc) - - **Response:** - - Array of collection objects - - **Status Codes:** - - 200: Success - - 401: Unauthorized diff --git a/bruno/collections/Get Device Mappings.yml b/bruno/collections/Get Device Mappings.yml deleted file mode 100644 index 2d2bf5b5..00000000 --- a/bruno/collections/Get Device Mappings.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Get Device Mappings - type: http - seq: 8 -http: - method: GET - url: '{{base_url}}/api/devices/{{device_id}}/collections' - auth: inherit - -docs: |- - ## Get Device Mappings - - Retrieves all collection mappings for a specific device. - - **Method:** GET - - **Endpoint:** /api/devices/{device_id}/collections - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - **Response:** - - Array of device mapping objects with collection details - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device not found diff --git a/bruno/collections/Remove Book from Collection.yml b/bruno/collections/Remove Book from Collection.yml deleted file mode 100644 index d0c95dc4..00000000 --- a/bruno/collections/Remove Book from Collection.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Remove Book from Collection - type: http - seq: 7 -http: - method: DELETE - url: '{{base_url}}/api/collections/{{collection_id}}/books/{{book_id}}' - auth: inherit - -docs: |- - ## Remove Book from Collection - - Removes a single book from a collection. - - **Method:** DELETE - - **Endpoint:** /api/collections/{collection_id}/books/{book_id} - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `collection_id` (string): Collection ID - - `book_id` (string): Book ID - - **Response:** - - `message` (string): Success message - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Collection or book not found diff --git a/bruno/collections/Update Collection.yml b/bruno/collections/Update Collection.yml deleted file mode 100644 index d945d590..00000000 --- a/bruno/collections/Update Collection.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Collection - type: http - seq: 4 -http: - method: PUT - url: '{{base_url}}/api/collections/{{collection_id}}' - auth: inherit - body: - type: json - data: "{\n \"name\": \"Sci-Fi Favorites\",\n \"description\": \"Updated description\",\n \"color\": \"#00ff00\",\n \"icon\": \"⭐\",\n \"auto_assign_rules\": [\n {\n \"id\": \"rule-2\",\n \"field\": \"series\",\n \"operator\": \"equals\",\n \"value\": \"Foundation\",\n \"priority\": 9" -docs: "## Update Collection\n\n Updates an existing collection's properties.\n \n **Method:** PUT\n \n **Endpoint:** /api/collections/{collection_id}\n \n **Authentication:** Required (Bearer token)\n \n **Path Parameters:**\n - `collection_id` (string): Collection ID\n \n **Request Body:**\n - `name` (string, optional): New collection name\n - `description` (string, optional): New description\n - `color` (string, optional): New hex color code\n - `icon` (string, optional): New emoji icon\n - `auto_assign_rules` (array, optional): Updated auto-assignment rules\n \n **Response:**\n - Updated collection object\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid request\n - 401: Unauthorized\n - 404: Collection not found\n - 409: Collection name already exists" diff --git a/bruno/collections/Update Device Mapping.yml b/bruno/collections/Update Device Mapping.yml deleted file mode 100644 index f0a7eea5..00000000 --- a/bruno/collections/Update Device Mapping.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Device Mapping - type: http - seq: 10 -http: - method: PUT - url: '{{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}' - auth: inherit - body: - type: json - data: "{\n \"device_shelf_name\": \"Science Fiction\",\n \"sync_direction\": \"book_to_device\"" -docs: "## Update Device Mapping\n\n Updates settings for a collection-to-device mapping.\n \n **Method:** PUT\n \n **Endpoint:** /api/devices/{device_id}/collections/{mapping_id}\n \n **Authentication:** Required (Bearer token)\n \n **Path Parameters:**\n - `device_id` (string): Device ID\n - `mapping_id` (string): Mapping ID\n \n **Request Body:**\n - `device_shelf_name` (string, optional): Custom shelf name on device\n - `sync_direction` (string, optional): Sync direction (book_to_device, device_to_book, bidirectional)\n \n **Response:**\n - Updated mapping object\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid request\n - 401: Unauthorized\n - 404: Device or mapping not found" diff --git a/bruno/collections/add_auto_assign_rule.yml b/bruno/collections/add_auto_assign_rule.yml new file mode 100644 index 00000000..4cab783a --- /dev/null +++ b/bruno/collections/add_auto_assign_rule.yml @@ -0,0 +1,31 @@ +info: + name: Add Auto-Assign Rule + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/collections/{{collection_id}}/rules' + auth: inherit + body: + type: json + data: |- + { + "field": "genre", + "operator": "equals", + "value": "Science Fiction", + "priority": 1, + "enabled": true + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Add Auto-Assign Rule + + `POST /api/collections/:id/rules` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/add_auto_assign_rule.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/bulk_add_books.yml b/bruno/collections/bulk_add_books.yml new file mode 100644 index 00000000..9a905cdc --- /dev/null +++ b/bruno/collections/bulk_add_books.yml @@ -0,0 +1,34 @@ +info: + name: Bulk Add Books + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/collections/bulk-add-books' + auth: inherit + body: + type: json + data: |- + { + "operations": [ + { + "collection_id": "550e8400-e29b-41d4-a716-446655440000", + "book_ids": [ + "660e8400-e29b-41d4-a716-446655440001" + ] + } + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Add Books + + `POST /api/collections/bulk-add-books` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/bulk_add_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/bulk_assign.yml b/bruno/collections/bulk_assign.yml new file mode 100644 index 00000000..7794b3d6 --- /dev/null +++ b/bruno/collections/bulk_assign.yml @@ -0,0 +1,31 @@ +info: + name: Bulk Assign Books + type: http + seq: 3 + +http: + method: POST + url: '{{base_url}}/api/collections/{{collection_id}}/books' + auth: inherit + body: + type: json + data: |- + { + "book_ids": [ + "660e8400-e29b-41d4-a716-446655440000", + "770e8400-e29b-41d4-a716-446655440000", + "880e8400-e29b-41d4-a716-446655440000" + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Assign Books + + `POST /api/collections/:id/books` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/bulk_assign.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/bulk_remove_books.yml b/bruno/collections/bulk_remove_books.yml new file mode 100644 index 00000000..887144e7 --- /dev/null +++ b/bruno/collections/bulk_remove_books.yml @@ -0,0 +1,30 @@ +info: + name: Bulk Remove Books + type: http + seq: 4 + +http: + method: POST + url: '{{base_url}}/api/collections/{{collection_id}}/books/bulk-remove' + auth: inherit + body: + type: json + data: |- + { + "book_ids": [ + "550e8400-e29b-41d4-a716-446655440000", + "660e8400-e29b-41d4-a716-446655440001" + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Remove Books + + `POST /api/collections/:id/books/bulk-remove` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/bulk_remove_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/create-custom-section-manual.yml b/bruno/collections/create-custom-section-manual.yml deleted file mode 100644 index 3d549307..00000000 --- a/bruno/collections/create-custom-section-manual.yml +++ /dev/null @@ -1,16 +0,0 @@ -meta: - name: Create Custom Section with Manual Books - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/collections' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"My Favorites\",\n \"icon\": \"⭐\",\n \"description\": \"My favorite books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"\",\n \"manual_book_ids\": [\"{{media_item_id}}\"],\n \"match_type\": \"all\"\n}" -runtime: - scripts: - - type: after-response - code: "function onResponse(res) {\ntest(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\nexpect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains collection name\", function() {\n expect(res.body.name).to.eql(\"My Favorites\");\n });\n}" -docs: "Create a custom collection with manually selected books.\n\n**Endpoint**: POST /api/collections\n**Auth**: Required (Bearer token)\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"name\": \"My Favorites\",\n \"icon\": \"⭐\",\n \"description\": \"My favorite books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"\",\n \"manual_book_ids\": [\"book-id-1\", \"book-id-2\"],\n \"match_type\": \"all\"\n}\n```\n\n## Response\n\nReturns the created collection with generated ID." diff --git a/bruno/collections/create-custom-section-missing-fields.yml b/bruno/collections/create-custom-section-missing-fields.yml deleted file mode 100644 index 524d61bb..00000000 --- a/bruno/collections/create-custom-section-missing-fields.yml +++ /dev/null @@ -1,19 +0,0 @@ -meta: - name: Create Custom Section Missing Required Fields - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/collections' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id}}\",\n \"icon\": \"\U0001F4DA\"\n}" -runtime: - scripts: - - type: after-response - code: "function onResponse(res) {\ntest(\"status must be 400 with missing name\", function() {\n expect(res.status).to.eql(400);\n});\n\ntest(\"response contains error message\", function() {\n expect(res.body.error).to.exist;\n });\n}" -docs: 'Test error handling when creating a collection without required fields. - - - **Expected Response**: 400 Bad Request with error message.' diff --git a/bruno/collections/create-custom-section-rules.yml b/bruno/collections/create-custom-section-rules.yml deleted file mode 100644 index e5e64f32..00000000 --- a/bruno/collections/create-custom-section-rules.yml +++ /dev/null @@ -1,17 +0,0 @@ -meta: - name: Create Custom Section with Filter Rules - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/collections' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"Science Fiction Books\",\n \"icon\": \"\U0001F680\",\n \"description\": \"All my sci-fi books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"{\\\"rules\\\":[{\\\"id\\\":\\\"rule1\\\",\\\"field\\\":\\\"genre\\\",\\\"operator\\\":\\\"equals\\\",\\\"value\\\":\\\"Sci-Fi\\\",\\\"priority\\\":1}]}\",\n \"manual_book_ids\": [],\n \"match_type\": \"all\"\n}" -runtime: - scripts: - - type: after-response - code: "function onResponse(res) {\ntest(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\nexpect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains collection name\", function() {\n expect(res.body.name).to.eql(\"Science Fiction Books\");\n });\n\ntest(\"response contains is_system false\", function() {\n expect(res.body.is_system).to.exist;\n expect(res.body.is_system).to.eql(false);\n });\n}" -docs: "Create a custom collection with filter rules.\n\n**Endpoint**: POST /api/collections\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| library_id | string | Yes | Library UUID |\n| name | string | Yes | Collection name |\n| icon | string | No | Emoji icon |\n| description | string | No | Collection description |\n| show_on_dashboard | boolean | No | Show on dashboard (default: true) |\n| auto_assign_rules | string | No | JSON string of filter rules |\n| manual_book_ids | array | No | Array of book IDs |\n| match_type | string | No | \"all\" (AND) or \"any\" (OR) |\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"name\": \"Science Fiction Books\",\n \"icon\": \"\U0001F680\",\n \"description\": \"All my sci-fi books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"{\\\"rules\\\":[{\\\"id\\\":\\\"rule1\\\",\\\"field\\\":\\\ - \"genre\\\",\\\"operator\\\":\\\"equals\\\",\\\"value\\\":\\\"Sci-Fi\\\",\\\"priority\\\":1}]}\",\n \"manual_book_ids\": [],\n \"match_type\": \"all\"\n}\n```\n\n## Response\n\nReturns the created collection with generated ID." diff --git a/bruno/collections/create_collection.yml b/bruno/collections/create_collection.yml new file mode 100644 index 00000000..229ed14e --- /dev/null +++ b/bruno/collections/create_collection.yml @@ -0,0 +1,33 @@ +info: + name: Create Collection + type: http + seq: 5 + +http: + method: POST + url: '{{base_url}}/api/collections' + auth: inherit + body: + type: json + data: |- + { + "name": "To Read", + "description": "Books I want to read soon", + "color": "#00FF00", + "icon": "📖", + "collection_type": "static", + "auto_assign_rules": [], + "view_settings": { "kobo": { "view_mode": "grid" } } + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Collection + + `POST /api/collections` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/create_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/create_shelf_mapping.yml b/bruno/collections/create_shelf_mapping.yml new file mode 100644 index 00000000..eff8e54c --- /dev/null +++ b/bruno/collections/create_shelf_mapping.yml @@ -0,0 +1,29 @@ +info: + name: Create Shelf Mapping + type: http + seq: 6 + +http: + method: POST + url: '{{base_url}}/api/devices/{{collection_id}}/collections' + auth: inherit + body: + type: json + data: |- + { + "collection_id": "550e8400-e29b-41d4-a716-446655440000", + "device_shelf_name": "Sci-Fi", + "sync_direction": "bidirectional" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Shelf Mapping + + `POST /api/devices/:id/collections` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/create_shelf_mapping.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/delete_collection.yml b/bruno/collections/delete_collection.yml new file mode 100644 index 00000000..04bcfcc6 --- /dev/null +++ b/bruno/collections/delete_collection.yml @@ -0,0 +1,21 @@ +info: + name: Delete Collection + type: http + seq: 7 + +http: + method: DELETE + url: '{{base_url}}/api/collections/{{collection_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Collection + + `DELETE /api/collections/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/delete_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/delete_shelf_mapping.yml b/bruno/collections/delete_shelf_mapping.yml new file mode 100644 index 00000000..4955fc36 --- /dev/null +++ b/bruno/collections/delete_shelf_mapping.yml @@ -0,0 +1,21 @@ +info: + name: Delete Shelf Mapping + type: http + seq: 8 + +http: + method: DELETE + url: '{{base_url}}/api/devices/{{collection_id}}/collections/{{collection_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Shelf Mapping + + `DELETE /api/devices/:id/collections/:collectionId` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/delete_shelf_mapping.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/get_collection.yml b/bruno/collections/get_collection.yml new file mode 100644 index 00000000..603c3a51 --- /dev/null +++ b/bruno/collections/get_collection.yml @@ -0,0 +1,21 @@ +info: + name: Get Collection + type: http + seq: 9 + +http: + method: GET + url: '{{base_url}}/api/collections/{{collection_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Collection + + `GET /api/collections/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/get_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/get_collection_books.yml b/bruno/collections/get_collection_books.yml new file mode 100644 index 00000000..006c435f --- /dev/null +++ b/bruno/collections/get_collection_books.yml @@ -0,0 +1,21 @@ +info: + name: Get Book Collections + type: http + seq: 10 + +http: + method: GET + url: '{{base_url}}/api/collections/{{collection_id}}/books' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Book Collections + + `GET /api/collections/:id/books` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/get_collection_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/list_collections.yml b/bruno/collections/list_collections.yml new file mode 100644 index 00000000..dc4bc487 --- /dev/null +++ b/bruno/collections/list_collections.yml @@ -0,0 +1,21 @@ +info: + name: List Collections + type: http + seq: 11 + +http: + method: GET + url: '{{base_url}}/api/collections' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Collections + + `GET /api/collections` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/list_collections.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/preview-collection.yml b/bruno/collections/preview-collection.yml deleted file mode 100644 index dc0c864f..00000000 --- a/bruno/collections/preview-collection.yml +++ /dev/null @@ -1,16 +0,0 @@ -meta: - name: Preview Collection - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/collections/preview' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id}}\",\n \"rules\": [\n{\n \"id\": \"rule1\",\n \"field\": \"genre\",\n\"operator\": \"equals\",\n \"value\": \"Fiction\",\n\"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}" -runtime: - scripts: - - type: after-response - code: "function onResponse(res) {\ntest(\"status must be 200 with valid request\", function() {\n expect(res.status).to.eql(200);\n});\n\ntest(\"response contains items array\", function() {\n expect(res.body.items).to.exist;\nexpect(res.body.items).to.be.an('array');\n });\n}" -docs: "Preview collection with filter rules before saving.\n\n**Endpoint**: POST /api/collections/preview\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| library_id | string | Yes | Library UUID |\n| rules | array | No | Filter rules to evaluate |\n| manual_book_ids | array | No | Manually selected book IDs |\n| limit | int | No | Max items to return (default: 20, max: 100) |\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"rules\": [\n {\n \"id\": \"rule1\",\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"Fiction\",\n \"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}\n```\n\n## Response\n\nReturns array of matching book items.\n\nThis endpoint is used by the Custom Section Builder to show users what books will match their filter rules before they save the collection." diff --git a/bruno/collections/preview_collection.yml b/bruno/collections/preview_collection.yml new file mode 100644 index 00000000..450ee6a8 --- /dev/null +++ b/bruno/collections/preview_collection.yml @@ -0,0 +1,30 @@ +info: + name: Preview Collection + type: http + seq: 12 + +http: + method: POST + url: '{{base_url}}/api/collections/preview' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "rules": [], + "manual_book_ids": [], + "limit": 20 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Preview Collection + + `POST /api/collections/preview` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/preview_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/remove_auto_assign_rule.yml b/bruno/collections/remove_auto_assign_rule.yml new file mode 100644 index 00000000..e4b869b9 --- /dev/null +++ b/bruno/collections/remove_auto_assign_rule.yml @@ -0,0 +1,21 @@ +info: + name: Remove Auto-Assign Rule + type: http + seq: 13 + +http: + method: DELETE + url: '{{base_url}}/api/collections/{{collection_id}}/rules/{{rule_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Remove Auto-Assign Rule + + `DELETE /api/collections/:id/rules/:ruleId` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/remove_auto_assign_rule.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/reorder_items.yml b/bruno/collections/reorder_items.yml new file mode 100644 index 00000000..5756959e --- /dev/null +++ b/bruno/collections/reorder_items.yml @@ -0,0 +1,30 @@ +info: + name: Reorder Collection Items + type: http + seq: 14 + +http: + method: PUT + url: '{{base_url}}/api/collections/{{collection_id}}/items/order' + auth: inherit + body: + type: json + data: |- + { + "item_ids": [ + "550e8400-e29b-41d4-a716-446655440000", + "660e8400-e29b-41d4-a716-446655440001" + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Reorder Collection Items + + `PUT /api/collections/:id/items/order` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/reorder_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/scenarios/Bulk Add Books to Collections.yml b/bruno/collections/scenarios/Bulk Add Books to Collections.yml deleted file mode 100644 index c39d4198..00000000 --- a/bruno/collections/scenarios/Bulk Add Books to Collections.yml +++ /dev/null @@ -1,18 +0,0 @@ -info: - name: Bulk Add Books to Collections - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/collections/bulk-add-books' - auth: inherit - body: - type: json - data: "{\n \"operations\": [\n {\n \"collection_id\": \"{{collectionId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Add Books to Collections\n\n Adds multiple books to multiple collections in a single request. Each operation specifies a collection and a list of books to add.\n\n **Method:** POST\n\n **Endpoint:** /api/collections/bulk-add-books\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `operations` (array): Array of collection-book operations\n - `collection_id` (string): Collection UUID\n - `book_ids` (array): Array of book UUIDs to add to the collection\n\n **Response:**\n - `results` (array): Results for each operation\n - `total` (number): Total number of operations\n - `success` (number): Number of successful operations\n - `failed` (number): Number of failed operations\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"operations\": [\n {\n \"collection_id\":\ - \ \"collection-uuid-1\",\n \"book_ids\": [\"book-1\", \"book-2\"]" diff --git a/bruno/collections/scenarios/Bulk Remove Books - All Books.yml b/bruno/collections/scenarios/Bulk Remove Books - All Books.yml deleted file mode 100644 index 6ecd70d4..00000000 --- a/bruno/collections/scenarios/Bulk Remove Books - All Books.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Bulk Remove Books - All Books - type: http - seq: 5 -http: - method: POST - url: '{{base_url}}/api/collections/{{collection_id}}/books/bulk-remove' - auth: inherit - body: - type: json - data: "{\n \"book_ids\": [\n \"{{bookId1" - headers: - - key: Content-Type - value: application/json -docs: "## Bulk Remove Books from Collection\n\n Removes multiple books from a collection in a single request.\n\n **Method:** POST\n\n **Endpoint:** /api/collections/{collection_id" diff --git a/bruno/collections/scenarios/Bulk Remove Books - Empty List.yml b/bruno/collections/scenarios/Bulk Remove Books - Empty List.yml deleted file mode 100644 index 997264eb..00000000 --- a/bruno/collections/scenarios/Bulk Remove Books - Empty List.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Bulk Remove Books - Empty List - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/collections/{{collection_id}}/books/bulk-remove' - auth: inherit - body: - type: json - data: "{\n \"book_ids\": []" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Remove Books - Empty List Validation\n\n Tests validation behavior when providing an empty book_ids array.\n\n **Expected Result:** 400 Bad Request\n\n **Validation Rule:** book_ids array must contain at least one book UUID.\n\n **Purpose:** Ensures the API properly validates input and rejects empty removal requests." diff --git a/bruno/collections/scenarios/Bulk Remove Books - Invalid IDs.yml b/bruno/collections/scenarios/Bulk Remove Books - Invalid IDs.yml deleted file mode 100644 index dcd3350a..00000000 --- a/bruno/collections/scenarios/Bulk Remove Books - Invalid IDs.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Bulk Remove Books - Invalid IDs - type: http - seq: 4 -http: - method: POST - url: '{{base_url}}/api/collections/{{collection_id}}/books/bulk-remove' - auth: inherit - body: - type: json - data: "{\n \"book_ids\": [\n \"{{bookId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Remove Books - Invalid IDs\n\n Tests behavior when the book_ids array contains invalid UUID formats or non-existent books.\n\n **Expected Result:** 200 OK with partial success\n\n **Purpose:** Verifies that:\n - Invalid UUID formats don't crash the endpoint\n - Non-existent book IDs are handled gracefully\n - Valid IDs in the same request are still processed\n - Response includes detailed results showing which succeeded/failed\n\n **Note:** The endpoint should process all valid IDs and report failures for invalid ones, allowing clients to handle partial failures appropriately." diff --git a/bruno/collections/scenarios/Bulk Remove Books - Single Book.yml b/bruno/collections/scenarios/Bulk Remove Books - Single Book.yml deleted file mode 100644 index b81960ba..00000000 --- a/bruno/collections/scenarios/Bulk Remove Books - Single Book.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Bulk Remove Books - Single Book - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/collections/{{collection_id}}/books/bulk-remove' - auth: inherit - body: - type: json - data: "{\n \"book_ids\": [\n \"{{bookId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Remove Books - Single Book\n\n Tests that bulk remove endpoint works correctly with a single book.\n\n **Expected Result:** 200 OK with removed: 1, total: 1\n\n **Purpose:** Verifies the bulk remove endpoint handles single-item arrays correctly, providing flexibility for clients to use the same endpoint for both single and multiple removals.\n\n **Note:** Using bulk remove for a single book is functionally equivalent to the single remove endpoint but allows for consistent error handling and response format." diff --git a/bruno/collections/scenarios/Test Collection Rules - Author Contains.yml b/bruno/collections/scenarios/Test Collection Rules - Author Contains.yml deleted file mode 100644 index 85c984ce..00000000 --- a/bruno/collections/scenarios/Test Collection Rules - Author Contains.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Test Collection Rules - Author Contains - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/collections/test-rules' - auth: inherit - body: - type: json - data: "{\n \"rules\": [\n {\n \"field\": \"author\",\n \"operator\": \"contains\",\n \"value\": \"Asimov\"" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Test Collection Rules - Author Contains\n\n Tests the \"contains\" operator on the author field to find books by a specific author (partial match).\n\n **Example Use Case:** Finding all books by an author whose name contains \"Asimov\" (e.g., \"Isaac Asimov\").\n\n **Operator:** `contains` - Matches if the field contains the specified value as a substring (case-insensitive typically).\n\n **Expected Result:** Returns all books where the author field contains \"Asimov\".\n\n **Purpose:** Demonstrates text-based partial matching for author searches, useful when you don't need the exact author name or want to find books by authors with similar names." diff --git a/bruno/collections/scenarios/Test Collection Rules - Copyright Year Greater Than.yml b/bruno/collections/scenarios/Test Collection Rules - Copyright Year Greater Than.yml deleted file mode 100644 index 27ee2ec4..00000000 --- a/bruno/collections/scenarios/Test Collection Rules - Copyright Year Greater Than.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Test Collection Rules - Copyright Year Greater Than - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/collections/test-rules' - auth: inherit - body: - type: json - data: "{\n \"rules\": [\n {\n \"field\": \"copyright_year\",\n \"operator\": \"greater_than\",\n \"value\": \"2000\"" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Test Collection Rules - Copyright Year Greater Than\n\n Tests the \"greater_than\" operator on the copyright_year field to find books published after a specific year.\n\n **Example Use Case:** Creating a \"Modern Books\" collection with books published after 2000.\n\n **Operator:** `greater_than` - Matches if the field value is greater than the specified value (numeric comparison).\n\n **Field:** `copyright_year` - The year the book was copyrighted/published.\n\n **Expected Result:** Returns all books with copyright_year greater than 2000 (i.e., published in 2001 or later).\n\n **Purpose:** Demonstrates numeric comparison operators for creating date-based collections, useful for organizing books by publication era." diff --git a/bruno/collections/scenarios/Test Collection Rules - Empty Rules Array.yml b/bruno/collections/scenarios/Test Collection Rules - Empty Rules Array.yml deleted file mode 100644 index 9ba18073..00000000 --- a/bruno/collections/scenarios/Test Collection Rules - Empty Rules Array.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Test Collection Rules - Empty Rules Array - type: http - seq: 5 -http: - method: POST - url: '{{base_url}}/api/collections/test-rules' - auth: inherit - body: - type: json - data: "{\n \"rules\": []" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Test Collection Rules - Empty Rules Array\n\n Tests validation behavior when providing an empty rules array.\n\n **Expected Result:** 400 Bad Request\n\n **Validation Rule:** rules array must contain at least one rule object.\n\n **Purpose:** Ensures the API properly validates input and rejects empty rule sets, preventing accidental queries that would return all books or cause performance issues.\n\n **Use Case:** Client-side validation should prevent sending empty rules, but the API should also validate to catch malformed requests." diff --git a/bruno/collections/scenarios/Test Collection Rules - No Matches.yml b/bruno/collections/scenarios/Test Collection Rules - No Matches.yml deleted file mode 100644 index dd16b427..00000000 --- a/bruno/collections/scenarios/Test Collection Rules - No Matches.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Test Collection Rules - No Matches - type: http - seq: 4 -http: - method: POST - url: '{{base_url}}/api/collections/test-rules' - auth: inherit - body: - type: json - data: "{\n \"rules\": [\n {\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"NonExistentGenre123456\"" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Test Collection Rules - No Matches\n\n Tests behavior when collection rules don't match any books in the library.\n\n **Example Use Case:** Validating that a new genre name doesn't exist before creating a collection for it, or testing edge cases.\n\n **Expected Result:** 200 OK with empty matches array and total: 0\n\n **Purpose:** Verifies that the API handles zero-match scenarios gracefully:\n - Returns 200 (success) not 404\n - Returns empty array, not null\n - Returns total: 0 for clarity\n - No errors thrown for no results\n\n **Note:** An empty result set is a valid response and doesn't indicate an error. This allows users to test rules confidently before creating collections." diff --git a/bruno/collections/scenarios/Test Collection Rules.yml b/bruno/collections/scenarios/Test Collection Rules.yml deleted file mode 100644 index f95fe571..00000000 --- a/bruno/collections/scenarios/Test Collection Rules.yml +++ /dev/null @@ -1,18 +0,0 @@ -info: - name: Test Collection Rules - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/collections/test-rules' - auth: inherit - body: - type: json - data: "{\n \"rules\": [\n {\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"Science Fiction\"" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Test Collection Rules\n\n Tests collection rules against the library to see which books match, without creating a collection. Useful for previewing what books would be included in a collection with specific rules.\n\n **Method:** POST\n\n **Endpoint:** /api/collections/test-rules\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `rules` (array): Array of rule objects to test\n - `field` (string): Field to test (genre, author, copyright_year, tags, etc.)\n - `operator` (string): Comparison operator\n - `equals`: Exact match\n - `contains`: Contains substring (for text fields)\n - `greater_than`: Greater than (for numeric fields)\n - `less_than`: Less than (for numeric fields)\n - `not_equals`: Not equal to\n - `starts_with`: Starts with\n - `ends_with`: Ends with\n - `is_empty`: Field is empty or null\n - `is_not_empty`: Field is not empty and not null\n - `value` (string): Value to compare against (not required\ - \ for is_empty/is_not_empty)\n\n **Response:**\n - `matches` (array): Array of matching books\n - `id` (string): Book UUID\n - `title` (string): Book title\n - `author` (string): Book author\n - `genre` (string): Book genre\n - Additional book metadata\n - `total` (number): Total number of matching books\n\n **Status Codes:**\n - 200: Success - returns matching books\n - 400: Invalid request (empty rules array, invalid field/operator)\n - 401: Unauthorized\n - 500: Internal server error\n\n **Example Request:**\n ```json\n {\n \"rules\": [\n {\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"Science Fiction\"" diff --git a/bruno/collections/test_rule.yml b/bruno/collections/test_rule.yml new file mode 100644 index 00000000..e2de3c73 --- /dev/null +++ b/bruno/collections/test_rule.yml @@ -0,0 +1,38 @@ +info: + name: Test Rule + type: http + seq: 15 + +http: + method: POST + url: '{{base_url}}/api/collections/test-rules' + auth: inherit + body: + type: json + data: |- + { + "rules": [ + { + "field": "genre", + "operator": "equals", + "value": "Science Fiction" + }, + { + "field": "author", + "operator": "contains", + "value": "Asimov" + } + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Test Rule + + `POST /api/collections/test-rules` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/test_rule.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/update_collection.yml b/bruno/collections/update_collection.yml new file mode 100644 index 00000000..11674c1d --- /dev/null +++ b/bruno/collections/update_collection.yml @@ -0,0 +1,34 @@ +info: + name: Update Collection + type: http + seq: 16 + +http: + method: PUT + url: '{{base_url}}/api/collections/{{collection_id}}' + auth: inherit + body: + type: json + data: |- + { + "name": "To Read (2026)", + "description": "Updated description", + "color": "#33CCFF", + "icon": "📚", + "auto_assign_rules": [], + "view_settings": { "hide_finished": true }, + "hide_finished": ["true"], + "show_up_next": ["true"] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Collection + + `PUT /api/collections/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/update_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/collections/update_rule.yml b/bruno/collections/update_rule.yml new file mode 100644 index 00000000..4d5d223b --- /dev/null +++ b/bruno/collections/update_rule.yml @@ -0,0 +1,27 @@ +info: + name: Update Collection Rule + type: http + seq: 17 + +http: + method: PUT + url: '{{base_url}}/api/collections/{{collection_id}}/rules/{{rule_id}}' + auth: inherit + body: + type: json + data: |- + { + "enabled": false + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Collection Rule + + `PUT /api/collections/:id/rules/:ruleId` — auth: jwt. + + Canonical reference: `docs/developer/api/collections/update_rule.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/Delete Conflict.yml b/bruno/conflicts/Delete Conflict.yml deleted file mode 100644 index de05a3ef..00000000 --- a/bruno/conflicts/Delete Conflict.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Delete Conflict - type: http - seq: 4 -http: - method: DELETE - url: '{{base_url}}/api/conflicts/{{conflict_id}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{token - -docs: |- - ## Delete Conflict - - Permanently deletes a specific conflict record from the system. - - **Method:** DELETE - - **Endpoint:** /api/conflicts/{conflict_id diff --git a/bruno/conflicts/Dismiss All Resolved.yml b/bruno/conflicts/Dismiss All Resolved.yml deleted file mode 100644 index 5227bdb0..00000000 --- a/bruno/conflicts/Dismiss All Resolved.yml +++ /dev/null @@ -1,37 +0,0 @@ -info: - name: Dismiss All Resolved Conflicts - type: http - seq: 5 -http: - method: POST - url: '{{base_url}}/api/conflicts/dismiss-all' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{token - -docs: |- - ## Dismiss All Resolved Conflicts - - Deletes all resolved conflicts for the authenticated user, cleaning up the conflict list. - - **Method:** POST - - **Endpoint:** /api/conflicts/dismiss-all - - **Authentication:** Bearer token - - **Response:** - - `deleted` (number): Number of conflict records that were deleted - - **Status Codes:** - - 200: Success - conflicts deleted - - 401: Unauthorized - - 500: Internal server error - - **Example Response:** - ```json - { - "deleted": 5 diff --git a/bruno/conflicts/Get Conflict Details.yml b/bruno/conflicts/Get Conflict Details.yml deleted file mode 100644 index d3f81af0..00000000 --- a/bruno/conflicts/Get Conflict Details.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Get Conflict Details - type: http - seq: 2 -http: - method: GET - url: '{{base_url}}/api/conflicts/{{conflict_id}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{token - -docs: |- - ## Get Conflict Details - - Retrieves detailed information about a specific conflict, including side-by-side comparison of conflicting data from all sources. - - **Method:** GET - - **Endpoint:** /api/conflicts/{conflict_id diff --git a/bruno/conflicts/List Conflicts.yml b/bruno/conflicts/List Conflicts.yml deleted file mode 100644 index dfe720b5..00000000 --- a/bruno/conflicts/List Conflicts.yml +++ /dev/null @@ -1,74 +0,0 @@ -info: - name: List Conflicts - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/conflicts?status=unresolved' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{token - -docs: |- - ## List Conflicts - - Lists all sync conflicts for the authenticated user with optional filtering. - - **Method:** GET - - **Endpoint:** /api/conflicts - - **Authentication:** Bearer token - - **Query Parameters:** - - `status` (string, optional): Filter by resolution status - - `unresolved`: Only unresolved conflicts (default) - - `user_resolved`: Conflicts resolved by user - - `auto_resolved`: Automatically resolved conflicts - - `all`: All conflicts regardless of status - - `type` (string, optional): Filter by conflict type - - `progress`: Reading progress conflicts - - `note`: Bookmark/note conflicts - - `highlight`: Highlight conflicts - - **Response:** - - `conflicts` (array): Array of conflict objects - - `total` (number): Total number of conflicts matching filters - - `unresolved` (number): Number of unresolved conflicts - - **Each Conflict Object:** - - `id` (string): Conflict UUID - - `media_item_id` (string): Associated book UUID - - `media_item_title` (string): Book title - - `conflict_type` (string): Type of conflict (progress, note, highlight) - - `conflict_data` (object): Side-by-side comparison of conflicting data - - `koreader`: Data from KOReader device - - `kobo`: Data from Kobo device - - `web`: Data from web interface - - `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved) - - `created_at` (string): ISO 8601 timestamp when conflict was detected - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 500: Internal server error - - **Example Request:** - ``` - GET /api/conflicts?status=unresolved&type=progress - ``` - - **Example Response:** - ```json - { - "conflicts": [ - { - "id": "conflict-uuid", - "media_item_id": "book-uuid", - "media_item_title": "Foundation", - "conflict_type": "progress", - "conflict_data": { - "koreader": { "percentage": 0.65, "epubcfi": "..." diff --git a/bruno/conflicts/Resolve Conflict.yml b/bruno/conflicts/Resolve Conflict.yml deleted file mode 100644 index 040fa785..00000000 --- a/bruno/conflicts/Resolve Conflict.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Resolve Conflict - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/conflicts/{{conflict_id}}/resolve' - auth: inherit - body: - type: json - data: "{\n \"winner\": \"koreader\",\n \"manual_data\": null,\n \"apply_to_all_future_conflicts\": false,\n \"reason\": \"User chose more recent progress\"" - headers: - - key: Authorization - value: Bearer {{token -docs: "## Resolve Conflict\n\n Resolves a sync conflict by choosing which source to use for the conflicting data.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/{conflict_id" diff --git a/bruno/conflicts/bulk_dismiss_conflicts.yml b/bruno/conflicts/bulk_dismiss_conflicts.yml new file mode 100644 index 00000000..1484044a --- /dev/null +++ b/bruno/conflicts/bulk_dismiss_conflicts.yml @@ -0,0 +1,30 @@ +info: + name: Bulk Dismiss Conflicts + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/conflicts/bulk-dismiss' + auth: inherit + body: + type: json + data: |- + { + "conflict_ids": [ + "550e8400-e29b-41d4-a716-446655440000", + "660e8400-e29b-41d4-a716-446655440001" + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Dismiss Conflicts + + `POST /api/conflicts/bulk-dismiss` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/bulk_dismiss_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/bulk_resolve_conflicts.yml b/bruno/conflicts/bulk_resolve_conflicts.yml new file mode 100644 index 00000000..1b894fc1 --- /dev/null +++ b/bruno/conflicts/bulk_resolve_conflicts.yml @@ -0,0 +1,31 @@ +info: + name: Bulk Resolve Conflicts + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/conflicts/bulk-resolve' + auth: inherit + body: + type: json + data: |- + { + "conflict_ids": [ + "550e8400-e29b-41d4-a716-446655440000", + "660e8400-e29b-41d4-a716-446655440001" + ], + "strategy": "highest_progress" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Resolve Conflicts + + `POST /api/conflicts/bulk-resolve` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/bulk_resolve_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/delete_conflict.yml b/bruno/conflicts/delete_conflict.yml new file mode 100644 index 00000000..8cb0b4d5 --- /dev/null +++ b/bruno/conflicts/delete_conflict.yml @@ -0,0 +1,21 @@ +info: + name: Delete Conflict + type: http + seq: 3 + +http: + method: DELETE + url: '{{base_url}}/api/conflicts/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Conflict + + `DELETE /api/conflicts/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/delete_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/dismiss_all_resolved.yml b/bruno/conflicts/dismiss_all_resolved.yml new file mode 100644 index 00000000..37627712 --- /dev/null +++ b/bruno/conflicts/dismiss_all_resolved.yml @@ -0,0 +1,21 @@ +info: + name: Dismiss All Resolved + type: http + seq: 4 + +http: + method: POST + url: '{{base_url}}/api/conflicts/dismiss-all' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Dismiss All Resolved + + `POST /api/conflicts/dismiss-all` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/dismiss_all_resolved.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/get_conflict.yml b/bruno/conflicts/get_conflict.yml new file mode 100644 index 00000000..990ad711 --- /dev/null +++ b/bruno/conflicts/get_conflict.yml @@ -0,0 +1,21 @@ +info: + name: Get Conflict Details + type: http + seq: 5 + +http: + method: GET + url: '{{base_url}}/api/conflicts/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Conflict Details + + `GET /api/conflicts/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/get_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/list_conflicts.yml b/bruno/conflicts/list_conflicts.yml new file mode 100644 index 00000000..0162fd32 --- /dev/null +++ b/bruno/conflicts/list_conflicts.yml @@ -0,0 +1,21 @@ +info: + name: List Conflicts + type: http + seq: 6 + +http: + method: GET + url: '{{base_url}}/api/conflicts' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Conflicts + + `GET /api/conflicts` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/list_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/resolve_conflict.yml b/bruno/conflicts/resolve_conflict.yml new file mode 100644 index 00000000..172fd972 --- /dev/null +++ b/bruno/conflicts/resolve_conflict.yml @@ -0,0 +1,27 @@ +info: + name: Resolve Conflict + type: http + seq: 7 + +http: + method: POST + url: '{{base_url}}/api/conflicts/{{media_item_id}}/resolve' + auth: inherit + body: + type: json + data: |- + { + "winner": "kobo" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Resolve Conflict + + `POST /api/conflicts/:id/resolve` — auth: jwt. + + Canonical reference: `docs/developer/api/conflicts/resolve_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/conflicts/scenarios/Bulk Dismiss Conflicts.yml b/bruno/conflicts/scenarios/Bulk Dismiss Conflicts.yml deleted file mode 100644 index a55d0746..00000000 --- a/bruno/conflicts/scenarios/Bulk Dismiss Conflicts.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Bulk Dismiss Conflicts - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/conflicts/bulk-dismiss' - auth: inherit - body: - type: json - data: "{\n \"conflict_ids\": [\n \"{{conflictId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Dismiss Conflicts\n\n Dismisses multiple sync conflicts without resolving them. This removes them from the conflict list while leaving the data unchanged.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-dismiss\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to dismiss\n\n **Response:**\n - `results` (array): Results for each dismissal\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully dismissed conflicts\n - `failed` (number): Number of failed dismissals\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\"]" diff --git a/bruno/conflicts/scenarios/Bulk Resolve Conflicts.yml b/bruno/conflicts/scenarios/Bulk Resolve Conflicts.yml deleted file mode 100644 index d17e89a3..00000000 --- a/bruno/conflicts/scenarios/Bulk Resolve Conflicts.yml +++ /dev/null @@ -1,18 +0,0 @@ -info: - name: Bulk Resolve Conflicts - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/conflicts/bulk-resolve' - auth: inherit - body: - type: json - data: "{\n \"conflict_ids\": [\n \"{{conflictId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Resolve Conflicts\n\n Resolves multiple sync conflicts in a single request using a specified resolution strategy.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Resolution strategy\n - `most_recent`: Use the most recently updated progress\n - `highest_progress`: Use the reading progress with the highest percent read\n - `server`: Always prefer server-side data\n - `device`: Always prefer device-side data\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n\ - \ - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\", \"uuid-3\"],\n \"strategy\": \"most_recent\"" diff --git a/bruno/conflicts/scenarios/Bulk Resolve Highest Progress.yml b/bruno/conflicts/scenarios/Bulk Resolve Highest Progress.yml deleted file mode 100644 index 16b6315c..00000000 --- a/bruno/conflicts/scenarios/Bulk Resolve Highest Progress.yml +++ /dev/null @@ -1,18 +0,0 @@ -info: - name: Bulk Resolve with Highest Progress Strategy - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/conflicts/bulk-resolve' - auth: inherit - body: - type: json - data: "{\n \"conflict_ids\": [\n \"{{conflictId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Resolve with Highest Progress Strategy\n\n Resolves multiple sync conflicts using the \"highest_progress\" strategy, which keeps the reading progress with the highest percentage read.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Must be \"highest_progress\"\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Note:** The highest progress strategy is ideal when you want to preserve the most reading progress across devices. Use this when you've been\ - \ reading on multiple devices and want to keep the furthest position." diff --git a/bruno/dashboard/create-collection-with-dashboard.yml b/bruno/dashboard/create-collection-with-dashboard.yml deleted file mode 100644 index eeeaf7a6..00000000 --- a/bruno/dashboard/create-collection-with-dashboard.yml +++ /dev/null @@ -1,38 +0,0 @@ -info: - name: Create Collection with Dashboard - type: http - seq: 4 -http: - method: POST - url: '{{base_url}}/api/collections' - auth: inherit -runtime: - scripts: - - type: tests - code: "test(\"creates collection successfully\", function() {\n expect(res.status).to.eql(201);" - -docs: |- - Create a new collection with dashboard visibility enabled. - - **Endpoint**: POST /api/collections - **Auth**: Required (Bearer token) - - ## Request Body - - | Field | Type | Required | Description | - |-------|------|----------|-------------| - | name | string | Yes | Collection name | - | description | string | No | Collection description | - | color | string | No | Hex color code | - | icon | string | No | Icon emoji or name | - | show_on_dashboard | boolean | No | Show on dashboard (default: false) | - - ## Example Request - - ```json - { - "name": "My Favorites", - "description": "My favorite books", - "color": "#FF5733", - "icon": "⭐", - "show_on_dashboard": true diff --git a/bruno/dashboard/get-dashboard-sections.yml b/bruno/dashboard/get-dashboard-sections.yml deleted file mode 100644 index e79ef6e5..00000000 --- a/bruno/dashboard/get-dashboard-sections.yml +++ /dev/null @@ -1,73 +0,0 @@ -info: - name: Get Dashboard Sections - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/dashboard/sections?library_id={{library_id}}' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200 with auth\", function() {\n expect(res.status).to.eql(200);\n\ - });\n\ntest(\"response contains sections array\", function() {\n expect(res.body.sections).to.exist;\n\ - expect(res.body.sections).to.be.an('array');\n });" - -docs: |- - Get all dashboard sections for a specific library. - - **Endpoint**: GET /api/dashboard/sections - **Auth**: Required (Bearer token via auth: inherit) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|----------|-------------| - | library_id | string | Yes | Library UUID | - | limit | int | No | Items per section (default: 20, max: 100) | - - ## Response - - Returns array of sections including: - - System collections (continue-reading, recently-added, recently-read, not-started) - - User collections marked with show_on_dashboard: true - - ## Section Structure - - Each section has: - - `id`: Collection name (string) - - `is_system`: Boolean indicating if this is a system collection - - `title`: Display title - - `description`: Collection description - - `icon`: Emoji icon - - `items`: Array of BookInfo objects - - `view_all_url`: URL to view all items (system collections only) - - `priority`: Display order (lower numbers first) - - ## Example Response - - ```json - { - "sections": [ - { - "id": "continue-reading", - "is_system": true, - "title": "Continue Reading", - "description": "Books you're currently reading (0 < progress < 1)", - "icon": "📖", - "items": [ - { - "media_item_id": "uuid", - "title": "Book Title", - "author": "Author Name", - "cover_image_path": "/path/to/cover.jpg" - } - ], - "view_all_url": "/section/continue-reading", - "priority": 1 - } - ] - } - ``` diff --git a/bruno/dashboard/get-sections-by-library.yml b/bruno/dashboard/get-sections-by-library.yml deleted file mode 100644 index ad95cb8a..00000000 --- a/bruno/dashboard/get-sections-by-library.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Get Dashboard Sections by Library - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/dashboard/sections' - auth: inherit -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Get all dashboard sections for a specific library. - - **Endpoint**: GET /api/dashboard/sections - **Auth**: Required (Bearer token) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|----------|-------------| - | library_id | string | Yes | Library UUID | - - ## Response - - Returns array of sections including: - - Smart sections (continue-reading, in-progress, recently-added, etc.) - - User collections marked for dashboard diff --git a/bruno/dashboard/get_preferences.yml b/bruno/dashboard/get_preferences.yml new file mode 100644 index 00000000..1e66d6a8 --- /dev/null +++ b/bruno/dashboard/get_preferences.yml @@ -0,0 +1,21 @@ +info: + name: Get Dashboard Preferences + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/dashboard/preferences' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Dashboard Preferences + + `GET /api/dashboard/preferences` — auth: jwt. + + Canonical reference: `docs/developer/api/dashboard/get_preferences.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/dashboard/get_sections.yml b/bruno/dashboard/get_sections.yml new file mode 100644 index 00000000..edbd9d91 --- /dev/null +++ b/bruno/dashboard/get_sections.yml @@ -0,0 +1,21 @@ +info: + name: Get Dashboard Sections + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/dashboard/sections' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Dashboard Sections + + `GET /api/dashboard/sections` — auth: jwt. + + Canonical reference: `docs/developer/api/dashboard/get_sections.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/dashboard/restore-system-collection.yml b/bruno/dashboard/restore-system-collection.yml deleted file mode 100644 index 65d8ee3b..00000000 --- a/bruno/dashboard/restore-system-collection.yml +++ /dev/null @@ -1,16 +0,0 @@ -info: - name: Restore System Collection - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/dashboard/restore-system-collection' - auth: inherit - body: - type: json - data: "{\n \"collection_name\": \"continue-reading\"\n}" -runtime: - scripts: - - type: tests - code: "test(\"status must be 200 with valid collection\", function() {\n expect(res.status).to.eql(200);\n});\n\ntest(\"response has success message\", function() {\n expect(res.body.message).to.exist;\n});" -docs: "Restore a system collection to its default state.\n\n**Endpoint**: POST /api/dashboard/restore-system-collection\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| collection_name | string | Yes | Name of system collection to restore |\n\nValid collection names:\n- `continue-reading`: Books you're currently reading (0 < progress < 1)\n- `recently-added`: Newly added items to this library\n- `recently-read`: Books you've finished (progress >= 1)\n- `not-started`: Books you haven't read yet (progress = 0 or no record)\n\n## Example Request\n\n```json\n{\n \"collection_name\": \"continue-reading\"\n}\n```\n\n## Response\n\nReturns success message on restore." diff --git a/bruno/dashboard/restore_system_collection.yml b/bruno/dashboard/restore_system_collection.yml new file mode 100644 index 00000000..acf884c5 --- /dev/null +++ b/bruno/dashboard/restore_system_collection.yml @@ -0,0 +1,28 @@ +info: + name: Restore System Collection + type: http + seq: 3 + +http: + method: POST + url: '{{base_url}}/api/dashboard/restore-system-collection' + auth: inherit + body: + type: json + data: |- + { + "collection_name": "Continue Reading", + "reset_type": "full" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Restore System Collection + + `POST /api/dashboard/restore-system-collection` — auth: jwt. + + Canonical reference: `docs/developer/api/dashboard/restore_system_collection.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/dashboard/update-collection-visibility.yml b/bruno/dashboard/update-collection-visibility.yml deleted file mode 100644 index 3f131f0a..00000000 --- a/bruno/dashboard/update-collection-visibility.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Update Collection Dashboard Visibility - type: http - seq: 5 -http: - method: PUT - url: '{{base_url}}/api/collections/{{collection_id}}' - auth: inherit -runtime: - scripts: - - type: tests - code: "test(\"updates collection successfully\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - Update collection settings including dashboard visibility. - - **Endpoint**: PUT /api/collections/{id diff --git a/bruno/dashboard/update-preferences.yml b/bruno/dashboard/update-preferences.yml deleted file mode 100644 index 6c1f541e..00000000 --- a/bruno/dashboard/update-preferences.yml +++ /dev/null @@ -1,16 +0,0 @@ -info: - name: Update Dashboard Preferences - type: http - seq: 2 -http: - method: PUT - url: '{{base_url}}/api/dashboard/preferences' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id}}\",\n \"hidden_collections\": [\"not-started\"],\n \"collection_order\": [\"recently-added\", \"continue-reading\", \"recently-read\"],\n \"items_per_section\": 20\n}" -runtime: - scripts: - - type: tests - code: "test(\"status must be 200 with valid request\", function() {\n expect(res.status).to.eql(200);\n});" -docs: "Update dashboard preferences for the authenticated user.\n\n**Endpoint**: PUT /api/dashboard/preferences\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| library_id | string | Yes | Library UUID |\n| hidden_collections | array | No | Collection names to hide from dashboard |\n| collection_order | array | No | Collection names in custom display order |\n| items_per_section | int | No | Items to show per section (default: 20) |\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"hidden_collections\": [\"not-started\"],\n \"collection_order\": [\"recently-added\", \"continue-reading\", \"recently-read\"],\n \"items_per_section\": 20\n}\n```\n\n## Response\n\nReturns updated preferences object on success." diff --git a/bruno/dashboard/update_preferences.yml b/bruno/dashboard/update_preferences.yml new file mode 100644 index 00000000..c70d4ffd --- /dev/null +++ b/bruno/dashboard/update_preferences.yml @@ -0,0 +1,30 @@ +info: + name: Update Dashboard Preferences + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/dashboard/preferences' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "hidden_collections": ["not-started"], + "collection_order": ["recently-added", "continue-reading", "recently-read"], + "items_per_section": 20 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Dashboard Preferences + + `PUT /api/dashboard/preferences` — auth: jwt. + + Canonical reference: `docs/developer/api/dashboard/update_preferences.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/add_to_shelf.yml b/bruno/devices/add_to_shelf.yml new file mode 100644 index 00000000..7e282edb --- /dev/null +++ b/bruno/devices/add_to_shelf.yml @@ -0,0 +1,29 @@ +info: + name: Add to Shelf + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/devices/{{device_id}}/shelves' + auth: inherit + body: + type: json + data: |- + { + "media_item_ids": ["550e8400-e29b-41d4-a716-446655440001"], + "shelf_name": "Default", + "shelf_position": 0 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Add to Shelf + + `POST /api/devices/:id/shelves` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/add_to_shelf.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/approve_device_registration.yml b/bruno/devices/approve_device_registration.yml new file mode 100644 index 00000000..8c3fe460 --- /dev/null +++ b/bruno/devices/approve_device_registration.yml @@ -0,0 +1,21 @@ +info: + name: Approve Device Registration + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/devices/approve/{{registration_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Approve Device Registration + + `GET /api/devices/approve/:registration_id` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/approve_device_registration.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/clear_shelf.yml b/bruno/devices/clear_shelf.yml new file mode 100644 index 00000000..5051407a --- /dev/null +++ b/bruno/devices/clear_shelf.yml @@ -0,0 +1,21 @@ +info: + name: Clear Shelf + type: http + seq: 3 + +http: + method: DELETE + url: '{{base_url}}/api/devices/{{device_id}}/shelves/clear' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Clear Shelf + + `DELETE /api/devices/:id/shelves/clear` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/clear_shelf.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/delete_device.yml b/bruno/devices/delete_device.yml new file mode 100644 index 00000000..10fd6352 --- /dev/null +++ b/bruno/devices/delete_device.yml @@ -0,0 +1,21 @@ +info: + name: Delete Device + type: http + seq: 4 + +http: + method: DELETE + url: '{{base_url}}/api/devices/{{device_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Device + + `DELETE /api/devices/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/delete_device.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/download_sidecar_config.yml b/bruno/devices/download_sidecar_config.yml new file mode 100644 index 00000000..8ce063d0 --- /dev/null +++ b/bruno/devices/download_sidecar_config.yml @@ -0,0 +1,21 @@ +info: + name: Download Sidecar Config + type: http + seq: 5 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/sidecar/download' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Download Sidecar Config + + `GET /api/devices/:id/sidecar/download` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/download_sidecar_config.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/get_device.yml b/bruno/devices/get_device.yml new file mode 100644 index 00000000..1c3029aa --- /dev/null +++ b/bruno/devices/get_device.yml @@ -0,0 +1,21 @@ +info: + name: Get Device + type: http + seq: 6 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device + + `GET /api/devices/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/get_device.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/get_device_mappings.yml b/bruno/devices/get_device_mappings.yml new file mode 100644 index 00000000..06930126 --- /dev/null +++ b/bruno/devices/get_device_mappings.yml @@ -0,0 +1,21 @@ +info: + name: Get Device Collection Mappings + type: http + seq: 7 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/collections' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device Collection Mappings + + `GET /api/devices/:id/collections` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/get_device_mappings.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/get_devices.yml b/bruno/devices/get_devices.yml new file mode 100644 index 00000000..0018f096 --- /dev/null +++ b/bruno/devices/get_devices.yml @@ -0,0 +1,27 @@ +info: + name: Get Device / Check Registration Status + type: http + seq: 8 + +http: + method: POST + url: '{{base_url}}/api/devices/register/status' + auth: inherit + body: + type: json + data: |- + { + "registration_id": "registration-uuid" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device / Check Registration Status + + `POST /api/devices/register/status` — auth: none. + + Canonical reference: `docs/developer/api/devices/get_devices.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/get_shelf.yml b/bruno/devices/get_shelf.yml new file mode 100644 index 00000000..371b528a --- /dev/null +++ b/bruno/devices/get_shelf.yml @@ -0,0 +1,21 @@ +info: + name: Get Shelf + type: http + seq: 9 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/shelves' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Shelf + + `GET /api/devices/:id/shelves` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/get_shelf.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/get_sidecar_config.yml b/bruno/devices/get_sidecar_config.yml new file mode 100644 index 00000000..7f4cf44c --- /dev/null +++ b/bruno/devices/get_sidecar_config.yml @@ -0,0 +1,21 @@ +info: + name: Get Device Sidecar Config + type: http + seq: 10 + +http: + method: GET + url: '{{base_url}}/api/devices/{{device_id}}/sidecar' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device Sidecar Config + + `GET /api/devices/:id/sidecar` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/get_sidecar_config.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/kobo/Get Analytics Tests.yml b/bruno/devices/kobo/Get Analytics Tests.yml deleted file mode 100644 index ce261904..00000000 --- a/bruno/devices/kobo/Get Analytics Tests.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Get Analytics Tests - type: http - seq: 7 -http: - method: POST - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/v1/analytics/gettests' - auth: inherit - headers: - - key: Content-Type - value: application/json - body: - type: json - data: "{\n \"platform\": \"android\",\n \"firmware_version\": \"4.30.19023\"\n}" -docs: "## Get Kobo Analytics Tests\n\nRetrieves A/B testing configuration and feature flags for Kobo device.\n\n**Method:** POST\n\n**Endpoint:** /api/sync/kobo/v1/analytics/gettests\n\n**Authentication:** Bearer token\n\n**Request Body:**\n- `platform` (string): Device platform\n - `android`: Kobo Android app\n - `kobo`: Native Kobo firmware\n- `firmware_version` (string): Firmware version (e.g., \"4.30.19023\")\n- `device_model` (string, optional): Device model identifier\n- `locale` (string, optional): Device locale (e.g., \"en_US\")\n\n**Response:**\n- `tests` (array): Active A/B tests\n - `test_name` (string): Test identifier\n - `variant` (string): Assigned variant (A, B, C, etc.)\n - `enabled` (boolean): Whether test is active\n - `parameters` (object): Test-specific parameters\n- `features` (object): Feature flags\n - `feature_name` (boolean/string): Feature state\n- `configuration` (object): Device configuration\n - `sync_interval_minutes` (integer): Recommended sync frequency\n\ - \ - `batch_size` (integer): Max items per batch sync\n - `timeout_seconds` (integer): Request timeout\n- `version` (string): Configuration version\n\n**Status Codes:**\n- 200: Success\n- 401: Unauthorized\n- 400: Invalid request parameters\n\n**Analytics Testing Purpose:**\n- Kobo uses A/B testing for UX features\n- Feature flags for gradual rollout\n- Performance monitoring configuration\n- Sync behavior optimization\n- Device-specific tuning\n\n**Common Tests:**\n- Sync frequency optimization\n- UI/UX variations\n- Network usage patterns\n- Battery life improvements\n- Feature set variations by model\n\n**Feature Flags:**\n- New sync features\n- Beta functionality\n- Platform-specific capabilities\n- Experimental features\n\n**Configuration Parameters:**\n- Optimal sync intervals for device\n- Batch size limits based on device capabilities\n- Timeout values for network conditions\n- Retry logic configuration\n- Cache policy settings\n\n**Usage:**\n- Called during device initialization\n\ - - Refreshed daily or weekly\n- Cached on device\n- Affects sync behavior\n- Can be overridden by server\n\n**Use Cases:**\n- Device initialization\n- Feature rollout testing\n- Performance optimization\n- UX experiment participation\n- Configuration management" diff --git a/bruno/devices/kobo/Get Kobo Library.yml b/bruno/devices/kobo/Get Kobo Library.yml deleted file mode 100644 index c13fd9fa..00000000 --- a/bruno/devices/kobo/Get Kobo Library.yml +++ /dev/null @@ -1,81 +0,0 @@ -info: - name: Get Kobo Library - type: http - seq: 6 - -http: - method: GET - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/v1/initialization' - auth: inherit - -docs: |- - ## Get Kobo Library - - Retrieves the user's Kobo library metadata for device sync. - - **Method:** GET - - **Endpoint:** /api/sync/kobo/library - - **Authentication:** Bearer token - - **Query Parameters:** - None (returns entire library) - - **Response:** - - `books` (array): Library items - - `ContentId` (string): Unique book identifier - - `Title` (string): Book title - - `Author` (string): Author name - - `Publisher` (string): Publisher name - - `Description` (string): Book description - - `ISBN` (string, optional): ISBN-13 - - `PublicationDate` (string): Release date - - `EntitlementId` (string): Kobo entitlement ID - - `CrossRevisionId` (string): Revision identifier - - `MimeType` (string): Content type (application/epub+zip) - - `FileSize` (integer): File size in bytes - - `CoverImageId` (string): Cover image identifier - - `DownloadUrls` (object): Download URLs - - `download_url` (string): Direct download link - - `download_acquisition_url` (string): OPDS acquisition URL - - `sync_metadata` (object): - - `last_sync` (string): Last library sync timestamp - - `total_books` (integer): Total book count - - `has_updates` (boolean): Whether updates are available - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Device not approved - - **Library Sync Purpose:** - - Kobo device needs to know available books - - Enables download via device browser - - Provides metadata for device display - - Supports Kobo's "My Books" feature - - Enables on-device purchasing integration - - **Kobo Device Usage:** - - Device fetches library on registration - - Refreshed daily or on manual sync - - User can browse library on device - - Books downloaded wirelessly to device - - Supports "Buy on Kobo, read on device" workflow - - **Authentication Methods:** - This endpoint uses **Bearer token authentication** (token in Authorization header). - Alternative: Use `/sync/kobo/{token}/library` with token in URL path. - - **Performance:** - - Typical response: 50-200KB for 100 books - - Processing time: 200-800ms - - Cache duration: 5 minutes - - Pagination available for large libraries (>500 books) - - **Use Cases:** - - Initial device registration - - Library refresh on device - - Book discovery on device - - Download link generation - - Metadata sync for OPDS diff --git a/bruno/devices/kobo/Get Unlinked Book Suggestions.yml b/bruno/devices/kobo/Get Unlinked Book Suggestions.yml deleted file mode 100644 index 10f29667..00000000 --- a/bruno/devices/kobo/Get Unlinked Book Suggestions.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Get Unlinked Book Suggestions - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/sync/unlinked-books/{{unlinkedBookId}}/suggestions' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{authToken - -docs: |- - ## Get Unlinked Book Suggestions - - Retrieves suggested media items from the library that match an unlinked book, enabling manual linking. - - **Method:** GET - - **Endpoint:** /sync/unlinked-books/{unlinkedBookId diff --git a/bruno/devices/kobo/scenarios/Auto Link Books.yml b/bruno/devices/kobo/scenarios/Auto Link Books.yml deleted file mode 100644 index 4446afb8..00000000 --- a/bruno/devices/kobo/scenarios/Auto Link Books.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Auto-Link Unlinked Books - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/sync/auto-link-books' - auth: inherit - body: - type: json - data: "{\n \"confidence_threshold\": 0.8,\n \"limit\": 50" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Auto-Link Unlinked Books\n\n Automatically links unlinked books to media items based on title and author matching with a configurable confidence threshold.\n\n **Method:** POST\n\n **Endpoint:** /sync/auto-link-books\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `confidence_threshold` (number, optional): Minimum confidence score for auto-linking (0-1, default: 0.8)\n - `limit` (number, optional): Maximum number of books to auto-link (default: 50)\n\n **Response:**\n - `results` (array): Results for each auto-link attempt\n - `total` (number): Total number of books processed\n - `success` (number): Number of successful links\n - `failed` (number): Number of failed links\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 500: Internal server error\n\n **Note:** Higher confidence thresholds produce fewer but more accurate matches. Consider the tradeoff between automation and accuracy." diff --git a/bruno/devices/kobo/scenarios/Bulk Link Books.yml b/bruno/devices/kobo/scenarios/Bulk Link Books.yml deleted file mode 100644 index 84950dd1..00000000 --- a/bruno/devices/kobo/scenarios/Bulk Link Books.yml +++ /dev/null @@ -1,17 +0,0 @@ -info: - name: Bulk Link Unlinked Books - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/sync/bulk-link-books' - auth: inherit - body: - type: json - data: "{\n \"links\": [\n {\n \"unlinked_book_id\": \"{{unlinkedBookId1" - headers: - - key: Content-Type - value: application/json - - key: Authorization - value: Bearer {{authToken -docs: "## Bulk Link Unlinked Books\n\n Links multiple unlinked books to media items in a single request.\n\n **Method:** POST\n\n **Endpoint:** /sync/bulk-link-books\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `links` (array): Array of link objects\n - `unlinked_book_id` (string): Unlinked book UUID\n - `media_item_id` (string): Media item UUID to link to\n - `confidence_score` (number): Match confidence (0-1)\n\n **Response:**\n - `results` (array): Results for each link attempt\n - `total` (number): Total number of links processed\n - `success` (number): Number of successful links\n - `failed` (number): Number of failed links\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 500: Internal server error\n\n **Note:** Use this endpoint after reviewing suggestions from the Get Unlinked Book Suggestions endpoint." diff --git a/bruno/devices/kobo/scenarios/Server Sync to Kobo.yml b/bruno/devices/kobo/scenarios/Server Sync to Kobo.yml deleted file mode 100644 index 68f07bc3..00000000 --- a/bruno/devices/kobo/scenarios/Server Sync to Kobo.yml +++ /dev/null @@ -1,16 +0,0 @@ -info: - name: Sync from Bookhoard to Kobo - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/sync-from-server' - auth: inherit - body: - type: json - data: "[\n {\n \"ContentId\": \"{{bookUUID" - headers: - - key: Authorization - value: Bearer {{koboToken -docs: "## Sync from Bookhoard to Kobo\n\n Server-initiated sync pushing progress, bookmarks, and highlights from Bookhoard to Kobo device. Two-way sync endpoint.\n\n **Method:** POST\n\n **Endpoint:** /api/sync/kobo/sync-from-server\n\n **Authentication:** Bearer token with Kobo device identification\n\n **Headers:**\n - `x-kobo-device` (string): JSON string containing Kobo device info\n - `DeviceId`: Kobo device ID\n - `Model`: Kobo device model\n - `SerialNumber`: Kobo device serial number\n\n **Request Body:** Array of sync data objects\n - `ContentId` (string): Book UUID\n - `PercentRead` (number): Reading progress percentage (0-100)\n - `LastModified` (string): ISO 8601 timestamp\n - `Bookmarks` (array, optional): Array of bookmark objects\n - `BookmarkId`: Unique bookmark ID\n - `ContentId`: Book UUID\n - `BookmarkText`: Bookmark text/note\n - `BookmarkType`: Type (bookmark, annotation, etc.)\n - `BookmarkTitle`: Bookmark title\n - `Highlights` (array,\ - \ optional): Array of highlight objects (same structure as bookmarks)\n\n **Response:**\n - Sync result confirmation\n\n **Status Codes:**\n - 200: Success\n - 401: Unauthorized\n - 500: Internal server error\n\n **Note:** Allows Bookhoard server to push updates to Kobo device, including reading progress, bookmarks, and highlights." diff --git a/bruno/devices/kobo/scenarios/bookmark-sync.yml b/bruno/devices/kobo/scenarios/bookmark-sync.yml deleted file mode 100644 index 4ed0b4b8..00000000 --- a/bruno/devices/kobo/scenarios/bookmark-sync.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Kobo Bookmark Sync - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/bookmark' - auth: inherit - body: - type: json - data: "{\n \"BookmarkSync\": [\n {\n \"BookmarkId\": \"bookmark_2\",\n \"ContentId\": \"kobo_xyz789\",\n \"BookmarkText\": \"Important note\",\n \"BookmarkType\": \"bookmark\",\n \"DateCreated\": \"2026-01-31T12:00:00Z\"" - headers: - - key: Authorization - value: Bearer {{device_token -docs: "## Kobo Bookmark Sync\n\n Synchronizes bookmarks from a Kobo device to the Bookhoard server.\n\n **Method:** POST\n\n **Endpoint:** /api/v1/kobo/bookmark\n\n **Authentication:** Bearer token (device token)\n\n **Request Body:**\n - `BookmarkSync` (array): Array of bookmark objects\n - `BookmarkId` (string): Unique bookmark ID\n - `ContentId` (string): Book/content ID\n - `BookmarkText` (string): Bookmark text or note\n - `BookmarkType` (string): Type (bookmark, highlight, note)\n - `DateCreated` (string): ISO 8601 timestamp\n\n **Response:**\n - `Status` (string): Sync status (Success, Partial)\n - `BookmarksSynced` (number): Number of bookmarks synced\n\n **Status Codes:**\n - 200: Success\n - 401: Unauthorized\n - 500: Internal server error" diff --git a/bruno/devices/kobo/scenarios/get-unlinked-books.yml b/bruno/devices/kobo/scenarios/get-unlinked-books.yml deleted file mode 100644 index 9f1ad829..00000000 --- a/bruno/devices/kobo/scenarios/get-unlinked-books.yml +++ /dev/null @@ -1,39 +0,0 @@ -info: - name: Get Unlinked Books - User View - type: http - seq: 4 -http: - method: GET - url: '{{base_url}}/api/devices/{{device_id}}/sync/unlinked-books' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{user_token - -docs: |- - ## Get Unlinked Books - User View - - Retrieves all unlinked books for the authenticated user that need manual linking. - - **Method:** GET - - **Endpoint:** /api/sync/unlinked-books - - **Authentication:** Bearer token - - **Response:** - - `unlinked` (array): Array of unlinked book objects - - `id` (string): Unlinked book UUID - - `title` (string): Book title - - `author` (string): Book author - - `device_id` (string): Source device ID - - `device_name` (string): Source device name - - `detected_at` (string): Detection timestamp - - `total` (number): Total count of unlinked books - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 500: Internal server error diff --git a/bruno/devices/kobo/scenarios/link-book.yml b/bruno/devices/kobo/scenarios/link-book.yml deleted file mode 100644 index ebb10d5b..00000000 --- a/bruno/devices/kobo/scenarios/link-book.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Link Unlinked Book - Manual Resolution - type: http - seq: 5 -http: - method: POST - url: '{{base_url}}/api/devices/{{device_id}}/sync/link-book' - auth: inherit - body: - type: json - data: "{\n \"unlinked_book_id\": \"{{unlinked_book_id" - headers: - - key: Authorization - value: Bearer {{user_token -docs: "## Link Unlinked Book - Manual Resolution\n\n Manually links an unlinked book to a media item in the library.\n\n **Method:** POST\n\n **Endpoint:** /api/sync/link-book\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `unlinked_book_id` (string): Unlinked book UUID\n - `media_item_id` (string): Media item UUID to link to\n - `confidence_score` (number): Match confidence (0-1, 1.0 for manual)\n\n **Response:**\n - `status` (string): Link status (linked)\n - `unlinked_book_id` (string): Unlinked book UUID\n - `media_item_id` (string): Media item UUID\n - `message` (string): Success message\n\n **Status Codes:**\n - 200: Success - book linked\n - 400: Invalid request\n - 401: Unauthorized\n - 404: Book or media item not found\n - 500: Internal server error" diff --git a/bruno/devices/kobo/scenarios/sync-bookmark-url-token.yml b/bruno/devices/kobo/scenarios/sync-bookmark-url-token.yml deleted file mode 100644 index dcf74757..00000000 --- a/bruno/devices/kobo/scenarios/sync-bookmark-url-token.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Sync Bookmark - URL Path Token - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/bookmark' - auth: none - body: - type: json - data: "{\n \"ContentId\": \"book-uuid\",\n \"BookmarkText\": \"Highlighted text\",\n \"BookmarkType\": \"annotation\",\n \"BookmarkTitle\": \"Chapter 3\"" - headers: - - key: Content-Type - value: application/json -docs: "## Sync Bookmark - URL Path Token\n\n Synchronizes bookmarks and annotations from Kobo device using token in URL path.\n\n **Method:** POST\n\n **Endpoint:** /sync/kobo/{kobo_device_token" diff --git a/bruno/devices/kobo/scenarios/sync-markup-url-token.yml b/bruno/devices/kobo/scenarios/sync-markup-url-token.yml deleted file mode 100644 index 4fbb3ba8..00000000 --- a/bruno/devices/kobo/scenarios/sync-markup-url-token.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Sync Markup - URL Path Token - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/markup' - auth: none - body: - type: json - data: "{\n \"ReadingSync\": [\n {\n \"ContentId\": \"book-uuid\",\n \"PercentRead\": 45.6,\n \"EntitlementId\": \"entitlement-id\",\n \"RemainingTimeMinutes\": 120,\n \"FirstReadTime\": \"2026-01-25T10:00:00Z\",\n \"LastModified\": \"2026-01-30T20:00:00Z\"" - headers: - - key: Content-Type - value: application/json -docs: "## Sync Reading Progress - URL Path Token\n\n Synchronizes reading progress from Kobo device using token in URL path.\n\n **Method:** POST\n\n **Endpoint:** /sync/kobo/{kobo_device_token" diff --git a/bruno/devices/koreader/Get Book Metadata.yml b/bruno/devices/koreader/Get Book Metadata.yml deleted file mode 100644 index ddb58a05..00000000 --- a/bruno/devices/koreader/Get Book Metadata.yml +++ /dev/null @@ -1,14 +0,0 @@ -docs: "## Get KOReader Book Metadata\n\nRetrieves metadata for a specific book from the server.\n\n**Method:** GET\n\n**Endpoint:** /api/sync/koreader/metadata/:book_uuid\n\n**Authentication:** Bearer token\n\n**Path Parameters:**\n- `book_uuid` (string): SHA-256 based book identifier\n\n**Response:**\n- `uuid` (string): Book UUID (SHA-256)\n- `title` (string): Book title\n- `authors` (array): Author names\n- `series` (string, optional): Series name\n- `series_index` (number, optional): Position in series\n- `publisher` (string, optional): Publisher name\n- `publication_date` (string, optional): Release date\n- `language` (string, optional): ISO 639-1 language code\n- `description` (string, optional): Book description\n- `cover_url` (string, optional): Cover image URL\n- `cover_thumbnail_url` (string, optional): Thumbnail URL\n- `identifiers` (object): Various identifiers\n - `isbn` (string, optional): ISBN-13\n - `asin` (string, optional): Amazon ASIN\n - `goodreads` (string, optional):\ - \ Goodreads ID\n - `google_books` (string, optional): Google Books ID\n- `metadata_sources` (array): Sources metadata was pulled from\n- `last_synced` (string): Last metadata sync timestamp\n- `file_metadata` (object):\n - `file_size` (integer): File size in bytes\n - `format` (string): File format (epub, pdf, mobi, etc.)\n - `pages` (integer, optional): Page count\n - `word_count` (integer, optional): Estimated words\n\n**Status Codes:**\n- 200: Success\n- 401: Unauthorized\n- 404: Book not found\n\n**Metadata Purpose:**\n- Enrich book information on device\n- Improve book organization\n- Enable better search\n- Support series sorting\n- Provide cover images\n- Link to external sources\n\n**KOReader Usage:**\n- Display in book info dialog\n- Used for library sorting\n- Shown in file browser\n- Series grouping\n- Cover display\n- Search optimization\n\n**SHA-256 Book ID:**\n- Primary identifier in KOReader\n- Universal across devices\n- Format-independent\n- Generated from file content\n\ - - Survives metadata changes\n\n**Metadata Sources:**\n- Google Books API\n- Open Library\n- Goodreads API\n- ISBN database lookup\n- Publisher metadata\n- User-provided metadata\n\n**Use Cases:**\n- Initial book import\n- Metadata refresh\n- Cover image download\n- Series organization\n- Duplicate detection\n- Library management" -http: - headers: [] - method: GET - url: '{{base_url}}/api/sync/koreader/metadata/{{book_uuid}}' - auth: - type: bearer - token: '{{koreader_device_token}}' -info: - name: Get Book Metadata - seq: 8 - type: http diff --git a/bruno/devices/koreader/Get Library.yml b/bruno/devices/koreader/Get Library.yml deleted file mode 100644 index 19b53696..00000000 --- a/bruno/devices/koreader/Get Library.yml +++ /dev/null @@ -1,14 +0,0 @@ -docs: "## KOReader Get Library\n\n Retrieves the user's library for KOReader sync operations.\n\n **Method:** GET\n\n **Endpoint:** /api/sync/koreader/library\n\n **Authentication:** Bearer token (device token)\n\n **Response:**\n - `library_sync` (object): Library sync data\n - `total_books` (number): Total number of books\n - `books` (array): Array of book objects\n\n **Status Codes:**\n - 200: Success\n - 401: Unauthorized\n - 500: Internal server error" -http: - body: - type: none - headers: [] - method: GET - url: '{{base_url}}/api/sync/koreader/library' - auth: - type: bearer - token: '{{koreader_device_token}}' -info: - name: KOReader Get Library - seq: 3 - type: http diff --git a/bruno/devices/koreader/Get User Library for KOReader.yml b/bruno/devices/koreader/Get User Library for KOReader.yml deleted file mode 100644 index b25026dc..00000000 --- a/bruno/devices/koreader/Get User Library for KOReader.yml +++ /dev/null @@ -1,15 +0,0 @@ -docs: "## Get KOReader User Library\n\nRetrieves the user's book library for KOReader device sync.\n\n**Method:** GET\n\n**Endpoint:** /api/sync/koreader/library\n\n**Authentication:** Bearer token (koreader_device_token)\n\n**Query Parameters:**\nNone (returns entire library)\n\n**Response:**\n- `books` (array): Library items\n - `uuid` (string): SHA-256 based book identifier\n - `title` (string): Book title\n - `authors` (array): Author names\n - `series` (string, optional): Series name\n - `series_index` (number, optional): Series position\n - `publisher` (string, optional): Publisher\n - `publication_date` (string, optional): Release date\n - `language` (string, optional): Language code\n - `description` (string, optional): Description\n - `cover_url` (string, optional): Cover image URL\n - `cover_thumbnail_url` (string, optional): Thumbnail URL\n - `file_metadata` (object):\n - `format` (string): File format\n - `file_size` (integer): Size in bytes\n - `pages` (integer,\ - \ optional): Page count\n - `progress` (object, optional): Reading progress\n - `percentage` (number): Progress 0-100\n - `last_read` (string): Last read timestamp\n - `epubcfi` (string): Current position\n - `annotations_count` (object, optional): Annotation stats\n - `highlights` (integer): Highlight count\n - `notes` (integer): Note count\n - `bookmarks` (integer): Bookmark count\n- `library_info` (object):\n - `total_books` (integer): Total book count\n - `last_sync` (string): Last library sync timestamp\n - `has_updates` (boolean): Updates available\n- `user_info` (object):\n - `user_id` (string): User identifier\n - `email` (string): Email (masked)\n - `libraries` (array): Available library IDs\n\n**Status Codes:**\n- 200: Success\n- 401: Unauthorized\n- 403: Device not approved\n\n**Library Purpose for KOReader:**\n- Discover books available for download\n- Browse catalog on device\n- Sync reading progress across books\n- Download covers/metadata\n- Series-based\ - \ organization\n- Cloud library access\n\n**KOReader Device Features:**\n- **OPDS catalog**: Native OPDS client\n- **File browser**: See server books\n- **Cloud download**: Download books on-demand\n- **Metadata sync**: Automatic metadata fetching\n- **Cover images**: Display in library view\n- **Progress sync**: See progress across all books\n- **Search**: Search library by title/author\n\n**KOReader-Specific Features:**\n- SHA-256 based book IDs\n- Multi-format support (EPUB, FB2, PDF, DJVU, MOBI, etc.)\n- Series sorting and grouping\n- Language filtering\n- Cover image caching\n- Metadata for file browser enhancement\n- Integration with KOReader's OPDS client\n\n**Performance:**\n- Typical response: 100-500KB for 100 books\n- Processing time: 300ms-1s\n- Cache duration: 5 minutes\n- Pagination support for libraries >500 books\n\n**Use Cases:**\n- Initial device setup\n- Library browsing on device\n- Book download\n- Metadata refresh\n- Cover image sync\n- Progress overview\n- Series-based\ - \ reading" -http: - headers: [] - method: GET - url: '{{base_url}}/api/sync/koreader/library' - auth: - type: bearer - token: '{{koreader_device_token}}' -info: - name: Get User Library for KOReader - seq: 9 - type: http diff --git a/bruno/devices/link_book.yml b/bruno/devices/link_book.yml new file mode 100644 index 00000000..3e4b544d --- /dev/null +++ b/bruno/devices/link_book.yml @@ -0,0 +1,33 @@ +info: + name: Link Book + type: http + seq: 11 + +http: + method: POST + url: '{{base_url}}/api/devices/{{device_id}}/sync/link-book' + auth: inherit + body: + type: json + data: |- + { + "device_file": { + "file_path": "/books/1984.epub", + "sha256": "abc123...", + "title": "1984" + }, + "media_item_id": "550e8400-e29b-41d4-a716-446655440000", + "confidence_score": 0.95 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Link Book + + `POST /api/devices/:deviceId/sync/link-book` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/link_book.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/list_devices.yml b/bruno/devices/list_devices.yml new file mode 100644 index 00000000..7de63388 --- /dev/null +++ b/bruno/devices/list_devices.yml @@ -0,0 +1,21 @@ +info: + name: List User Devices + type: http + seq: 12 + +http: + method: GET + url: '{{base_url}}/api/devices' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List User Devices + + `GET /api/devices` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/list_devices.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/list_pending_registrations.yml b/bruno/devices/list_pending_registrations.yml new file mode 100644 index 00000000..4af207ec --- /dev/null +++ b/bruno/devices/list_pending_registrations.yml @@ -0,0 +1,21 @@ +info: + name: List Pending Device Registrations:<|end|><|start|><|start|>assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + type: http + seq: 13 + +http: + method: GET + url: '{{base_url}}/api/devices/pending' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Pending Device Registrations:<|end|><|start|><|start|>assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + + `GET /api/devices/pending` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/list_pending_registrations.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/regenerate_device_token.yml b/bruno/devices/regenerate_device_token.yml new file mode 100644 index 00000000..b370e73d --- /dev/null +++ b/bruno/devices/regenerate_device_token.yml @@ -0,0 +1,21 @@ +info: + name: Regenerate Device Token + type: http + seq: 14 + +http: + method: PUT + url: '{{base_url}}/api/devices/{{device_id}}/regenerate-token' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Regenerate Device Token + + `PUT /api/devices/:id/regenerate-token` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/regenerate_device_token.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/register_device.yml b/bruno/devices/register_device.yml new file mode 100644 index 00000000..68153b1d --- /dev/null +++ b/bruno/devices/register_device.yml @@ -0,0 +1,29 @@ +info: + name: Register Device + type: http + seq: 15 + +http: + method: POST + url: '{{base_url}}/api/devices/register' + auth: inherit + body: + type: json + data: |- + { + "device_name": "My Kobo Clara", + "device_type": "kobo", + "device_identifier": "hardware-specific-id" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Register Device + + `POST /api/devices/register` — auth: none. + + Canonical reference: `docs/developer/api/devices/register_device.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/reject_device_registration.yml b/bruno/devices/reject_device_registration.yml new file mode 100644 index 00000000..7e287b3a --- /dev/null +++ b/bruno/devices/reject_device_registration.yml @@ -0,0 +1,21 @@ +info: + name: Reject Device Registration + type: http + seq: 16 + +http: + method: POST + url: '{{base_url}}/api/devices/reject/{{registration_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Reject Device Registration + + `POST /api/devices/reject/:registration_id` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/reject_device_registration.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/remove_from_shelf.yml b/bruno/devices/remove_from_shelf.yml new file mode 100644 index 00000000..5afc04de --- /dev/null +++ b/bruno/devices/remove_from_shelf.yml @@ -0,0 +1,21 @@ +info: + name: Remove from Shelf + type: http + seq: 17 + +http: + method: DELETE + url: '{{base_url}}/api/devices/{{device_id}}/shelves' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Remove from Shelf + + `DELETE /api/devices/:id/shelves` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/remove_from_shelf.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/scenarios/Add Books to Kobo Shelf.yml b/bruno/devices/scenarios/Add Books to Kobo Shelf.yml deleted file mode 100644 index 37c2f819..00000000 --- a/bruno/devices/scenarios/Add Books to Kobo Shelf.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Add Books to Kobo Shelf - type: http - seq: 1 -http: - method: POST - url: '{{baseURL}}/api/devices/{{deviceID}}/shelves' - auth: inherit - body: - type: json - data: "{\n \"media_item_ids\": [\n \"{{bookUUID1" - headers: - - key: Authorization - value: Bearer {{userToken -docs: "## Add Books to Kobo Shelf\n\n Add one or more books to a Kobo device shelf. Manages which books should be synced to a specific Kobo device. Supports multiple shelves for organization.\n\n **Method:** POST\n\n **Endpoint:** /api/devices/{deviceID" diff --git a/bruno/devices/scenarios/Check Registration Status.yml b/bruno/devices/scenarios/Check Registration Status.yml deleted file mode 100644 index 4030c259..00000000 --- a/bruno/devices/scenarios/Check Registration Status.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Check Registration Status - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/devices/register/status' - auth: none - body: - type: json - data: "{\n \"registration_id\": \"{{registrationId}}" -docs: "## Check Registration Status\n\n Checks the current status of a device registration request.\n\n **Method:** POST\n\n **Endpoint:** /api/devices/register/status\n\n **Authentication:** None\n\n **Request Body:**\n - `registration_id` (string, required): Registration request ID\n\n **Response:**\n - `status` (string): Registration status (pending, approved, rejected, expired)\n - `device_name` (string): Device name\n - `device_type` (string): Device type\n - `created_at` (string): Request timestamp\n - `expires_at` (string): Expiration timestamp\n\n **Status Codes:**\n - 200: Success\n - 404: Registration ID not found\n - 410: Registration expired" diff --git a/bruno/devices/scenarios/Clear Kobo Shelf.yml b/bruno/devices/scenarios/Clear Kobo Shelf.yml deleted file mode 100644 index e71a1272..00000000 --- a/bruno/devices/scenarios/Clear Kobo Shelf.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Clear Kobo Shelf - type: http - seq: 1 -http: - method: DELETE - url: '{{baseURL}}/api/devices/{{deviceID}}/shelves/clear?shelf={{shelfName}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{userToken - -docs: |- - ## Clear Kobo Shelf - - Clear all books from a Kobo device shelf, or all shelves if no shelf name is specified. - - **Method:** DELETE - - **Endpoint:** /api/devices/{deviceID diff --git a/bruno/devices/scenarios/Create Device File Alias.yml b/bruno/devices/scenarios/Create Device File Alias.yml deleted file mode 100644 index 2d40e18c..00000000 --- a/bruno/devices/scenarios/Create Device File Alias.yml +++ /dev/null @@ -1,19 +0,0 @@ -info: - name: Create Device File Alias - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/devices/{{device_id}}/file-aliases' - auth: inherit - body: - type: json - data: "{\n \"file_path\": \"/mnt/sd/books/my-book.kepub.epub\",\n \"media_item_id\": \"{{media_item_id" - headers: - - key: Content-Type - value: application/json -runtime: - scripts: - - type: tests - code: "test_create_device_file_alias_success(status, headers, body) {\n if (status !== 201 && status !== 200) {\n throw new Error(\"Expected status 201 or 200, got \" + status);" -docs: "## Create Device File Alias\n\n Creates a new file alias for a device. File aliases map device-specific file paths to media items.\n\n **Method:** POST\n\n **Endpoint:** /api/devices/:id/file-aliases\n\n **Authentication:** Required (Bearer token)\n\n **Path Parameters:**\n - `id` (string, required): Device UUID\n\n **Request Body:**\n - `file_path` (string, required): Device-specific file path\n - `media_item_id` (string, required): Media item UUID to link to\n\n **Response:** Created file alias object\n - `id` (string): Alias UUID\n - `device_id` (string): Device UUID\n - `file_path` (string): Device-specific file path\n - `media_item_id` (string): Associated media item UUID\n - `created_at` (string): Creation timestamp\n - `updated_at` (string): Last update timestamp\n\n **Status Codes:**\n - 201: Created\n - 200: Success\n - 400: Invalid request body\n - 401: Unauthorized\n - 404: Device not found\n - 500: Internal server error" diff --git a/bruno/devices/scenarios/Delete Device.yml b/bruno/devices/scenarios/Delete Device.yml deleted file mode 100644 index f8e96718..00000000 --- a/bruno/devices/scenarios/Delete Device.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Delete Device - type: http - seq: 6 -http: - method: DELETE - url: '{{base_url}}/api/devices/{{device_id}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{token - -docs: |- - ## Delete Device - - Deletes a device and unregisters it from the user's account. - - **Method:** DELETE - - **Endpoint:** /api/devices/{deviceId diff --git a/bruno/devices/scenarios/Get Kobo Shelf.yml b/bruno/devices/scenarios/Get Kobo Shelf.yml deleted file mode 100644 index f7df0378..00000000 --- a/bruno/devices/scenarios/Get Kobo Shelf.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Get Kobo Shelf Books - type: http - seq: 1 -http: - method: GET - url: '{{baseURL}}/api/devices/{{deviceID}}/shelves?shelf={{shelfName}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{userToken - -docs: |- - ## Get Kobo Shelf Books - - Get all books on a Kobo device shelf, optionally filter by shelf name. - - **Method:** GET - - **Endpoint:** /api/devices/{deviceID diff --git a/bruno/devices/scenarios/Initiate Device Registration.yml b/bruno/devices/scenarios/Initiate Device Registration.yml deleted file mode 100644 index 7a802734..00000000 --- a/bruno/devices/scenarios/Initiate Device Registration.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Initiate Device Registration - type: http - seq: 1 -http: - method: POST - url: '{{base_url}}/api/devices/register' - auth: none - body: - type: json - data: "{\n \"device_name\": \"My Kindle Paperwhite\",\n \"device_type\": \"koreader\",\n \"device_identifier\": \"kindle-pw5-hardware-id-12345\"" -docs: "## Initiate Device Registration\n\n Initiates a new device registration request that requires admin approval.\n\n **Method:** POST\n\n **Endpoint:** /api/devices/register\n\n **Authentication:** None (open endpoint)\n\n **Request Body:**\n - `device_name` (string, required): Human-readable device name (max 100 chars)\n - `device_type` (string, required): Device type (kobo, koreader, kindle, etc.)\n - `device_identifier` (string, required): Unique hardware ID (max 200 chars)\n\n **Response:**\n - `registration_id` (string): Unique registration request ID\n - `status` (string): Initial status (pending)\n - `expires_at` (string): Expiration timestamp (usually 24 hours)\n - `message` (string): Informational message\n\n **Status Codes:**\n - 201: Registration initiated\n - 400: Invalid request\n - 409: Device already registered" diff --git a/bruno/devices/scenarios/Remove Book from Kobo Shelf.yml b/bruno/devices/scenarios/Remove Book from Kobo Shelf.yml deleted file mode 100644 index e1b6795f..00000000 --- a/bruno/devices/scenarios/Remove Book from Kobo Shelf.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Remove Book from Kobo Shelf - type: http - seq: 1 -http: - method: DELETE - url: '{{baseURL}}/api/devices/{{deviceID}}/shelves?media_item_id={{bookUUID}}' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{userToken - -docs: |- - ## Remove Book from Kobo Shelf - - Remove a specific book from a Kobo device shelf, preventing it from syncing to that device. - - **Method:** DELETE - - **Endpoint:** /api/devices/{deviceID diff --git a/bruno/devices/scenarios/Update Device.yml b/bruno/devices/scenarios/Update Device.yml deleted file mode 100644 index 49a980b4..00000000 --- a/bruno/devices/scenarios/Update Device.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Device - type: http - seq: 5 -http: - method: PUT - url: '{{base_url}}/api/devices/{{device_id}}' - auth: inherit - body: - type: json - data: "{\n \"device_name\": \"My Updated Kindle\",\n \"sync_enabled\": true,\n \"auto_sync\": true,\n \"sync_frequency_minutes\": 10" -docs: "## Update Device\n\n Updates device settings and synchronization preferences.\n\n **Method:** PUT\n\n **Endpoint:** /api/devices/{device_id}\n\n **Authentication:** Required (Bearer token, admin or device owner)\n\n **Path Parameters:**\n - `device_id` (string): Device ID\n\n **Request Body:**\n - `device_name` (string, optional): New device name (max 100 chars)\n - `sync_enabled` (boolean, optional): Enable/disable synchronization\n - `auto_sync` (boolean, optional): Enable automatic synchronization\n - `sync_frequency_minutes` (integer, optional): Sync frequency in minutes (5-1440)\n\n **Response:**\n - Updated device object\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request\n - 401: Unauthorized\n - 403: Forbidden (not device owner)\n - 404: Device not found" diff --git a/bruno/devices/update_device.yml b/bruno/devices/update_device.yml new file mode 100644 index 00000000..ec8e70d5 --- /dev/null +++ b/bruno/devices/update_device.yml @@ -0,0 +1,30 @@ +info: + name: Update Device + type: http + seq: 18 + +http: + method: PUT + url: '{{base_url}}/api/devices/{{device_id}}' + auth: inherit + body: + type: json + data: |- + { + "device_name": "My Kobo Clara", + "sync_enabled": true, + "auto_sync": true, + "sync_frequency_minutes": 60 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Device + + `PUT /api/devices/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/update_device.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/devices/update_device_mapping.yml b/bruno/devices/update_device_mapping.yml new file mode 100644 index 00000000..154d19f3 --- /dev/null +++ b/bruno/devices/update_device_mapping.yml @@ -0,0 +1,28 @@ +info: + name: Update Device Collection Mapping + type: http + seq: 19 + +http: + method: PUT + url: '{{base_url}}/api/devices/{{device_id}}/collections/{{collection_id}}' + auth: inherit + body: + type: json + data: |- + { + "device_shelf_name": "Kobo", + "sync_direction": "bidirectional" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Device Collection Mapping + + `PUT /api/devices/:id/collections/:collectionId` — auth: jwt. + + Canonical reference: `docs/developer/api/devices/update_device_mapping.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/environments/Bookhoard.yml b/bruno/environments/Bookhoard.yml index 8643c49e..3b3b1ea1 100644 --- a/bruno/environments/Bookhoard.yml +++ b/bruno/environments/Bookhoard.yml @@ -1,5 +1,21 @@ name: Bookhoard variables: + - secret: true + name: device_id + - secret: true + name: rule_id + - secret: true + name: annotation_id + - secret: true + name: alias_id + - secret: true + name: track_index + - secret: true + name: registration_id + - secret: true + name: unlinked_book_id + - secret: true + name: device_token - name: base_url value: http://localhost:8765 - secret: true diff --git a/bruno/highlights/Create Media Highlight.yml b/bruno/highlights/Create Media Highlight.yml deleted file mode 100644 index afaf46a5..00000000 --- a/bruno/highlights/Create Media Highlight.yml +++ /dev/null @@ -1,46 +0,0 @@ -info: - name: Create Media Highlight - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights' - auth: inherit - body: - type: json - data: |- - { - "selection_text": "This is the highlighted text from the media item.", - "start_position": "page:45:offset:120", - "end_position": "page:45:offset:145", - "color": "#ffff00", - "note_id": "" - } - -docs: |- - ## Create Media Highlight - - Creates a new highlight for a specific media item. - - **Method:** POST - - **Endpoint:** /api/media-items/:id/highlights - - **Path Parameters:** - - `id` (string): Media item ID - - **Request Body:** - - `selection_text` (string): Highlighted text (required, 1-5000 chars) - - `start_position` (string): Start position (required, max 100 chars) - - `end_position` (string): End position (required, max 100 chars) - - `color` (string): Highlight color in hex format (optional, default #ffff00) - - `note_id` (string): Optional associated note ID - - **Response:** - - Highlight object with all fields including generated ID and timestamps - - **Status Codes:** - - 201: Created - - 400: Invalid request - - 401: Unauthorized - - 404: Media item not found diff --git a/bruno/highlights/Delete Media Highlight.yml b/bruno/highlights/Delete Media Highlight.yml deleted file mode 100644 index 45ffff4a..00000000 --- a/bruno/highlights/Delete Media Highlight.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Delete Media Highlight - type: http - seq: 5 -http: - method: DELETE - url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' - auth: inherit - body: - type: none - -docs: |- - ## Delete Media Highlight - - Deletes a specific highlight. - - **Method:** DELETE - - **Endpoint:** /api/media-items/:id/highlights/:highlightId - - **Path Parameters:** - - `id` (string): Media item ID - - `highlightId` (string): Highlight ID - - **Response:** - - 204 No Content on success - - **Status Codes:** - - 204: Success - - 401: Unauthorized - - 404: Highlight not found diff --git a/bruno/highlights/Get Media Highlights.yml b/bruno/highlights/Get Media Highlights.yml deleted file mode 100644 index 01bded49..00000000 --- a/bruno/highlights/Get Media Highlights.yml +++ /dev/null @@ -1,40 +0,0 @@ -info: - name: Get Media Highlights - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights' - auth: inherit - body: - type: none - -docs: |- - ## Get Media Highlights - - Retrieves all highlights for a specific media item for the authenticated user. - - **Method:** GET - - **Endpoint:** /api/media-items/:id/highlights - - **Path Parameters:** - - `id` (string): Media item ID - - **Response:** - - Array of highlight objects with fields: - - `id` (string): Highlight ID - - `media_item_id` (string): Media item ID - - `user_id` (string): User ID - - `selection_text` (string): Highlighted text - - `start_position` (string): Start position - - `end_position` (string): End position - - `color` (string): Highlight color (hex) - - `note_id` (string): Optional associated note ID - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Media item not found diff --git a/bruno/highlights/Get Single Media Highlight.yml b/bruno/highlights/Get Single Media Highlight.yml deleted file mode 100644 index 8a12d995..00000000 --- a/bruno/highlights/Get Single Media Highlight.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Get Single Media Highlight - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' - auth: inherit - body: - type: none - -docs: |- - ## Get Single Media Highlight - - Retrieves a specific highlight by ID. - - **Method:** GET - - **Endpoint:** /api/media-items/:id/highlights/:highlightId - - **Path Parameters:** - - `id` (string): Media item ID - - `highlightId` (string): Highlight ID - - **Response:** - - Highlight object with all fields - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Highlight not found diff --git a/bruno/highlights/Update Media Highlight.yml b/bruno/highlights/Update Media Highlight.yml deleted file mode 100644 index 318cf8ed..00000000 --- a/bruno/highlights/Update Media Highlight.yml +++ /dev/null @@ -1,47 +0,0 @@ -info: - name: Update Media Highlight - type: http - seq: 4 -http: - method: PUT - url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' - auth: inherit - body: - type: json - data: |- - { - "selection_text": "This is the updated highlighted text.", - "start_position": "page:45:offset:125", - "end_position": "page:45:offset:150", - "color": "#ffeb3b", - "note_id": "" - } - -docs: |- - ## Update Media Highlight - - Updates an existing highlight. - - **Method:** PUT - - **Endpoint:** /api/media-items/:id/highlights/:highlightId - - **Path Parameters:** - - `id` (string): Media item ID - - `highlightId` (string): Highlight ID - - **Request Body:** - - `selection_text` (string): Updated highlighted text (required, 1-5000 chars) - - `start_position` (string): Updated start position (required, max 100 chars) - - `end_position` (string): Updated end position (required, max 100 chars) - - `color` (string): Updated highlight color in hex format (optional) - - `note_id` (string): Updated associated note ID (optional) - - **Response:** - - Updated highlight object with all fields - - **Status Codes:** - - 200: Success - - 400: Invalid request - - 401: Unauthorized - - 404: Highlight not found diff --git a/bruno/highlights/create_highlight.yml b/bruno/highlights/create_highlight.yml new file mode 100644 index 00000000..9c64b482 --- /dev/null +++ b/bruno/highlights/create_highlight.yml @@ -0,0 +1,32 @@ +info: + name: Create Highlight + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights' + auth: inherit + body: + type: json + data: |- + { + "selection_text": "Highlighted text...", + "start_position": "epubcfi(/6/4/2:15)", + "end_position": "epubcfi(/6/4/2:20)", + "color": "#ffff00", + "percentage_start": 0.45, + "percentage_end": 0.47 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Highlight + + `POST /api/media-items/:id/highlights` — auth: jwt. + + Canonical reference: `docs/developer/api/highlights/create_highlight.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/highlights/delete_highlight.yml b/bruno/highlights/delete_highlight.yml new file mode 100644 index 00000000..8d0f2c7b --- /dev/null +++ b/bruno/highlights/delete_highlight.yml @@ -0,0 +1,21 @@ +info: + name: Delete Highlight + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Highlight + + `DELETE /api/media-items/:id/highlights/:highlightId` — auth: jwt. + + Canonical reference: `docs/developer/api/highlights/delete_highlight.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/highlights/get_highlights.yml b/bruno/highlights/get_highlights.yml new file mode 100644 index 00000000..151306c7 --- /dev/null +++ b/bruno/highlights/get_highlights.yml @@ -0,0 +1,21 @@ +info: + name: Get Highlights + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Highlights + + `GET /api/media-items/:id/highlights` — auth: jwt. + + Canonical reference: `docs/developer/api/highlights/get_highlights.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/highlights/update_highlight.yml b/bruno/highlights/update_highlight.yml new file mode 100644 index 00000000..84362c65 --- /dev/null +++ b/bruno/highlights/update_highlight.yml @@ -0,0 +1,28 @@ +info: + name: Update Highlight + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' + auth: inherit + body: + type: json + data: |- + { + "selection_text": "Updated text", + "color": "#00ff00" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Highlight + + `PUT /api/media-items/:id/highlights/:highlightId` — auth: jwt. + + Canonical reference: `docs/developer/api/highlights/update_highlight.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/jobs/create_job.yml b/bruno/jobs/create_job.yml new file mode 100644 index 00000000..0de90d22 --- /dev/null +++ b/bruno/jobs/create_job.yml @@ -0,0 +1,28 @@ +info: + name: Create Job + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/jobs' + auth: inherit + body: + type: json + data: |- + { + "type": "import", + "params": { "library_id": "990e8400-e29b-41d4-a716-446655440000" } + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Job + + `POST /api/jobs` — auth: jwt. + + Canonical reference: `docs/developer/api/jobs/create_job.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/jobs/get_job_status.yml b/bruno/jobs/get_job_status.yml new file mode 100644 index 00000000..d84a360b --- /dev/null +++ b/bruno/jobs/get_job_status.yml @@ -0,0 +1,21 @@ +info: + name: Get Job Status + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/jobs/{{job_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Job Status + + `GET /api/jobs/:jobId` — auth: jwt. + + Canonical reference: `docs/developer/api/jobs/get_job_status.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/kobo/analytics_gettests.yml b/bruno/kobo/analytics_gettests.yml new file mode 100644 index 00000000..2ec967a6 --- /dev/null +++ b/bruno/kobo/analytics_gettests.yml @@ -0,0 +1,30 @@ +info: + name: Analytics GetTests + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/v1/analytics/gettests' + auth: inherit + body: + type: json + data: |- + { + "data": {} + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Analytics GetTests + + `POST /api/sync/kobo/:token/v1/analytics/gettests` — auth: device-token. + + Canonical reference: `docs/developer/api/kobo/analytics_gettests.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/kobo/bookmark_sync.yml b/bruno/kobo/bookmark_sync.yml new file mode 100644 index 00000000..515a1ca3 --- /dev/null +++ b/bruno/kobo/bookmark_sync.yml @@ -0,0 +1,38 @@ +info: + name: Bookmark Sync + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/bookmark' + auth: inherit + body: + type: json + data: |- + { + "bookmarks": [ + { + "BookmarkID": "bookmark123", + "ContentID": "book-uuid", + "StartPosition": 1234, + "Text": "Chapter 5", + "DateCreated": "2026-02-08T10:00:00Z" + } + ] + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bookmark Sync + + `POST /api/sync/kobo/:token/bookmark` — auth: device-token. + + Canonical reference: `docs/developer/api/kobo/bookmark_sync.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/kobo/initialization.yml b/bruno/kobo/initialization.yml new file mode 100644 index 00000000..1bf145b7 --- /dev/null +++ b/bruno/kobo/initialization.yml @@ -0,0 +1,24 @@ +info: + name: Kobo Initialization + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/v1/initialization' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Kobo Initialization + + `GET /api/sync/kobo/:token/v1/initialization` — auth: device-token. + + Canonical reference: `docs/developer/api/kobo/initialization.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/kobo/markup_sync.yml b/bruno/kobo/markup_sync.yml new file mode 100644 index 00000000..e17492ca --- /dev/null +++ b/bruno/kobo/markup_sync.yml @@ -0,0 +1,42 @@ +info: + name: Markup Sync + type: http + seq: 4 + +http: + method: POST + url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/markup' + auth: inherit + body: + type: json + data: |- + { + "bookmarks": [ + { + "BookmarkID": "bookmark123", + "ContentID": "book-uuid", + "StartPosition": 1234, + "EndPosition": 1456, + "Text": "Highlighted text from book", + "Annotation": "My annotation", + "DateCreated": "2026-02-08T10:00:00Z", + "Chapter": "Chapter 1", + "Hidden": false + } + ] + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Markup Sync + + `POST /api/sync/kobo/:token/markup` — auth: device-token. + + Canonical reference: `docs/developer/api/kobo/markup_sync.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/kobo/sync_from_server.yml b/bruno/kobo/sync_from_server.yml new file mode 100644 index 00000000..d3f7670f --- /dev/null +++ b/bruno/kobo/sync_from_server.yml @@ -0,0 +1,31 @@ +info: + name: Sync From Server + type: http + seq: 5 + +http: + method: POST + url: '{{base_url}}/api/sync/kobo/{{kobo_device_token}}/sync-from-server' + auth: inherit + body: + type: json + data: |- + { + "book_ids": ["content-id-1", "content-id-2"], + "full_sync": false + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Sync From Server + + `POST /api/sync/kobo/:token/sync-from-server` — auth: device-token. + + Canonical reference: `docs/developer/api/kobo/sync_from_server.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/get_library.yml b/bruno/koreader/get_library.yml new file mode 100644 index 00000000..4949b81d --- /dev/null +++ b/bruno/koreader/get_library.yml @@ -0,0 +1,24 @@ +info: + name: Get Library + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/sync/koreader/library' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library + + `GET /api/sync/koreader/library` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/get_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/get_metadata.yml b/bruno/koreader/get_metadata.yml new file mode 100644 index 00000000..f226720f --- /dev/null +++ b/bruno/koreader/get_metadata.yml @@ -0,0 +1,24 @@ +info: + name: Get Metadata + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/sync/koreader/metadata/{{book_uuid}}' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Metadata + + `GET /api/sync/koreader/metadata/:uuid` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/get_metadata.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/get_rating.yml b/bruno/koreader/get_rating.yml new file mode 100644 index 00000000..84287573 --- /dev/null +++ b/bruno/koreader/get_rating.yml @@ -0,0 +1,24 @@ +info: + name: Get KOReader Rating + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/sync/koreader/rating' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get KOReader Rating + + `GET /api/sync/koreader/rating` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/get_rating.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/get_review.yml b/bruno/koreader/get_review.yml new file mode 100644 index 00000000..7fcd86d9 --- /dev/null +++ b/bruno/koreader/get_review.yml @@ -0,0 +1,24 @@ +info: + name: Get KOReader Review + type: http + seq: 4 + +http: + method: GET + url: '{{base_url}}/api/sync/koreader/review' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get KOReader Review + + `GET /api/sync/koreader/review` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/get_review.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/push_stats.yml b/bruno/koreader/push_stats.yml new file mode 100644 index 00000000..84adf0e2 --- /dev/null +++ b/bruno/koreader/push_stats.yml @@ -0,0 +1,30 @@ +info: + name: Push KOReader Stats + type: http + seq: 5 + +http: + method: POST + url: '{{base_url}}/api/sync/koreader/stats' + auth: inherit + body: + type: json + data: |- + { + "total_seconds": 3600 + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Push KOReader Stats + + `POST /api/sync/koreader/stats` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/push_stats.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/resolve_book.yml b/bruno/koreader/resolve_book.yml new file mode 100644 index 00000000..7f9c528c --- /dev/null +++ b/bruno/koreader/resolve_book.yml @@ -0,0 +1,24 @@ +info: + name: Resolve Book:<|end|><|start|> assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + type: http + seq: 6 + +http: + method: GET + url: '{{base_url}}/api/sync/koreader/resolve' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Resolve Book:<|end|><|start|> assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + + `GET /api/sync/koreader/resolve` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/resolve_book.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/set_rating.yml b/bruno/koreader/set_rating.yml new file mode 100644 index 00000000..73097208 --- /dev/null +++ b/bruno/koreader/set_rating.yml @@ -0,0 +1,30 @@ +info: + name: Set KOReader Rating + type: http + seq: 7 + +http: + method: POST + url: '{{base_url}}/api/sync/koreader/rating' + auth: inherit + body: + type: json + data: |- + { + "rating": 8 + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Set KOReader Rating + + `POST /api/sync/koreader/rating` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/set_rating.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/set_review.yml b/bruno/koreader/set_review.yml new file mode 100644 index 00000000..c60fe772 --- /dev/null +++ b/bruno/koreader/set_review.yml @@ -0,0 +1,30 @@ +info: + name: Set KOReader Review + type: http + seq: 8 + +http: + method: POST + url: '{{base_url}}/api/sync/koreader/review' + auth: inherit + body: + type: json + data: |- + { + "review": "A slow start, but the second act earns it." + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Set KOReader Review + + `POST /api/sync/koreader/review` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/set_review.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/sync_bookmarks.yml b/bruno/koreader/sync_bookmarks.yml new file mode 100644 index 00000000..76433ad8 --- /dev/null +++ b/bruno/koreader/sync_bookmarks.yml @@ -0,0 +1,53 @@ +info: + name: Sync Bookmarks + type: http + seq: 9 + +http: + method: POST + url: '{{base_url}}/api/sync/koreader/bookmarks' + auth: inherit + body: + type: json + data: |- + { + "book_sha256": "64-hex-char-sha256", + "bookmarks": [ + { + "chapter": 3, + "datetime": "2026-08-20T10:00:00Z", + "pos0": "/body/Doc[4]/Sec[2]", + "page": 25, + "text": "", + "type": "bookmark", + "percentage": 0.125 + } + ], + "highlights": [ + { + "datetime": "2026-08-20T10:05:00Z", + "pos0": "/body/Doc[4]/Sec[2]/text()[3]:0", + "pos1": "/body/Doc[4]/Sec[2]/text()[3]:42", + "text": "Text to remember", + "notes": "Why this matters", + "type": "highlight", + "color": "blue", + "dedup_key": "echo-key-from-server" + } + ] + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Sync Bookmarks + + `POST /api/sync/koreader/bookmarks` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/sync_bookmarks.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/koreader/sync_progress.yml b/bruno/koreader/sync_progress.yml new file mode 100644 index 00000000..93182aa0 --- /dev/null +++ b/bruno/koreader/sync_progress.yml @@ -0,0 +1,47 @@ +info: + name: Sync Progress + type: http + seq: 10 + +http: + method: POST + url: '{{base_url}}/api/sync/koreader/progress' + auth: inherit + body: + type: json + data: |- + { + "books": [ + { + "uuid": "774641f9-317b-4087-8e04-53bb4392ae56", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "title": "1984", + "authors": ["George Orwell"], + "percentage": 0.045, + "context_text": "from day to day, but there was none in which Goldstein was not the principal figure. He was the prim", + "page": 14, + "total_pages": 311, + "epubcfi": "/body/DocFragment[1]/body/p[29]/text().598", + "device_info": { + "koreader_version": "v2026.07.1", + "device_model": "emulator" + } + } + ], + "sync_mode": "immediate" + } + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Sync Progress + + `POST /api/sync/koreader/progress` — auth: device-token. + + Canonical reference: `docs/developer/api/koreader/sync_progress.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/add_library_folder.yml b/bruno/libraries/add_library_folder.yml new file mode 100644 index 00000000..4253178c --- /dev/null +++ b/bruno/libraries/add_library_folder.yml @@ -0,0 +1,27 @@ +info: + name: Add Library Folder + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/folders' + auth: inherit + body: + type: json + data: |- + { + "folder_path": "/path/to/comics" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Add Library Folder + + `POST /api/libraries/:id/folders` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/add_library_folder.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/browse-folders.yml b/bruno/libraries/browse-folders.yml new file mode 100644 index 00000000..c4e1e24e --- /dev/null +++ b/bruno/libraries/browse-folders.yml @@ -0,0 +1,21 @@ +info: + name: Browse Library Folders + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/libraries/browse' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Browse Library Folders + + `GET /api/libraries/browse` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/browse-folders.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/create_library.yml b/bruno/libraries/create_library.yml new file mode 100644 index 00000000..10e1d314 --- /dev/null +++ b/bruno/libraries/create_library.yml @@ -0,0 +1,29 @@ +info: + name: Create Library + type: http + seq: 3 + +http: + method: POST + url: '{{base_url}}/api/libraries' + auth: inherit + body: + type: json + data: |- + { + "name": "Comics Collection", + "description": "Digital comics", + "type": "comics" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Library + + `POST /api/libraries` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/create_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/delete_library.yml b/bruno/libraries/delete_library.yml new file mode 100644 index 00000000..0aedcca6 --- /dev/null +++ b/bruno/libraries/delete_library.yml @@ -0,0 +1,21 @@ +info: + name: Delete Library + type: http + seq: 4 + +http: + method: DELETE + url: '{{base_url}}/api/libraries/{{ebook_library_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Library + + `DELETE /api/libraries/:id` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/delete_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/delete_library_folder.yml b/bruno/libraries/delete_library_folder.yml new file mode 100644 index 00000000..fe181a00 --- /dev/null +++ b/bruno/libraries/delete_library_folder.yml @@ -0,0 +1,27 @@ +info: + name: Delete Library Folder + type: http + seq: 5 + +http: + method: DELETE + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/folders' + auth: inherit + body: + type: json + data: |- + { + "folder_path": "/path/to/library/folder" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Library Folder + + `DELETE /api/libraries/:id/folders` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/delete_library_folder.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/delete_processing_issue.yml b/bruno/libraries/delete_processing_issue.yml new file mode 100644 index 00000000..33adb865 --- /dev/null +++ b/bruno/libraries/delete_processing_issue.yml @@ -0,0 +1,21 @@ +info: + name: Delete Processing Issue + type: http + seq: 6 + +http: + method: DELETE + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/issues/{{issueid}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Processing Issue + + `DELETE /api/libraries/:id/issues/:issueId` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/delete_processing_issue.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_library.yml b/bruno/libraries/get_library.yml new file mode 100644 index 00000000..9d7c69e6 --- /dev/null +++ b/bruno/libraries/get_library.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Details:<|end|><|start|> assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + type: http + seq: 7 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Details:<|end|><|start|> assistant to=edit<|channel|>analysis <|constrain|>xml<|message|> + + `GET /api/libraries/:id` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_library_folders.yml b/bruno/libraries/get_library_folders.yml new file mode 100644 index 00000000..e698150b --- /dev/null +++ b/bruno/libraries/get_library_folders.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Folders + type: http + seq: 8 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/folders' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Folders + + `GET /api/libraries/:id/folders` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_library_folders.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_library_media_items.yml b/bruno/libraries/get_library_media_items.yml new file mode 100644 index 00000000..d34a3877 --- /dev/null +++ b/bruno/libraries/get_library_media_items.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Media Items + type: http + seq: 9 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/media-items' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Media Items + + `GET /api/libraries/:id/media-items` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_library_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_library_stats.yml b/bruno/libraries/get_library_stats.yml new file mode 100644 index 00000000..143fd4d2 --- /dev/null +++ b/bruno/libraries/get_library_stats.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Statistics + type: http + seq: 10 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/stats' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Statistics + + `GET /api/libraries/:id/stats` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_library_stats.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_library_types.yml b/bruno/libraries/get_library_types.yml new file mode 100644 index 00000000..b60d2a86 --- /dev/null +++ b/bruno/libraries/get_library_types.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Types + type: http + seq: 11 + +http: + method: GET + url: '{{base_url}}/api/libraries/types' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Types + + `GET /api/libraries/types` — auth: none. + + Canonical reference: `docs/developer/api/libraries/get_library_types.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_processing_issue_stats.yml b/bruno/libraries/get_processing_issue_stats.yml new file mode 100644 index 00000000..79ec9576 --- /dev/null +++ b/bruno/libraries/get_processing_issue_stats.yml @@ -0,0 +1,21 @@ +info: + name: Get Processing Issue Stats + type: http + seq: 12 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/issues/stats' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Processing Issue Stats + + `GET /api/libraries/:id/issues/stats` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_processing_issue_stats.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_scan_settings.yml b/bruno/libraries/get_scan_settings.yml new file mode 100644 index 00000000..144a2e31 --- /dev/null +++ b/bruno/libraries/get_scan_settings.yml @@ -0,0 +1,21 @@ +info: + name: Get Scan Settings + type: http + seq: 13 + +http: + method: GET + url: '{{base_url}}/api/libraries/scan-settings' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Scan Settings + + `GET /api/libraries/scan-settings` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/get_scan_settings.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/get_visible_libraries.yml b/bruno/libraries/get_visible_libraries.yml new file mode 100644 index 00000000..56589d96 --- /dev/null +++ b/bruno/libraries/get_visible_libraries.yml @@ -0,0 +1,21 @@ +info: + name: Get Visible Libraries + type: http + seq: 14 + +http: + method: GET + url: '{{base_url}}/api/libraries/visibility' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Visible Libraries + + `GET /api/libraries/visibility` — auth: jwt. + + Canonical reference: `docs/developer/api/libraries/get_visible_libraries.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/list_libraries.yml b/bruno/libraries/list_libraries.yml new file mode 100644 index 00000000..cf27a514 --- /dev/null +++ b/bruno/libraries/list_libraries.yml @@ -0,0 +1,21 @@ +info: + name: List Libraries (Admin) + type: http + seq: 15 + +http: + method: GET + url: '{{base_url}}/api/libraries' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Libraries (Admin) + + `GET /api/libraries` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/list_libraries.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/list_processing_issues.yml b/bruno/libraries/list_processing_issues.yml new file mode 100644 index 00000000..cb3c5167 --- /dev/null +++ b/bruno/libraries/list_processing_issues.yml @@ -0,0 +1,21 @@ +info: + name: List Processing Issues + type: http + seq: 16 + +http: + method: GET + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/issues/list' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Processing Issues + + `GET /api/libraries/:id/issues/list` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/list_processing_issues.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/resolve_processing_issue.yml b/bruno/libraries/resolve_processing_issue.yml new file mode 100644 index 00000000..0eeec08e --- /dev/null +++ b/bruno/libraries/resolve_processing_issue.yml @@ -0,0 +1,21 @@ +info: + name: Resolve Processing Issue + type: http + seq: 17 + +http: + method: POST + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/issues/{{issueid}}/{{mediaitemid}}/resolve' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Resolve Processing Issue + + `POST /api/libraries/:id/issues/:issueId/:mediaItemId/resolve` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/resolve_processing_issue.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/scan_library.yml b/bruno/libraries/scan_library.yml new file mode 100644 index 00000000..e329dc3a --- /dev/null +++ b/bruno/libraries/scan_library.yml @@ -0,0 +1,21 @@ +info: + name: Scan Library + type: http + seq: 18 + +http: + method: POST + url: '{{base_url}}/api/libraries/{{ebook_library_id}}/scan' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Scan Library + + `POST /api/libraries/:id/scan` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/scan_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/set_library_visibility.yml b/bruno/libraries/set_library_visibility.yml new file mode 100644 index 00000000..0adf7893 --- /dev/null +++ b/bruno/libraries/set_library_visibility.yml @@ -0,0 +1,28 @@ +info: + name: Set Library Visibility + type: http + seq: 19 + +http: + method: POST + url: '{{base_url}}/api/libraries/visibility' + auth: inherit + body: + type: json + data: |- + { + "library_id": "library-uuid", + "is_visible": true + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Set Library Visibility + + `POST /api/libraries/visibility` — auth: jwt. + + Canonical reference: `docs/developer/api/libraries/set_library_visibility.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/update_library.yml b/bruno/libraries/update_library.yml new file mode 100644 index 00000000..c7033fa0 --- /dev/null +++ b/bruno/libraries/update_library.yml @@ -0,0 +1,28 @@ +info: + name: Update Library + type: http + seq: 20 + +http: + method: PUT + url: '{{base_url}}/api/libraries/{{ebook_library_id}}' + auth: inherit + body: + type: json + data: |- + { + "name": "My Updated Library", + "description": "Updated description" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Library + + `PUT /api/libraries/:id` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/update_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/libraries/update_scan_settings.yml b/bruno/libraries/update_scan_settings.yml new file mode 100644 index 00000000..44fa7344 --- /dev/null +++ b/bruno/libraries/update_scan_settings.yml @@ -0,0 +1,28 @@ +info: + name: Update Scan Settings + type: http + seq: 21 + +http: + method: PUT + url: '{{base_url}}/api/libraries/scan-settings' + auth: inherit + body: + type: json + data: |- + { + "scan_poll_interval_seconds": 3600, + "auto_scan_enabled": true + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Scan Settings + + `PUT /api/libraries/scan-settings` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/libraries/update_scan_settings.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/library/Add Library Folder.yml b/bruno/library/Add Library Folder.yml deleted file mode 100644 index 50eea252..00000000 --- a/bruno/library/Add Library Folder.yml +++ /dev/null @@ -1,53 +0,0 @@ -info: - name: Add Library Folder - type: http - seq: 1 - -http: - method: POST - url: "{{base_url}}/api/libraries/{{ebook_library_id}}/folders" - body: - type: json - data: |- - { - "folder_path": "{{library_folder}}/Ebooks" - } - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const id = responseBody.id; - - if (id) { - bru.setEnvVar("library_folder_id", id, { persist: true }); - console.log("Library Folder ID saved:", id); - } - - if (!id) { - console.log("No id found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Add Library Folder - - Adds a new folder path to a library for media scanning and indexing. - - **Method:** POST - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Create Library.yml b/bruno/library/Create Library.yml deleted file mode 100644 index 0c081f9d..00000000 --- a/bruno/library/Create Library.yml +++ /dev/null @@ -1,83 +0,0 @@ -info: - name: Create Library - type: http - seq: 2 - -http: - method: POST - url: "{{base_url}}/api/libraries" - body: - type: json - data: |- - { - "name": "My Ebook Library", - "description": "A collection of technical books and novels", - "type": "ebooks" - } - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const id = responseBody.id; - - if (id) { - bru.setEnvVar("ebook_library_id", id, { persist: true }); - console.log("Library ID saved:", id); - } - - if (!id) { - console.log("No id found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Create Library - - Creates a new library for organizing media items. - - **Method:** POST - - **Endpoint:** /api/libraries - - **Authentication:** Required (Bearer token) - - **Request Body:** - - `name` (string, required): Library name - - `description` (string, optional): Library description - - `type` (string, required): Library type - - `"ebooks"`: Electronic books - - `"audiobooks"`: Audio books - - `"videos"`: Video content - - `"other"`: Other media types - - **Response:** Library object - - `id` (string): Library UUID - - `name` (string): Library name - - `description` (string, optional): Library description - - `type` (string): Library type - - `is_visible` (boolean): Library visibility status - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 201: Library created successfully - - 400: Invalid request data - - 401: Unauthorized - - 403: Forbidden (insufficient permissions) - - 409: Library name already exists - - 500: Internal server error diff --git a/bruno/library/Delete Library Folder.yml b/bruno/library/Delete Library Folder.yml deleted file mode 100644 index 95e3f77a..00000000 --- a/bruno/library/Delete Library Folder.yml +++ /dev/null @@ -1,38 +0,0 @@ -info: - name: Delete Library Folder - type: http - seq: 13 - -http: - method: DELETE - url: "{{base_url}}/api/libraries/{{library_id}}/folders" - headers: - - name: "" - value: application/json - body: - type: json - data: "" - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_delete_library_folder_success(status, headers, body) { - if (status !== 204) { - throw new Error("Expected status 204, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Delete Library Folder - - Removes a folder from a library's scanning configuration. - - **Method:** DELETE - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Delete Library.yml b/bruno/library/Delete Library.yml deleted file mode 100644 index 82e8d4f9..00000000 --- a/bruno/library/Delete Library.yml +++ /dev/null @@ -1,27 +0,0 @@ -info: - name: Delete Library - type: http - seq: 12 - -http: - method: DELETE - url: "{{base_url}}/api/libraries/{{library_id}}" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Delete Library - - Deletes a library and all associated media items. - - **Method:** DELETE - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Get Libraries (Admin).yml b/bruno/library/Get Libraries (Admin).yml deleted file mode 100644 index 598a07a2..00000000 --- a/bruno/library/Get Libraries (Admin).yml +++ /dev/null @@ -1,95 +0,0 @@ -info: - name: Get Libraries (Admin) - type: http - seq: 3 - -http: - method: GET - url: "{{base_url}}/api/libraries" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - let ebook_id - let comic_id - let manga_id - - if (res.status === 200) { - for (var lib of responseBody.data) { - if (lib.name.includes("Ebook")) ebook_id = lib.id - if (lib.name.includes("Comic")) comic_id = lib.id - if (lib.name.includes("Manga")) manga_id = lib.id - } - } - - if (ebook_id && ebook_id !== "") { - bru.setEnvVar("ebook_library_id", ebook_id, { persist: true }); - console.log("Ebook Library ID saved:", ebook_id); - } - - if (comic_id && comic_id !== "") { - bru.setEnvVar("comic_library_id", comic_id, { persist: true }); - console.log("Comic Library ID saved:", comic_id); - } - - if (manga_id && manga_id !== "") { - bru.setEnvVar("manga_library_id", manga_id, { persist: true }); - console.log("Manga Library ID saved:", manga_id); - } - - if (!ebook_id || ebook_id === "") { - console.log("No Ebook id found in response."); - } - if (!comic_id || comic_id === "") { - console.log("No Comic id found in response."); - } - if (!manga_id || manga_id === "") { - console.log("No Manga id found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Libraries (Admin) - - Retrieves all libraries in the system (admin access required). - - **Method:** GET - - **Endpoint:** /api/libraries - - **Authentication:** Required (Bearer token, admin permissions) - - **Response:** Array of library objects - - `id` (string): Library UUID - - `name` (string): Library name - - `description` (string, optional): Library description - - `type` (string): Library type (ebooks, audiobooks, videos, other) - - `is_visible` (boolean): Library visibility to users - - `media_count` (number): Number of media items in library - - `folder_count` (number): Number of folders associated - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 500: Internal server error diff --git a/bruno/library/Get Library Folders.yml b/bruno/library/Get Library Folders.yml deleted file mode 100644 index 68798d1c..00000000 --- a/bruno/library/Get Library Folders.yml +++ /dev/null @@ -1,27 +0,0 @@ -info: - name: Get Library Folders - type: http - seq: 5 - -http: - method: GET - url: "{{base_url}}/api/libraries/{{library_id}}/folders" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Library Folders - - Retrieves all folders associated with a specific library. - - **Method:** GET - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Get Library Stats.yml b/bruno/library/Get Library Stats.yml deleted file mode 100644 index 448d5250..00000000 --- a/bruno/library/Get Library Stats.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Library Stats - type: http - seq: 14 - -http: - method: GET - url: "{{base_url}}/api/libraries/{{library_id}}/stats" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_get_library_stats_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Library Stats - - Retrieves statistical information about a library's media items. - - **Method:** GET - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Get Library Types.yml b/bruno/library/Get Library Types.yml deleted file mode 100644 index 34e9168c..00000000 --- a/bruno/library/Get Library Types.yml +++ /dev/null @@ -1,39 +0,0 @@ -info: - name: Get Library Types - type: http - seq: 6 - -http: - method: GET - url: "{{base_url}}/api/libraries/types" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Library Types - - Retrieves all available library types that can be used when creating libraries. - - **Method:** GET - - **Endpoint:** /api/libraries/types - - **Authentication:** Required (Bearer token) - - **Response:** Array of library type objects - - `id` (string): Type identifier (e.g., "ebooks", "audiobooks", "videos", "other") - - `name` (string): Display name for the type (e.g., "Ebooks", "Audiobooks", "Videos", "Other") - - `description` (string, optional): Description of what this type is used for - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 500: Internal server error diff --git a/bruno/library/Get Library.yml b/bruno/library/Get Library.yml deleted file mode 100644 index e6a02cdd..00000000 --- a/bruno/library/Get Library.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Library - type: http - seq: 4 - -http: - method: GET - url: "{{base_url}}/api/libraries/{{library_id}}" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_get_library_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Library - - Retrieves detailed information about a specific library by ID. - - **Method:** GET - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Get Scan Settings.yml b/bruno/library/Get Scan Settings.yml deleted file mode 100644 index 028ef437..00000000 --- a/bruno/library/Get Scan Settings.yml +++ /dev/null @@ -1,42 +0,0 @@ -info: - name: Get Scan Settings - type: http - seq: 10 - -http: - method: GET - url: "{{base_url}}/api/libraries/scan-settings" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Scan Settings - - Retrieves the user's current library scanning settings. - - **Method:** GET - - **Endpoint:** /api/libraries/scan-settings - - **Authentication:** Required (Bearer token) - - **Response:** - - `scan_poll_interval_seconds` (number): Minutes between automatic scans (minimum 1) - - `auto_scan_enabled` (boolean): Whether automatic scanning is enabled - - `last_scan_at` (string, optional): Timestamp of last scan - - `next_scan_at` (string, optional): Timestamp of next scheduled scan - - `library_id` (string, optional): Library ID for context - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden (access denied) - - 500: Internal server error diff --git a/bruno/library/Get User Visible Libraries.yml b/bruno/library/Get User Visible Libraries.yml deleted file mode 100644 index 5a342587..00000000 --- a/bruno/library/Get User Visible Libraries.yml +++ /dev/null @@ -1,43 +0,0 @@ -info: - name: Get User Visible Libraries - type: http - seq: 7 - -http: - method: GET - url: "{{base_url}}/api/libraries/visibility" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get User Visible Libraries - - Retrieves all libraries that are visible to regular users. - - **Method:** GET - - **Endpoint:** /api/libraries/visibility - - **Authentication:** Required (Bearer token) - - **Response:** Array of visible library objects - - `id` (string): Library UUID - - `name` (string): Library name - - `description` (string, optional): Library description - - `type` (string): Library type (ebooks, audiobooks, videos, other) - - `is_visible` (boolean): Always true for this endpoint - - `media_count` (number): Number of media items in library - - `created_at` (string): Creation timestamp - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 500: Internal server error diff --git a/bruno/library/Set Library Visibility.yml b/bruno/library/Set Library Visibility.yml deleted file mode 100644 index dc6f25ea..00000000 --- a/bruno/library/Set Library Visibility.yml +++ /dev/null @@ -1,55 +0,0 @@ -info: - name: Set Library Visibility - type: http - seq: 8 - -http: - method: POST - url: "{{base_url}}/api/libraries/visibility" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "library_id": "{{library_id}}", - "is_visible": true - } - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Set Library Visibility - - Updates the visibility status of a library for regular users. - - **Method:** POST - - **Endpoint:** /api/libraries/visibility - - **Authentication:** Required (Bearer token, admin permissions) - - **Request Body:** - - `library_id` (string, required): Library UUID - - `is_visible` (boolean, required): Visibility status - - `true`: Library visible to all users - - `false`: Library hidden from regular users - - **Response:** Updated library visibility object - - `library_id` (string): Library UUID - - `is_visible` (boolean): Updated visibility status - - `updated_at` (string): Update timestamp - - **Status Codes:** - - 200: Visibility updated successfully - - 400: Invalid request data - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 404: Library not found - - 500: Internal server error diff --git a/bruno/library/Update Library.yml b/bruno/library/Update Library.yml deleted file mode 100644 index bb870f58..00000000 --- a/bruno/library/Update Library.yml +++ /dev/null @@ -1,38 +0,0 @@ -info: - name: Update Library - type: http - seq: 11 - -http: - method: PUT - url: "{{base_url}}/api/libraries/{{library_id}}" - headers: - - name: "" - value: application/json - body: - type: json - data: "" - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_update_library_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Update Library - - Updates an existing library's information. - - **Method:** PUT - - **Endpoint:** /api/libraries/{id diff --git a/bruno/library/Update Scan Settings.yml b/bruno/library/Update Scan Settings.yml deleted file mode 100644 index db43a927..00000000 --- a/bruno/library/Update Scan Settings.yml +++ /dev/null @@ -1,48 +0,0 @@ -info: - name: Update Scan Settings - type: http - seq: 9 - -http: - method: PUT - url: "{{base_url}}/api/libraries/scan-settings" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "scan_poll_interval_seconds": 30, - "auto_scan_enabled": true - } - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Update Scan Settings - - Updates the user's media scanning settings. - - **Method:** PUT - - **Endpoint:** /api/libraries/scan-settings - - **Authentication:** Required - - **Request Body:** - - `scan_poll_interval_seconds` (integer, required): Minutes between automatic scans (15-1440) - - `auto_scan_enabled` (boolean, required): Whether automatic scanning is enabled - - **Response:** - - `message` (string): Success message - - **Status Codes:** - - 200: Success - - 400: Invalid settings - - 401: Unauthorized diff --git a/bruno/media-items/Create Media Item.yml b/bruno/media-items/Create Media Item.yml deleted file mode 100644 index a005f04c..00000000 --- a/bruno/media-items/Create Media Item.yml +++ /dev/null @@ -1,52 +0,0 @@ -info: - name: Create Media Item - type: http - seq: 3 - -http: - method: POST - url: "{{base_url}}/api/media-items" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "library_id": "{{library_id}}", - "title": "Test Book", - "author": "Test Author", - "file_path": "/path/to/book.epub", - "file_size": 1024, - "mime_type": "application/epub+zip" - } - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_create_media_item_success(status, headers, body) { - if (status !== 201) { - throw new Error("Expected status 201, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Create Media Item - - Creates a new media item in a library with full metadata. - - **Method:** POST - - **Endpoint:** /api/media-items - - **Authentication:** Required (Bearer token, admin only) - - **Prerequisites** - - Library must have at least one folder configured before media items can be added - - Use `POST /api/libraries/{library_id diff --git a/bruno/media-items/Create Media Rating.yml b/bruno/media-items/Create Media Rating.yml deleted file mode 100644 index cda41242..00000000 --- a/bruno/media-items/Create Media Rating.yml +++ /dev/null @@ -1,33 +0,0 @@ -info: - name: Create Media Rating - type: http - seq: 4 - -http: - method: POST - url: "{{base_url}}/api/media-items/{{media_item_id}}/rating" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "rating": 7 - } - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Create Media Rating - - Creates or updates the authenticated user's rating for a specific media item. - - **Method:** POST - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/Delete Media Item.yml b/bruno/media-items/Delete Media Item.yml deleted file mode 100644 index 8594e93d..00000000 --- a/bruno/media-items/Delete Media Item.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Delete Media Item - type: http - seq: 11 - -http: - method: DELETE - url: "{{base_url}}/api/media-items/{{media_item_id}}" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_delete_media_item_success(status, headers, body) { - if (status !== 204) { - throw new Error("Expected status 204, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Delete Media Item - - Deletes a media item from the library. - - **Method:** DELETE - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/Delete Media Rating.yml b/bruno/media-items/Delete Media Rating.yml deleted file mode 100644 index 9c78f47d..00000000 --- a/bruno/media-items/Delete Media Rating.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Delete Media Rating - type: http - seq: 13 - -http: - method: DELETE - url: "{{base_url}}/api/media-items/{{media_item_id}}/rating" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_delete_media_rating_success(status, headers, body) { - if (status !== 204) { - throw new Error("Expected status 204, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Delete Media Rating - - Deletes a user's rating for a specific media item. - - **Method:** DELETE - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/EPUB Download.yml b/bruno/media-items/EPUB Download.yml deleted file mode 100644 index b26e73f0..00000000 --- a/bruno/media-items/EPUB Download.yml +++ /dev/null @@ -1,23 +0,0 @@ -info: - name: Download Media Item - type: http - seq: 5 - -http: - method: GET - url: "{{base_url}}/api/media-items/{{media_item_id}}/download" - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Download Media Item - - Download a media item file (EPUB, PDF, etc.) from Bookhoard server. - - **Method:** GET - - **Endpoint:** /api/media-items/{bookUUID diff --git a/bruno/media-items/Get Audio Chapters.yml b/bruno/media-items/Get Audio Chapters.yml deleted file mode 100644 index e998bdbf..00000000 --- a/bruno/media-items/Get Audio Chapters.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Get Audio Chapters - type: http - seq: 30 - -http: - method: GET - url: "{{base_url}}/api/media-items/{{media_item_id}}/chapters" - headers: - - name: "" - value: application/json - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Audio Chapters - - The format-abstraction chapter list for audiobooks - (media_items.audio_metadata). Single-file books return `layout: ""` and - an empty `tracks` array; folder-of-tracks books return `layout: - "folder"` and zip books `layout: "zip"`, each with one `tracks` entry - per playable file (global-seconds start/end plus the per-track `url`). - - **Method:** GET - - **Endpoint:** /api/media-items/{id}/chapters diff --git a/bruno/media-items/Get Audio Track.yml b/bruno/media-items/Get Audio Track.yml deleted file mode 100644 index 2d47c859..00000000 --- a/bruno/media-items/Get Audio Track.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Get Audio Track - type: http - seq: 31 - -http: - method: GET - url: "{{base_url}}/api/media-items/{{media_item_id}}/audio/{{track_index}}" - headers: - - name: Range - value: bytes=0-1023 - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Audio Track - - Streams one track of a multi-file audiobook (folder or zip layout). - Range requests are honored: folder tracks are served from the real - file; zip tracks are extracted in place (backward seeks re-open the - entry). Requires the item's library to be visible to the requester - (403 otherwise). Single-file books have no track manifest — 404. - - **Method:** GET - - **Endpoint:** /api/media-items/{id}/audio/{trackIndex} diff --git a/bruno/media-items/Get Media Item.yml b/bruno/media-items/Get Media Item.yml deleted file mode 100644 index 0f1bd428..00000000 --- a/bruno/media-items/Get Media Item.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Media Item - type: http - seq: 8 - -http: - method: GET - url: "{{base_url}}/api/media-items/{{media_item_id}}" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_get_media_item_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Media Item - - Retrieves detailed information about a specific media item. - - **Method:** GET - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/Get Media Rating.yml b/bruno/media-items/Get Media Rating.yml deleted file mode 100644 index a76765b1..00000000 --- a/bruno/media-items/Get Media Rating.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Media Rating - type: http - seq: 12 - -http: - method: GET - url: "{{base_url}}/api/media-items/{{media_item_id}}/rating" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_get_media_rating_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Get Media Rating - - Retrieves a user's rating for a specific media item. - - **Method:** GET - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/List Media Items Sorted.yml b/bruno/media-items/List Media Items Sorted.yml deleted file mode 100644 index baaafef7..00000000 --- a/bruno/media-items/List Media Items Sorted.yml +++ /dev/null @@ -1,81 +0,0 @@ -info: - name: List Media Items with Sorting - type: http - seq: 2 - -http: - method: GET - url: "{{base_url}}/api/media-items?library_id={{ebook_library_id}}&sort=title+ASC&limit=10&offset=0" - headers: - - name: "" - value: application/json - params: - - name: library_id - value: "{{ebook_library_id}}" - type: query - - name: sort - value: title+ASC - type: query - - name: limit - value: "10" - type: query - - name: offset - value: "0" - type: query - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## List Media Items with Sorting - - **Method:** GET - - **Endpoint:** /api/media-items - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `library_id` (string, required): UUID of the library - - `sort` (string, optional): Sort field and direction - - Available options: - - `created_at ASC` - Oldest added first - - `created_at DESC` - Newest added first (default) - - `title ASC` - Title A-Z - - `title DESC` - Title Z-A - - `author ASC` - Author A-Z - - `author DESC` - Author Z-A - - `series ASC` - Series order - - `series DESC` - Series reverse order - - `date_published ASC` - Oldest published first - - `date_published DESC` - Newest published first - - `copyright_year ASC` - Oldest copyright first - - `copyright_year DESC` - Newest copyright first - - `page_count ASC` - Shortest first - - `page_count DESC` - Longest first - - `tags ASC` - Genre A-Z - - `tags DESC` - Genre Z-A - - `limit` (integer, optional): Number of items to return (default: 50, max: 1000) - - `offset` (integer, optional): Number of items to skip (default: 0) - - **Response:** Object containing array of media items - - **Status Codes:** - - 200: Success - - 400: Bad request (invalid parameters) - - 401: Unauthorized - - 500: Internal server error - - **Examples:** - - Sort by title: `/api/media-items?library_id=xxx&sort=title+ASC` - - Sort by author descending: `/api/media-items?library_id=xxx&sort=author+DESC` - - Sort by page count: `/api/media-items?library_id=xxx&sort=page_count+ASC` - - **Sorting Behavior:** - - All sorts are secondary-sorted by series_number then title for consistency - - NULL values are sorted last for ascending, first for descending - - Sorting is case-insensitive for text fields diff --git a/bruno/media-items/List Media Items.yml b/bruno/media-items/List Media Items.yml deleted file mode 100644 index 4d428baa..00000000 --- a/bruno/media-items/List Media Items.yml +++ /dev/null @@ -1,61 +0,0 @@ -info: - name: List Media Items - type: http - seq: 1 - -http: - method: GET - url: "{{base_url}}/api/media-items?library_id={{ebook_library_id}}&limit=20&offset=0" - headers: - - name: "" - value: application/json - params: - - name: library_id - value: "{{ebook_library_id}}" - type: query - - name: limit - value: "20" - type: query - - name: offset - value: "0" - type: query - auth: inherit - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## List Media Items - - Retrieves a paginated list of media items from a specific library. - - **Method:** GET - - **Endpoint:** /api/media-items - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `library_id` (string, required): Library UUID to filter items - - `limit` (number, optional): Number of results per page (default: 20, max: 100) - - `offset` (number, optional): Pagination offset (default: 0) - - **Response:** Array of media item objects - - `id` (string): Media item UUID - - `title` (string): Media item title - - `library_id` (string): Library UUID - - `media_type` (string): Type of media (e.g., "ebook", "audiobook") - - `file_path` (string): Path to media file - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 200: Success - - 400: Invalid query parameters - - 401: Unauthorized - - 403: Forbidden (library access denied) - - 404: Library not found - - 500: Internal server error diff --git a/bruno/media-items/Rescan Media Item.yml b/bruno/media-items/Rescan Media Item.yml deleted file mode 100644 index 9bbf4e2c..00000000 --- a/bruno/media-items/Rescan Media Item.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Rescan Media Item - type: http - seq: 9 - -http: - method: POST - url: "{{base_url}}/api/media-items/{{media_item_id}}/rescan" - headers: - - name: "" - value: application/json - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_rescan_media_item_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Rescan Media Item - - Re-extracts metadata and cover art for a single media item from the file on disk. - - **Method:** POST - - **Endpoint:** /api/media-items/{id}/rescan diff --git a/bruno/media-items/Update Media Item.yml b/bruno/media-items/Update Media Item.yml deleted file mode 100644 index 87e609ff..00000000 --- a/bruno/media-items/Update Media Item.yml +++ /dev/null @@ -1,43 +0,0 @@ -info: - name: Update Media Item - type: http - seq: 10 - -http: - method: PUT - url: "{{base_url}}/api/media-items/{{media_item_id}}" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "title": "Updated Book Title", - "author": "Updated Author", - "description": "Updated description" - } - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_update_media_item_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Update Media Item - - Updates an existing media item's metadata. - - **Method:** PUT - - **Endpoint:** /api/media-items/{id diff --git a/bruno/media-items/Update Media Rating.yml b/bruno/media-items/Update Media Rating.yml deleted file mode 100644 index 765a1494..00000000 --- a/bruno/media-items/Update Media Rating.yml +++ /dev/null @@ -1,66 +0,0 @@ -info: - name: Update Media Rating - type: http - seq: 9 - -http: - method: PUT - url: "{{base_url}}/api/media-items/{{media_item_id}}/rating" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "rating": 9, - "review": "Updated review text" - } - auth: inherit - -runtime: - scripts: - - type: tests - code: |- - test_update_media_rating_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Update Media Rating - - Updates an existing rating for a media item. - - **Method:** PUT - - **Endpoint:** /api/media-items/:id/rating - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string, required): Media item UUID - - **Request Body:** - - `rating` (number, required): Rating value (1-10 integer scale; displayed as 1-5 stars with half-star precision) - - `review` (string, optional): Review text - - **Response:** Updated rating object - - `id` (string): Rating ID - - `media_item_id` (string): Media item UUID - - `rating` (number): Rating value - - `review` (string): Review text - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 200: Success - - 400: Invalid request body - - 401: Unauthorized - - 404: Media item or rating not found - - 500: Internal server error diff --git a/bruno/media-items/bulk_delete_media_items.yml b/bruno/media-items/bulk_delete_media_items.yml new file mode 100644 index 00000000..7708c647 --- /dev/null +++ b/bruno/media-items/bulk_delete_media_items.yml @@ -0,0 +1,31 @@ +info: + name: Bulk Delete Media Items + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/media-items/bulk-delete' + auth: inherit + body: + type: json + data: |- + { + "media_item_ids": [ + "550e8400-e29b-41d4-a716-446655440001", + "660e8400-e29b-41d4-a716-446655440002", + "770e8400-e29b-41d4-a716-446655440003" + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Delete Media Items + + `POST /api/media-items/bulk-delete` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/bulk_delete_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/bulk_update_media_items.yml b/bruno/media-items/bulk_update_media_items.yml new file mode 100644 index 00000000..8f683aa0 --- /dev/null +++ b/bruno/media-items/bulk_update_media_items.yml @@ -0,0 +1,43 @@ +info: + name: Bulk Update Media Items + type: http + seq: 2 + +http: + method: POST + url: '{{base_url}}/api/media-items/bulk-update' + auth: inherit + body: + type: json + data: |- + { + "media_item_updates": [ + { + "media_item_id": "550e8400-e29b-41d4-a716-446655440001", + "updates": { + "title": "Updated Title", + "genre": "Science Fiction", + "tags": ["science fiction", "space opera", "ACME CORP."] + } + }, + { + "media_item_id": "660e8400-e29b-41d4-a716-446655440002", + "updates": { + "author": "Updated Author Name", + "language": "en" + } + } + ] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Bulk Update Media Items + + `POST /api/media-items/bulk-update` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/bulk_update_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/create_media_bookmark.yml b/bruno/media-items/create_media_bookmark.yml new file mode 100644 index 00000000..ba02e2e8 --- /dev/null +++ b/bruno/media-items/create_media_bookmark.yml @@ -0,0 +1,35 @@ +info: + name: Create Media Bookmark + type: http + seq: 3 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/bookmarks' + auth: inherit + body: + type: json + data: |- + { + "title": "Chapter 3", + "position": "kindle:pos:1:2", + "notes": "revisit this", + "cfi_position": "epubcfi(/6/4)", + "page_number": 42, + "chapter_number": 3, + "percentage": 35.5, + "chapter_reference": 3, + "origin": "android" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Media Bookmark + + `POST /api/media-items/:id/bookmarks` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/create_media_bookmark.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/create_media_item.yml b/bruno/media-items/create_media_item.yml new file mode 100644 index 00000000..a479cb0c --- /dev/null +++ b/bruno/media-items/create_media_item.yml @@ -0,0 +1,39 @@ +info: + name: Create Media Item + type: http + seq: 4 + +http: + method: POST + url: '{{base_url}}/api/media-items' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "title": "Example Book Title", + "author": "Jane Doe", + "isbn": "978-0-123456-78-9", + "description": "A great book about technology", + "file_path": "/library/books/example.epub", + "file_size": 1048576, + "mime_type": "application/epub+zip", + "series": "Tech Series", + "series_number": 1, + "tags": ["science-fiction", "technology", "ACME CORP."], + "publisher": "O'Reilly Media", + "contributors": ["John Smith", "ACME CORP."] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Media Item + + `POST /api/media-items` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/create_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/delete_media_bookmark.yml b/bruno/media-items/delete_media_bookmark.yml new file mode 100644 index 00000000..5045d84e --- /dev/null +++ b/bruno/media-items/delete_media_bookmark.yml @@ -0,0 +1,21 @@ +info: + name: Delete Media Bookmark + type: http + seq: 5 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/bookmarks/{{bookmarkid}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Media Bookmark + + `DELETE /api/media-items/:id/bookmarks/:bookmarkId` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/delete_media_bookmark.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/delete_media_item.yml b/bruno/media-items/delete_media_item.yml new file mode 100644 index 00000000..2efaa8fe --- /dev/null +++ b/bruno/media-items/delete_media_item.yml @@ -0,0 +1,21 @@ +info: + name: Delete Media Item + type: http + seq: 6 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Media Item + + `DELETE /api/media-items/:id` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/delete_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/delete_media_review.yml b/bruno/media-items/delete_media_review.yml new file mode 100644 index 00000000..53b575c4 --- /dev/null +++ b/bruno/media-items/delete_media_review.yml @@ -0,0 +1,21 @@ +info: + name: Delete Media Review + type: http + seq: 7 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/review' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Media Review + + `DELETE /api/media-items/:id/review` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/delete_media_review.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/deleted_annotations.yml b/bruno/media-items/deleted_annotations.yml new file mode 100644 index 00000000..21d1168e --- /dev/null +++ b/bruno/media-items/deleted_annotations.yml @@ -0,0 +1,21 @@ +info: + name: Deleted Annotations History + type: http + seq: 8 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/annotations/deleted' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Deleted Annotations History + + `GET /api/media-items/:id/annotations/deleted` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/deleted_annotations.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/download_media_item_file.yml b/bruno/media-items/download_media_item_file.yml new file mode 100644 index 00000000..b73ad7f9 --- /dev/null +++ b/bruno/media-items/download_media_item_file.yml @@ -0,0 +1,21 @@ +info: + name: Download Media Item File + type: http + seq: 9 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/download' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Download Media Item File + + `GET /api/media-items/:id/download` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/download_media_item_file.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/filter_count.yml b/bruno/media-items/filter_count.yml new file mode 100644 index 00000000..b703e5b4 --- /dev/null +++ b/bruno/media-items/filter_count.yml @@ -0,0 +1,21 @@ +info: + name: Filter Count + type: http + seq: 10 + +http: + method: GET + url: '{{base_url}}/api/media-items/filter-count' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Filter Count + + `GET /api/media-items/filter-count` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/filter_count.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_audio_chapters.yml b/bruno/media-items/get_audio_chapters.yml new file mode 100644 index 00000000..3d7a451d --- /dev/null +++ b/bruno/media-items/get_audio_chapters.yml @@ -0,0 +1,21 @@ +info: + name: Get Audio Chapters + type: http + seq: 11 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/chapters' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Audio Chapters + + `GET /api/media-items/:id/chapters` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_audio_chapters.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_audio_track.yml b/bruno/media-items/get_audio_track.yml new file mode 100644 index 00000000..9643919a --- /dev/null +++ b/bruno/media-items/get_audio_track.yml @@ -0,0 +1,21 @@ +info: + name: Get Audio Track + type: http + seq: 12 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/audio/{{track_index}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Audio Track + + `GET /api/media-items/:id/audio/:trackIndex` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_audio_track.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_facets.yml b/bruno/media-items/get_facets.yml new file mode 100644 index 00000000..61b575e0 --- /dev/null +++ b/bruno/media-items/get_facets.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Item Facets + type: http + seq: 13 + +http: + method: GET + url: '{{base_url}}/api/media-items/facets' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Item Facets + + `GET /api/media-items/facets` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_facets.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_media_bookmarks.yml b/bruno/media-items/get_media_bookmarks.yml new file mode 100644 index 00000000..2e4a57d3 --- /dev/null +++ b/bruno/media-items/get_media_bookmarks.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Bookmarks + type: http + seq: 14 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/bookmarks' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Bookmarks + + `GET /api/media-items/:id/bookmarks` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_media_bookmarks.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_media_highlight.yml b/bruno/media-items/get_media_highlight.yml new file mode 100644 index 00000000..beffce58 --- /dev/null +++ b/bruno/media-items/get_media_highlight.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Highlight + type: http + seq: 15 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/highlights/{{highlight_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Highlight + + `GET /api/media-items/:id/highlights/:highlightId` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_media_highlight.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_media_item.yml b/bruno/media-items/get_media_item.yml new file mode 100644 index 00000000..f61ccb97 --- /dev/null +++ b/bruno/media-items/get_media_item.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Item + type: http + seq: 16 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Item + + `GET /api/media-items/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_media_note.yml b/bruno/media-items/get_media_note.yml new file mode 100644 index 00000000..0e9d984c --- /dev/null +++ b/bruno/media-items/get_media_note.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Note + type: http + seq: 17 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Note + + `GET /api/media-items/:id/notes/:noteId` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_media_note.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/get_media_review.yml b/bruno/media-items/get_media_review.yml new file mode 100644 index 00000000..701c5d85 --- /dev/null +++ b/bruno/media-items/get_media_review.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Review + type: http + seq: 18 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/review' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Review + + `GET /api/media-items/:id/review` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/get_media_review.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/list_media_items.yml b/bruno/media-items/list_media_items.yml new file mode 100644 index 00000000..2e5b886c --- /dev/null +++ b/bruno/media-items/list_media_items.yml @@ -0,0 +1,21 @@ +info: + name: List Media Items + type: http + seq: 19 + +http: + method: GET + url: '{{base_url}}/api/media-items' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Media Items + + `GET /api/media-items` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/list_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/merge_archived_media_item.yml b/bruno/media-items/merge_archived_media_item.yml new file mode 100644 index 00000000..cc4a448b --- /dev/null +++ b/bruno/media-items/merge_archived_media_item.yml @@ -0,0 +1,27 @@ +info: + name: Merge Archived Media Item + type: http + seq: 20 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/merge' + auth: inherit + body: + type: json + data: |- + { + "target_id": "550e8400-e29b-41d4-a716-446655440000" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Merge Archived Media Item + + `POST /api/media-items/:id/merge` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/merge_archived_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/purge_archived_media_items.yml b/bruno/media-items/purge_archived_media_items.yml new file mode 100644 index 00000000..19673369 --- /dev/null +++ b/bruno/media-items/purge_archived_media_items.yml @@ -0,0 +1,21 @@ +info: + name: Purge Archived Media Items + type: http + seq: 21 + +http: + method: POST + url: '{{base_url}}/api/media-items/purge-archived' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Purge Archived Media Items + + `POST /api/media-items/purge-archived` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/purge_archived_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/purge_deleted_annotation.yml b/bruno/media-items/purge_deleted_annotation.yml new file mode 100644 index 00000000..0c377777 --- /dev/null +++ b/bruno/media-items/purge_deleted_annotation.yml @@ -0,0 +1,21 @@ +info: + name: Purge Deleted Annotation + type: http + seq: 22 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/annotations/{{annotation_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Purge Deleted Annotation + + `DELETE /api/media-items/:id/annotations/:annotationId` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/purge_deleted_annotation.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/rescan_media_item.yml b/bruno/media-items/rescan_media_item.yml new file mode 100644 index 00000000..28d0bbc4 --- /dev/null +++ b/bruno/media-items/rescan_media_item.yml @@ -0,0 +1,21 @@ +info: + name: Rescan Media Item + type: http + seq: 23 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/rescan' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Rescan Media Item + + `POST /api/media-items/:id/rescan` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/rescan_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/restore_deleted_annotation.yml b/bruno/media-items/restore_deleted_annotation.yml new file mode 100644 index 00000000..28c4ee7f --- /dev/null +++ b/bruno/media-items/restore_deleted_annotation.yml @@ -0,0 +1,21 @@ +info: + name: Restore Deleted Annotation + type: http + seq: 24 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/annotations/{{annotation_id}}/restore' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Restore Deleted Annotation + + `POST /api/media-items/:id/annotations/:annotationId/restore` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/restore_deleted_annotation.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/search_media_items.yml b/bruno/media-items/search_media_items.yml new file mode 100644 index 00000000..422c5a1d --- /dev/null +++ b/bruno/media-items/search_media_items.yml @@ -0,0 +1,21 @@ +info: + name: Search Media Items (Unified):<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/create_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/update_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|channel|>Edited docs/developer/api/media-items/delete_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/search_media_items.md (1 replacement) + type: http + seq: 25 + +http: + method: GET + url: '{{base_url}}/api/media-items/search' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Search Media Items (Unified):<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/create_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/update_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|channel|>Edited docs/developer/api/media-items/delete_media_item.md (1 replacement)<|end|><|start|>edit to=assistant<|channel|>analysis<|message|>Edited docs/developer/api/media-items/search_media_items.md (1 replacement) + + `GET /api/media-items/search` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/search_media_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/set_audio_speed.yml b/bruno/media-items/set_audio_speed.yml new file mode 100644 index 00000000..34ad4deb --- /dev/null +++ b/bruno/media-items/set_audio_speed.yml @@ -0,0 +1,27 @@ +info: + name: Set Audio Speed + type: http + seq: 26 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/audio-speed' + auth: inherit + body: + type: json + data: |- + { + "speed": 1.5 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Set Audio Speed + + `PUT /api/media-items/:id/audio-speed` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/set_audio_speed.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/unarchive_media_item.yml b/bruno/media-items/unarchive_media_item.yml new file mode 100644 index 00000000..a0b80ffe --- /dev/null +++ b/bruno/media-items/unarchive_media_item.yml @@ -0,0 +1,21 @@ +info: + name: Unarchive Media Item + type: http + seq: 27 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/unarchive' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Unarchive Media Item + + `POST /api/media-items/:id/unarchive` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/unarchive_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/update_media_bookmark.yml b/bruno/media-items/update_media_bookmark.yml new file mode 100644 index 00000000..96847bc3 --- /dev/null +++ b/bruno/media-items/update_media_bookmark.yml @@ -0,0 +1,29 @@ +info: + name: Update Media Bookmark + type: http + seq: 28 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/bookmarks/{{bookmarkid}}' + auth: inherit + body: + type: json + data: |- + { + "title": "Chapter 3 (renamed)", + "notes": "updated note", + "position": "kindle:pos:1:2" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Media Bookmark + + `PUT /api/media-items/:id/bookmarks/:bookmarkId` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/update_media_bookmark.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/update_media_item.yml b/bruno/media-items/update_media_item.yml new file mode 100644 index 00000000..8ef6e89b --- /dev/null +++ b/bruno/media-items/update_media_item.yml @@ -0,0 +1,31 @@ +info: + name: Update Media Item + type: http + seq: 29 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}' + auth: inherit + body: + type: json + data: |- + { + "title": "Updated Title", + "author": "Updated Author", + "description": "Updated description", + "series": "Series", + "series_number": 2 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Media Item + + `PUT /api/media-items/:id` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/media-items/update_media_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/media-items/upsert_media_review.yml b/bruno/media-items/upsert_media_review.yml new file mode 100644 index 00000000..2fba62a2 --- /dev/null +++ b/bruno/media-items/upsert_media_review.yml @@ -0,0 +1,27 @@ +info: + name: Upsert Media Review + type: http + seq: 30 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/review' + auth: inherit + body: + type: json + data: |- + { + "review": "A slow start, but the second act earns it." + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Upsert Media Review + + `POST /api/media-items/:id/review` — auth: jwt. + + Canonical reference: `docs/developer/api/media-items/upsert_media_review.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/notes/Create Media Note.yml b/bruno/notes/Create Media Note.yml deleted file mode 100644 index 8d52518b..00000000 --- a/bruno/notes/Create Media Note.yml +++ /dev/null @@ -1,40 +0,0 @@ -info: - name: Create Media Note - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/media-items/{{media_item_id}}/notes' - auth: inherit - body: - type: json - data: |- - { - "content": "This is a test note about this media item.", - "position": "page:45" - } - -docs: |- - ## Create Media Note - - Creates a new note for a specific media item. - - **Method:** POST - - **Endpoint:** /api/media-items/:id/notes - - **Path Parameters:** - - `id` (string): Media item ID - - **Request Body:** - - `content` (string): Note content (required, 1-100000 chars) - - `position` (string): Optional position reference (max 100 chars) - - **Response:** - - Note object with all fields including generated ID and timestamps - - **Status Codes:** - - 201: Created - - 400: Invalid request - - 401: Unauthorized - - 404: Media item not found diff --git a/bruno/notes/Delete Media Note.yml b/bruno/notes/Delete Media Note.yml deleted file mode 100644 index 2c088433..00000000 --- a/bruno/notes/Delete Media Note.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Delete Media Note - type: http - seq: 5 -http: - method: DELETE - url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' - auth: inherit - body: - type: none - -docs: |- - ## Delete Media Note - - Deletes a specific note. - - **Method:** DELETE - - **Endpoint:** /api/media-items/:id/notes/:noteId - - **Path Parameters:** - - `id` (string): Media item ID - - `noteId` (string): Note ID - - **Response:** - - 204 No Content on success - - **Status Codes:** - - 204: Success - - 401: Unauthorized - - 404: Note not found diff --git a/bruno/notes/Get Media Notes.yml b/bruno/notes/Get Media Notes.yml deleted file mode 100644 index 2be16883..00000000 --- a/bruno/notes/Get Media Notes.yml +++ /dev/null @@ -1,37 +0,0 @@ -info: - name: Get Media Notes - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/media-items/{{media_item_id}}/notes' - auth: inherit - body: - type: none - -docs: |- - ## Get Media Notes - - Retrieves all notes for a specific media item for the authenticated user. - - **Method:** GET - - **Endpoint:** /api/media-items/:id/notes - - **Path Parameters:** - - `id` (string): Media item ID - - **Response:** - - Array of note objects with fields: - - `id` (string): Note ID - - `media_item_id` (string): Media item ID - - `user_id` (string): User ID - - `content` (string): Note content - - `position` (string): Optional position reference - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Media item not found diff --git a/bruno/notes/Get Single Media Note.yml b/bruno/notes/Get Single Media Note.yml deleted file mode 100644 index 5e5dce63..00000000 --- a/bruno/notes/Get Single Media Note.yml +++ /dev/null @@ -1,31 +0,0 @@ -info: - name: Get Single Media Note - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' - auth: inherit - body: - type: none - -docs: |- - ## Get Single Media Note - - Retrieves a specific note by ID. - - **Method:** GET - - **Endpoint:** /api/media-items/:id/notes/:noteId - - **Path Parameters:** - - `id` (string): Media item ID - - `noteId` (string): Note ID - - **Response:** - - Note object with all fields - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Note not found diff --git a/bruno/notes/Update Media Note.yml b/bruno/notes/Update Media Note.yml deleted file mode 100644 index 9c03217d..00000000 --- a/bruno/notes/Update Media Note.yml +++ /dev/null @@ -1,41 +0,0 @@ -info: - name: Update Media Note - type: http - seq: 4 -http: - method: PUT - url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' - auth: inherit - body: - type: json - data: |- - { - "content": "This is the updated note content.", - "position": "page:47" - } - -docs: |- - ## Update Media Note - - Updates an existing note. - - **Method:** PUT - - **Endpoint:** /api/media-items/:id/notes/:noteId - - **Path Parameters:** - - `id` (string): Media item ID - - `noteId` (string): Note ID - - **Request Body:** - - `content` (string): Updated note content (required, 1-100000 chars) - - `position` (string): Updated position reference (optional, max 100 chars) - - **Response:** - - Updated note object with all fields - - **Status Codes:** - - 200: Success - - 400: Invalid request - - 401: Unauthorized - - 404: Note not found diff --git a/bruno/notes/create_note.yml b/bruno/notes/create_note.yml new file mode 100644 index 00000000..2b94dbab --- /dev/null +++ b/bruno/notes/create_note.yml @@ -0,0 +1,29 @@ +info: + name: Create Note + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/notes' + auth: inherit + body: + type: json + data: |- + { + "content": "This is a note", + "position": "epubcfi(/6/4/2:15)", + "origin": "web" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Note + + `POST /api/media-items/:id/notes` — auth: jwt. + + Canonical reference: `docs/developer/api/notes/create_note.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/notes/delete_note.yml b/bruno/notes/delete_note.yml new file mode 100644 index 00000000..2bbab87f --- /dev/null +++ b/bruno/notes/delete_note.yml @@ -0,0 +1,21 @@ +info: + name: Delete Note + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Note + + `DELETE /api/media-items/:id/notes/:noteId` — auth: jwt. + + Canonical reference: `docs/developer/api/notes/delete_note.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/notes/get_notes.yml b/bruno/notes/get_notes.yml new file mode 100644 index 00000000..57ea6db8 --- /dev/null +++ b/bruno/notes/get_notes.yml @@ -0,0 +1,21 @@ +info: + name: Get Notes + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/notes' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Notes + + `GET /api/media-items/:id/notes` — auth: jwt. + + Canonical reference: `docs/developer/api/notes/get_notes.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/notes/update_note.yml b/bruno/notes/update_note.yml new file mode 100644 index 00000000..3a8179cd --- /dev/null +++ b/bruno/notes/update_note.yml @@ -0,0 +1,28 @@ +info: + name: Update Note + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/notes/{{note_id}}' + auth: inherit + body: + type: json + data: |- + { + "content": "Updated note content", + "position": "epubcfi(/6/4/2:20)" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Note + + `PUT /api/media-items/:id/notes/:noteId` — auth: jwt. + + Canonical reference: `docs/developer/api/notes/update_note.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/opds/Download Book EPUB.yml b/bruno/opds/Download Book EPUB.yml deleted file mode 100644 index e5d66bae..00000000 --- a/bruno/opds/Download Book EPUB.yml +++ /dev/null @@ -1,34 +0,0 @@ -info: - name: Download Book (EPUB) - type: http - seq: 3 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}' - -docs: |- - ## Download Book (EPUB) - - Downloads a book in standard EPUB format for reading on e-reader devices. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/download/{book_id} - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - `book_id` (string): Book ID - - **Query Parameters:** - - `format` (string, optional): File format (epub, default) - - **Response:** - - Binary EPUB file content - - **Status Codes:** - - 200: Success (file download) - - 401: Unauthorized - - 403: Forbidden (device not authorized for this book) - - 404: Book not found diff --git a/bruno/opds/Download Book KEPUB (On-the-fly Conversion).yml b/bruno/opds/Download Book KEPUB (On-the-fly Conversion).yml deleted file mode 100644 index e92bf123..00000000 --- a/bruno/opds/Download Book KEPUB (On-the-fly Conversion).yml +++ /dev/null @@ -1,19 +0,0 @@ -info: - name: Download Book KEPUB (On-the-fly Conversion) - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/opds/devices/{{deviceId}}/download/{{mediaItemId}}?format=kepub' - auth: inherit - body: - type: none - -docs: |- - ## Download Book KEPUB (On-the-fly Conversion) - - Downloads a book in Kobo EPUB (KEPUB) format with on-the-fly conversion if needed. - - **Method:** GET - - **Endpoint:** /opds/devices/{deviceId diff --git a/bruno/opds/Download Book KEPUB.yml b/bruno/opds/Download Book KEPUB.yml deleted file mode 100644 index 5446f0e8..00000000 --- a/bruno/opds/Download Book KEPUB.yml +++ /dev/null @@ -1,34 +0,0 @@ -info: - name: Download Book (KEPUB) - type: http - seq: 4 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/download/{{book_id}}?format=kepub' - -docs: |- - ## Download Book (KEPUB) - - Downloads a book in Kobo-specific KEPUB format, optimized for Kobo e-readers. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/download/{book_id} - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - `book_id` (string): Book ID - - **Query Parameters:** - - `format` (string, required): File format (kepub for Kobo devices) - - **Response:** - - Binary KEPUB file content (EPUB with Kobo-specific enhancements) - - **Status Codes:** - - 200: Success (file download) - - 401: Unauthorized - - 403: Forbidden (device not authorized for this book) - - 404: Book not found diff --git a/bruno/opds/Get Cover Image.yml b/bruno/opds/Get Cover Image.yml deleted file mode 100644 index 9f47a7e3..00000000 --- a/bruno/opds/Get Cover Image.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Cover Image - type: http - seq: 5 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/cover/{{book_id}}' - -docs: |- - ## Get Cover Image - - Retrieves the cover image for a specific book. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/cover/{book_id} - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - `book_id` (string): Book ID - - **Query Parameters:** - - `width` (integer, optional): Desired width in pixels - - `height` (integer, optional): Desired height in pixels - - `quality` (integer, optional): Image quality (1-100, default 85) - - **Response:** - - Binary image file (JPEG or PNG) - - **Status Codes:** - - 200: Success (image file) - - 401: Unauthorized - - 404: Book or cover not found diff --git a/bruno/opds/Get Device Catalog.yml b/bruno/opds/Get Device Catalog.yml deleted file mode 100644 index 6779141c..00000000 --- a/bruno/opds/Get Device Catalog.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Get Device Catalog - type: http - seq: 1 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/catalog?page=1&per_page=50' - -docs: |- - ## Get Device Catalog - - Retrieves a paginated catalog of books available for the device, compatible with OPDS feed format. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/catalog - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - **Query Parameters:** - - `page` (integer, optional): Page number (default 1) - - `per_page` (integer, optional): Items per page (default 50, max 200) - - `sort_by` (string, optional): Sort field (title, author, date_added) - - `order` (string, optional): Sort order (asc, desc) - - **Response:** - - OPDS 2.0 feed with book entries including metadata and download links - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device not found diff --git a/bruno/opds/Get Device Navigation.yml b/bruno/opds/Get Device Navigation.yml deleted file mode 100644 index 74410664..00000000 --- a/bruno/opds/Get Device Navigation.yml +++ /dev/null @@ -1,34 +0,0 @@ -info: - name: Get Device Navigation - type: http - seq: 6 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/nav' - -docs: |- - ## Get Device Navigation - - Provides OPDS navigation feed for device, including collections and series groups. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/nav - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - **Response:** - - OPDS navigation feed with links to: - - All books - - Collections - - Series - - Authors - - Recent additions - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device not found diff --git a/bruno/opds/List Formats.yml b/bruno/opds/List Formats.yml deleted file mode 100644 index 8fd2f1e4..00000000 --- a/bruno/opds/List Formats.yml +++ /dev/null @@ -1,34 +0,0 @@ -info: - name: List Formats - type: http - seq: 7 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/formats/{{book_id}}' - -docs: |- - ## List Formats - - Lists all available file formats for a specific book. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/formats/{book_id} - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - `book_id` (string): Book ID - - **Response:** - - Array of available formats: - - `format` (string): Format identifier (epub, kepub, pdf, etc.) - - `file_size` (integer): File size in bytes - - `download_url` (string): Download link - - `optimized_for` (array): Compatible device types - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Book not found diff --git a/bruno/opds/Search Device Catalog.yml b/bruno/opds/Search Device Catalog.yml deleted file mode 100644 index 19518992..00000000 --- a/bruno/opds/Search Device Catalog.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Search Device Catalog - type: http - seq: 2 -http: - method: GET - url: '{{opds_base_url}}/devices/{{device_id}}/search?q=hobbit' - -docs: |- - ## Search Device Catalog - - Searches the device's book catalog for matching titles, authors, or series. - - **Method:** GET - - **Endpoint:** /devices/{device_id}/search - - **Authentication:** Required (Device token or Bearer token) - - **Path Parameters:** - - `device_id` (string): Device ID - - **Query Parameters:** - - `q` (string, required): Search query - - `page` (integer, optional): Page number (default 1) - - `per_page` (integer, optional): Items per page (default 50) - - `search_in` (string, optional): Search fields (title, author, series, all) - - **Response:** - - OPDS 2.0 feed with matching book entries - - **Status Codes:** - - 200: Success - - 400: Invalid query - - 401: Unauthorized diff --git a/bruno/opds/acquisition.yml b/bruno/opds/acquisition.yml new file mode 100644 index 00000000..fa5c97c6 --- /dev/null +++ b/bruno/opds/acquisition.yml @@ -0,0 +1,24 @@ +info: + name: OPDS Acquisition + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/opds/devices/{{device_id}}/download/{{book_uuid}}' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # OPDS Acquisition + + `GET /opds/devices/:deviceId/download/:bookId` — auth: device-token. + + Canonical reference: `docs/developer/api/opds/acquisition.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/opds/feeds.yml b/bruno/opds/feeds.yml new file mode 100644 index 00000000..1d8dc6f5 --- /dev/null +++ b/bruno/opds/feeds.yml @@ -0,0 +1,24 @@ +info: + name: OPDS Feeds + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/opds/devices/{{device_id}}/catalog' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # OPDS Feeds + + `GET /opds/devices/:deviceId/catalog` — auth: device-token. + + Canonical reference: `docs/developer/api/opds/feeds.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/opds/publication.yml b/bruno/opds/publication.yml new file mode 100644 index 00000000..9a0919bc --- /dev/null +++ b/bruno/opds/publication.yml @@ -0,0 +1,24 @@ +info: + name: OPDS Publication + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/opds/devices/{{device_id}}/cover/{{book_uuid}}' + auth: inherit + headers: + - key: Authorization + value: Bearer {{device_token}} +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # OPDS Publication + + `GET /opds/devices/:deviceId/cover/:bookId` — auth: device-token. + + Canonical reference: `docs/developer/api/opds/publication.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/opencollection.yml b/bruno/opencollection.yml index 40b11a68..18fa3f8d 100644 --- a/bruno/opencollection.yml +++ b/bruno/opencollection.yml @@ -21,184 +21,24 @@ request: docs: content: |- - # Bruno OpenCollection YAML API Tests for Bookhoard - This directory contains Bruno OpenCollection YAML format tests for testing the Bookhoard API with comprehensive REST documentation. - Setup - 1. Install Bruno: https://www.usebruno.com/ - 2. Open Bruno and import this collection folder - 3. Select the "localhost" environment - 4. Start the application with `podman-compose up --build` or `docker-compose up --build` - 5. Register/Login first, then use Bearer token for protected endpoints - Available Tests - Authentication (Public & Private) - - **Register User**: POST /api/auth/register - Create new account with role-based restrictions - - **Login User**: POST /api/auth/login - Authenticate (email or username) - - **Refresh Token**: POST /api/auth/refresh - Get new access token - - **Logout**: POST /api/auth/logout - Invalidate refresh token - User Profile Management - - **Get Profile**: GET /api/auth/profile - Get current user info - - **Update Profile**: PUT /api/auth/profile - Update first_name, last_name - - **Update Email**: PUT /api/auth/email - Update email address - - **Update Username**: PUT /api/auth/username - Update username - - **Update Password**: PUT /api/auth/password - Update password - - **Update Theme**: PUT /api/auth/theme - Update theme preference - Admin User Management - - **List Users**: GET /api/auth/users - Get all users with complete info (admin only) - - **Delete Account**: DELETE /api/auth/account - Delete own account or admin deletes other accounts - Libraries (Admin Only) - - **Create Library**: POST /api/libraries - Create new library (Ebooks, Comics, Manga) - - **Get Libraries**: GET /api/libraries - List all libraries (admin) - - **Get Library**: GET /api/libraries/:id - Get library details - - **Update Library**: PUT /api/libraries/:id - Update library settings - - **Delete Library**: DELETE /api/libraries/:id - Remove library - - **Add Library Folder**: POST /api/libraries/:id/folders - Add scanning folder - - **Get Library Folders**: GET /api/libraries/:id/folders - List folders - - **Delete Library Folder**: DELETE /api/libraries/:id/folders/:folder_id - Remove folder - - **Get Library Stats**: GET /api/libraries/:id/stats - Library statistics - - **Get Library Types**: GET /api/libraries/types - Available library types - Media Items (Mixed Access) - - **List Media Items**: GET /api/media-items - Paginated list (filter/sort by library, author, series, etc.) - - **Get Media Item**: GET /api/media-items/:id - Single item details (all users) - - **Create Media Item**: POST /api/media-items - Add new item (admin only) - - **Update Media Item**: PUT /api/media-items/:id - Modify metadata (admin only) - - **Delete Media Item**: DELETE /api/media-items/:id - Remove item (admin only) - - **Filter Media Items**: POST /api/media-items/filter - Advanced filtering - - **EPUB Download**: GET /api/media-items/:id/download - Download EPUB file - - **Cover Image**: GET /api/media-items/:id/cover - Get cover image - Reading Progress (All Users) - - **Get Progress**: GET /api/progress/:media_id - User's reading progress for media item - - **Update Progress**: PUT /api/progress/:media_id - Update reading progress - - **Get Device Progress**: GET /api/progress/device/:device_id - Progress by device - Universal Progress (All Users) - - **Get Universal Progress**: GET /api/universal-progress/:sha256 - Get progress by book hash - - **Update Universal Progress**: PUT /api/universal-progress - Update universal progress - Notes (All Users) - - **Get Notes**: GET /api/notes/:media_id - Get notes for media item - - **Create Note**: POST /api/notes - Add new note - - **Update Note**: PUT /api/notes/:id - Update note content - - **Delete Note**: DELETE /api/notes/:id - Remove note - Highlights (All Users) - - **Get Highlights**: GET /api/highlights/:media_id - Get highlights for media item - - **Create Highlight**: POST /api/highlights - Add new highlight - - **Update Highlight**: PUT /api/highlights/:id - Update highlight - - **Delete Highlight**: DELETE /api/highlights/:id - Remove highlight - Ratings (All Users) - - **Get Rating**: GET /api/media-items/:id/rating - User's rating (returns null if unrated) - - **Create/Update Rating**: POST /api/media-items/:id/rating - Rate media item (1-10 scale, displayed as 1-5 stars with half-star precision). POST upserts; PUT also available. - - **Update Rating**: PUT /api/media-items/:id/rating - Update rating (upsert) - - **Delete Rating**: DELETE /api/media-items/:id/rating - Remove rating - Collections (All Users) - - **List Collections**: GET /api/collections - Get user's collections - - **Get Collection**: GET /api/collections/:id - Collection details with media items - - **Create Collection**: POST /api/collections - Create new collection - - **Update Collection**: PUT /api/collections/:id - Update collection - - **Delete Collection**: DELETE /api/collections/:id - Remove collection - - **Add Auto-Assign Rule**: POST /api/collections/:id/rules - Add automatic rule - - **Remove Auto-Assign Rule**: DELETE /api/collections/:id/rules/:rule_id - Remove rule - - **Test Rule**: POST /api/collections/:id/rules/test - Preview rule matches - - **Bulk Assign**: POST /api/collections/:id/assign - Manually add media items - Device Management (All Users) - - **Register Device**: POST /api/devices/register - Register new device - - **List Devices**: GET /api/devices - Get user's devices - - **Get Device**: GET /api/devices/:id - Device details - - **Delete Device**: DELETE /api/devices/:id - Unregister device - - **Sync Device**: POST /api/devices/:id/sync - Trigger device sync - Sync Protocols (Device Integration) - - **KOReader Sync**: POST /api/sync/koreader - KOReader progress/notes/highlights sync - - **Kobo Sync**: POST /api/sync/kobo - Kobo progress/notes/highlights sync - Scanner (Admin Only) - - **Scan Libraries**: POST /api/scanner/scan - Scan library folders - - **Start Scanner**: POST /api/scanner/start - Start real-time monitoring - - **Stop Scanner**: POST /api/scanner/stop - Stop monitoring - - **Get Scan Settings**: GET /api/scanner/settings - Scan configuration - Analytics (Admin Only) - - **Get Analytics**: GET /api/analytics - Usage statistics and metrics - Book Matching (All Users) - - **Search Books**: GET /api/book-matching/search - Search by ISBN, title, author - - **Link Book**: POST /api/book-matching/link - Link media item to external database - OPDS (All Users) - - **OPDS Feeds**: GET /opds/* - OPDS catalog for e-reader integration - - **OPDS Acquisition**: GET /opds/acquisition/* - Download media items - WebSocket (Real-time) - - **WebSocket**: WS /ws/sync - Real-time sync events (progress, notes, highlights) - Collection Organization - bruno/ - ├── user/ # User authentication and profile - │ ├── auth/ # Login, register, refresh - │ ├── profile/ # Profile management - │ └── admin/ # User administration (admin only) - ├── library/ # Library management - │ ├── Create/Update/Delete Libraries - │ ├── Library Folders - │ ├── Library Stats - │ └── Scan Settings - ├── media-items/ # Media item operations - │ ├── List/Get/Create/Update/Delete - │ ├── Filter and Sort - │ ├── Download EPUB - │ ├── Cover Images - │ └── Ratings - ├── progress/ # Reading progress tracking - ├── universal-progress/ # Cross-device universal progress - ├── notes/ # User notes - ├── highlights/ # Book highlights - ├── collections/ # Smart collections - ├── devices/ # Device registration - ├── sync-koreader/ # KOReader sync protocol - ├── sync-kobo/ # Kobo sync protocol - ├── scanner/ # Library scanning - ├── analytics/ # Usage statistics - ├── books/ # Book matching/linking - ├── kobo/ # Kobo-specific operations - ├── koreader/ # KOReader-specific operations - ├── opds/ # OPDS catalog feeds - └── admin/ # Admin operations - ## Security Features - ### Registration Restrictions - - **First User**: Automatically gets admin role regardless of request - - **Existing Admins**: Only authenticated admins can create new admin accounts - - **Regular Users**: Anyone can create regular user accounts - - **Unauthenticated**: Can only create first admin, not subsequent admins - ### User Management - - **Self-Deletion**: Users can delete their own accounts - - **Admin Override**: Admins can delete any user account - - **Last Admin Protection**: Cannot delete the last admin account in the system - ### Role System - - **Admin**: Full access - manage libraries, media items, users, scanner - - **User**: Read access - view media items, create collections, track progress, rate, annotate - ### Device Authentication - - **No Passwords**: Devices use QR code registration and access tokens - - **User Approval**: Device registration requires user approval via web interface - ### JWT Tokens - - **Access Token**: Valid for 1 hour, sent via Bearer header - - **Refresh Token**: Valid for 7 days, used to get new access tokens - ### Rate Limiting - - **Auth Endpoints**: 10 requests/minute per IP - ### Data Isolation - - **Progress, Notes, Highlights, Ratings**: User-specific - - **Collections**: User-specific (admins see all users' collections) - - **Devices**: User-specific - ## Documentation Features - Each request includes: - - **Detailed descriptions** of functionality - - **Parameter specifications** (required/optional, types) - - **Request/Response examples** - - **Error response codes** and meanings - - **Authentication requirements** - ## Notes - - **Authentication Flow**: Register → Login → Use Bearer token for all other requests - - **Media Items vs Books**: The API uses "media items" (supports ebooks, comics, manga) - - **Library System**: Organized by libraries (Ebooks, Comics, Manga) with scanning folders - - **Universal Progress**: Cross-device sync using SHA-256 book hashes - - **Smart Collections**: Auto-assign rules based on genre, author, series, tags, etc. - - **OPDS Support**: Wireless book delivery to e-readers (Kobo, KOReader) - - **Device Protocols**: Native sync for KOReader and Kobo devices - - **Rating System**: Half-star precision (1-10 scale internally, displayed as 1-5 stars) - - **Admin Setup**: First admin must be created by updating user role in database - - **Variables**: Update collection variables for testing (media_id, library_id, device_id, etc.) - - **Security**: Passwords hashed with bcrypt, unique email/username constraints, role-based access control - - **JSON**: All requests/responses use JSON format - - **WebSocket**: Real-time events for sync updates across devices + # Bookhoard API — executable verification (OpenCollection / Bruno) + + Every request in this collection is GENERATED from the canonical API + docs (docs/developer/api/**). Do not hand-edit request files — edit + the doc and re-run scripts/gen_bruno_from_docs.py --apply. + + Hand-authored exceptions (not generated): + - NewDevDBSetup/ — ordered dev-database bootstrap sequence + - */scenarios/, media-items/filters, media-items/search — edge-case suites + - environments/Bookhoard.yml — variables + + Run order: NewDevDBSetup (RegisterUser captures {{token}}), then any + request. Device-token routes (kobo/koreader/opds) need {{device_token}} + or {{kobo_device_token}} in the environment. + + The docs site (Developer → API) is the single source of truth for + shapes; internal/docs/drift_test.go enforces docs-to-routes and + docs-to-Bruno parity. type: text/markdown bundled: false extensions: {} diff --git a/bruno/progress/Delete Reading Progress.yml b/bruno/progress/Delete Reading Progress.yml deleted file mode 100644 index 8951cae9..00000000 --- a/bruno/progress/Delete Reading Progress.yml +++ /dev/null @@ -1,42 +0,0 @@ -info: - name: Delete Reading Progress - type: http - seq: 1 -http: - method: DELETE - url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' - auth: inherit - headers: - - key: Content-Type - value: application/json -runtime: - scripts: - - type: tests - code: "test_delete_reading_progress_success(status, headers, body) {\n if (status\ - \ !== 200 && status !== 204) {\n throw new Error(\"Expected status 200\ - \ or 204, got \" + status);" - -docs: |- - ## Delete Reading Progress - - Deletes reading progress for a media item. This is a legacy endpoint - consider using universal progress endpoints instead. - - **Method:** DELETE - - **Endpoint:** /api/media-items/:id/progress - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string, required): Media item UUID - - **Response:** Success message or empty - - **Status Codes:** - - 200: Success - - 204: Success (no content) - - 401: Unauthorized - - 404: Media item not found - - 500: Internal server error - - **Note:** This is a legacy endpoint. Use `/api/progress/:id` endpoints for new implementations. diff --git a/bruno/progress/Get Reading Progress.yml b/bruno/progress/Get Reading Progress.yml deleted file mode 100644 index ced0b528..00000000 --- a/bruno/progress/Get Reading Progress.yml +++ /dev/null @@ -1,30 +0,0 @@ -info: - name: Get Reading Progress - type: http - seq: 6 -http: - method: GET - url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' - auth: inherit - body: - type: none - -docs: |- - ## Get Reading Progress - - Retrieves the authenticated user's reading progress for a media item. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Media Item UUID - - **Response:** - - `id` (string): Media Item UUID - - `user_id` (string): User UUID - - `current_page` (number): Current page number - - `total_pages` (number, nullable): Total pages - - `last_read_at` (string): Last read timestamp - - **Error Responses:** - - 401: Invalid authentication diff --git a/bruno/progress/Update Reading Progress.yml b/bruno/progress/Update Reading Progress.yml deleted file mode 100644 index 5f9fbad0..00000000 --- a/bruno/progress/Update Reading Progress.yml +++ /dev/null @@ -1,35 +0,0 @@ -info: - name: Update Reading Progress - type: http - seq: 7 -http: - method: PUT - url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' - auth: inherit - body: - type: json - data: |- - { - "current_page": 45, - "total_pages": 200 - } - -docs: |- - ## Update Reading Progress - - Updates the authenticated user's reading progress for a media item. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Media Item UUID - - **Request Body:** - - `current_page` (number, required): Current page number - - `total_pages` (number, optional): Total pages in book - - **Response:** Updated progress object - - **Error Responses:** - - 401: Invalid authentication - - 400: Invalid request data diff --git a/bruno/progress/delete_media_progress.yml b/bruno/progress/delete_media_progress.yml new file mode 100644 index 00000000..81a46dba --- /dev/null +++ b/bruno/progress/delete_media_progress.yml @@ -0,0 +1,21 @@ +info: + name: Delete Media Item Progress + type: http + seq: 1 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Media Item Progress + + `DELETE /api/media-items/:id/progress` — auth: jwt. + + Canonical reference: `docs/developer/api/progress/delete_media_progress.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/progress/get_media_progress.yml b/bruno/progress/get_media_progress.yml new file mode 100644 index 00000000..abc1aca1 --- /dev/null +++ b/bruno/progress/get_media_progress.yml @@ -0,0 +1,21 @@ +info: + name: Get Media Item Progress + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Media Item Progress + + `GET /api/media-items/:id/progress` — auth: jwt. + + Canonical reference: `docs/developer/api/progress/get_media_progress.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/progress/get_progress_history.yml b/bruno/progress/get_progress_history.yml new file mode 100644 index 00000000..6212c0cb --- /dev/null +++ b/bruno/progress/get_progress_history.yml @@ -0,0 +1,21 @@ +info: + name: Get Progress History + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/progress/{{media_item_id}}/history' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Progress History + + `GET /api/progress/:id/history` — auth: jwt. + + Canonical reference: `docs/developer/api/progress/get_progress_history.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/progress/update_media_progress.yml b/bruno/progress/update_media_progress.yml new file mode 100644 index 00000000..0032cd80 --- /dev/null +++ b/bruno/progress/update_media_progress.yml @@ -0,0 +1,29 @@ +info: + name: Update Media Item Progress + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/progress' + auth: inherit + body: + type: json + data: |- + { + "percentage": 0.0415, + "context_text": "was at war with one of these Powers it was generally at peace with the other. But what was strange w", + "epubcfi": "epubcfi(/6/4!/4/8[_idContainer003]/62/1:456)" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Media Item Progress + + `PUT /api/media-items/:id/progress` — auth: jwt. + + Canonical reference: `docs/developer/api/progress/update_media_progress.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/Clear Device Queue.yml b/bruno/queue/Clear Device Queue.yml deleted file mode 100644 index eb442a0d..00000000 --- a/bruno/queue/Clear Device Queue.yml +++ /dev/null @@ -1,39 +0,0 @@ -info: - name: Clear Device Queue - type: http - seq: 6 -http: - method: DELETE - url: '{{base_url}}/api/queue/devices/{{device_id}}/clear' - auth: inherit - body: - type: none - -docs: |- - ## Clear Device Queue - - Clears all queue items for a specific device. - - **Method:** DELETE - - **Endpoint:** /api/queue/devices/:device_id/clear - - **Authentication:** Bearer token - - **Path Parameters:** - - `device_id` (string): Device UUID - - **Response:** - - Success message with count of cleared items - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device not found - - **Example Response:** - ```json - { - "message": "Queue cleared", - "device_id": "uuid", - "cleared_count": 10 diff --git a/bruno/queue/Delete Queue Item.yml b/bruno/queue/Delete Queue Item.yml deleted file mode 100644 index 9f90c024..00000000 --- a/bruno/queue/Delete Queue Item.yml +++ /dev/null @@ -1,38 +0,0 @@ -info: - name: Delete Queue Item - type: http - seq: 5 -http: - method: DELETE - url: '{{base_url}}/api/queue/items/{{item_id}}' - auth: inherit - body: - type: none - -docs: |- - ## Delete Queue Item - - Deletes a queue item from the sync queue. - - **Method:** DELETE - - **Endpoint:** /api/queue/items/:item_id - - **Authentication:** Bearer token - - **Path Parameters:** - - `item_id` (string): Queue item UUID - - **Response:** - - Success message confirming deletion - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Queue item not found - - **Example Response:** - ```json - { - "message": "Queue item deleted", - "item_id": "uuid" diff --git a/bruno/queue/List All Queue Items (Admin).yml b/bruno/queue/List All Queue Items (Admin).yml deleted file mode 100644 index b0d31387..00000000 --- a/bruno/queue/List All Queue Items (Admin).yml +++ /dev/null @@ -1,39 +0,0 @@ -info: - name: List All Queue Items (Admin) - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/queue/items' - auth: inherit - body: - type: none - -docs: |- - ## List All Queue Items (Admin) - - Retrieves all queue items across all devices (admin only). - - **Method:** GET - - **Endpoint:** /api/queue/items - - **Authentication:** Bearer token (admin role required) - - **Response:** - - Array of queue items with device and status information - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden - admin role required - - **Example Response:** - ```json - [ - { - "id": "uuid", - "device_id": "uuid", - "item_type": "progress", - "status": "pending", - "created_at": "2024-01-01T00:00:00Z" diff --git a/bruno/queue/List Device Queue Items.yml b/bruno/queue/List Device Queue Items.yml deleted file mode 100644 index a8580b26..00000000 --- a/bruno/queue/List Device Queue Items.yml +++ /dev/null @@ -1,42 +0,0 @@ -info: - name: List Device Queue Items - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/queue/devices/{{device_id}}/items' - auth: inherit - body: - type: none - -docs: |- - ## List Device Queue Items - - Retrieves all queue items for a specific device. - - **Method:** GET - - **Endpoint:** /api/queue/devices/:device_id/items - - **Authentication:** Bearer token - - **Path Parameters:** - - `device_id` (string): Device UUID - - **Response:** - - Array of queue items for the specified device - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Device not found - - **Example Response:** - ```json - [ - { - "id": "uuid", - "device_id": "uuid", - "item_type": "progress", - "status": "pending", - "data": { diff --git a/bruno/queue/List Queue Items (Pagination).yml b/bruno/queue/List Queue Items (Pagination).yml deleted file mode 100644 index cbd9bf13..00000000 --- a/bruno/queue/List Queue Items (Pagination).yml +++ /dev/null @@ -1,53 +0,0 @@ -info: - name: List Queue Items (With Pagination) - type: http - seq: 2 -http: - method: GET - url: '{{base_url}}/api/queue/items?limit=50&offset=0' - auth: inherit - body: - type: none -runtime: - scripts: - - type: tests - code: "test(\"status must be 200\", function() {\n expect(res.status).to.eql(200);" - -docs: |- - List items in the sync queue with pagination. - - **Endpoint**: GET /queue/items - **Auth**: Required (Bearer token) - - ## Query Parameters - - | Parameter | Type | Required | Description | - |-----------|------|-----------|-------------| - | limit | int | No | Items per page | - | offset | int | No | Pagination offset | - - ## Response Fields - - | Field | Type | Description | - |-------|------|-------------| - | items | array | List of queue items | - | total | int | Total number of items | - | page | int | Current page number | - | per_page | int | Items per page | - - ## Example Request - - ``` - GET /queue/items?limit=50&offset=0 - ``` - - ## Error Responses - - | Code | Description | - |------|-------------| - | 401 | Unauthorized | - | 500 | Internal server error | - - ## Notes - - - Supports pagination for large queue lists diff --git a/bruno/queue/Retry Queue Item.yml b/bruno/queue/Retry Queue Item.yml deleted file mode 100644 index e472f148..00000000 --- a/bruno/queue/Retry Queue Item.yml +++ /dev/null @@ -1,39 +0,0 @@ -info: - name: Retry Queue Item - type: http - seq: 4 -http: - method: POST - url: '{{base_url}}/api/queue/items/{{item_id}}/retry' - auth: inherit - body: - type: none - -docs: |- - ## Retry Queue Item - - Retries a failed queue item. - - **Method:** POST - - **Endpoint:** /api/queue/items/:item_id/retry - - **Authentication:** Bearer token - - **Path Parameters:** - - `item_id` (string): Queue item UUID - - **Response:** - - Success message indicating retry initiated - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 404: Queue item not found - - 400: Invalid item status - - **Example Response:** - ```json - { - "message": "Queue item retry initiated", - "item_id": "uuid" diff --git a/bruno/queue/clear_device_queue.yml b/bruno/queue/clear_device_queue.yml new file mode 100644 index 00000000..138567b7 --- /dev/null +++ b/bruno/queue/clear_device_queue.yml @@ -0,0 +1,21 @@ +info: + name: Clear Device Queue + type: http + seq: 1 + +http: + method: DELETE + url: '{{base_url}}/api/queue/devices/{{device_id}}/clear' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Clear Device Queue + + `DELETE /api/queue/devices/:device_id/clear` — auth: jwt. + + Canonical reference: `docs/developer/api/queue/clear_device_queue.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/delete_queue_item.yml b/bruno/queue/delete_queue_item.yml new file mode 100644 index 00000000..50d6e5d2 --- /dev/null +++ b/bruno/queue/delete_queue_item.yml @@ -0,0 +1,21 @@ +info: + name: Delete Queue Item + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/queue/items/{{item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Queue Item + + `DELETE /api/queue/items/:item_id` — auth: jwt. + + Canonical reference: `docs/developer/api/queue/delete_queue_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/get_device_queue_stats.yml b/bruno/queue/get_device_queue_stats.yml new file mode 100644 index 00000000..a1f85458 --- /dev/null +++ b/bruno/queue/get_device_queue_stats.yml @@ -0,0 +1,21 @@ +info: + name: Get Device Queue Statistics + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/queue/devices/{{device_id}}/stats' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Device Queue Statistics + + `GET /api/queue/devices/:device_id/stats` — auth: jwt. + + Canonical reference: `docs/developer/api/queue/get_device_queue_stats.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/list_all_queue_items.yml b/bruno/queue/list_all_queue_items.yml new file mode 100644 index 00000000..927f6f69 --- /dev/null +++ b/bruno/queue/list_all_queue_items.yml @@ -0,0 +1,21 @@ +info: + name: List All Queue Items (Admin) + type: http + seq: 4 + +http: + method: GET + url: '{{base_url}}/api/queue/items' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List All Queue Items (Admin) + + `GET /api/queue/items` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/queue/list_all_queue_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/list_device_queue_items.yml b/bruno/queue/list_device_queue_items.yml new file mode 100644 index 00000000..d47d0a27 --- /dev/null +++ b/bruno/queue/list_device_queue_items.yml @@ -0,0 +1,21 @@ +info: + name: List Device Queue Items + type: http + seq: 5 + +http: + method: GET + url: '{{base_url}}/api/queue/devices/{{device_id}}/items' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Device Queue Items + + `GET /api/queue/devices/:device_id/items` — auth: jwt. + + Canonical reference: `docs/developer/api/queue/list_device_queue_items.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/queue/retry_queue_item.yml b/bruno/queue/retry_queue_item.yml new file mode 100644 index 00000000..406a3ca1 --- /dev/null +++ b/bruno/queue/retry_queue_item.yml @@ -0,0 +1,21 @@ +info: + name: Retry Queue Item + type: http + seq: 6 + +http: + method: POST + url: '{{base_url}}/api/queue/items/{{item_id}}/retry' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Retry Queue Item + + `POST /api/queue/items/:item_id/retry` — auth: jwt. + + Canonical reference: `docs/developer/api/queue/retry_queue_item.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/ratings/create_rating.yml b/bruno/ratings/create_rating.yml new file mode 100644 index 00000000..a49443a4 --- /dev/null +++ b/bruno/ratings/create_rating.yml @@ -0,0 +1,27 @@ +info: + name: Create Rating + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/media-items/{{media_item_id}}/rating' + auth: inherit + body: + type: json + data: |- + { + "rating": 8 + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Rating + + `POST /api/media-items/:id/rating` — auth: jwt. + + Canonical reference: `docs/developer/api/ratings/create_rating.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/ratings/delete_rating.yml b/bruno/ratings/delete_rating.yml new file mode 100644 index 00000000..0385f1ce --- /dev/null +++ b/bruno/ratings/delete_rating.yml @@ -0,0 +1,21 @@ +info: + name: Delete Rating + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/media-items/{{media_item_id}}/rating' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Rating + + `DELETE /api/media-items/:id/rating` — auth: jwt. + + Canonical reference: `docs/developer/api/ratings/delete_rating.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/ratings/get_ratings.yml b/bruno/ratings/get_ratings.yml new file mode 100644 index 00000000..7cfc5c70 --- /dev/null +++ b/bruno/ratings/get_ratings.yml @@ -0,0 +1,21 @@ +info: + name: Get Rating + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/media-items/{{media_item_id}}/rating' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Rating + + `GET /api/media-items/:id/rating` — auth: jwt. + + Canonical reference: `docs/developer/api/ratings/get_ratings.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/ratings/update_rating.yml b/bruno/ratings/update_rating.yml new file mode 100644 index 00000000..cd5300a1 --- /dev/null +++ b/bruno/ratings/update_rating.yml @@ -0,0 +1,28 @@ +info: + name: Update Rating + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/media-items/{{media_item_id}}/rating' + auth: inherit + body: + type: json + data: |- + { + "rating": 8, + "review": "Great book! Very enjoyable read." + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Rating + + `PUT /api/media-items/:id/rating` — auth: jwt. + + Canonical reference: `docs/developer/api/ratings/update_rating.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/saved-filters/Create Saved Filter.yml b/bruno/saved-filters/Create Saved Filter.yml deleted file mode 100644 index 1d7612b5..00000000 --- a/bruno/saved-filters/Create Saved Filter.yml +++ /dev/null @@ -1,14 +0,0 @@ -info: - name: Create Saved Filter - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/saved-filters' - auth: inherit - body: - type: json - data: "{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\",\n \"author_filter\": \"\"\n }\n}" -docs: "## Create Saved Filter\n\nCreates a new saved filter for the authenticated user.\n\n**Method:** POST\n\n**Endpoint:** /api/saved-filters\n\n**Authentication:** Required (Bearer token)\n\n**Request Body:**\n```json\n{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\",\n \"author_filter\": \"\",\n \"series_filter\": \"\"\n }\n}\n```\n\n**Required Fields:**\n- `name` (string, required): Filter name\n - Must be unique per user + resource type combination\n - Maximum 100 characters\n - Cannot be empty\n- `resource_type` (string, required): Type of resource to filter\n - Examples: \"media-items\", \"collections\", \"devices\"\n - Must match valid resource types\n- `filters` (object, required): Key-value pairs of filter criteria\n - Flexible structure - any valid filter fields\n - Serialized as JSONB in database\n\n**Common Filter Fields for media-items:**\n- `search`:\ - \ General search term\n- `author_filter`: Filter by author name\n- `genre_filter`: Filter by genre\n- `series_filter`: Filter by series\n- `language_filter`: Filter by language\n- `year_min`: Minimum copyright year\n- `year_max`: Maximum copyright year\n- `has_cover`: Boolean for cover image\n- `sort`: Sort order (e.g., \"title ASC\", \"created_at DESC\")\n\n**Response:**\n```json\n{\n \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\"\n },\n \"created_at\": \"2024-03-20T12:00:00Z\",\n \"updated_at\": \"2024-03-20T12:00:00Z\"\n}\n```\n\n**Status Codes:**\n- 201: Created - Filter successfully created\n- 400: Bad Request - Invalid request body or missing required fields\n- 401: Unauthorized - Invalid or missing authentication token\n- 409: Conflict - Filter with this name already exists for this resource type\n\n**Error Response\ - \ Examples:**\n\nDuplicate name error:\n```json\n{\n \"error\": \"filter with name 'My Sci-Fi Books' already exists for this resource type\"\n}\n```\n\n**Example Usage:**\n```bash\ncurl -X POST \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\"\n }\n }' \\\n \"{{base_url}}/api/saved-filters\"\n```\n\n**Validation Rules:**\n- Name must be unique per user + resource type\n- Name cannot be empty or whitespace only\n- Resource type must be valid\n- Filters object must be provided (can be empty object {})" diff --git a/bruno/saved-filters/Delete Saved Filter.yml b/bruno/saved-filters/Delete Saved Filter.yml deleted file mode 100644 index 9051c465..00000000 --- a/bruno/saved-filters/Delete Saved Filter.yml +++ /dev/null @@ -1,95 +0,0 @@ -info: - name: Delete Saved Filter - type: http - seq: 4 -http: - method: DELETE - url: '{{base_url}}/api/saved-filters/{{filter_id}}' - auth: inherit - -docs: |- - ## Delete Saved Filter - - Permanently deletes a saved filter. - - **Method:** DELETE - - **Endpoint:** /api/saved-filters/{filter_id} - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `filter_id` (string, required): UUID of the filter to delete - - Must be a valid UUID - - Must belong to the authenticated user - - **Response:** - - 204 No Content (success) - - Empty response body - - **Status Codes:** - - 204: No Content - Filter deleted successfully - - 400: Bad Request - Invalid filter ID format - - 401: Unauthorized - Invalid or missing authentication token - - 404: Not Found - Filter doesn't exist or doesn't belong to user - - 500: Internal Server Error - Database error - - **Example Usage:** - ```bash - curl -X DELETE \ - -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters/{{filter_id}}" - ``` - - **Important Notes:** - - You can only delete filters that belong to you - - Deletion is permanent - cannot be undone - - Returns 204 No Content on success (no response body) - - If filter doesn't exist or belongs to another user, returns 404 - - **Error Response Examples:** - - Invalid filter ID: - ```json - { - "error": "invalid filter ID" - } - ``` - - Filter not found: - ```json - { - "error": "failed to delete filter" - } - ``` - - **Testing Deletion:** - ```bash - # First, create a filter - CREATE_RESPONSE=$(curl -s -X POST \ - -H "Authorization: Bearer YOUR_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "To Be Deleted", - "resource_type": "media-items", - "filters": {} - }' \ - "{{base_url}}/api/saved-filters") - - # Extract the ID - FILTER_ID=$(echo $CREATE_RESPONSE | jq -r '.id') - - # Delete the filter - curl -X DELETE \ - -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters/$FILTER_ID" - - # Verify it's gone - curl -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters?resource_type=media-items" - ``` - - **Cascade Effects:** - - No cascade effects - saved filters are standalone records - - User deletion automatically cascades to delete their filters - - No foreign key dependencies on other tables diff --git a/bruno/saved-filters/Get Saved Filter By ID.yml b/bruno/saved-filters/Get Saved Filter By ID.yml deleted file mode 100644 index b6d4eaca..00000000 --- a/bruno/saved-filters/Get Saved Filter By ID.yml +++ /dev/null @@ -1,64 +0,0 @@ -info: - name: Get Saved Filter By ID - type: http - seq: 4 -http: - method: GET - url: "{{base_url}}/api/saved-filters/{{filter_id}}" - auth: inherit - -docs: |- - ## Get Saved Filter By ID - - Retrieves a single saved filter by its ID. - - **Method:** GET - - **Endpoint:** /api/saved-filters/:id - - **Authentication:** Required (Bearer token) - - **URL Parameters:** - - `id` (UUID, required): The unique identifier of the saved filter - - Example: "550e8400-e29b-41d4-a716-446655440000" - - Must be a valid UUID format - - **Response:** - Single saved filter object: - ```json - { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "My Sci-Fi Books", - "resource_type": "media-items", - "filters": { - "genre_filter": "Science Fiction", - "sort": "title ASC" - }, - "created_at": "2024-03-20T12:00:00Z", - "updated_at": "2024-03-20T12:00:00Z" - } - ``` - - **Status Codes:** - - 200: Success - Returns the saved filter - - 400: Bad Request - Invalid filter ID format - - 401: Unauthorized - Invalid or missing authentication token - - 404: Not Found - Filter doesn't exist or doesn't belong to user - - **Example Usage:** - ```bash - # Get a specific saved filter - curl -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters/550e8400-e29b-41d4-a716-446655440000" - ``` - - **Notes:** - - Filters are user-specific (ownership verified via JWT) - - Returns 404 if filter doesn't exist OR doesn't belong to authenticated user - - Use `GET /api/saved-filters?resource_type=X` to list all filters first - - **Scenarios:** - - Get filter by ID from list response - - Load filter details for editing - - Verify filter exists before updating - - Mobile app on-demand filter loading diff --git a/bruno/saved-filters/List Saved Filters.yml b/bruno/saved-filters/List Saved Filters.yml deleted file mode 100644 index 3a31f6c4..00000000 --- a/bruno/saved-filters/List Saved Filters.yml +++ /dev/null @@ -1,63 +0,0 @@ -info: - name: List Saved Filters - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/saved-filters?resource_type=media-items' - auth: inherit - -docs: |- - ## List Saved Filters - - Retrieves all saved filters for the authenticated user and a specific resource type. - - **Method:** GET - - **Endpoint:** /api/saved-filters - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `resource_type` (string, required): Filter by resource type - - Examples: "media-items", "collections", "devices" - - Required parameter - must be provided - - **Response:** - Array of saved filter objects: - ```json - [ - { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "My Sci-Fi Books", - "resource_type": "media-items", - "filters": { - "genre_filter": "Science Fiction", - "sort": "title ASC" - }, - "created_at": "2024-03-20T12:00:00Z", - "updated_at": "2024-03-20T12:00:00Z" - } - ] - ``` - - **Status Codes:** - - 200: Success - Returns array of saved filters - - 400: Bad Request - Missing resource_type parameter - - 401: Unauthorized - Invalid or missing authentication token - - **Example Usage:** - ```bash - # Get all saved filters for media items - curl -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters?resource_type=media-items" - - # Get all saved filters for collections - curl -H "Authorization: Bearer YOUR_TOKEN" \ - "{{base_url}}/api/saved-filters?resource_type=collections" - ``` - - **Notes:** - - Filters are specific to each user (user-scoped via JWT) - - Returns empty array [] if no filters exist for the resource type - - Results ordered by created_at DESC (newest first) diff --git a/bruno/saved-filters/Update Saved Filter.yml b/bruno/saved-filters/Update Saved Filter.yml deleted file mode 100644 index ae606fc3..00000000 --- a/bruno/saved-filters/Update Saved Filter.yml +++ /dev/null @@ -1,14 +0,0 @@ -info: - name: Update Saved Filter - type: http - seq: 3 -http: - method: PUT - url: '{{base_url}}/api/saved-filters/{{filter_id}}' - auth: inherit - body: - type: json - data: "{\n \"name\": \"Updated Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction Fantasy\",\n \"sort\": \"author ASC\",\n \"year_min\": \"2000\"\n }\n }" -docs: "## Update Saved Filter\n\nUpdates an existing saved filter's name and/or filter criteria.\n\n**Method:** PUT\n\n**Endpoint:** /api/saved-filters/{filter_id}\n\n**Authentication:** Required (Bearer token)\n\n**Path Parameters:**\n- `filter_id` (string, required): UUID of the filter to update\n - Must be a valid UUID\n - Must belong to the authenticated user\n\n**Request Body:**\n```json\n{\n \"name\": \"Updated Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction Fantasy\",\n \"sort\": \"author ASC\",\n \"year_min\": \"2000\",\n \"year_max\": \"2024\"\n }\n}\n```\n\n**Required Fields:**\n- `name` (string, required): New filter name\n - Must be unique per user + resource type (excluding current filter)\n - Maximum 100 characters\n- `resource_type` (string, required): Resource type (must match existing)\n - Cannot change resource type after creation\n- `filters` (object, required): Updated filter criteria\n\ - \ - Replaces existing filters entirely\n\n**Response:**\n```json\n{\n \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"name\": \"Updated Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction Fantasy\",\n \"sort\": \"author ASC\",\n \"year_min\": \"2000\",\n \"year_max\": \"2024\"\n },\n \"created_at\": \"2024-03-20T12:00:00Z\",\n \"updated_at\": \"2024-03-20T15:30:00Z\"\n}\n```\n\n**Status Codes:**\n- 200: Success - Filter updated successfully\n- 400: Bad Request - Invalid request body or filter ID\n- 401: Unauthorized - Invalid or missing authentication token\n- 404: Not Found - Filter doesn't exist or doesn't belong to user\n- 409: Conflict - New name conflicts with existing filter\n\n**Error Response Examples:**\n\nFilter not found:\n```json\n{\n \"error\": \"filter not found or access denied\"\n}\n```\n\nDuplicate name:\n```json\n{\n \"error\": \"filter with name 'Updated Sci-Fi Books' already exists\ - \ for this resource type\"\n}\n```\n\n**Example Usage:**\n```bash\ncurl -X PUT \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Updated Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction Fantasy\",\n \"sort\": \"author ASC\",\n \"year_min\": \"2000\"\n }\n }' \\\n \"{{base_url}}/api/saved-filters/{{filter_id}}\"\n```\n\n**Important Notes:**\n- You can only update filters that belong to you\n- The `updated_at` timestamp is automatically updated\n- Cannot change the resource_type (it's immutable)\n- New name must not conflict with other filters you own for the same resource type\n- All filter fields are replaced entirely (partial updates not supported)" diff --git a/bruno/saved-filters/create_saved_filter.yml b/bruno/saved-filters/create_saved_filter.yml new file mode 100644 index 00000000..6fe608a0 --- /dev/null +++ b/bruno/saved-filters/create_saved_filter.yml @@ -0,0 +1,29 @@ +info: + name: Create Saved Filter + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/saved-filters' + auth: inherit + body: + type: json + data: |- + { + "name": "My Filter", + "resource_type": "media-items", + "filters": {} + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Create Saved Filter + + `POST /api/saved-filters` — auth: jwt. + + Canonical reference: `docs/developer/api/saved-filters/create_saved_filter.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/saved-filters/delete_saved_filter.yml b/bruno/saved-filters/delete_saved_filter.yml new file mode 100644 index 00000000..6baccd16 --- /dev/null +++ b/bruno/saved-filters/delete_saved_filter.yml @@ -0,0 +1,21 @@ +info: + name: Delete Saved Filter + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/saved-filters/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete Saved Filter + + `DELETE /api/saved-filters/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/saved-filters/delete_saved_filter.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/saved-filters/get_saved_filter.yml b/bruno/saved-filters/get_saved_filter.yml new file mode 100644 index 00000000..6002be68 --- /dev/null +++ b/bruno/saved-filters/get_saved_filter.yml @@ -0,0 +1,21 @@ +info: + name: Get Saved Filter + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/saved-filters/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Saved Filter + + `GET /api/saved-filters/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/saved-filters/get_saved_filter.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/saved-filters/list_saved_filters.yml b/bruno/saved-filters/list_saved_filters.yml new file mode 100644 index 00000000..0ece9fa8 --- /dev/null +++ b/bruno/saved-filters/list_saved_filters.yml @@ -0,0 +1,21 @@ +info: + name: List Saved Filters + type: http + seq: 4 + +http: + method: GET + url: '{{base_url}}/api/saved-filters' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Saved Filters + + `GET /api/saved-filters` — auth: jwt. + + Canonical reference: `docs/developer/api/saved-filters/list_saved_filters.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/saved-filters/update_saved_filter.yml b/bruno/saved-filters/update_saved_filter.yml new file mode 100644 index 00000000..c0d771a8 --- /dev/null +++ b/bruno/saved-filters/update_saved_filter.yml @@ -0,0 +1,29 @@ +info: + name: Update Saved Filter + type: http + seq: 5 + +http: + method: PUT + url: '{{base_url}}/api/saved-filters/{{media_item_id}}' + auth: inherit + body: + type: json + data: |- + { + "name": "Renamed Filter", + "resource_type": "media-items", + "filters": {} + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Saved Filter + + `PUT /api/saved-filters/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/saved-filters/update_saved_filter.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/Get Scan Status.yml b/bruno/scanner/Get Scan Status.yml deleted file mode 100644 index d1f49f01..00000000 --- a/bruno/scanner/Get Scan Status.yml +++ /dev/null @@ -1,33 +0,0 @@ -info: - name: Get Scan Status - type: http - seq: 4 -http: - method: GET - url: '{{base_url}}/api/scanner/status/{{job_id}}' - auth: inherit - -docs: |- - ## Get Scan Status - - Retrieves the status and progress of an asynchronous scan job. - - **Method:** GET - - **Endpoint:** /api/scanner/status/:jobId - - **Authentication:** Required (Bearer token, Admin only) - - **URL Parameters:** - - `jobId` (string, required): The job ID returned from the scan endpoint - - Example: `550e8400-e29b-41d4-a716-446655440000` - - **Response:** - ```json - { - "job_id": "550e8400-e29b-41d4-a716-446655440000", - "status": "completed", - "error": "", - "result": { - "message": "scan completed", - "library_id": "550e8400-e29b-41d4-a716-446655440000" diff --git a/bruno/scanner/Get Watch Mode Status.yml b/bruno/scanner/Get Watch Mode Status.yml deleted file mode 100644 index 06d3f0f3..00000000 --- a/bruno/scanner/Get Watch Mode Status.yml +++ /dev/null @@ -1,28 +0,0 @@ -info: - name: Get Watch Mode Status - type: http - seq: 7 -http: - method: GET - url: '{{base_url}}/api/scanner/watch/status' - auth: inherit - -docs: |- - ## Get Watch Mode Status - - Retrieves the current status of watch mode for all libraries. - - **Method:** GET - - **Endpoint:** /api/scanner/watch/status - - **Authentication:** Required (Bearer token, Admin only) - - **Response:** HTTP 200 (OK) - ```json - { - "watching_libraries": [ - "550e8400-e29b-41d4-a716-446655440000", - "660e8400-e29b-41d4-a716-446655440001" - ], - "total_watching": 2 diff --git a/bruno/scanner/Scan Media Items.yml b/bruno/scanner/Scan Media Items.yml deleted file mode 100644 index 974a1290..00000000 --- a/bruno/scanner/Scan Media Items.yml +++ /dev/null @@ -1,74 +0,0 @@ -info: - name: Scan Media Items (Background) - type: http - seq: 1 - -http: - method: POST - url: "{{base_url}}/api/scanner/scan" - headers: - - name: Content-Type - value: application/json - params: - - name: "" - value: "" - type: query - body: - type: json - data: |- - { - "library_id": "{{library_id}}", - "force": true - } - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const job_id = responseBody.job_id; - - if (job_id) { - bru.setEnvVar("job_id", job_id, { persist: true }); - console.log("Access job_id saved:", job_id); - } - - if (!job_id) { - console.log("No job_ids found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Scan Media Items (Background) - - Triggers an asynchronous media items scanning operation. The scan runs in the background and can be monitored using the job ID. - - **Method:** POST - - **Endpoint:** /api/scanner/scan - - **Authentication:** Required (Bearer token, Admin only) - - **Request Body:** - - `folder_paths` (array of strings, required): List of folder paths to scan - - Example: `["/path/to/media", "/another/path"]` - - **Response:** HTTP 202 (Accepted) - ```json - { - "message": "scan job enqueued", - "job_id": "550e8400-e29b-41d4-a716-446655440000", - "status": "pending" diff --git a/bruno/scanner/Start Scanner.yml b/bruno/scanner/Start Scanner.yml deleted file mode 100644 index 153bf4ea..00000000 --- a/bruno/scanner/Start Scanner.yml +++ /dev/null @@ -1,32 +0,0 @@ -info: - name: Start Scanner - type: http - seq: 2 -http: - method: POST - url: '{{base_url}}/api/scanner/start' - auth: inherit - -docs: |- - ## Start Scanner - - Starts the media scanner service for indexing library content. - - **Method:** POST - - **Endpoint:** /api/scanner/start - - **Authentication:** Required (Bearer token) - - **Response:** - - JSON object containing scanner status - - `status` (string): Scanner state ("started", "running") - - `message` (string): Status message - - `started_at` (string): Timestamp when scanner started - - **Status Codes:** - - 200: Scanner started successfully - - 401: Unauthorized - - 403: Forbidden (insufficient permissions) - - 409: Scanner already running - - 500: Internal server error diff --git a/bruno/scanner/Start Watch Mode.yml b/bruno/scanner/Start Watch Mode.yml deleted file mode 100644 index ffa3e937..00000000 --- a/bruno/scanner/Start Watch Mode.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Start Watch Mode - type: http - seq: 5 -http: - method: POST - url: '{{base_url}}/api/scanner/watch/start' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id" -docs: "## Start Watch Mode\n \n Starts real-time file system monitoring for a specific library. New media items will be detected and processed almost instantly.\n \n **Method:** POST\n \n **Endpoint:** /api/scanner/watch/start\n \n **Authentication:** Required (Bearer token, Admin only)\n \n **Request Body:**\n - `library_id` (string, required): UUID of the library to watch\n - Example: `\"550e8400-e29b-41d4-a716-446655440000\"`\n \n **Response:** HTTP 200 (OK)\n ```json\n {\n \"message\": \"watch mode started for library\",\n \"library_id\": \"550e8400-e29b-41d4-a716-446655440000\"" diff --git a/bruno/scanner/Stop Scanner.yml b/bruno/scanner/Stop Scanner.yml deleted file mode 100644 index 6f736b58..00000000 --- a/bruno/scanner/Stop Scanner.yml +++ /dev/null @@ -1,32 +0,0 @@ -info: - name: Stop Scanner - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/scanner/stop' - auth: inherit - -docs: |- - ## Stop Scanner - - Stops the currently running media scanner service. - - **Method:** POST - - **Endpoint:** /api/scanner/stop - - **Authentication:** Required (Bearer token) - - **Response:** - - JSON object containing scanner status - - `status` (string): Scanner state ("stopped", "idle") - - `message` (string): Status message - - `stopped_at` (string): Timestamp when scanner stopped - - **Status Codes:** - - 200: Scanner stopped successfully - - 401: Unauthorized - - 403: Forbidden (insufficient permissions) - - 409: Scanner not running - - 500: Internal server error diff --git a/bruno/scanner/Stop Watch Mode.yml b/bruno/scanner/Stop Watch Mode.yml deleted file mode 100644 index 59c3d35c..00000000 --- a/bruno/scanner/Stop Watch Mode.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Stop Watch Mode - type: http - seq: 6 -http: - method: POST - url: '{{base_url}}/api/scanner/watch/stop' - auth: inherit - body: - type: json - data: "{\n \"library_id\": \"{{library_id" -docs: "## Stop Watch Mode\n \n Stops real-time file system monitoring for a specific library.\n \n **Method:** POST\n \n **Endpoint:** /api/scanner/watch/stop\n \n **Authentication:** Required (Bearer token, Admin only)\n \n **Request Body:**\n - `library_id` (string, required): UUID of the library to stop watching\n - Example: `\"550e8400-e29b-41d4-a716-446655440000\"`\n \n **Response:** HTTP 200 (OK)\n ```json\n {\n \"message\": \"watch mode stopped for library\",\n \"library_id\": \"550e8400-e29b-41d4-a716-446655440000\"" diff --git a/bruno/scanner/cancel_scan.yml b/bruno/scanner/cancel_scan.yml new file mode 100644 index 00000000..dbb8c07f --- /dev/null +++ b/bruno/scanner/cancel_scan.yml @@ -0,0 +1,21 @@ +info: + name: Cancel Scan + type: http + seq: 1 + +http: + method: POST + url: '{{base_url}}/api/scanner/cancel/{{job_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Cancel Scan + + `POST /api/scanner/cancel/:jobId` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/cancel_scan.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/get_active_scans.yml b/bruno/scanner/get_active_scans.yml new file mode 100644 index 00000000..7da86642 --- /dev/null +++ b/bruno/scanner/get_active_scans.yml @@ -0,0 +1,21 @@ +info: + name: Get Active Scans + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/scanner/active' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Active Scans + + `GET /api/scanner/active` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/get_active_scans.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/get_scan_status.yml b/bruno/scanner/get_scan_status.yml new file mode 100644 index 00000000..0e3013e7 --- /dev/null +++ b/bruno/scanner/get_scan_status.yml @@ -0,0 +1,21 @@ +info: + name: Get Scan Status + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/scanner/status/{{job_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Scan Status + + `GET /api/scanner/status/:jobId` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/get_scan_status.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/get_scan_summary.yml b/bruno/scanner/get_scan_summary.yml new file mode 100644 index 00000000..ccfe763b --- /dev/null +++ b/bruno/scanner/get_scan_summary.yml @@ -0,0 +1,21 @@ +info: + name: Get Scan Summary + type: http + seq: 4 + +http: + method: GET + url: '{{base_url}}/api/scanner/active-summary' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Scan Summary + + `GET /api/scanner/active-summary` — auth: jwt. + + Canonical reference: `docs/developer/api/scanner/get_scan_summary.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/get_watch_mode_status.yml b/bruno/scanner/get_watch_mode_status.yml new file mode 100644 index 00000000..c286701f --- /dev/null +++ b/bruno/scanner/get_watch_mode_status.yml @@ -0,0 +1,21 @@ +info: + name: Get Watch Mode Status + type: http + seq: 5 + +http: + method: GET + url: '{{base_url}}/api/scanner/watch/status' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Watch Mode Status + + `GET /api/scanner/watch/status` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/get_watch_mode_status.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/scan_library.yml b/bruno/scanner/scan_library.yml new file mode 100644 index 00000000..487bc095 --- /dev/null +++ b/bruno/scanner/scan_library.yml @@ -0,0 +1,28 @@ +info: + name: Scan Library + type: http + seq: 6 + +http: + method: POST + url: '{{base_url}}/api/scanner/scan' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "force": false + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Scan Library + + `POST /api/scanner/scan` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/scan_library.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/start_scanner.yml b/bruno/scanner/start_scanner.yml new file mode 100644 index 00000000..73658cf5 --- /dev/null +++ b/bruno/scanner/start_scanner.yml @@ -0,0 +1,27 @@ +info: + name: Start Scanner + type: http + seq: 7 + +http: + method: POST + url: '{{base_url}}/api/scanner/start' + auth: inherit + body: + type: json + data: |- + { + "folder_paths": ["/data/books"] + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Start Scanner + + `POST /api/scanner/start` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/start_scanner.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/start_watch_mode.yml b/bruno/scanner/start_watch_mode.yml new file mode 100644 index 00000000..b398ef2c --- /dev/null +++ b/bruno/scanner/start_watch_mode.yml @@ -0,0 +1,27 @@ +info: + name: Start Watch Mode + type: http + seq: 8 + +http: + method: POST + url: '{{base_url}}/api/scanner/watch/start' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Start Watch Mode + + `POST /api/scanner/watch/start` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/start_watch_mode.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/stop_scanner.yml b/bruno/scanner/stop_scanner.yml new file mode 100644 index 00000000..985374fd --- /dev/null +++ b/bruno/scanner/stop_scanner.yml @@ -0,0 +1,21 @@ +info: + name: Stop Scanner + type: http + seq: 9 + +http: + method: POST + url: '{{base_url}}/api/scanner/stop' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Stop Scanner + + `POST /api/scanner/stop` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/stop_scanner.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/scanner/stop_watch_mode.yml b/bruno/scanner/stop_watch_mode.yml new file mode 100644 index 00000000..d9a5b80d --- /dev/null +++ b/bruno/scanner/stop_watch_mode.yml @@ -0,0 +1,27 @@ +info: + name: Stop Watch Mode + type: http + seq: 10 + +http: + method: POST + url: '{{base_url}}/api/scanner/watch/stop' + auth: inherit + body: + type: json + data: |- + { + "library_id": "550e8400-e29b-41d4-a716-446655440000" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Stop Watch Mode + + `POST /api/scanner/watch/stop` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/scanner/stop_watch_mode.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/series/get_series_books.yml b/bruno/series/get_series_books.yml new file mode 100644 index 00000000..1b821dee --- /dev/null +++ b/bruno/series/get_series_books.yml @@ -0,0 +1,21 @@ +info: + name: Get Series Books + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/series/books' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Series Books + + `GET /api/series/books` — auth: jwt. + + Canonical reference: `docs/developer/api/series/get_series_books.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/series/list_series.yml b/bruno/series/list_series.yml new file mode 100644 index 00000000..8922e1c5 --- /dev/null +++ b/bruno/series/list_series.yml @@ -0,0 +1,21 @@ +info: + name: List Series + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/series' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # List Series + + `GET /api/series` — auth: jwt. + + Canonical reference: `docs/developer/api/series/list_series.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/sidecar/Download Device Sidecar File.yml b/bruno/sidecar/Download Device Sidecar File.yml deleted file mode 100644 index f8deddaf..00000000 --- a/bruno/sidecar/Download Device Sidecar File.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Download Device Sidecar File - type: http - seq: 2 -http: - method: GET - url: '{{base_url}}/api/devices/{{device_id}}/sidecar/download' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{user_token - -docs: |- - ## Download Device Sidecar File - - Downloads the sidecar configuration file for a device in JSON format. - - **Method:** GET - - **Endpoint:** /api/devices/{device_id diff --git a/bruno/sidecar/Get Device Sidecar Config.yml b/bruno/sidecar/Get Device Sidecar Config.yml deleted file mode 100644 index ca8d2652..00000000 --- a/bruno/sidecar/Get Device Sidecar Config.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Get Device Sidecar Config - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/devices/{{device_id}}/sidecar' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{user_token - -docs: |- - ## Get Device Sidecar Config - - Retrieves sidecar configuration for a specific device. - - **Method:** GET - - **Endpoint:** /api/devices/{device_id diff --git a/bruno/sidecar/Get System Configuration.yml b/bruno/sidecar/Get System Configuration.yml deleted file mode 100644 index 2990bb84..00000000 --- a/bruno/sidecar/Get System Configuration.yml +++ /dev/null @@ -1,36 +0,0 @@ -info: - name: Get System Configuration - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/system/config' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{admin_token - -docs: |- - ## Get System Configuration - - Retrieves system-wide configuration settings. - - **Method:** GET - - **Endpoint:** /api/system/config - - **Authentication:** Bearer token (admin only) - - **Response:** - - `base_url` (string): Base URL - - `opds_base_url` (string): OPDS endpoint URL - - `api_base_url` (string): API endpoint URL - - Additional configuration fields - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden (admin only) - - 500: Internal server error diff --git a/bruno/sidecar/Update System Configuration.yml b/bruno/sidecar/Update System Configuration.yml deleted file mode 100644 index 8d9cd5bd..00000000 --- a/bruno/sidecar/Update System Configuration.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Update System Configuration - type: http - seq: 4 -http: - method: PUT - url: '{{base_url}}/api/system/config' - auth: inherit - body: - type: json - data: "{\n \"base_url\": \"https://bookhoard.example.com\",\n \"opds_base_url\": \"https://bookhoard.example.com/opds\",\n \"api_base_url\": \"https://bookhoard.example.com/api\"" - headers: - - key: Authorization - value: Bearer {{admin_token -docs: "## Update System Configuration\n\n Updates system-wide configuration settings for the Bookhoard instance.\n\n **Method:** PUT\n\n **Endpoint:** /api/system/config\n\n **Authentication:** Bearer token (admin only)\n\n **Request Body:**\n - `base_url` (string): Base URL for the instance\n - `opds_base_url` (string): OPDS endpoint base URL\n - `api_base_url` (string): API endpoint base URL\n\n **Response:**\n - `status` (string): Update status\n - `config` (object): Updated configuration\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid configuration\n - 401: Unauthorized\n - 403: Forbidden (admin only)\n - 500: Internal server error" diff --git a/bruno/stats/get_book_stats.yml b/bruno/stats/get_book_stats.yml new file mode 100644 index 00000000..bc61a24e --- /dev/null +++ b/bruno/stats/get_book_stats.yml @@ -0,0 +1,21 @@ +info: + name: Get Book Stats + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/stats/books/{{media_item_id}}' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Book Stats + + `GET /api/stats/books/:id` — auth: jwt. + + Canonical reference: `docs/developer/api/stats/get_book_stats.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/stats/get_library_usage.yml b/bruno/stats/get_library_usage.yml new file mode 100644 index 00000000..72d5e640 --- /dev/null +++ b/bruno/stats/get_library_usage.yml @@ -0,0 +1,21 @@ +info: + name: Get Library Usage Statistics + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/stats/library-usage' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Library Usage Statistics + + `GET /api/stats/library-usage` — auth: jwt. + + Canonical reference: `docs/developer/api/stats/get_library_usage.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/stats/get_summary.yml b/bruno/stats/get_summary.yml new file mode 100644 index 00000000..1cdbfc81 --- /dev/null +++ b/bruno/stats/get_summary.yml @@ -0,0 +1,21 @@ +info: + name: Get Reading Summary + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/stats/summary' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Reading Summary + + `GET /api/stats/summary` — auth: jwt. + + Canonical reference: `docs/developer/api/stats/get_summary.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/stats/get_this_week.yml b/bruno/stats/get_this_week.yml new file mode 100644 index 00000000..896fdf83 --- /dev/null +++ b/bruno/stats/get_this_week.yml @@ -0,0 +1,21 @@ +info: + name: Get This Week's Stats + type: http + seq: 4 + +http: + method: GET + url: '{{base_url}}/api/stats/this-week' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get This Week's Stats + + `GET /api/stats/this-week` — auth: jwt. + + Canonical reference: `docs/developer/api/stats/get_this_week.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/system/get-scan-settings.yml b/bruno/system/get-scan-settings.yml deleted file mode 100644 index 96ff1297..00000000 --- a/bruno/system/get-scan-settings.yml +++ /dev/null @@ -1,19 +0,0 @@ -info: - name: Get System Scan Settings - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/libraries/scan-settings' - auth: inherit - headers: - - key: Content-Type - value: application/json - -docs: |- - ## Get System Scan Settings - - Retrieves current system-wide scan settings for all libraries. - - **Authentication**: Admin token required - **Response**: Current scan frequency and auto-scan status diff --git a/bruno/system/get_config.yml b/bruno/system/get_config.yml new file mode 100644 index 00000000..78eb7487 --- /dev/null +++ b/bruno/system/get_config.yml @@ -0,0 +1,21 @@ +info: + name: Get System Configuration + type: http + seq: 1 + +http: + method: GET + url: '{{base_url}}/api/system/config' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get System Configuration + + `GET /api/system/config` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/system/get_config.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/system/get_settings.yml b/bruno/system/get_settings.yml new file mode 100644 index 00000000..6da13d66 --- /dev/null +++ b/bruno/system/get_settings.yml @@ -0,0 +1,21 @@ +info: + name: Get System Settings + type: http + seq: 2 + +http: + method: GET + url: '{{base_url}}/api/system/settings' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get System Settings + + `GET /api/system/settings` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/system/get_settings.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/system/update-scan-settings.yml b/bruno/system/update-scan-settings.yml deleted file mode 100644 index f54613af..00000000 --- a/bruno/system/update-scan-settings.yml +++ /dev/null @@ -1,15 +0,0 @@ -info: - name: Update System Scan Settings - type: http - seq: 2 -http: - method: PUT - url: '{{base_url}}/api/libraries/scan-settings' - auth: inherit - body: - type: json - data: "{\n \"scan_poll_interval_seconds\": 30,\n \"auto_scan_enabled\": true\n}" - headers: - - key: Content-Type - value: application/json -docs: "## Update System Scan Settings\n\n Updates system-wide scan settings that apply to all libraries.\n\n **Authentication**: Admin token required\n **Request**: Scan frequency (15-1440 minutes) and enabled status\n **Response**: Success message" diff --git a/bruno/system/update_config.yml b/bruno/system/update_config.yml new file mode 100644 index 00000000..c582ae55 --- /dev/null +++ b/bruno/system/update_config.yml @@ -0,0 +1,28 @@ +info: + name: Update System Configuration + type: http + seq: 3 + +http: + method: PUT + url: '{{base_url}}/api/system/config' + auth: inherit + body: + type: json + data: |- + { + "base_url": "https://books.example.com", + "default_timezone": "UTC" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update System Configuration + + `PUT /api/system/config` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/system/update_config.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/system/update_setting.yml b/bruno/system/update_setting.yml new file mode 100644 index 00000000..85c56f7b --- /dev/null +++ b/bruno/system/update_setting.yml @@ -0,0 +1,28 @@ +info: + name: Update System Setting + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/system/settings' + auth: inherit + body: + type: json + data: |- + { + "key": "scan_poll_interval_seconds", + "value": "60" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update System Setting + + `PUT /api/system/settings` — auth: jwt+admin. + + Canonical reference: `docs/developer/api/system/update_setting.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/universal-progress/Get Progress History.yml b/bruno/universal-progress/Get Progress History.yml deleted file mode 100644 index f476e7fa..00000000 --- a/bruno/universal-progress/Get Progress History.yml +++ /dev/null @@ -1,22 +0,0 @@ -info: - name: Get Progress History - type: http - seq: 3 -http: - method: GET - url: '{{base_url}}/api/progress/{{mediaItemId}}/history' - auth: inherit - body: - type: none - headers: - - key: Authorization - value: Bearer {{jwt - -docs: |- - ## Get Progress History - - Retrieves historical reading progress sessions for a specific media item. - - **Method:** GET - - **Endpoint:** /api/progress/{mediaItemId diff --git a/bruno/user/admin/Delete User.yml b/bruno/user/admin/Delete User.yml deleted file mode 100644 index b370c53e..00000000 --- a/bruno/user/admin/Delete User.yml +++ /dev/null @@ -1,25 +0,0 @@ -info: - name: Delete User - type: http - seq: 12 -http: - method: DELETE - url: '{{base_url}}/api/auth/profile/{{user_id}}' - auth: inherit - -docs: |- - ## Delete User Account - - Delete a user account. Users can delete their own account, admins can delete any user. - - **Method:** DELETE - - **Endpoint:** /api/auth/profile (self) or /api/auth/profile/:id (admin) - - **Authentication:** Required (Bearer token) - - **Status Codes:** - - 200: Success - - 400: Cannot delete the last admin - - 403: Not an admin (when trying to delete another user) - - 404: User not found diff --git a/bruno/user/admin/List Users.yml b/bruno/user/admin/List Users.yml deleted file mode 100644 index 121bbb1e..00000000 --- a/bruno/user/admin/List Users.yml +++ /dev/null @@ -1,45 +0,0 @@ -info: - name: List Users - type: http - seq: 5 -http: - method: GET - url: '{{base_url}}/api/auth/users' - auth: inherit - body: - type: none - -docs: |- - ## List Users - - Retrieves a list of all users with complete user information. - - **Method:** GET - - **Endpoint:** /api/auth/users - - **Authentication:** Required (Admin only) - - **Response:** Array of user objects with complete information: - - `id` (string): User ID (UUID) - - `email` (string): Email address - - `username` (string): Username - - `first_name` (string): First name (empty if not set) - - `last_name` (string): Last name (empty if not set) - - `role` (string): User role ("user" or "admin") - - `theme` (string): Theme preference (empty if default) - - `max_devices` (integer): Maximum number of devices allowed - - `device_count` (integer): Current number of registered devices - - `created_at` (string): Creation timestamp (ISO 8601) - - `updated_at` (string): Last update timestamp (ISO 8601) - - **Status Codes:** - - 200: Success - - 401: Unauthorized - - 403: Forbidden (admin access required) - - **Features:** - - Admin-only endpoint with complete user information - - Returns first_name, last_name, role, theme fields - - Includes device limits and current device count - - Useful for user management interfaces diff --git a/bruno/user/admin/Register Admin User.yml b/bruno/user/admin/Register Admin User.yml deleted file mode 100644 index 13652abf..00000000 --- a/bruno/user/admin/Register Admin User.yml +++ /dev/null @@ -1,64 +0,0 @@ -info: - name: Register Admin User - type: http - seq: 4 -http: - method: POST - url: '{{base_url}}/api/auth/register' - auth: inherit - body: - type: json - data: |- - { - "email": "admin@example.com", - "username": "admin", - "password": "Admin@Pass123!", - "first_name": "Admin", - "last_name": "User", - "role": "admin" - } - -docs: |- - ## Register Admin User - - Creates a new admin user account with role-based restrictions. - - **Method:** POST - - **Endpoint:** /api/auth/register - - **Request Body:** - - `email` (string): Email address - - `username` (string): Username - - `password` (string): Password - - `first_name` (string, optional): First name - - `last_name` (string, optional): Last name - - `role` (string): Must be "admin" - - **Response:** - - `token` (string): JWT token with admin role - - `user` (object): User details - - `id` (string): User ID - - `email` (string): Email - - `username` (string): Username - - `theme` (string): User theme preference - - `first_name` (string, optional): First name - - `last_name` (string, optional): Last name - - `role` (string): User role ("admin") - - **Status Codes:** - - 201: Created - - 400: Invalid input data - - 403: Forbidden - admin creation restrictions apply - - 409: User exists - - **Role Restrictions:** - - **First User**: Anyone can create first admin (auto-assigned) - - **Existing Admins Present**: Only authenticated admins can create new admin accounts - - **Unauthenticated Users**: Cannot create admin accounts if any admin exists - - **Security**: Requires admin authentication for subsequent admin creation - - **Usage Notes:** - - Use this request only when specifically creating admin accounts - - For regular user creation, use "Register User" request - - Admin token will have elevated privileges for administrative operations diff --git a/bruno/user/admin/Reset User Password.yml b/bruno/user/admin/Reset User Password.yml deleted file mode 100644 index 531fa2f6..00000000 --- a/bruno/user/admin/Reset User Password.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Reset User Password - type: http - seq: 11 -http: - method: PUT - url: '{{base_url}}/api/auth/password/{{user_id}}' - auth: inherit - body: - type: json - data: "{\n \"new_password\": \"NewSecurePassword123!\",\n \"confirm_password\": \"NewSecurePassword123!\"\n}" -docs: "## Admin Reset User Password\n \n Admin-only endpoint to reset another user's password.\n \n **Method:** PUT\n \n **Endpoint:** /api/auth/password/:id\n \n **Authentication:** Required (Admin Bearer token)\n \n **Request Body:**\n - `new_password` (string, required): New password\n - `confirm_password` (string, required): Must match new_password\n \n **Status Codes:**\n - 200: Success\n - 400: Passwords do not match or invalid format\n - 403: Not an admin\n - 404: User not found" diff --git a/bruno/user/admin/Update User Max Devices.yml b/bruno/user/admin/Update User Max Devices.yml deleted file mode 100644 index c251f11c..00000000 --- a/bruno/user/admin/Update User Max Devices.yml +++ /dev/null @@ -1,27 +0,0 @@ -info: - name: Update User Max Devices - type: http - seq: 6 -http: - method: PUT - url: '{{base_url}}/api/auth/users/{{user_id}}/max-devices' - auth: inherit - -docs: |- - ## Update User Max Devices (Admin) - - Updates the maximum number of devices a user can register. - - **Method:** PUT - - **Endpoint:** /api/auth/users/:id/max-devices - - **Authentication:** Required (Admin only) - - **URL Parameters:** - - `id` (string): User ID (UUID) - - **Request Body:** - ```json - { - "max_devices": 10 diff --git a/bruno/user/admin/Update User.yml b/bruno/user/admin/Update User.yml deleted file mode 100644 index b380d4a4..00000000 --- a/bruno/user/admin/Update User.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update User - type: http - seq: 10 -http: - method: PUT - url: '{{base_url}}/api/auth/profile/{{user_id}}' - auth: inherit - body: - type: json - data: "{\n \"username\": \"newusername\",\n \"email\": \"newemail@example.com\",\n \"role\": \"admin\"\n}" -docs: "## Admin Update User\n \n Admin-only endpoint to update another user's profile.\n \n **Method:** PUT\n \n **Endpoint:** /api/auth/profile/:id\n \n **Authentication:** Required (Admin Bearer token)\n \n **Request Body:**\n - `username` (string, optional): New username\n - `email` (string, optional): New email\n - `first_name` (string, optional): First name\n - `last_name` (string, optional): Last name\n - `theme` (string, optional): Theme preference\n - `role` (string, optional): Role (\"user\" or \"admin\") - admin only\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid role or cannot demote last admin\n - 403: Not an admin\n - 404: User not found\n - 409: Username or email already taken" diff --git a/bruno/user/auth/Login User.yml b/bruno/user/auth/Login User.yml deleted file mode 100644 index 30286a44..00000000 --- a/bruno/user/auth/Login User.yml +++ /dev/null @@ -1,77 +0,0 @@ -info: - name: Login User - type: http - seq: 1 - -http: - method: POST - url: "{{base_url}}/api/auth/login" - body: - type: json - data: |- - { - "login": "testuser@tests.bookhoard.internal", - "password": "Test@Pass123!" - } - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const token = responseBody.access_token; - const refreshToken = responseBody.refresh_token; - - if (token) { - bru.setEnvVar("token", token, { persist: true }); - console.log("Access token saved:", token); - } - - if (refreshToken) { - bru.setEnvVar("refresh_token", refreshToken, { persist: true }); - console.log("Refresh token saved:", refreshToken); - } - - if (!token && !refreshToken) { - console.log("No tokens found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Login User - - Authenticates a user with email/username and password. - - **Method:** POST - - **Endpoint:** /api/auth/login - - **Request Body:** - - `login` (string): Email or username - - `password` (string): Password - - **Response:** - - `token` (string): JWT token - - `user` (object): User details - - `id` (string): User ID - - `email` (string): Email - - `username` (string): Username - - `theme` (string): User theme preference - - `first_name` (string): First name - - `last_name` (string): Last name - - **Status Codes:** - - 200: Success - - 401: Invalid credentials diff --git a/bruno/user/auth/Logout User.yml b/bruno/user/auth/Logout User.yml deleted file mode 100644 index 7de12cd4..00000000 --- a/bruno/user/auth/Logout User.yml +++ /dev/null @@ -1,43 +0,0 @@ -info: - name: Logout User - type: http - seq: 3 -http: - method: POST - url: '{{base_url}}/api/auth/logout' - auth: inherit - body: - type: json - data: |- - { - "refresh_token": "{{refresh_token}}" - } - headers: - - key: Content-Type - value: application/json - -docs: |- - ## Logout User - - Logs out the user by revoking their refresh token. If no refresh token is provided, the request succeeds but no token is revoked. - - **Method:** POST - - **Endpoint:** /api/auth/logout - - **Authentication:** Bearer token (optional) - - **Request Body:** - - `refresh_token` (string, optional): Refresh token to revoke - - **Response:** - - `message` (string): Success message - - **Status Codes:** - - 200: Success - user logged out (token revoked if provided) - - 401: Unauthorized - - **Example Response:** - ```json - { - "message": "logged out successfully" diff --git a/bruno/user/auth/Refresh Token.yml b/bruno/user/auth/Refresh Token.yml deleted file mode 100644 index 1d321c40..00000000 --- a/bruno/user/auth/Refresh Token.yml +++ /dev/null @@ -1,83 +0,0 @@ -info: - name: Refresh Access Token - type: http - seq: 4 - -http: - method: POST - url: "{{base_url}}/api/auth/refresh" - headers: - - name: "" - value: application/json - body: - type: json - data: |- - { - "refresh_token": "{{refresh_token}}" - } - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const token = responseBody.access_token; - const refreshToken = responseBody.refresh_token; - - if (token) { - bru.setEnvVar("token", token, { persist: true }); - console.log("Access token saved:", token); - } - - if (refreshToken) { - bru.setEnvVar("refresh_token", refreshToken, { persist: true }); - console.log("Refresh token saved:", refreshToken); - } - - if (!token && !refreshToken) { - console.log("No tokens found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Refresh Access Token - - Refreshes an access token using a valid refresh token. Returns a new access token with 1-hour expiration. - - **Method:** POST - - **Endpoint:** /api/auth/refresh - - **Authentication:** Not required (refresh token is in request body) - - **Request Body:** - - `refresh_token` (string): Valid refresh token UUID - - **Response:** - - `access_token` (string): New JWT access token (1 hour expiration) - - `token_type` (string): Token type (usually "Bearer") - - `expires_in` (number): Token lifetime in seconds (3600) - - **Status Codes:** - - 200: Success - new access token generated - - 401: Unauthorized - invalid or expired refresh token - - **Example Response (Success):** - ```json - { - "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "token_type": "Bearer", - "expires_in": 3600 diff --git a/bruno/user/auth/Register User.yml b/bruno/user/auth/Register User.yml deleted file mode 100644 index 038e56ea..00000000 --- a/bruno/user/auth/Register User.yml +++ /dev/null @@ -1,98 +0,0 @@ -info: - name: Register User - type: http - seq: 2 - -http: - method: POST - url: "{{base_url}}/api/auth/register" - body: - type: json - data: |- - { - "username": "testuser", - "email": "testuser@tests.bookhoard.internal", - "password": "Test@Pass123!", - "first_name": "Test", - "last_name": "User" - } - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - function onResponse(res) { - try { - const responseBody = res.getBody(); - const token = responseBody.access_token; - const refreshToken = responseBody.refresh_token; - - if (token) { - bru.setEnvVar("token", token, { persist: true }); - console.log("Access token saved:", token); - } - - if (refreshToken) { - bru.setEnvVar("refresh_token", refreshToken, { persist: true }); - console.log("Refresh token saved:", refreshToken); - } - - if (!token && !refreshToken) { - console.log("No tokens found in response."); - } - } catch (error) { - console.error("Error in post-response script:", error.message); - } - } - onResponse(res); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 - -docs: |- - ## Register User - - Creates a new user account with role-based restrictions. - - **Method:** POST - - **Endpoint:** /api/auth/register - - **Request Body:** - - `email` (string): Email address - - `username` (string): Username - - `password` (string): Password - - `first_name` (string, optional): First name - - `last_name` (string, optional): Last name - - `role` (string): User role ("user" or "admin") - - **Response:** - - `token` (string): JWT token - - `user` (object): User details - - `id` (string): User ID - - `email` (string): Email - - `username` (string): Username - - `theme` (string): User theme preference - - `first_name` (string, optional): First name - - `last_name` (string, optional): Last name - - `role` (string): User role ("user" or "admin") - - **Status Codes:** - - 201: Created - - 400: Invalid input data - - 403: Forbidden - role-based restrictions apply - - 409: User exists - - **Role Restrictions:** - - **First User**: Automatically gets admin role regardless of request - - **Existing Admins Present**: Only authenticated admins can create new admin accounts - - **No Admins Yet**: Anyone can create first admin (auto-assigned) - - **Regular User Creation**: Anyone can create regular user accounts - - **Unauthenticated Users**: Can only create first admin, not subsequent admins - - **Examples:** - - First admin creation: `{"email": "admin@example.com", "username": "admin", "password": "password123", "role": "admin" diff --git a/bruno/user/profile/Get Profile.yml b/bruno/user/profile/Get Profile.yml deleted file mode 100644 index e89a8ed7..00000000 --- a/bruno/user/profile/Get Profile.yml +++ /dev/null @@ -1,33 +0,0 @@ -info: - name: Get Profile - type: http - seq: 1 -http: - method: GET - url: '{{base_url}}/api/auth/profile' - auth: inherit - body: - type: none - -docs: |- - ## Get User Profile - - Retrieves the authenticated user's profile. - - **Method:** GET - - **Endpoint:** /api/auth/profile - - **Authentication:** Required - - **Response:** - - `id` (string): User ID - - `email` (string): Email - - `username` (string): Username - - `theme` (string): User theme preference - - `first_name` (string, optional): First name - - `last_name` (string, optional): Last name - - **Status Codes:** - - 200: Success - - 401: Unauthorized diff --git a/bruno/user/profile/Update Password.yml b/bruno/user/profile/Update Password.yml deleted file mode 100644 index 28d37e4b..00000000 --- a/bruno/user/profile/Update Password.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Password - type: http - seq: 3 -http: - method: PUT - url: '{{base_url}}/api/auth/password' - auth: inherit - body: - type: json - data: "{\n \"current_password\": \"password123\",\n \"new_password\": \"newpassword123\",\n \"confirm_password\": \"newpassword123\"" -docs: "## Update Password\n \n Updates the authenticated user's password.\n \n **Method:** PUT\n \n **Endpoint:** /api/auth/password\n \n **Authentication:** Required\n \n **Request Body:**\n - `current_password` (string, required): Current password for verification\n - `new_password` (string, required): New password (minimum 6 characters)\n - `confirm_password` (string, required): Confirmation of new password\n \n **Response:**\n - `message` (string): Success message\n \n **Status Codes:**\n - 200: Success\n - 400: Password validation failed\n - 401: Current password incorrect\n - 401: Unauthorized" diff --git a/bruno/user/profile/Update Profile.yml b/bruno/user/profile/Update Profile.yml deleted file mode 100644 index cfb0925a..00000000 --- a/bruno/user/profile/Update Profile.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Profile - type: http - seq: 4 -http: - method: PUT - url: '{{base_url}}/api/auth/profile' - auth: inherit - body: - type: json - data: "{\n \"username\": \"updateduser\",\n \"email\": \"updated@tests.bookhoard.internal\",\n \"first_name\": \"Updated\",\n \"last_name\": \"User\",\n \"theme\": \"dracula\"\n}" -docs: "## Update User Profile\n \n Updates the authenticated user's profile information.\n \n **Method:** PUT\n \n **Endpoint:** /api/auth/profile\n \n **Authentication:** Required (Bearer token)\n \n **Request Body:**\n - `username` (string, optional): New username (must be unique)\n - `email` (string, optional): New email address (must be unique)\n - `first_name` (string, optional): First name\n - `last_name` (string, optional): Last name\n - `theme` (string, optional): Theme preference\n \n **Response:**\n - `message` (string): Success message\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid request\n - 401: Unauthorized\n - 409: Username or email already taken" diff --git a/bruno/user/profile/Update Theme.yml b/bruno/user/profile/Update Theme.yml deleted file mode 100644 index 4fa5b686..00000000 --- a/bruno/user/profile/Update Theme.yml +++ /dev/null @@ -1,12 +0,0 @@ -info: - name: Update Theme - type: http - seq: 5 -http: - method: PUT - url: '{{base_url}}/api/auth/theme' - auth: inherit - body: - type: json - data: "{\n \"theme\": \"dracula\"\n}" -docs: "## Update Theme\n \n Fast theme switching for the application UI.\n \n **Method:** PUT\n \n **Endpoint:** /api/auth/theme\n \n **Authentication:** Required (Bearer token)\n \n **Request Body:**\n - `theme` (string, required): Theme name\n \n **Response:**\n - `message` (string): Success message\n \n **Status Codes:**\n - 200: Success\n - 400: Invalid theme\n - 401: Unauthorized" diff --git a/bruno/users/change_password.yml b/bruno/users/change_password.yml new file mode 100644 index 00000000..4c8b9658 --- /dev/null +++ b/bruno/users/change_password.yml @@ -0,0 +1,29 @@ +info: + name: Change Password + type: http + seq: 1 + +http: + method: PUT + url: '{{base_url}}/api/auth/password' + auth: inherit + body: + type: json + data: |- + { + "current_password": "OldPassword123!", + "new_password": "NewSecureP@ss123!", + "confirm_password": "NewSecureP@ss123!" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Change Password + + `PUT /api/auth/password` — auth: jwt. + + Canonical reference: `docs/developer/api/users/change_password.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/users/delete_user.yml b/bruno/users/delete_user.yml new file mode 100644 index 00000000..681fdacf --- /dev/null +++ b/bruno/users/delete_user.yml @@ -0,0 +1,21 @@ +info: + name: Delete User + type: http + seq: 2 + +http: + method: DELETE + url: '{{base_url}}/api/auth/profile' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Delete User + + `DELETE /api/auth/profile` — auth: jwt. + + Canonical reference: `docs/developer/api/users/delete_user.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/users/get_profile.yml b/bruno/users/get_profile.yml new file mode 100644 index 00000000..7c9c7042 --- /dev/null +++ b/bruno/users/get_profile.yml @@ -0,0 +1,21 @@ +info: + name: Get Current User Profile + type: http + seq: 3 + +http: + method: GET + url: '{{base_url}}/api/auth/profile' + auth: inherit +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Get Current User Profile + + `GET /api/auth/profile` — auth: jwt. + + Canonical reference: `docs/developer/api/users/get_profile.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/bruno/users/update_profile.yml b/bruno/users/update_profile.yml new file mode 100644 index 00000000..1ec3d374 --- /dev/null +++ b/bruno/users/update_profile.yml @@ -0,0 +1,31 @@ +info: + name: Update Profile + type: http + seq: 4 + +http: + method: PUT + url: '{{base_url}}/api/auth/profile' + auth: inherit + body: + type: json + data: |- + { + "username": "newusername", + "email": "newemail@example.com", + "first_name": "John", + "last_name": "Smith", + "theme": "dracula" + } +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + # Update Profile + + `PUT /api/auth/profile` — auth: jwt. + + Canonical reference: `docs/developer/api/users/update_profile.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file. diff --git a/internal/docs/drift_test.go b/internal/docs/drift_test.go index d4ba936d..8f5a3b49 100644 --- a/internal/docs/drift_test.go +++ b/internal/docs/drift_test.go @@ -186,6 +186,63 @@ func parseHandlerMiddleware(rest string) []string { // ---------- the gate ---------- +// TestBrunoMirrorsDocs keeps the executable-verification layer honest: +// every frontmatter'd endpoint doc must have a generated OpenCollection +// (Bruno) request whose method and URL shape match. Bruno is regenerated +// from the docs (scripts/gen_bruno_from_docs.py); if this test fails, the +// collection drifted from the canonical docs or a new doc was added +// without regenerating. +func TestBrunoMirrorsDocs(t *testing.T) { + docsDir := "../../docs/developer/api" + docs, err := LoadAPIDocs(docsDir) + if err != nil { + t.Fatalf("walk %s: %v", docsDir, err) + } + + // Collect bruno requests: "METHOD normalized-path" -> relpath. + bruno := map[string]string{} + files, _ := filepath.Glob("../../bruno/**/*.yml") + for _, file := range files { + rel, _ := filepath.Rel("../../bruno", file) + parts := strings.Split(rel, string(filepath.Separator)) + if len(parts) > 0 && (parts[0] == "NewDevDBSetup" || parts[0] == "environments") { + continue + } + if strings.Contains(file, "/scenarios/") || strings.Contains(file, "/filters/") || strings.Contains(file, "/search/") { + continue // hand-authored edge-case suites, not 1:1 mirrors + } + raw, err := os.ReadFile(file) + if err != nil { + t.Fatalf("read %s: %v", file, err) + } + var method, url string + for _, line := range strings.Split(string(raw), "\n") { + s := strings.TrimSpace(line) + if m := regexp.MustCompile(`^method:\s*(\w+)$`).FindStringSubmatch(s); m != nil && method == "" { + method = strings.ToUpper(m[1]) + } + if m := regexp.MustCompile(`^url:\s*'?([^']+)`).FindStringSubmatch(s); m != nil && url == "" { + url = m[1] + } + } + if method == "" || url == "" { + continue // folder.yml and other metadata + } + // {{base_url}}/api/x/{{var}} -> /api/x/{} (params collapse to {}) + path := regexp.MustCompile(`^\{\{(?:base|opds_base)_url\}\}`).ReplaceAllString(url, "") + path = regexp.MustCompile(`\{\{[^}]+\}\}`).ReplaceAllString(path, "{}") + bruno[method+" "+path] = rel + } + + for _, doc := range docs { + path := regexp.MustCompile(`:[^/{}]+`).ReplaceAllString(doc.Frontmatter.Path, "{}") + key := doc.Frontmatter.Method + " " + path + if _, ok := bruno[key]; !ok { + t.Errorf("%s: no Bruno request mirrors %q — run scripts/gen_bruno_from_docs.py --apply", doc.RelPath, key) + } + } +} + func TestDocsMatchRoutes(t *testing.T) { routes := extractRoutesFromRouterSource(t) diff --git a/scripts/gen_bruno_from_docs.py b/scripts/gen_bruno_from_docs.py new file mode 100644 index 00000000..7d421384 --- /dev/null +++ b/scripts/gen_bruno_from_docs.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Generate OpenCollection (Bruno) request YAMLs from the canonical API docs. + +Truth flow: code -> docs/developer/api (canonical) -> bruno/ (executable +verification, never hand-authored). Every frontmatter'd endpoint doc +becomes one request file at bruno//.yml, with the +gate-verified example-request JSON as the body. + +Re-run after doc changes: python3 scripts/gen_bruno_from_docs.py [--apply] +Without --apply, prints the plan only. +""" +import os, re, sys, glob + +DOCS = "docs/developer/api" +BRUNO = "bruno" +KEEP_DIRS = {"NewDevDBSetup", "environments"} # hand-authored setup + env +ROOT_KEEP = {"opencollection.yml", "NewDB.sh"} +# Hand-authored edge-case verification (not docs mirrors) — kept as-is. +KEEP_SUBSTRINGS = ("/scenarios/", "/filters/", "/search/") + +# Token-capture after-response scripts, keyed by doc filename — these chain +# the collection (later requests need {{token}} / {{refresh_token}}). +TOKEN_SCRIPT = """function onResponse(res) { + try { + const responseBody = res.getBody(); + const token = responseBody.access_token; + const refreshToken = responseBody.refresh_token; + if (token) { + bru.setEnvVar("token", token, { persist: true }); + console.log("Access token saved"); + } + if (refreshToken) { + bru.setEnvVar("refresh_token", refreshToken, { persist: true }); + console.log("Refresh token saved"); + } + } catch (error) { + console.error("Token capture failed:", error.message); + } +} +onResponseResponsePlaceholder();""" +TOKEN_SCRIPT = TOKEN_SCRIPT.replace("onResponseResponsePlaceholder();", "onResponse();") +SCRIPT_DOCS = {"login", "register", "refresh_token"} + +# Param-name -> env var (checked longest-first, then generic :id by area). +NAMED_PARAMS = { + "registration_id": "registration_id", + "deviceid": "device_id", + "libraryid": "ebook_library_id", + "collectionid": "collection_id", + "trackindex": "track_index", + "highlightid": "highlight_id", + "annotationid": "annotation_id", + "ruleid": "rule_id", + "aliasid": "alias_id", + "folderid": "library_folder_id", + "jobid": "job_id", + "bookid": "book_uuid", + "sha256": "book_sha256", + "noteid": "note_id", + "userid": "user_id", + "uuid": "book_uuid", + "token": "kobo_device_token", +} + +# (area, subpath-contains) -> env for generic :id +ID_BY_CONTEXT = [ + ("media-items", "media_item_id"), + ("collections", "collection_id"), + ("libraries", "ebook_library_id"), + ("devices", "device_id"), + ("progress", "media_item_id"), + ("stats/books", "media_item_id"), + ("annotations", "annotation_id"), +] + +NEW_ENV_VARS = [ + "device_id", "rule_id", "annotation_id", "alias_id", "track_index", + "registration_id", "unlinked_book_id", "device_token", +] + + +def norm(p: str) -> str: + p = re.sub(r"/+", "/", p) + return re.sub(r":[^/{}]+", lambda m: "{" + m.group(0)[1:] + "}", p).rstrip("/") + + +def parse_doc(path: str): + src = open(path).read() + m = re.match(r"---\n(.*?)\n---\n", src, re.S) + if not m: + return None + fm = m.group(1) + method = re.search(r"method:\s*(\w+)", fm) + route = re.search(r"path:\s*(\S+)", fm) + auth = re.search(r"auth:\s*(\S+)", fm) + if not (method and route): + return None + body_src = src[m.end():] + title = re.search(r"^# (.+)$", body_src, re.M) + ex = re.search(r"```json example-request\n(.*?)\n```", body_src, re.S) + return { + "rel": os.path.relpath(path, DOCS), + "area": os.path.relpath(path, DOCS).split(os.sep)[0], + "name": os.path.splitext(os.path.basename(path))[0], + "method": method.group(1).upper(), + "path": route.group(1), + "auth": auth.group(1) if auth else "jwt", + "title": title.group(1).strip() if title else route.group(1), + "example": ex.group(1) if ex else None, + } + + +def env_for(area: str, path: str, param: str) -> str: + key = param.lstrip(":").lower() + if key in NAMED_PARAMS: + return NAMED_PARAMS[key] + if key == "id": + ctx = f"{area}/{path.split('/')[-2] if '/' in path else ''}" + for prefix, var in ID_BY_CONTEXT: + if ctx.startswith(prefix) or f"/api/{prefix}/" in path + "/": + return var + if path.startswith("/api/sync/unlinked-books"): + return "unlinked_book_id" + return "media_item_id" # best default for this API's :id grammar + return key # fallback: env var named after the param + + +def yaml_str_block(indent: int, text: str) -> str: + pad = " " * indent + lines = text.rstrip("\n").split("\n") + out = [f"{pad}|-"] + out += [pad + " " + ln if ln else "" for ln in lines] + return "\n".join(out) + + +def emit(doc: dict, seq: int) -> str: + path = norm(doc["path"]) + path = re.sub(r"\{(\w+)\}", lambda m: "{{" + env_for(doc["area"], doc["path"], m.group(1)) + "}}", path) + url = "{{base_url}}" + path + + out = [ + "info:", + f" name: {doc['title']}", + " type: http", + f" seq: {seq}", + "", + "http:", + f" method: {doc['method']}", + f" url: '{url}'", + " auth: inherit", + ] + if doc["example"]: + out += [" body:", " type: json", " data: " + yaml_str_block(4, doc["example"]).lstrip()] + if doc["auth"] == "device-token": + out += [ + " headers:", + " - key: Authorization", + " value: Bearer {{device_token}}", + ] + out += [ + "settings:", + " encodeUrl: true", + " timeout: 0", + " followRedirects: true", + " maxRedirects: 5", + ] + if doc["name"] in SCRIPT_DOCS: + out += [ + "", + "runtime:", + " scripts:", + " - type: after-response", + " code: " + yaml_str_block(8, TOKEN_SCRIPT).lstrip(), + ] + out += [ + "", + "docs: " + yaml_str_block(0, ( + f"# {doc['title']}\n\n" + f"`{doc['method']} {doc['path']}` — auth: {doc['auth']}.\n\n" + f"Canonical reference: `docs/developer/api/{doc['rel']}` " + f"(docs site → Developer → API). This request is GENERATED from " + f"that doc; edit the doc, not this file." + )), + "", + ] + return "\n".join(out) + + +def main(): + apply = "--apply" in sys.argv + docs = [] + for path in sorted(glob.glob(f"{DOCS}/**/*.md", recursive=True)): + d = parse_doc(path) + if d: + docs.append(d) + print(f"{len(docs)} frontmatter'd endpoint docs") + + # Plan writes + writes = {} + by_area = {} + for d in docs: + by_area.setdefault(d["area"], []).append(d) + for area, ds in sorted(by_area.items()): + ds.sort(key=lambda d: d["name"]) + for i, d in enumerate(ds, 1): + writes[f"{BRUNO}/{area}/{d['name']}.yml"] = emit(d, i) + + # Plan deletes: existing request ymls not regenerated (setup/env/root, + # and hand-authored scenario suites kept) + deletes = [] + for path in glob.glob(f"{BRUNO}/**/*.yml", recursive=True): + rel = os.path.relpath(path, BRUNO) + top = rel.split(os.sep)[0] + if top in KEEP_DIRS or rel in ROOT_KEEP or os.path.basename(path) == "folder.yml": + continue + if any(s in path for s in KEEP_SUBSTRINGS): + continue + if path not in writes: + deletes.append(path) + + print(f"write/update {len(writes)}, delete {len(deletes)}") + for p in sorted(deletes): + print(" DEL", p) + + if not apply: + print("(dry run — pass --apply)") + return + import collections + for path, content in writes.items(): + os.makedirs(os.path.dirname(path), exist_ok=True) + open(path, "w").write(content) + for p in deletes: + os.remove(p) + # prune empty dirs + for root, dirs, files in os.walk(BRUNO, topdown=False): + if root == BRUNO: + continue + if not files and not dirs and os.path.basename(root) not in KEEP_DIRS: + os.rmdir(root) + # extend env file with any new vars + envp = f"{BRUNO}/environments/Bookhoard.yml" + env = open(envp).read() + add = [v for v in NEW_ENV_VARS if f"name: {v}\n" not in env] + if add: + block = "".join(f" - secret: true\n name: {v}\n" for v in add) + env = env.replace("variables:\n", "variables:\n" + block, 1) + open(envp, "w").write(env) + print("env vars added:", ", ".join(add)) + print("applied.") + + +if __name__ == "__main__": + main()