fix(ui): install prod deps in Docker runtime; add reindex endpoint for chapters_idx

- ui/Dockerfile: copy package-lock.json and run npm ci --omit=dev in the
  runtime stage so marked (and other runtime deps) are available to
  adapter-node at startup — fixes ERR_MODULE_NOT_FOUND for 'marked'
- storage: add ReindexChapters to Store interface and HybridStore — walks
  MinIO objects for a slug, reads chapter titles, upserts chapters_idx
- server: add POST /api/reindex/{slug} to rebuild chapters_idx from MinIO
This commit is contained in:
Admin
2026-03-04 00:59:36 +05:00
parent 49ba2c27c2
commit f80b83309a
4 changed files with 116 additions and 0 deletions

View File

@@ -126,6 +126,8 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
// Scrape status
mux.HandleFunc("GET /api/scrape/status", s.handleScrapeStatus)
mux.HandleFunc("GET /api/scrape/tasks", s.handleScrapeTasks)
// Re-index chapters for a book from MinIO into PocketBase chapters_idx
mux.HandleFunc("POST /api/reindex/{slug}", s.handleReindex)
// Progress API
mux.HandleFunc("GET /api/progress", s.handleGetProgress)
mux.HandleFunc("POST /api/progress/{slug}", s.handleSetProgress)
@@ -1067,3 +1069,43 @@ func (s *Server) handleScrapeTasks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(tasks)
}
// handleReindex handles POST /api/reindex/{slug}.
// It rebuilds the chapters_idx PocketBase collection for the given book by
// walking its MinIO objects. Use this when chapters were scraped but the index
// is out of sync (e.g. after a failed UpsertChapterIdx during scraping).
func (s *Server) handleReindex(w http.ResponseWriter, r *http.Request) {
slug := r.PathValue("slug")
if slug == "" {
http.Error(w, `{"error":"missing slug"}`, http.StatusBadRequest)
return
}
type reindexer interface {
ReindexChapters(ctx context.Context, slug string) (int, error)
}
ri, ok := s.store.(reindexer)
if !ok {
http.Error(w, `{"error":"store does not support reindex"}`, http.StatusNotImplemented)
return
}
count, err := ri.ReindexChapters(r.Context(), slug)
if err != nil {
s.log.Error("reindex failed", "slug", slug, "indexed", count, "err", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"error": err.Error(),
"indexed": count,
})
return
}
s.log.Info("reindex complete", "slug", slug, "indexed", count)
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"slug": slug,
"indexed": count,
})
}