Extracts the ZIP index parser into a dedicated utility that avoids
loading the entire file into a single memory buffer. This fixes an
issue where CBZ files larger than 2GB would fail to load due to
Node.js buffer limits. Also extracts CBR error handling into helper
functions for readability.
Closes#457
Introduces Swagger and OpenAPI documentation generation for the
backend API. Configures bearer authentication globally while
allowing explicit bypasses for public endpoints. Exports nested
DTO classes across several modules so the NestJS Swagger plugin
can correctly reflect their schema definitions.
Closes#399
Routes multiple book selection to the edit tab instead of details.
Fixes a bug where deactivated kept-alive sources would passively
clobber the active navigation context by tracking component
activation state. Converts route param computed refs to shallow
refs to prevent reactivity issues during navigation.
Closes#455
The drizzle-orm query builder hits a RangeError when generating a single
bulk INSERT for tens of thousands of authors during backfill. Chunking
the insertions into smaller batches resolves the issue.
Closes#452
Introduce BookDockWorkQueue to serialize metadata extraction and auto-finalize
jobs. Files process newest-first with a 250 ms drain window and 500-1000 ms
inter-job spacing to avoid hammering metadata providers. Watched-folder rescans
now requeue files stuck in extracting or fetching status after a server restart
rather than silently dropping them.
Replace the global ThrottlerGuard with SensitiveEndpointThrottlerGuard, which
only activates on endpoints that carry explicit @Throttle() metadata. This
eliminates all @SkipThrottle() decorators from non-auth controllers and adds
missing throttle coverage for oidcGenerateLinkState and oidcGeneratePreviewState.
Update the login page to match the simplified 429 error message.
Fix countsByStatus to aggregate extracting and fetching rows into the pending
bucket so the UI counter stays accurate mid-pipeline. Add a secondary id sort
to findAll for deterministic pagination ordering.
Auto-detect the container memory limit via cgroup v1/v2 in entrypoint.sh and
compute --max-old-space-size proportionally (50-75 % of limit) instead of
defaulting to a fixed 2048 MB that OOMs small containers.
Closes#441
Pass the authenticated API fetcher to the streaming book loader instead of a static token. This ensures the token is automatically refreshed if it expires while reading, preventing 401 Unauthorized errors when navigating between chapters.
Closes#446
Extract content disposition header logic into a shared utility and use it consistently across book and koreader downloads, fixing failures when filenames contain non-ASCII characters.
Closes#448
The reader previously hardcoded left/right interactions (arrows, clicks,
swipes) to prev/next actions. This change uses physical left/right
navigation methods to respect the EPUB's resolved text direction (RTL).
Closes#444
Add full-stack support for uploading, managing, and using custom SVG icons for libraries and smart scopes. Includes database schema, server module for storage and sanitization, and client UI under Appearance settings.
Horizontal rules (`---`) are used in GitHub release notes as visual separators
between individual highlights, not as section boundaries. Treating them as
terminators caused only the first highlight to be parsed when a release body
used the multi-item format.
Remove the HR_RE guard from extractHighlightsBlock and stripHighlightsSection
so that only ATX headings end the Highlights block.
Record the major release boundary so semantic-release publishes the next release as v2.0.0 after the unreleased KOReader and Kobo sync work.
BREAKING CHANGE: establish the v2 database migration baseline for KOReader and Kobo sync.
Deploy the v2 image normally and allow startup migrations to complete before serving traffic.
Keep unreleased migration timestamps monotonic so Drizzle applies the pending schema changes for existing installs. Add a backend test guard to catch any future journal timestamp regressions before release.
Add the KOReader catalog surfaces, plugin package metadata, and device progress handling needed for the plugin workflow. Keep progress status updates non-blocking so secondary status failures do not reject successful sync writes.
* feat(kobo): support syncing native kepub files
* test(kobo): cover native kepub delivery paths
Add coverage for native kepub downloads and delivery hashes so CI validates the sync behavior changed by the PR.
---------
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
The organization mode determines how the scanner maps files to books. Changing it after creation leaves the library in an inconsistent state. This change disables the mode selector in the library edit modal and enforces the restriction in the backend service.
Additionally:
- Fixed TooltipContent z-index so it renders above modals.
- Bypassed rate limiting on the cover proxy endpoint to prevent 429 errors when loading large lists.
Users rely on community feedback when evaluating books in their library. This change
introduces aggregated community ratings and rating counts from external metadata
providers, allowing users to see popular opinion directly within the book details
without needing to leave the application.
Closes#414
Reuse the shared OPF mapper for Book Dock EPUB staging so embedded subject genres and Calibre page counts stay aligned with library imports.
Closes#423
Adds hardcoverEditionId to the database schema and shared types, enabling it to be stored as first-class metadata. Updates the file metadata parsers and writers (EPUB, PDF, CBZ) to read and embed this ID, and exposes it in the frontend metadata editor.
Closes#420
Previously, the edition release_date was ignored if the book had a release_year, due to the nullish coalescing operator treating the book release_year as a match for the first argument to parseYear. The new logic resolves the edition published year using both of its own date fields before falling back to the book date fields.
Closes#413
Allow users to define new custom fields for books beyond the standard
metadata properties. This provides flexibility for cataloging arbitrary
data like reading formats, editions, or personal ratings.
Closes#419
- Cache view state (DOM, scroll, virtual lists) using KeepAlive during navigation.
- Restore scroll position on view activation using requestAnimationFrame for grids and TanStack scrollToIndex for tables.
- Wrap multi-root views in single elements to support Transition animations.
- Blur focused descendants when aria-hidden is applied to fix Reka UI Chrome 124+ warnings.
Closes#417
Both AddToCollection and SendBook previously only operated on the explicitly provided list of book IDs. This prevented them from working correctly when a user selected all items across multiple pages in the UI. They now inherit from BulkSelectionDto and resolve queries to apply to all matching books.
Closes#416
Introduce a per-book sync toggle in the book detail view that lets
users include or exclude individual books from Hardcover sync.
Add a book sync scope setting with two modes:
- all_eligible: sync all books unless explicitly excluded (default)
- selected_only: sync only books the user explicitly includes
Extract sync eligibility logic into hardcover-sync-policy.ts for
testability. The toggle handles unread books correctly: sync is skipped
while the book is unread, but the user's inclusion intent is stored so
sync activates automatically once reading starts.
Closes#412
Pass metadata search params into stored provider lookups so providers can reject cached work identifiers that resolve to the wrong edition. Match Hardcover editions by normalized ISBN before falling back to provider search.
Closes#411
Add two per-file override flags to the Book Dock finalize flow:
- skipDuplicateCheck: bypasses ISBN/title+author duplicate detection for
a specific file. In book_per_folder mode the file naturally lands in
the same folder as the existing book, so createBookRecord auto-attaches
it as a second format on the same record.
- targetFileName: lets the user rename the file at the destination to
resolve a same-path collision. The extension is always appended from
the file format; if the user includes the extension it is stripped
first to avoid doubling. Setting targetFileName also implicitly skips
the duplicate check. A directory-traversal guard rejects any name
whose resolved path escapes the intended destination folder.
Frontend: results panel shows an "Import anyway" button for isDuplicate
rows and an inline rename input for file-exists-at-target rows. Both
retry calls forward defaultLibraryId/defaultFolderId so files that rely
on dialog-level defaults get the right destination. Concurrent double-
click is guarded. reimportingIds uses reactive(Set) so spinner and
disabled states update correctly.
Closes#213
triggerGlobal and triggerForLibrary skipped unpauseIfNeeded when no new
books were eligible (queued = 0), leaving a previously paused queue
permanently stuck on re-trigger.
Also add a live statusLabel to the auto-fetch settings components that
shows remaining queue count when active, replacing the misleading
"~0 eligible" count that appeared immediately after triggering a run.
Closes#408
Ensure the settings UI saves inherit toggles immediately and guards eligible-count previews against stale async responses. Align library override DTO validation with the shared partial override contract, and record manual library runs even when nothing queues.
Allow authenticated EPUB archive file requests to bypass the global request throttle so the streaming reader can load fragmented books without tripping 429 responses. Keep the existing access and archive path checks in place.
Fixes: #409
Update copyToClipboard to reliably handle the legacy execCommand fallback across browsers. The utility now selects text before copying, returns a success boolean, and ensures proper element cleanup. Added comprehensive tests for clipboard.ts and updated MagicLinksSettings tests.
Closes#204
Extract and map multiple series memberships from AudNexus (primary, secondary, and fallback series) to a new seriesMemberships array. Update Goodreads mapper tests to verify correct extraction of the first series.
Closes#383
The epub reader's "Define" feature relies on api.dictionaryapi.dev and
wiktionary.org. These domains were being blocked by the server's Content
Security Policy. Adding them to the connect-src directive allows the
client to successfully fetch word definitions.
Closes#403
Tooltips on the color and style pickers were rendering behind the
popup due to z-index conflicts. Removing them fixes the issue and
simplifies the UI.
Closes#402
Calculate CFI range overlaps to detect when a selection intersects with
an existing annotation. Pass the overlapping annotation ID to the popup
so the delete option correctly renders.
Closes#401
Add a preview-and-review workflow that matches Hardcover books by cached IDs, ISBNs, slugs, and title/author confidence before updating BookOrbit read status.
Import reading progress only when requested and safe, preserve existing local progress, and keep status imports resilient when progress writes fail.
Closes#394
Use the current book's effective metadata provider configuration to decide which provider ID fields and search providers are available in metadata editing flows.
Preserve explicit empty provider selections so library overrides can remove all provider fields.
Closes#320
Emit transfer events when watcher reconciliation moves a book between libraries. Keep file-as-book and folder-as-book watcher paths consistent across Docker, direct runs, and delayed copy-delete moves.
Closes#386
Send injected empty JSON bodies as Buffer chunks so Nest Fastify parsers can consume them without crashing.
Acknowledge BookOrbit collection item updates locally instead of proxying synthetic tags upstream.
Closes#396
The SeriesMembershipEditor was losing focus on every keystroke when typing into a previously empty series row because the key was bound to the seriesName, causing the input component to remount. This introduces stable row keys to ensure the input retains focus while updating the model.
Closes#370
Expose zero-book first-class metadata entities in browse so admins can review orphaned rows before deleting them.
Keep inline metadata values out of empty filtering because they only exist through book metadata rows.
Closes#370
The central directory entries are now sorted by filename to ensure the correct first image is used as the cover, rather than relying on the order of entries in the ZIP archive.
Closes#378
The GitHub Actions Trivy scan was failing due to CVE-2026-12151, CVE-2026-6734, and CVE-2026-9697 in undici version 7.24.7. It is a transitive dependency of cheerio in the server package. Running pnpm update undici removes the vulnerable version from the lockfile.
Offload large PDF parsing to a worker thread to prevent the Node.js event loop from being blocked. This ensures the application remains responsive during heavy metadata extraction tasks.
Closes#371
Update cover mutation paths to bump the book timestamp used for cover cache busting, and carry per-book collapsed-series cover versions so child covers do not reuse a representative book timestamp.