Commit Graph

635 Commits

Author SHA1 Message Date
Admin
2f74b2b229 fix: only pull app images during deploy, not infra images
Some checks failed
Release / Test backend (push) Successful in 1m8s
Release / Check ui (push) Successful in 1m59s
Release / Docker (push) Failing after 1m37s
Release / Deploy to prod (push) Has been skipped
Release / Gitea Release (push) Has been skipped
docker compose pull without arguments pulls ALL services including
getmeili/meilisearch:latest — a recent Meilisearch version bump broke
compatibility with existing data on the server, causing meilisearch to
crash immediately on restart.

CI deploy now only pulls the 5 custom app images (backend, runner, ui,
caddy, pocketbase). Infrastructure images (meilisearch, valkey, minio,
redis, crowdsec) are updated deliberately via `just pull-infra`.

Also add pocketbase to `just pull-images` now that it's a custom image.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.1.4
2026-04-15 19:19:31 +05:00
Admin
cb9598a786 chore: migrate Docker builds to docker buildx bake
Some checks failed
Release / Test backend (push) Successful in 54s
Release / Check ui (push) Successful in 2m10s
Release / Docker (push) Failing after 3m43s
Release / Deploy to prod (push) Has been skipped
Release / Gitea Release (push) Has been skipped
Replace 5 sequential build-push-action steps with a single docker/bake-action
call backed by docker-bake.hcl. BuildKit now builds all images in parallel:
backend/runner/pocketbase share one Go builder stage (compiled once), while
caddy and ui build concurrently alongside the Go targets.

Workflow YAML goes from ~130 lines of build steps to ~35. Adding a new image
now only requires a new target block in docker-bake.hcl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.1.3
2026-04-15 19:09:27 +05:00
Admin
fc73756308 fix: run pocketbase as root + add healthcheck start_period
Some checks failed
Release / Test backend (push) Successful in 56s
Release / Check ui (push) Successful in 1m59s
Release / Docker (push) Successful in 7m49s
Release / Deploy to prod (push) Failing after 33s
Release / Gitea Release (push) Successful in 29s
- Remove non-root user from pocketbase Docker image; the existing pb_data
  volume was created by the previous root-running image so files are owned
  by root — running as a non-root appuser caused an immediate permission
  error and container exit
- Increase healthcheck retries to 10 and add start_period=30s so migrations
  have time to run on first boot before liveness checks begin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.1.2
2026-04-15 19:00:21 +05:00
Admin
3f436877ee fix: pocketbase healthcheck (add wget) + saveIfAbsent infinite recursion
Some checks failed
Release / Test backend (push) Successful in 1m6s
Release / Check ui (push) Successful in 1m56s
Release / Docker (push) Successful in 8m19s
Release / Deploy to prod (push) Failing after 36s
Release / Gitea Release (push) Successful in 22s
- Add wget to pocketbase Alpine image so the docker-compose healthcheck
  (wget http://localhost:8090/api/health) can actually run
- Fix saveIfAbsent calling itself instead of app.Save(c) — was an
  infinite recursion that would stack-overflow on a fresh install

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.1.1
2026-04-15 18:05:02 +05:00
Admin
812028e50d ci: add pocketbase image build + automated prod deploy step
Some checks failed
Release / Test backend (push) Successful in 5m39s
Release / Check ui (push) Successful in 2m1s
Release / Docker (push) Successful in 7m46s
Release / Deploy to prod (push) Failing after 1m53s
Release / Gitea Release (push) Successful in 35s
release.yaml:
  - Build and push kalekber/libnovel-pocketbase image on every release tag
  - Add deploy job (runs after docker): copies docker-compose.yml from the
    tagged commit to /opt/libnovel on prod, pulls new images, restarts
    changed services with --remove-orphans (cleans up removed pb-init)

ci.yaml:
  - Validate cmd/pocketbase builds on every branch push

Required new Gitea secrets: PROD_HOST, PROD_USER, PROD_SSH_KEY,
PROD_SSH_KNOWN_HOSTS (see deploy job comments for instructions).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.1.0
2026-04-15 11:06:27 +05:00
Admin
38cf1c82a1 fix: make migration 1 idempotent for existing installs
Each collection creator now skips creation if the collection already exists,
so deploying to an existing prod install no longer requires running
`migrate history-sync` manually. Migration 2 (missing fields) still applies
as normal since those fields genuinely don't exist yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:38:26 +05:00
Admin
fd0f2afe16 feat: replace pb-init-v3.sh with PocketBase Go migrations
Adds a custom PocketBase binary (cmd/pocketbase) that embeds PocketBase as a
Go framework with version-controlled migrations, replacing the fragile 419-line
shell script. Migrations apply automatically on every `serve` startup.

Migration 1 (20260414000001): full baseline schema — all 21 collections, both
  indexes on chapters_idx, and initial superuser creation from env vars.
Migration 2 (20260414000002): adds three fields found in code but missing from
  the old script — books.rating, app_users.notify_new_chapters_push,
  book_comments.chapter.

docker-compose: pocketbase service now uses the custom kalekber/libnovel-pocketbase
image; pb-init one-shot container removed (migrations replace it entirely).

Existing installs: run `migrate history-sync` once to mark migration 1 as done,
then restart — migration 2 will apply the three previously missing fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:30:42 +05:00
Admin
0f9977744a feat: enforce bearer token auth on all /api/admin/* endpoints
All checks were successful
Release / Test backend (push) Successful in 1m1s
Release / Check ui (push) Successful in 2m12s
Release / Docker (push) Successful in 6m30s
Release / Gitea Release (push) Successful in 29s
Adds BACKEND_ADMIN_TOKEN env var (set in Doppler) as a required Bearer
token for every admin route. Also fixes PocketBase filter injection in
notification queries and wires BACKEND_ADMIN_TOKEN through docker-compose
to both backend and ui services. Includes CLAUDE.md for AI assistant guidance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v4.0.0
2026-04-14 18:04:10 +05:00
root
9f1c82fe05 chore: add site_config collection to pb-init-v3.sh 2026-04-14 10:09:59 +05:00
root
419bb7e366 feat: seasonal decoration overlay + logo animation, admin site-theme config page 2026-04-13 21:42:12 +05:00
root
734ba68eed perf: cache translated chapter responses for 1 hour
Translation content is immutable once generated — add Cache-Control: public,
max-age=3600, stale-while-revalidate=86400 to handleTranslationRead so the
browser and any CDN reuse the response without hitting MinIO on repeat views.
Forward the header through the SvelteKit /api/translation/[slug]/[n] proxy
which previously stripped it by constructing a bare Content-Type-only Response.
2026-04-13 21:34:02 +05:00
root
708f8bcd6f fix: ai-jobs page empty list + missing Review button + no results in modal
Three bugs:

1. +page.server.ts returned an unawaited Promise — SvelteKit awaits it on
   the server anyway so data.jobs arrived as a plain AIJob[] on the client,
   not a Promise. The $effect calling .then() on an array silently failed,
   leaving jobs=[] and the table empty. Fixed by awaiting in the load fn.

2. +page.svelte $effect updated to assign data.jobs directly (plain array)
   instead of calling .then() on it.

3. handlers_textgen.go: final UpdateAIJob (payload+status write) used
   r.Context() which is cancelled when the SSE client disconnects. If the
   browser navigated away mid-job, results were silently dropped and the
   payload stayed as the initial {pattern} stub with no results array.
   Fixed by using context.Background() for the final write, matching the
   pattern already used in handlers_image.go.
2026-04-13 21:25:52 +05:00
root
7009b24568 fix: repair notification system (broken type assertion + wrong PocketBase filter quotes)
All checks were successful
Release / Test backend (push) Successful in 55s
Release / Check ui (push) Successful in 2m0s
Release / Docker (push) Successful in 7m19s
Release / Gitea Release (push) Successful in 26s
- Add bookstore.NotificationStore interface and wire it directly to *storage.Store
  in Dependencies, bypassing the Asynq wrapper that caused Producer.(*storage.Store)
  to fail with a 500 on every notification endpoint when Redis is configured
- Replace s.deps.Producer.(*storage.Store) type assertion in all 5 notification
  handlers with s.deps.NotificationStore (nil-safe, always works)
- Fix PocketBase filter single-quote bug in ListNotifications, ClearAllNotifications,
  and MarkAllNotificationsRead (PocketBase requires double quotes for string values)
v3.0.20260413-notifications
2026-04-13 21:18:49 +05:00
root
5b90667b4b fix: replace {#await} IIFE trick with $effect for streamed data on discover page
All checks were successful
Release / Test backend (push) Successful in 49s
Release / Check ui (push) Successful in 2m0s
Release / Docker (push) Successful in 7m4s
Release / Gitea Release (push) Successful in 28s
The {#await ... then} + {@const} IIFE pattern for assigning to $state
variables stopped working reliably in Svelte 5.53+. Replaced with a
proper $effect that awaits both streamed promises and assigns to state,
which correctly triggers reactivity.

Also: switch library page selection mode entry from long-press to a
'Select' button in the page header.
v2.6.93
2026-04-13 21:14:14 +05:00
root
dec11f0c01 fix: hero carousel — horizontal book spine stack instead of vertical overlap
All checks were successful
Release / Test backend (push) Successful in 49s
Release / Check ui (push) Successful in 1m55s
Release / Docker (push) Successful in 7m23s
Release / Gitea Release (push) Successful in 53s
v2.6.92
2026-04-13 21:08:51 +05:00
root
0f1ded2269 feat: stacked card effect on home hero carousel (desktop sm+)
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m54s
Release / Docker (push) Successful in 7m15s
Release / Gitea Release (push) Successful in 27s
v2.6.91
2026-04-13 19:56:51 +05:00
root
2473a0213e feat: redesign discover page — desktop two-col, full-screen mobile, skeleton, streaming, keyboard shortcuts
Some checks failed
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m48s
Release / Docker (push) Failing after 3m23s
Release / Gitea Release (push) Has been skipped
v2.6.90
2026-04-13 17:11:09 +05:00
root
1064c784d4 fix: clamp hero carousel card height to cover aspect ratio, prevent text overflow
All checks were successful
Release / Test backend (push) Successful in 58s
Release / Check ui (push) Successful in 1m59s
Release / Docker (push) Successful in 6m24s
Release / Gitea Release (push) Successful in 23s
v2.6.89
2026-04-13 10:32:51 +05:00
root
ed9eeb6262 feat: admin archive/delete UI for books (Danger Zone panel)
All checks were successful
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Successful in 1m48s
Release / Docker (push) Successful in 6m54s
Release / Gitea Release (push) Successful in 28s
v2.6.88
2026-04-12 22:49:15 +05:00
root
e6f7f7297d feat: add sticky sidebar to chapter reader with ToC, progress, book info, and chapter nav
Some checks failed
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Successful in 2m2s
Release / Docker (push) Failing after 7m3s
Release / Gitea Release (push) Has been skipped
v2.6.87
2026-04-12 22:44:24 +05:00
root
93cc0b6eb0 perf: fix discover page 4s load — parallel fetches + per-user caching
Three compounding issues caused the 4+ second load:

1. getAllRatings() ran sequentially after the first Promise.all group,
   adding it unnecessarily to the critical path. Now runs in parallel
   with listBooks/getVotedSlugs/getSavedSlugs (all 4 concurrent).

2. discovery_votes was fetched twice on every page load — once inside
   getBooksForDiscovery (via getVotedSlugs) and again by getVotedBooks.
   Fixed by caching getVotedSlugs results with a 30s TTL so the second
   call hits cache instead of PocketBase.

3. getVotedSlugs and getSavedSlugs were always uncached, hitting
   PocketBase on every navigation. Added short-TTL per-user Valkey
   cache entries (voted: 30s, saved: 60s). Cache is invalidated
   immediately after each write (upsertDiscoveryVote, clearDiscoveryVotes,
   undoDiscoveryVote, saveBook) so stale data is never served.
2026-04-12 22:34:46 +05:00
root
6af5a4966f fix: remove redundant X icons from SearchModal search input
Removed the custom clear button (shown when query is non-empty) and
suppressed the browser-native webkit search cancel button via CSS.
Only the single Cancel button remains, avoiding the double/triple X
clutter on wider screens.
2026-04-12 22:24:58 +05:00
root
14388e8186 fix: persist chapter-names results into job payload from sync SSE handler
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 1m56s
Release / Docker (push) Successful in 5m35s
Release / Gitea Release (push) Successful in 23s
The SSE (non-async) chapter-names handler streamed results to the client
but never wrote them into the PocketBase job payload — only the initial
{pattern} stub was stored. The Review button then fetched the job and
found no results, showing 'No results found in this job's payload.'

Fix: accumulate allResults across batches (same as the async handler) and
write the full {pattern, slug, results:[...]} payload when marking done.
v3-cleanup-20260413b
2026-04-12 18:44:09 +05:00
root
5cebbb1692 fix: restore pointer-events on ListeningMode and ChapterPickerOverlay
The wrapper div in +layout.svelte had pointer-events:none which blocked
all taps inside ListeningMode (chapter rows, buttons, scrolling). Removed
the wrapper div and moved the fly transition onto ListeningMode's own root
element so the slide-in animation works without stealing pointer events.
2026-04-12 18:31:50 +05:00
root
a0e705beec feat: redesign notifications settings with per-category in-app/push table
All checks were successful
Release / Test backend (push) Successful in 53s
Release / Check ui (push) Successful in 1m49s
Release / Docker (push) Successful in 5m49s
Release / Gitea Release (push) Successful in 21s
- Add notify_new_chapters_push field to AppUser, PATCH /api/profile, and profile loader
- Fix bell panel to reload notifications on every open (not just once on mount)
- Replace flat in-app + push toggles with structured category table (Category | In-app | Push)
- Add browser push master subscribe/unsubscribe row above the table
- Push column toggle disabled until browser is subscribed; shows — when unsupported/denied
- Update Notifications row hint to summarise active channels (In-app · Push / Off)
v3-cleanup-20260413
2026-04-12 17:56:53 +05:00
root
761ca83da5 fix: add push_subscriptions collection and notify_new_chapters migration to pb-init-v3.sh 2026-04-12 17:49:23 +05:00
root
48d0ae63bf feat: unified chapter picker overlay + currently reading quick-switch modal on reader
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m41s
Release / Docker (push) Successful in 5m40s
Release / Gitea Release (push) Successful in 21s
v3-cleanup-20260412b
2026-04-12 17:42:45 +05:00
root
44f81bbf5c surface audio-ready chapters: headphones badge on chapter list, instant-play prompt on reader
All checks were successful
Release / Test backend (push) Successful in 1m1s
Release / Check ui (push) Successful in 1m42s
Release / Docker (push) Successful in 6m13s
Release / Gitea Release (push) Successful in 21s
- getReadyChaptersForSlug(slug, preferredVoice) in pocketbase.ts: per-slug done jobs map, cached 60s, prefers user's voice
- GET /api/audio/chapters?slug=&voice= endpoint
- Chapter list (/books/[slug]/chapters): amber headphones icon on ready rows, play button for instant listen, banner showing ready count
- Chapter reader: audioReady + availableVoice from server load; 'Audio ready — Listen now' banner shown when audio exists and player is not yet active; sets voice preference before expanding player
v2.6.86
2026-04-12 11:13:36 +05:00
root
a2ce907480 fix svelte-check errors in /listen page: use meta_updated for sort, untrack data props
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 2m6s
Release / Docker (push) Successful in 6m7s
Release / Gitea Release (push) Successful in 21s
v2.6.85
2026-04-12 10:25:53 +05:00
root
e4631e7486 refactor: profile page grouped menu layout inspired by iOS settings style
Some checks failed
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Failing after 32s
Release / Docker (push) Has been skipped
Release / Gitea Release (push) Has been skipped
v3-cleanup-20260412
2026-04-12 10:21:20 +05:00
root
015cb8a0cd add Ready to Listen feature: audio book shelf on home + /listen browse page
Some checks failed
Release / Test backend (push) Successful in 53s
Release / Check ui (push) Failing after 45s
Release / Docker (push) Has been skipped
Release / Gitea Release (push) Has been skipped
- getBooksWithAudioCount() in pocketbase.ts aggregates done audio_jobs, deduplicates by chapter per slug, caches 5 min
- GET /api/audio/books endpoint
- home page: readyToListen shelf with headphones badge, chapter count, Listen button, hideable
- /listen page: full grid with search, sort (most narrated / A-Z / recent), empty state
v2.6.84
2026-04-12 10:18:40 +05:00
root
53edb6fdef fix: seek bars work on iOS (onchange+oninput), minimal bar is range input, float drag direction corrected
All checks were successful
Release / Test backend (push) Successful in 54s
Release / Check ui (push) Successful in 1m43s
Release / Docker (push) Successful in 6m0s
Release / Gitea Release (push) Successful in 20s
v2.6.83
2026-04-12 08:28:59 +05:00
root
f79538f6b2 fix: use untrack() in float clamp effect to prevent reactive loop that locked up the page
All checks were successful
Release / Test backend (push) Successful in 50s
Release / Check ui (push) Successful in 1m46s
Release / Docker (push) Successful in 6m18s
Release / Gitea Release (push) Successful in 21s
v2.6.82
2026-04-12 07:49:08 +05:00
root
a3a218fef1 fix: float circle releases pointer capture on pointerup/cancel so page stays responsive
All checks were successful
Release / Test backend (push) Successful in 49s
Release / Check ui (push) Successful in 1m53s
Release / Docker (push) Successful in 6m28s
Release / Gitea Release (push) Successful in 21s
v2.6.81
2026-04-11 23:56:14 +05:00
root
0c6c3b8c43 feat: show search button on chapter reader pages
All checks were successful
Release / Test backend (push) Successful in 1m3s
Release / Check ui (push) Successful in 1m51s
Release / Docker (push) Successful in 6m16s
Release / Gitea Release (push) Successful in 25s
v2.6.80
2026-04-11 23:37:12 +05:00
root
a47cc0e711 feat: float player is now a draggable circle with viewport clamping and tap-to-play/pause
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 2m15s
Release / Docker (push) Successful in 6m36s
Release / Gitea Release (push) Successful in 39s
v2.6.79
2026-04-11 23:35:49 +05:00
root
ac3d6e1784 fix: move hamburger backdrop outside <header> so drawer items are not blurred
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m49s
Release / Docker (push) Successful in 6m21s
Release / Gitea Release (push) Successful in 28s
v2.6.78
2026-04-11 23:22:32 +05:00
root
adacd8944b fix: AudioPlayer chapter picker highlights audioStore.chapter (playing) not page chapter prop
All checks were successful
Release / Test backend (push) Successful in 59s
Release / Check ui (push) Successful in 1m56s
Release / Docker (push) Successful in 6m24s
Release / Gitea Release (push) Successful in 28s
v2.6.77
2026-04-11 23:13:24 +05:00
root
ea58dab71c fix: hamburger backdrop starts below header so menu items are not blurred
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 1m58s
Release / Docker (push) Successful in 6m31s
Release / Gitea Release (push) Successful in 37s
v2.6.76
2026-04-11 18:39:32 +05:00
root
cf3a3ad910 feat: add backdrop blur overlay when mobile hamburger menu is open
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m46s
Release / Docker (push) Successful in 6m5s
Release / Gitea Release (push) Successful in 34s
v2.6.75
2026-04-11 17:30:55 +05:00
root
8660c675b6 fix: suppress mini-bar for float/minimal player styles; persist float position
All checks were successful
Release / Test backend (push) Successful in 1m0s
Release / Check ui (push) Successful in 1m42s
Release / Docker (push) Successful in 5m55s
Release / Gitea Release (push) Successful in 39s
v2.6.74
2026-04-11 17:20:09 +05:00
root
1f4d67dc77 fix: player float mode now works; add minimal player style
All checks were successful
Release / Test backend (push) Successful in 52s
Release / Check ui (push) Successful in 1m49s
Release / Docker (push) Successful in 6m29s
Release / Gitea Release (push) Successful in 35s
Float mode was broken because AudioPlayer was unmounted the moment
audioStore.active became true — exactly when the float overlay needs
to render. Fix: keep AudioPlayer mounted in float and minimal modes
regardless of audioStore.active; only standard mode shows the
'Controls below' message.

Adds a third 'minimal' style: a compact single-row bar (skip ±,
play/pause, seek, time) with no voice picker or chapter browser.
Voice picker and chapter button are hidden in the idle pill too.

Settings UI updated to show all three options with a live
description of what each style does.
v2.6.73
2026-04-11 16:00:46 +05:00
root
b0e23cb50a feat: floating scroll nav buttons in scroll reader mode
All checks were successful
Release / Test backend (push) Successful in 46s
Release / Check ui (push) Successful in 1m38s
Release / Docker (push) Successful in 6m29s
Release / Gitea Release (push) Successful in 31s
Up/down chevron buttons fixed to the bottom-right of the viewport.
At the top of the chapter the up button becomes a Prev chapter link;
at the bottom the down button becomes an amber Next chapter link.
Hidden in focus mode (uses its own pill). Lifts above the audio
mini-player when it is active.
v2.6.72
2026-04-11 15:52:14 +05:00
root
1e886a705d feat: notifications modal, admin dedup, and in-app notification preferences
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 1m53s
Release / Docker (push) Successful in 6m22s
Release / Gitea Release (push) Successful in 35s
- Replace bell dropdown with full-screen NotificationsModal (mirrors SearchModal pattern)
- Notifications visible to all logged-in users (not just admin)
- Admin users excluded from new-chapter fan-out (dedup vs Scrape Complete notification)
- Users with notify_new_chapters=false opted out of new-chapter in-app notifications
- Toggle in profile page to enable/disable in-app new-chapter notifications
- PATCH /api/profile endpoint to save notification preferences
- User-facing /notifications page (admin redirects to /admin/notifications)
v3-cleanup-20260411b v2.6.71
2026-04-11 15:31:37 +05:00
root
19b5b44454 feat: hold-to-repeat page buttons and tap-counter slider in paginated reader
All checks were successful
Release / Test backend (push) Successful in 49s
Release / Check ui (push) Successful in 1m47s
Release / Docker (push) Successful in 6m8s
Release / Gitea Release (push) Successful in 36s
v2.6.70
2026-04-11 15:13:34 +05:00
root
b95c811898 feat: web push notifications for new chapters
All checks were successful
Release / Test backend (push) Successful in 4m12s
Release / Check ui (push) Successful in 1m53s
Release / Docker (push) Successful in 5m46s
Release / Gitea Release (push) Successful in 35s
- Service worker (src/service-worker.ts) handles push events and
  notification clicks, navigating to the book page on tap
- Web app manifest (manifest.webmanifest) linked in app.html
- Profile page: push notification toggle (subscribe/unsubscribe)
  using the browser Notification + PushManager API with VAPID
- API route POST/DELETE /api/push-subscription proxies to backend
- Go backend: push_subscriptions PocketBase collection storage
  methods (SavePushSubscription, DeletePushSubscription,
  ListPushSubscriptionsByBook) in storage/store.go
- handlers_push.go: GET vapid-public-key, POST/DELETE subscription
- webpush package: VAPID-signed sends via webpush-go, SendToBook
  fans out to all users who have the book in their library
- Runner fires push to subscribers whenever ChaptersScraped > 0
  after a successful book scrape
- Config: VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT env vars
- domain.ScrapeResult gets a Slug field; orchestrator populates it
v3-cleanup-20260411
2026-04-11 14:59:21 +05:00
root
3a9f3b773e fix: reduce log noise during catalogue/book scrapes
All checks were successful
Release / Test backend (push) Successful in 55s
Release / Check ui (push) Successful in 2m0s
Release / Docker (push) Successful in 5m57s
Release / Gitea Release (push) Successful in 32s
Demote per-book and per-chapter-list-page Info logs to Debug — these
fire hundreds of times per catalogue run and drown out meaningful signals:
- orchestrator: RunBook starting (per book)
- metadata saved (per book)
- chapter list fetched (per book)
- scraping chapter list page N (per pagination page per book)

The 'book scrape finished' summary log (with scraped/skipped/errors
counters) remains at Info — it is the useful signal per book.
v2.6.69
2026-04-11 12:39:41 +05:00
root
6776d9106f fix: catalogue job always shows 0 counters after cancel/finish
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 1m55s
Release / Docker (push) Successful in 6m5s
Release / Gitea Release (push) Successful in 32s
Two bugs fixed in runScrapeTask / runCatalogueTask:

1. FinishScrapeTask was called with the task's own context, which is
   already cancelled when the task is stopped. The PATCH to PocketBase
   failed silently, leaving all counters at their initial zero values.
   Fix: use a fresh context.WithTimeout(Background, 15s) for the write.

2. BooksFound was double-counted: RunBook already sets BooksFound=1 on
   success, but the accumulation loop added an extra +1 unconditionally,
   reporting 2 books per successful scrape.
   Fix: result.BooksFound += bookResult.BooksFound  (drop the + 1).
v2.6.68
2026-04-11 12:33:30 +05:00
root
ada7de466a perf: remove voice picker from profile, parallelize server load
All checks were successful
Release / Test backend (push) Successful in 50s
Release / Check ui (push) Successful in 1m52s
Release / Docker (push) Successful in 5m58s
Release / Gitea Release (push) Successful in 33s
Remove the TTS voice section from the profile page — it fetched
/api/voices on every mount, blocking paint for the full round-trip.
Voice selection lives on the chapter page where voices are already loaded.

Rewrite the server load to run avatar, sessions+stats, and reading history
all concurrently via Promise.allSettled instead of sequentially, cutting
SSR latency by ~2-3x on the profile route.
v2.6.67
2026-04-11 10:41:35 +05:00
root
c91dd20c8c refactor: clean up profile page UI — remove decorative icons
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 1m51s
Release / Docker (push) Successful in 6m21s
Release / Gitea Release (push) Successful in 36s
Remove all decorative SVG icons (checkmarks, chevrons, stars, fire,
external-link arrows, empty-state illustrations). Replace icon-only
interactive elements with text (avatar hover shows 'Edit', voice sample
buttons show 'Play'/'Stop', danger zone toggle shows 'Open'/'Close').
Replace SVG avatar placeholder with the user's initial. Strip emoji
from stats cards and genre chips. Tighten playback toggle descriptions.
v2.6.66
2026-04-11 10:21:14 +05:00