fix(router): use service layer for SSR book loading
Replace direct database call with service layer to fix SSR returning 0 books on initial page load. Root Cause: - SSR was calling cfg.Queries.SearchMediaItemsUnified directly - API was using MediaHandler.ExecuteSearch via service layer - Both code paths had different parameter structures Solution: - Use same MediaHandler.ExecuteSearch handler as API - Build services.SearchParams struct (same as API path) - Convert user.ID string to pgtype.UUID for service layer - Remove unused books variable Changes: - Parse user.ID to UUID before building search params - Build services.SearchParams with empty filters for SSR - Call cfg.MediaHandler.ExecuteSearch instead of direct DB - Use textToString helper (already exists in router package) - Remove unused books variable declaration Both SSR and API now use identical search logic, ensuring consistent behavior. HTMX search continues working as before. Fixes: Issue #1 - SSR returns 0 books on initial load Related: Issue #2 - Search/filter returning JSON instead of HTML
This commit is contained in:
+32
-26
@@ -175,7 +175,6 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch first page of books for SSR
|
// Fetch first page of books for SSR
|
||||||
var books []database.SearchMediaItemsUnifiedRow
|
|
||||||
var bookInfoList []handlers.BookInfo
|
var bookInfoList []handlers.BookInfo
|
||||||
totalCount := 0
|
totalCount := 0
|
||||||
limit := 50
|
limit := 50
|
||||||
@@ -195,42 +194,49 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
offset = o
|
offset = o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Convert user.ID (string) to pgtype.UUID for service layer
|
||||||
books, err = cfg.Queries.SearchMediaItemsUnified(c.Request().Context(), database.SearchMediaItemsUnifiedParams{
|
userUUID, err := uuid.Parse(user.ID)
|
||||||
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
|
||||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
|
||||||
AuthorFilter: pgtype.Text{String: "", Valid: true}, // Changed: Valid=true for empty string
|
|
||||||
SeriesFilter: pgtype.Text{String: "", Valid: true}, // Changed
|
|
||||||
GenreFilter: pgtype.Text{String: "", Valid: true}, // Changed
|
|
||||||
LanguageFilter: pgtype.Text{String: "", Valid: true}, // Changed
|
|
||||||
YearMin: pgtype.Int4{Int32: 0, Valid: true}, // Changed: use 0 as default
|
|
||||||
YearMax: pgtype.Int4{Int32: 0, Valid: true}, // Changed
|
|
||||||
HasCover: pgtype.Bool{Bool: false, Valid: true}, // Changed
|
|
||||||
SearchQuery: pgtype.Text{String: "", Valid: true}, // NEW: Required
|
|
||||||
IsExactSearch: pgtype.Bool{Bool: false, Valid: true}, // NEW: Required
|
|
||||||
// SearchPattern: pgtype.Text{Valid: false}, // NEW: Empty for no exact search
|
|
||||||
Sort: pgtype.Text{String: "created_at DESC", Valid: true},
|
|
||||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
|
||||||
Offset: pgtype.Int4{Int32: int32(offset), Valid: true},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("SearchMediaItemsUnified failed: %v", err)
|
log.Printf("Failed to parse user ID: %v", err)
|
||||||
|
return renderErrorPage(c, "Error loading user", "user_id_error")
|
||||||
|
}
|
||||||
|
// Build search params (same as search.go:76-91)
|
||||||
|
params := services.SearchParams{
|
||||||
|
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||||
|
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
||||||
|
SearchQuery: "", // Empty for initial SSR load
|
||||||
|
AuthorFilter: "",
|
||||||
|
SeriesFilter: "",
|
||||||
|
GenreFilter: "",
|
||||||
|
TagsFilter: "",
|
||||||
|
LanguageFilter: "",
|
||||||
|
YearMin: 0,
|
||||||
|
YearMax: 0,
|
||||||
|
HasCover: false,
|
||||||
|
Sort: "created_at DESC",
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
// Execute search using the same handler as API (search.go:93)
|
||||||
|
var results []database.SearchMediaItemsUnifiedRow
|
||||||
|
results, totalCount, err = cfg.MediaHandler.ExecuteSearch(c.Request().Context(), params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ExecuteSearch failed: %v", err)
|
||||||
// Continue without books - will show empty state
|
// Continue without books - will show empty state
|
||||||
} else {
|
} else {
|
||||||
// Convert database rows to BookInfo structs (matching BuildSections pattern)
|
// Convert to BookInfo (same as search.go:99-109)
|
||||||
bookInfoList = make([]handlers.BookInfo, len(books))
|
bookInfoList = make([]handlers.BookInfo, len(results))
|
||||||
for i, book := range books {
|
for i, book := range results {
|
||||||
bookUUID, _ := uuid.FromBytes(book.ID.Bytes[0:16])
|
bookUUID, _ := uuid.FromBytes(book.ID.Bytes[0:16])
|
||||||
bookLibUUID, _ := uuid.FromBytes(book.LibraryID.Bytes[0:16])
|
bookLibUUID, _ := uuid.FromBytes(book.LibraryID.Bytes[0:16])
|
||||||
bookInfoList[i] = handlers.BookInfo{
|
bookInfoList[i] = handlers.BookInfo{
|
||||||
MediaItemID: bookUUID.String(),
|
MediaItemID: bookUUID.String(),
|
||||||
Title: book.Title,
|
Title: book.Title,
|
||||||
Author: getText(book.Author),
|
Author: textToString(book.Author),
|
||||||
CoverImagePath: utils.ResolveMediaURL(pgtype.UUID{Bytes: bookLibUUID, Valid: true}, book.CoverImagePath),
|
CoverImagePath: utils.ResolveMediaURL(pgtype.UUID{Bytes: bookLibUUID, Valid: true}, book.CoverImagePath),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalCount = len(bookInfoList)
|
log.Printf("SSR: fetched %d books for library %s", len(bookInfoList), libraryID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user