Commit Graph

72 Commits

Author SHA1 Message Date
neon db11f4eab9 fix(kobo): sync manual book read status updates to kobo devices
Project manual book status changes onto Kobo reading states and mark device snapshot rows as unsynced. This ensures manual status updates made in the web UI reach registered Kobo devices on the next sync without overwriting device bookmarks.

Closes #812
2026-07-28 22:24:15 +00:00
neon d0047bbf18 feat(library): add FB2 file metadata writing and library settings
Add server support for writing metadata, embedded covers, and FictionBook XML structures directly into FB2 format files. Introduce migration 0058 to persist FB2 file write preferences per library, and update library creator UI and locale catalogs.
2026-07-28 06:50:37 +00:00
neon dbb10b5c2f feat(koreader): add bookmark sync and exchange support
Add two-way bookmark sync support between KOReader plugin and server. Include migration 0056 for bookmark sync columns, server endpoints and services for bookmark exchange, and KOReader plugin bookmark management with comprehensive unit and E2E tests.
2026-07-28 04:34:46 +00:00
neon edc2a2ccfc feat(reader): make KOReader plugin sync non-blocking and resumable
Lifecycle handlers previously blocked on network work, so closing or suspending a book stalled the
device on slow or offline links. Persist snapshots first and drain them through a lifecycle outbox
with connected-state gates and phase-aware retries, so the UI stays responsive and pending work
survives restarts.

Move persistence behind a state manager with atomic flushes, and chunk sweeps, bulk downloads, and
annotation exchange through a background queue with checkpoints and transfer policies, so large
libraries make progress incrementally instead of restarting from scratch after an interruption.

Server side, add bulk manifest and page stats endpoints with batched queries and catalog query
filtering so the plugin can reconcile many books per round trip rather than one request per book.
2026-07-28 02:06:50 +00:00
neon 58c5b422d3 feat(scanner): align bookdock duplicate detection with upload behavior
Replace metadata-based duplicate checks (ISBN, title/author) during BookDock finalization with destination path lookup. This allows books with shared or incorrect metadata to be imported whenever their resolved library target paths differ, matching the standard upload workflow.

Closes #736
2026-07-22 20:06:49 +00:00
neon fb93f6ec20 fix(kobo): support GET request on analytics gettests endpoint
Kobo devices send GET requests to /v1/analytics/gettests during sync checks. Previously only POST was handled, causing 404 errors during subsequent syncs.

Closes #697
2026-07-22 17:39:45 +00:00
neon a38288b1a2 fix(books): join metadata table when resolving query-based book ids
Query-based book selections failed with a 500 error when filtering by metadata fields because findIdsByWhere queried the bare books table without joining book_metadata. Left join book_metadata so WHERE clause predicates referencing metadata fields resolve correctly.

Closes #732
2026-07-22 06:31:31 +00:00
neon 06286d49b1 fix(metadata): persist hardcover and open library ids during book dock import
Pass external provider IDs (including Hardcover and Open Library IDs) from book dock metadata selection through ingest and finalization so they are saved to book metadata on import.

Closes #704
2026-07-16 05:45:36 +00:00
neon 504c9ce0e7 feat(books): expand and refine library jump navigation
Extend jump rails across alphabetical, temporal, and categorical sorts.

Keep large libraries responsive with bounded database buckets and exact offsets.

Refine active-state tracking, Unicode initials, localized labels, accessibility, adaptive sizing, and flat or collapsed-series navigation.
2026-07-15 22:32:59 -06:00
neon 5923ea95a5 feat(books): add adaptive temporal jump rail
Add bounded, user-scoped temporal buckets for supported date sorts and adapt resolution to populated dates. Keep rail navigation responsive with viewport sizing, sparse-gap compression, and virtual-grid synchronization.
2026-07-15 22:32:58 -06:00
neon ed5dbc9507 feat(books): add duplicate book detection and resolution tools
Introduce backend duplicate scanning services, persistence schemas, and frontend view components to allow users to scan their library for duplicate books by title/author similarity, group potential matches, and bulk delete redundant duplicates safely.
2026-07-15 05:20:16 +00:00
Jadehawk ec84703cef feat(koreader): enhance settings and plugin behavior (#669)
* feat(koreader): add device file naming and improve plugin downloads (BO-658)

* fix(koreader): secure download paths and test collisions

* fix(koreader): preserve bulk collision identities

* fix(koreader): improve device file naming validation and path generation

* fix(koreader): remove duplicate settings lookup and audit file naming changes

* fix(koreader): resolve remaining CodeRabbit review issues

* fix(koreader): validate book detail query and finalize file naming release

validate book detail deviceId via DTO and ValidationPipe

localize new KOReader file naming settings (de, nl, sl)

bump KOReader plugin version to 1.3.0

* chore: rerun CI

* chore: normalized Closes #658 to not have embedded URL to issue. This is failing PR Conventions test

* test(koreader): align source and device removal assertions

* test(koreader): cover file naming settings flows

* test(koreader): cover file naming sync requests

* test(koreader): close remaining patch coverage gaps

* test(koreader): cover file naming server paths

* docs: add fixed2 modifier to file naming reference

* feat(koreader): support custom file naming patterns for KOReader sync

Add support for configurable KOReader file naming patterns, allowing users to define dynamic path templates for sync targets. Include UI draft settings, preview updates, and server path resolution.

Closes #658

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Co-authored-by: jadehawk <jadehawk@hotmail.com>
2026-07-14 11:26:27 -06:00
Chris Mansell a7c281955a feat(stats): expose privacy-aware reading insights (#662)
* build(deps): synchronize i18n lockfile

Record the locale dependencies so frozen installs can resolve the client workspace.

* feat(stats): expose privacy-aware reading insights

- Give administrators permission-gated access to bounded account activity.
- Let readers opt into auditable shared reading insights.

Ref #501
2026-07-12 23:11:35 -06:00
neon c70ff593ca fix(kobo): prevent reading state sync feedback loop (#657)
Exclude the originating device from reading-state fan-out and ignore stale or identical state echoes so paginated syncs can advance. Preserve pending new entitlements while propagating genuine changes to other devices.

Closes #654
2026-07-12 23:10:43 -06:00
neon f68a3ca98e feat(stats): add reading attempt history
Track repeat reads as distinct attempts while preserving existing status flows, device integrations, goals, sessions, and legacy data.

Closes #652
2026-07-12 17:41:35 +00:00
neon 95a7b94184 fix(scanner): support bigint inodes to prevent folder mis-merges
Store file inodes as numeric rather than bigint on PostgreSQL, and read/process them as BigInt in Node.js. This preserves exact 64-bit inodes from filesystems like MergerFS (hybrid-hash) and avoids folder mis-merging or disabled rename detection.

Closes #618
2026-07-11 20:18:10 +00:00
neon 5541e1acdc fix(kobo): track library snapshots and sync progress per device
Migrate the snapshot and snapshot books tables to be per-device instead
of per-user. This resolves a sync race condition where only the first
registered Kobo device for a user receives the library delta, leaving the
others empty.

Closes #314
2026-07-09 20:57:02 +00:00
neon 7154a6c0d3 feat(library): allow discarding duplicate books from finalize dialog
Provide an action in the Book Dock finalize dialog to discard files that
are identified as duplicate candidates. This saves users from having to close
the finalize window, re-select the duplicates in the main queue, and manually
discard them.

Closes #591
2026-07-09 04:01:11 +00:00
blinkidy ec4e5e2fc4 feat(storygraph): add StoryGraph sync integration (#568)
* feat(storygraph): add StoryGraph sync integration

Sync reading status and progress from BookOrbit to StoryGraph using
session-cookie authentication. Includes book matching with manual
rematch and edition selection, per-user sync toggles, a throttled
request queue, a storygraph_sync permission, and a StoryGraph
settings page.

Closes #274

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vi6e8ZJRAGMmRSmSj9sWXR

* fix(storygraph): address automated review findings

- clear stale cookie validation badge when inputs change
- reject saves with only one cookie value instead of silently dropping it
- handle linked-books load failures with a retryable error state so post-action
  reloads cannot misattribute failures to the action
- add a 30s request timeout so a hung StoryGraph response cannot stall a user's
  sync queue indefinitely
- reserve throttle slots synchronously so concurrent requests serialize
- reject empty cookie strings that would overwrite stored credentials
- report the retry response status when the rereading fallback fails
- add component specs for the connection card and linked books, plus regression
  tests for the throttle, settings, and retry-status fixes

* test(storygraph): fully restore setTimeout spies between queue tests

* feat(storygraph): add sync-scope option

Adds the book sync scope selector (all eligible books vs selected books
only) and the per-book StoryGraph sync control on the book details page,
mirroring the Hardcover integration's controls. All books with a syncable
status are now eligible — the previous connected-at cutoff that skipped
books finished before connecting is removed in favor of explicit scope
and per-book selection.

* fix(storygraph): stream live sync progress and surface failed books

The manual sync progress stream emitted the same mutated status object,
so distinctUntilChanged dropped every update after the first and the bar
never moved. Progress snapshots are now immutable and track processed
books (synced + skipped + failed), so the bar advances for every book.

Adds live synced/skipped/failed counters, a persistent last-run summary
from a terminal completed/cancelled event, and a failed-books list under
Manual sync backed by GET /storygraph/sync/failures. Details-page sync
state now refetches when the global sync scope changes.

(cherry picked from commit 075eeaa0c8678d4ebbba5dcce7aa15df42d64f51)

* feat(storygraph): show distinguishing identifiers in the editions list

Edition rows previously showed only format, pages, and language, making
identical-looking editions impossible to tell apart. The editions parser
now also scrapes ISBN, publisher, publication date, and cover image when
present, and each row shows the edition title, those identifiers, a cover
thumbnail, and the StoryGraph edition id linking to the edition page for
verification before switching.

(cherry picked from commit 09ec111613e9be149b556f64eb242cd3d82b296c)

* fix(storygraph): auto-refresh linked books after sync and add unmatched hint

The Linked books list only loaded on mount, so a book linked during a manual
sync run stayed on 'Not linked yet' until a manual page refresh. It now
silently reloads when a run completes (no spinner flash, keeps expanded rows),
guarded by a request id so overlapping reloads can't resolve out of order.
Books StoryGraph couldn't match automatically now show a short hint pointing
at 'Try auto-match' or pasting the StoryGraph URL.

(cherry picked from commit 1d0cfa1e06f3e211258620f7ad5e61373df7a310)

* feat(storygraph): compact edition link and wrap identifiers on mobile

The editions list showed the raw StoryGraph edition UUID as a long
monospace hyperlink, which was hard to read and cluttered the row. Replace
it with a small icon-only 'Open on StoryGraph' button (aria-labelled) next
to 'Use this', and let the publisher/date/ISBN line wrap instead of
truncating so the ISBN is no longer cut off on narrow/mobile screens.

(cherry picked from commit cdf4d64063534adeea9daafad11cd58a0cbc9a00)

* fix(storygraph): re-sync books re-added after being marked unread, clarify labels

A book synced while 'read' then marked 'unread' left its synced markers in
place, so re-marking it read compared against a stale lastSyncedStatus and was
silently skipped (shown as Linked but never re-pushed) until 'Try auto-match'.
On the unread transition the synced markers are now cleared while keeping the
match, so re-adding the book re-syncs.

Also present match methods with real casing (ISBN/Title/Cached/Manual) and
clarify that the manual-link field takes a StoryGraph URL or book ID (the code
after /books/), not an ISBN.

(cherry picked from commit 771e7e4fe793afbc30800717ae1c017ca307e5e1)

* refactor(storygraph): compare skippedBooks explicitly in sync-status dedup

The active-sync stream's equality check omitted skippedBooks, relying on
processedBooks (synced+skipped+failed) to capture skip-only changes. That is
correct but non-obvious; comparing every counter explicitly makes the intent
clear. No behavior change.

(cherry picked from commit f267088939f0d2493caadab8eb90f24ab4b28c0b)

* fix(storygraph): encode manually-entered book id in the lookup path

The manual-link id can be pasted directly, so a stray ?, #, or / would change
which /books/ URL is requested. Encode it before interpolating; a bad id now
404s cleanly instead of being stored.

* style(storygraph): fix prettier formatting and remove em dashes

The CI Lint (Client) job runs prettier --check, which flagged
StorygraphLinkedBooks.vue and StorygraphSyncProgress.vue. Reflow both
to prettier style and replace em dashes in UI strings and comments with
regular punctuation per project convention.

* fix(storygraph): harden sync access and batching

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-07-08 17:15:17 -06:00
Oscar Nogueira Neto 4eed87a6fd feat(readwise): sync highlights to Readwise (#578)
* feat(types): add readwise_sync permission

* feat(types): add readwise settings types

* feat(db): add readwise_user_settings table

* feat(readwise): add constants

* feat(readwise): add throttle queue

* feat(readwise): add REST client

* feat(readwise): add repository

* feat(readwise): add settings service

* feat(readwise): add sync service with watermark

* refactor(readwise): drop unused logger in settings service

* feat(readwise): add debounced scheduler

* feat(readwise): add event listener

* feat(readwise): add controller and dtos

* feat(readwise): wire module into app

* test(readwise): add readwise_sync to authorization matrix

* feat(annotation): emit annotation.created on device sync ingest

* feat(client): add readwise api client

* feat(client): add useReadwiseSettings composable

* feat(client): add Readwise settings page

* feat(client): add Readwise settings route

* feat(client): add Readwise to settings nav

* fix(readwise): avoid typeof this in settings type annotation

Production build (nest build → tsc via tsconfig.build.json) rejects
`Parameters<typeof this.repo.upsertSettings>[1]` with TS2304. Reference
the repository class type directly so the prod image compiles.

* fix(client): add Readwise sync to admin permission list

The admin UserFormDrawer renders a hardcoded permission list, not an
iteration of PERMISSION_LABELS, so readwise_sync was ungrantable via UI.
Add it to the Devices & Access group and the Standard preset, mirroring
Hardcover sync.

* feat(readwise): send book cover to Readwise via OpenLibrary ISBN

Populate the Readwise highlight image_url with the OpenLibrary cover URL
built from the book's isbn13 (falling back to isbn10). Uses ?default=false
so a missing cover yields 404 (no image) rather than a blank placeholder.
Books without an ISBN send no image_url. BookOrbit's own cover endpoint is
auth-gated and stores no public source URL, so a public third-party cover
URL is the zero-infra way to give Readwise an image.

* test(readwise): add type parameters to vi.fn mocks

oxlint (vitest/require-mock-type-parameters) fails on untyped vi.fn()
in the settings page test; the pre-commit hook only runs eslint, so
this surfaced under full lint:check.

* docs(readme): add Readwise Sync to features list

* fix(readwise): surface settings fetch errors in the UI

The settings page destructured error from useReadwiseSettings but never
rendered it. A failed fetch on mount left the user with an empty default
form and no indication that loading failed. Render an error banner when
error is set, and cover it with component tests.

* test(readwise): cover settings page token test, save, and visibility toggle

Adds component tests for the Test-connection flow (empty/valid/invalid
token), Save (success clears the field, failure surfaces an error toast),
and the show/hide token toggle — the interactive handlers Codecov flagged
as uncovered in ReadwiseSettings.vue.

* test(readwise): match settings buttons by exact label in test helper

Use an exact text match instead of substring matching in the testButton
helper so it can never return the wrong button if labels overlap.

* fix(readwise): harden highlight sync

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Co-authored-by: Oscar Nogueira Neto <oscarnogueira@gmail.com>
2026-07-07 19:09:28 -06:00
neon 39d9be30f9 feat(library): support LIBRARY_BROWSE_ROOT to restrict file path access
Introduce a LIBRARY_BROWSE_ROOT environment variable (defaulting to the
current directory) to restrict directories users can browse and select when
setting up library paths. Add a PathPolicyService on the backend to enforce
path containment checks securely, and update the frontend FolderPickerModal
to prevent navigating above the configured browse root.

Closes #567
2026-07-07 17:02:46 +00:00
neon 14b262fa35 feat(server): import Booklore reading state
Import Booklore and Grimmory reading sessions, personal ratings, and progress state through the migration pipeline.

Preserve book-level progress as primary-file progress when source file progress rows are absent.

Closes #482

Closes #483
2026-06-30 18:54:06 +00:00
neon c28ea29671 feat(sync): three way (kobo, koreader and bookorbit) progress and annotation sync (#338)
Introduces a custom KOReader plugin to synchronize reading progress, stats, and annotations. Adds backend controllers, services, and database schema updates to ingest plugin data. Updates the frontend to display KOReader annotations alongside built-in reader highlights.

Closes #337
2026-06-25 20:13:04 -06:00
neon 920aa3b477 build(deps): refresh client and server dependencies
Keep the dependency baseline current after the release window, and adapt the icon and zip integrations to current package APIs.
2026-06-21 06:00:55 +00:00
neon b1dff917ad feat(books): add jump rail navigation to book listings (#318)
Add server-backed jump buckets so large library, collection, and smart-scope listings can jump directly to sorted sections without loading every row up front.

Closes #163
2026-06-11 23:16:58 -06:00
neon 57e2f39ec5 feat(opds): expose all content files for multi-format books
Select all acquisition content files from the database for each book
instead of only the primary file. Return multiple acquisition links
in the OPDS catalog feed for books available in multiple formats,
and update unit and E2E tests to verify catalog output and downloads.

Closes #224
2026-06-03 20:21:01 +00:00
neon 3a29ac7679 fix(scanner): prevent nested folders from being marked missing
Virtual sibling cleanup is now deferred until the final missing pass instead of being handled eagerly during upsert. This ensures that real nested book folders are not prematurely marked as missing before their own candidates can be processed. A new validation step against the disk state guarantees only genuinely missing books are removed.

Closes #170
2026-06-02 04:52:25 +00:00
neon f20efdeedd fix(book-dock): refine duplicate detection for books without isbn
The duplicate detection logic in BookDockFinalizeService previously identified books as duplicates if they had the same title, even if their authors differed, when ISBNs were unavailable. This has been updated to include author names in the duplicate check for books missing ISBNs to prevent incorrect duplicate identifications.

Closes #175
2026-06-01 23:20:34 +00:00
Chris Mansell e659111e4e feat(library): add bulk rename tool and e2e coverage (#124)
* feat(library): add bulk rename based on file naming settings

Provides a way to rename all books in a library according to the user's
configured file naming template without requiring manual metadata edits.
Includes a streaming endpoint to report progress back to the frontend.

Closes #121

* test(library): add e2e tests for bulk file rename

Introduce a comprehensive E2E test suite for the bulk rename functionality to verify behavior across different library modes (book per file, book per folder) and handle edge cases like naming collisions or special characters.

Additionally fix a TypeScript compilation error in BulkRenameView.vue by replacing `replaceAll` with a regex-based `replace` for broader target compatibility.

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-05-28 09:27:03 -06:00
neon df20b6f8e6 fix(auth): tolerate refresh-token rotation races
Parallel refresh requests from a single client (multi-tab SPA, mobile tab restore)
tripped reuse-detection and logged users out every few hours. Track rotation
provenance via new rotated_at + replaced_by_token_hash columns and accept revoked-
token reuse only inside a configurable grace window with a chain that reaches a
live, non-logout link. Scope the rotate UPDATE with revoked_at IS NULL so a
concurrent logout or admin revoke fails the racing refresh instead of handing the
user a fresh access token.

Closes #120
2026-05-28 04:56:05 +00:00
neon 3a72ece26d fix(settings): gate integration tabs by integration permissions
Prevent unauthorized users from seeing integrations they cannot use and keep settings routing consistent with API authorization behavior. Add hardcover_sync and enforce it server-side so tab visibility and endpoint access stay aligned.
2026-05-26 19:52:32 +00:00
neon 53736a95b5 fix(server): deduplicate migration user-state upsert batches (#81)
* fix(server): deduplicate migration user-state upsert batches

Prevent migration failures when source data contains duplicate user-state rows for the same target keys. Keep the latest row per upsert key and prefer richer reading progress signals on timestamp ties, then cover the behavior with focused unit and e2e tests.

Closes #71

* ci: raise commitlint body line limit to 500 chars

Allow longer commit body lines so convention checks do not fail on detailed but still concise commit explanations in this repository.
2026-05-21 22:30:13 -06:00
adams79 aeedecc5ce fix(opds): search catalog by author, series, and ISBN (#78) 2026-05-21 20:11:55 -06:00
neon db2b3f9696 ci: run E2E only on nightly schedule, skip E2E on PR/push/manual CI, keep manual e2e workflow for on-demand runs, and align matrix/docs accordingly 2026-05-14 16:37:18 +00:00
neon c85379e4ff feat(auth): enforce edit restrictions on demo-restricted accounts 2026-04-25 23:34:57 -06:00
neon cfc3672a6c refactor: rename book-bucket to book-dock and lens to smart-scope 2026-04-20 22:57:27 -06:00
neon ce6a439062 feat(server): add unified entity manager with duplicate detection
- Remove author insights and duplicate suggestions panels, composables, DTOs, and all backend support
- Add genre and tag duplicate detection, merge, and dismiss with server-side paginated scan results
- Consolidate per-entity-type tools into a single entity-manager module backed by the Strategy pattern
- Support browse, merge, rename, delete, split, and dismiss across all entity types (author, genre, tag, narrator, publisher, language, series)
- Replace old tools module with unified entity-manager controller, service, and repository; add 85+ unit tests
2026-04-20 19:10:47 -06:00
neon f2ede06ea2 feat(scanner): add incremental dir-mtime scanning with concurrency fix
Core performance overhaul - new library_dir_scan_state schema table persists per-directory
mtimes so unchanged directories are skipped entirely on subsequent scans, reducing filesystem
stat() calls from O(all-files) to O(changed-dirs) for large libraries.

Incremental scan (walk.ts / scanner.service.ts):
- New library_dir_scan_state table (bigint mtime, FK to library_folders ON DELETE CASCADE)
- findBookCandidates / findLooseFileCandidates now accept knownDirMtimes and return WalkResult
  (candidates + skippedDirs + unchangedDirs + dirMtimes) instead of bare BookCandidate[]
- Per-dir mtime skip with 3 safety rules: never skip disc dirs, never skip dirs with disc
  subdirs, never skip non-root dirs whose parent mtime changed (guards stem-named merging)
- Root directory now also skippable - parent-mtime check conditionally skipped for root since
  root-level files are never stem-merged (explicitly excluded in buildBookCandidates)
- Settings hash invalidation: SHA-256 of {allowedFormats, formatPriority, excludePatterns,
  organizationMode} forces full scan when settings change or on first scan after server restart
- isInUnchangedDir guard prevents false book-missing marks for skipped dirs
- Math.round() on stat().mtimeMs before storing - fixes bigint rejection of macOS fractional
  milliseconds (e.g. 1776138191093.9556) in PostgreSQL

Walk improvements:
- Bounded concurrency: subdirs processed in chunks of 50 to avoid EMFILE on deep trees
- format + role cached on FileStat to avoid re-classifying the same file twice
- statFilesIntoAcc helper extracted to deduplicate parallel stat logic
- skippedDirs set propagated up from permission-denied dirs

ScanJobStore:
- acquireStartLock / releaseStartLock prevent concurrent scan starts for the same library
- markPendingRescan / consumePendingRescan queue a follow-up scan if one arrives while running

waitForStability:
- Accepts optional knownMtimeMs to skip a redundant stat() call when mtime is already known

File event processor (Issue 25, 11, 20):
- Own-book restore: check file's own book before searching all missing books, fixing false
  book-moved results when an editor saves with a new inode (atomic-save pattern)
- Settings cache per library avoids repeated DB lookups during reconcile of many missing books
- Partial move guard: verify old file is gone before reassigning inode (multi-file books)

Live notifications (scanner.service.ts / file-watcher.service.ts):
- scanBookFolder now emits scan:books:added via existing WebSocket infrastructure so the
  frontend grid updates live when the file watcher adds a book (all 3 code paths covered)
- bufferWatcherNotification() with 30s debounce fires one ScanCompleted notification per
  library summarising watcher-driven additions and removals ('Novels updated - 3 added')
- Removals wired in FileWatcherService.process() and reconcile() on book-missing events
- findLibraryName() added to ScannerRepository for notification title lookup
2026-04-17 17:18:14 -06:00
neon 40d829a8a9 feat(notifications): add notification center with real-time updates
- add notifications DB schema, repository, service, gateway, and controller
- add shared notification types, categories, and preference models
- add NotificationAccess permission with authorization matrix probe
- add frontend composable with Socket.IO subscription and REST API
- add NotificationPopover, NotificationItem, and NotificationPreferences components
- integrate notification producers in scanner, metadata fetch, book bucket, author enrichment, email, migration, and file write-back services
- add 30-day auto-cleanup via existing CleanupService cron
- add 29 backend unit tests for notification service and controller
2026-04-15 03:15:05 -06:00
neon 67ec10f703 feat: implement security audit fixes SEC-001 through SEC-031 2026-04-12 16:15:03 -06:00
neon 1f43ea2cb5 feat: enforce non-empty icons for collections, lenses, and libraries 2026-04-12 10:32:31 -06:00
neon b0d6bc1957 chore: setup git hooks, refactor e2e test setup and fix ts build errors 2026-04-10 18:46:41 -06:00
neon 698951a245 refactor(metadata): improve parsing, cover storage, and content type 2026-04-10 00:26:45 -06:00
neon b0ed068fd6 chore: improve download feature 2026-04-08 22:52:52 -06:00
neon 26d1e6ebc8 feat: improve header UI and optimize docker storage configuration 2026-04-08 20:34:38 -06:00
neon 9f6b7d4f2b feat: enhance PDF metadata extraction and refactor book cover storage 2026-04-08 20:32:38 -06:00
neon 54f2894762 feat(scanner): enhance upload durability and add E2E test suites 2026-04-07 12:44:12 -06:00
neon cfc424ea37 feat: move file write settings from global to per-library 2026-04-06 14:54:50 -06:00
neon decd0f1a2e fix(server): fix migration executor bugs and schema corrections 2026-04-05 20:24:08 -06:00
neon 9fd4e162aa feat(server): harden booklore migration with adapters and E2E tests 2026-04-05 16:01:04 -06:00