- Add GetUserVisibleLibrariesData() method for server-side rendering
- Add GetLibraryTypeData() method for SSR type fetching
- These helpers return data directly instead of JSON responses
- Enables Hybrid SSR pattern while preserving API endpoints
- Add context import for new methods
Complete the rename by updating:
- DeviceCatalogs struct field: BookmannUuid → BookhoardUuid (models.go)
- Generated queries: Update all references (queries.sql.go)
- Local variables: bookmannUUID → bookhoardUUID (kobo.go)
- Struct field access: catalog.BookmannUuid → catalog.BookhoardUuid
All "bookmann" and "BOOKMANN" references are now eliminated from the codebase.
Part of project rename to Bookhoard.
Changes:
- Update comments: "Bookmann UUID" → "Bookhoard UUID"
- Rename sidecar struct field: Bookmann → Bookhoard
- Update type names: SidecarBookmannConfig → SidecarBookhoardConfig
- Fix test database name in queue_test.go
- Fix uppercase env var examples in KOBO_SETUP.md
Internal Go variable names (BookmannUuid, bookmannUUID) left unchanged
as they're implementation details that don't affect functionality.
Part of project rename to Bookhoard.
- Removed comment about 'Ebook notes handlers (backward compatibility using views)'
- Removed comment references to non-existent GetEbookNotes and GetEbookHighlights
- Cleaned up misleading legacy documentation
This is part of legacy code cleanup Phase 1.
Phase 1: Documentation Cleanup
- Integrate conversion service with OPDS handler
- Convert EPUB to KEPUB format on download request
- Cache converted files to reduce processing time
- Support per-device catalog with format availability
- Maintain backward compatibility with existing downloads
- BulkLinkBooks: manually link multiple unlinked books to media items
- AutoLinkBooks: automatically link books above confidence threshold
- GetUnlinkedBookSuggestions: get match suggestions for specific unlinked book
- Support batch operations with individual result tracking
- Configurable confidence thresholds and limits
- Add reading stats endpoint with daily/monthly history
- Add device usage statistics (sync count, time spent)
- Add popular books view with completion rates
- Server-side rendered analytics page with HTMX
- Date range filtering for reading history
Add extensive unit tests for collection rule matching logic:
Test Coverage (30+ tests):
1. Rule Evaluation Tests:
- Equals operator (match and no match)
- Not equals operator (match and no match)
- Contains operator (case-insensitive)
- Not contains operator
- Starts with operator
- Ends with operator
- Greater than operator (numeric)
- Less than operator (numeric)
- NULL field handling
2. Comparison Function Tests:
- Case-insensitive string matching
- Empty string edge cases
- Numeric edge cases (0, negative, large numbers)
- Type conversion validation
3. Multi-Rule Tests:
- Matches first rule
- Matches second rule (first fails)
- No matches across all rules
- Empty rules array
4. Complex Rule Scenarios:
- Multiple conditions on same book
- Different field types (genre, author, year, series)
- Various operators tested
- Table-driven test for 8 scenarios
5. Edge Cases and Error Handling:
- Invalid operator returns false
- Non-existent field returns false
- Invalid numeric strings handled
- Type conversion failures
Test Structure:
- Clear test names explaining what's being tested
- Assertion messages explain expected vs actual
- Uses testify/assert for better error messages
- Table-driven tests for multiple scenarios
- Comprehensive edge case coverage
Code Coverage:
- evaluateRule() function
- compareValues() function
- checkRulesAgainstBook() function
- All operators: equals, not_equals, contains, not_contains,
starts_with, ends_with, greater_than, less_than
- All field types: genre, author, series, copyright_year
Test Results:
- All 30+ tests passing
- Coverage of critical collection rule logic
- Prevents regressions in rule matching
- Validates edge case handling
This test suite ensures the collection auto-assignment
feature works correctly for all supported rule types and operators.
Implement real-time collection updates when books are added/removed:
Backend Changes:
- Added connManager to CollectionHandler struct
- Updated constructor to accept ConnectionManager
- Updated all NewCollectionHandler() calls in ebook.go and main.go
- Added WebSocket broadcasts in AddBooks() handler
- Added WebSocket broadcasts in BulkRemoveBooks() handler
- Broadcasts collection_updated events with:
- collection_id: Which collection changed
- action: books_added or books_removed
- book_ids: Array of affected book IDs
- count: Number of books changed
Frontend Changes:
- Added WebSocket connection in collections UI
- connectWebSocket() establishes connection to /ws/sync
- Listens for collection_updated events
- Shows toast notification on collection change
- Auto-reloads page after 1 second to show updated book list
- Auto-reconnect on disconnect (5s delay)
- Error handling for WebSocket failures
WebSocket Event Format:
{
"type": "collection_updated",
"timestamp": "2026-02-01T12:00:00Z",
"data": {
"collection_id": "uuid",
"action": "books_added",
"book_ids": ["uuid1", "uuid2"],
"count": 2
}
}
User Experience:
- When another user adds books to a collection, all connected clients see:
1. Toast notification: "Collection updated: books_added (2 books)"
2. Page auto-refreshes after 1 second
3. Updated book list displays
- Same for book removal
- Works across multiple browser tabs/devices
- No manual refresh needed
Technical Notes:
- Broadcasts to ALL connected WebSocket clients
- Client-side filtering by collection_id
- Existing progress/conflict broadcasts continue to work
- Connection manager handles broadcast distribution
Resolves Limitation #4: Real-time Collection Updates
Complete bulk operations for collections management:
BULK ADD BOOKS:
- Implemented searchBooks() with real API integration
- Multi-select checkboxes for book selection
- SelectedBooks Set tracks chosen books
- AddSelectedBooks() sends array to existing endpoint
- Uses existing POST /api/collections/:id/books endpoint
BULK REMOVE BOOKS:
- New endpoint: POST /api/collections/:id/books/bulk-remove
- Checkboxes on each book card for selection
- BooksToRemove Set tracks selections
- Live counter showing selected count
- BulkRemoveBooks() handler removes all in one API call
- More efficient than N individual DELETE requests
Frontend Changes:
- Selected counter badge shows number selected
- Bulk remove button (enabled when books selected)
- Checkboxes on all books for multi-select
- Confirmation dialog for bulk operations
- Toast notifications with counts
Backend Changes:
- BulkRemoveBooks() handler in collections.go
- Accepts book_ids array, returns removed/total counts
- Iterates and removes, counting successes
- Route: POST /api/collections/:id/books/bulk-remove
API Request:
{
"book_ids": ["uuid1", "uuid2", "uuid3"]
}
API Response:
{
"removed": 3,
"total": 3
}
Tests Added:
- TestCompareValues_* (existing)
- TestEvaluateRule_* (existing)
Resolves Limitations #2 (Bulk Operations) and #5 (Bulk Remove)
Add comprehensive API support for Phase 9 features:
Collections API (/api/collections):
- GET /collections: List all user collections
- POST /collections: Create new collection
- GET /collections/🆔 Get collection details
- PUT /collections/🆔 Update collection
- DELETE /collections/🆔 Delete collection
- GET /collections/:id/books: Get books in collection
- POST /collections/:id/books: Add books to collection
- DELETE /collections/:id/books/:bookId: Remove book from collection
Device Shelf Mapping API (/api/devices/:id/collections):
- GET: Get all collection-to-shelf mappings for device
- POST: Create new mapping
- PUT /:collectionId: Update mapping
- DELETE /:collectionId: Delete mapping
Book Matching API:
- POST /sync/books/query: Query books by identifiers
- POST /devices/:deviceId/sync/link-book: Manual book linking
- GET /devices/:deviceId/sync/unlinked-books: List unmatched books
- GET /devices/:id/file-aliases: Get device file aliases
- POST /devices/:id/file-aliases: Create file alias
- PUT /devices/:id/file-aliases/:aliasId: Update alias
- DELETE /devices/:id/file-aliases/:aliasId: Delete alias
- GET /books/match: Search for book matches
All new endpoints - no existing APIs modified.
Add data retrieval helpers for SSR template rendering:
- GetDeviceMappingsData: Fetch device shelf mappings for device settings UI
- GetUserCollectionsList: Get all user collections for dropdowns and listings
- GetCollectionData: Get single collection with metadata
- GetCollectionBooksData: Get books in a collection
These functions support the Phase 9 frontend features by providing
efficient data access for template rendering without modifying
existing API endpoints.
Add progress list page showing reading progress across all devices:
- GetAllProgress API endpoint: Returns all user progress with media details
- GetAllProgressData helper: Fetches progress data for SSR rendering
- ProgressWithMedia struct: Combines progress with book metadata
- Progress page template: Displays progress bars, device icons, sync sources
Features:
- Visual progress bars with percentage
- Device-specific icons (Kobo, KOReader, Web, Mobile)
- Last sync timestamp and device attribution
- EPUB CFI location display
- Cover image support with fallback
- Responsive grid layout
This gives users a unified view of their reading progress across all synced devices.
- Add GetDevicesData() to DeviceHandler (returns raw data, not JSON)
- Update devices template signature to accept pre-rendered data
- Add server-side rendering of devices and pending registrations
- Update JavaScript to use location.reload() after CRUD operations
- Remove getDeviceIcon dependency on JavaScript function
- Use templ if/else instead of ternary operators for device status
Preserves all API endpoints and backward compatibility
- Add OPDS handler for device catalog and book downloads
- Add collections handler for collection CRUD
- Add book matching service for cross-device book linking
- Add sidecar handler for Kobo metadata sync
- Add sync handler for device synchronization
- Add max_devices column to users table (default: 10)
- Add UpdateUserMaxDevices database query
- Add CountUserDevices database query
- Add UpdateUserMaxDevices handler with validation (1-100 devices)
- Add PUT /api/auth/users/:id/max-devices endpoint (admin only)
- Update UserList struct to include max_devices field
- Validate user ID format and max_devices range
- Return appropriate errors for invalid requests
Implement conflict detection for concurrent reading progress updates from different devices. Adds conflict management endpoints for listing, viewing, and resolving conflicts.
- Add ConflictHandler with CRUD endpoints for conflict management
- Implement automatic conflict detection in KOReader progress updates
- Add WebSocket broadcast for real-time conflict notifications
- Add database query for listing user conflicts by status
- Add integration tests and Bruno API test collection
- Add parseTokenUUID() helper to convert string to pgtype.UUID
- Update RefreshAccessToken to parse token string to UUID before validation
- Update Logout to parse token string to UUID before revoking
- Update CreateRefreshToken to pass UUID directly to database
- Update auth.go: fix return value order from CreateRefreshToken
- Remove unnecessary comments for cleaner code
- Add Kobo sync handler with markup, bookmark, analytics, and initialization endpoints
- Add Kobo integration tests and Bruno API test collection
- Move device approve/reject routes from public to protected routes
- Enhance test infrastructure with DATABASE_URL support and helper functions
- Fix device GetDevice handler nil pointer handling
- Clean up test reports and session files
Add WebSocket broadcast to KOReader progress sync:
- Integrate ConnectionManager into KOReaderHandler
- Broadcast progress updates on successful sync
- Include source device information (model, type)
- Real-time updates to all connected clients
When KOReader devices sync reading progress, all connected
WebSocket clients (web browsers, mobile apps, other devices)
receive instant updates.
Add WebSocket ConnectionManager to Handler:
- Add connManager field to Handler struct
- Update NewHandler to accept ConnectionManager parameter
- Update SetupRoutes to pass ConnectionManager through
- Import sync package with alias to avoid conflicts
This enables progress handlers to broadcast updates via WebSocket.
- Create SyncProgress for bidirectional progress synchronization
- Create GetMetadata for book progress and annotation retrieval
- Create GetLibrary for user library sync
- Create SyncBookmarks for annotation management
- Support device matching by UUID, file path, or title/author
- Implement immediate and checkpoint sync modes
- Handle bookmarks, highlights, and notes synchronization
- Part of Phase 3 KOReader Integration implementation
- Add POST /api/libraries/:id/scan endpoint for admin library scanning
- Add GET /api/libraries/:id/media-items endpoint for library media items
- Move /api/libraries/types to public endpoint (no auth required)
- Update ScanEbooks handler to support library_id parameter
- Fix CreateMediaItem ISBN field to use pgtype.Text wrapper
- Fix UpdateMediaItem to use correct Isbn field name
- Resolve type mismatch between request and database params
Resolves compilation errors in media item handlers
- Use c.Get("user").(database.Users) instead of c.Get("user_id").(string)
- Extract userUUID from user.ID.Bytes ([16]byte)
- Properly convert to pgtype.UUID for service layer
- Remove unnecessary uuid.Parse call
This fixes 500 Internal Server Error when creating libraries via API.
The JWT middleware sets user as database.Users struct, not string.
Related to Bruno Create Library request testing.
- Fix scoping issue with err variable in os.Stat check
- Properly check for non-existent vs inaccessible folders
- Use reassignment (=) instead of declaration (:=) since err already declared
- Add GET /api/media-items/search endpoint
- Try partial matching first (ILIKE with wildcards)
- Fallback to fuzzy search if no results found
- Return 404 with 'no results found' when no matches
- Limit results to 50 items by default
- Supports search across title, author, series, tags, contributors
- Respects library visibility settings per user
- Add /bookshelf route as default page for logged-in users
- Update login and register handlers to redirect to /bookshelf
- Update homepage to auto-redirect to /bookshelf when logged in
- Preserve /dashboard route for backward compatibility
- Update test redirects to use /bookshelf
Changes:
- main.go: Add /bookshelf protected route
- auth.go: Change login/register redirects from /api/dashboard to /bookshelf (2 locations)
- edge_cases_test.go: Update test redirect to /bookshelf
- Maintains backward compatibility with existing /dashboard route
This makes the beautiful bookshelf the default landing page
for all authenticated users while keeping the old dashboard accessible.
- Fix scheduler.go log.Printf calls to convert pgtype.UUID to string before formatting
- Fix ebook.go fmt.Printf calls to convert pgtype.UUID to string before formatting
- Add missing Enabled field to rate limiter config in security test
- Prevents format string errors when logging library IDs
This resolves compilation errors where pgtype.UUID was being formatted
with %s which expects a string, not a UUID struct.