fix: cover proxy routing, session filtering, library tab deep-link, profile UX
Some checks failed
Release / Test backend (push) Successful in 1m3s
Release / Test UI (push) Successful in 58s
Release / Build and push images (push) Successful in 5m55s
Release / Deploy to prod (push) Failing after 48s
Release / Deploy to homelab (push) Successful in 21s
Release / Gitea Release (push) Successful in 29s
Some checks failed
Release / Test backend (push) Successful in 1m3s
Release / Test UI (push) Successful in 58s
Release / Build and push images (push) Successful in 5m55s
Release / Deploy to prod (push) Failing after 48s
Release / Deploy to homelab (push) Successful in 21s
Release / Gitea Release (push) Successful in 29s
- Catalogue/cover: rewrite raw scraped cover URLs to /api/cover/{domain}/{slug}
in handleCatalogue so all covers route through the backend proxy; fix broken
cdn.novelfire.net fallback in handleGetCover to read stored URL from PocketBase
- Catalogue/profile: add Svelte 5 onerror handlers on cover <img> tags to show
letter-initial placeholder when image fails to load
- Library page: read ?status URL param to initialise activeShelf tab on load so
/books?status=reading correctly pre-selects the Reading tab
- Sessions: filter bot/tool user-agents (curl, python, wget, etc.) and debug-IP
sessions from listUserSessions display; also purge them in pruneStaleUserSessions
- Profile: show email under username, quick stats chips (streak/chapters/completed)
in header, reading count on Library row, dedicated Sign out row, history covers
routed through /api/cover proxy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -293,7 +293,8 @@ func (s *Server) handleGetRanking(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// handleGetCover handles GET /api/cover/{domain}/{slug}.
|
||||
// Serves the cover image directly from MinIO when available; falls back to a
|
||||
// redirect to the novelfire CDN when the cover has not yet been downloaded.
|
||||
// redirect to the stored cover URL from PocketBase when the cover has not yet
|
||||
// been downloaded to MinIO.
|
||||
func (s *Server) handleGetCover(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("slug")
|
||||
if slug == "" {
|
||||
@@ -318,10 +319,20 @@ func (s *Server) handleGetCover(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: redirect to the CDN. The caller sees a working image; the
|
||||
// cover will be populated on the next catalogue refresh run.
|
||||
coverURL := fmt.Sprintf("https://cdn.novelfire.net/covers/%s.jpg", slug)
|
||||
http.Redirect(w, r, coverURL, http.StatusFound)
|
||||
// Fallback: read the stored cover URL from PocketBase and redirect to it.
|
||||
// This avoids the broken cdn.novelfire.net domain and uses the actual URL
|
||||
// scraped from the source. If the book is not found, return 404.
|
||||
meta, ok, err := s.deps.BookReader.ReadMetadata(r.Context(), slug)
|
||||
if err != nil {
|
||||
s.deps.Log.Warn("handleGetCover: ReadMetadata error", "slug", slug, "err", err)
|
||||
http.Error(w, "cover not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if !ok || meta.Cover == "" || strings.HasPrefix(meta.Cover, "/api/cover/") {
|
||||
http.Error(w, "cover not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, meta.Cover, http.StatusFound)
|
||||
}
|
||||
|
||||
// ── Preview (live scrape, no store writes) ─────────────────────────────────────
|
||||
@@ -1891,6 +1902,16 @@ func (s *Server) handleCatalogue(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Rewrite raw scraped cover URLs to go through the backend cover proxy.
|
||||
// /api/cover/{domain}/{slug} serves from MinIO when available, otherwise
|
||||
// redirects to the CDN. This avoids ERR_BLOCKED_BY_ORB when the source
|
||||
// site returns HTML error pages instead of images.
|
||||
for i := range books {
|
||||
if !strings.HasPrefix(books[i].Cover, "/api/cover/") {
|
||||
books[i].Cover = fmt.Sprintf("/api/cover/novelfire.net/%s", books[i].Slug)
|
||||
}
|
||||
}
|
||||
|
||||
hasNext := int64(page*limit) < total
|
||||
|
||||
w.Header().Set("Cache-Control", "public, max-age=60")
|
||||
|
||||
Reference in New Issue
Block a user