Remove the FilePath field from book metadata responses in the GetShelf endpoint.
This change improves security by not exposing internal file paths to API clients,
as the application now uses relative path storage with URL resolution via the
library service.
Changes:
- Remove FilePath field from BookPreview struct in GetShelf response
- Remove FilePath field from shelf items response
Related to previous commit implementing relative path storage.
- Add libraryService dependency to CollectionHandler and OPDSHandler for centralized path resolution
- Create internal/utils/mediaurl.go with ResolveMediaURL() function as single source of truth
- Update GetMediaItem and ListMediaItems handlers to return resolved URLs in API responses
- Update collection handlers (GetCollection, TestRules, PreviewCollection) to use resolved cover URLs
- Update progress handler (GetAllProgress) to use resolved cover URLs
- Add library_id to GetCollectionItems SQL query to enable URL resolution
- Refactor media scanner to store relative paths instead of absolute filesystem paths
- Add ResolveMediaPath() to LibraryService for resolving relative paths to absolute paths
- Add ServeFile endpoint at /uploads/library-:id/* for authenticated file serving
- Add MimeTypes map to library_service.go for consistent MIME type handling
- Update DownloadBook handler to use resolved filesystem paths
- Add getRelativePath() helper to MediaScanner for converting absolute to relative paths
- Use strings.EqualFold for case-insensitive path comparisons in zip extraction
This change enables the application to work with relative paths stored in the
database, making it portable across different server environments while
maintaining backward compatibility with existing absolute paths.
- Allow media items to be created/updated with invalid ISBN by storing empty string
- Fix test to use valid ISBN-13 format (9780306406157)
- Add small delay to prevent race condition in pagination test
Enhance NormalizeISBN to validate and convert ISBNs:
- Validate length (10 or 13 digits), return error if invalid
- Convert ISBN-10 to ISBN-13 by prefixing '978' and recalculating checksum
- Add NormalizeISBNSafe for backward compatibility in scanners
This ensures all ISBNs stored in database are valid ISBN-13 format.
Add strict validation to return 400 Bad Request when any media_item_id
is empty in the bulk-update request, rather than treating it as a
partial failure with 200 OK.
This aligns the handler behavior with test expectations for the
BulkUpdateBooks_EmptyBookIDs test case.
- Check library has folders before creating media items
- Return HTTP 400 with clear error message if no folders
- Proper error code (400) instead of generic 500
- Improved user feedback for invalid operations
- Inject LibraryService into MediaHandler
Fixes: TestCollectionsBulkOperations HTTP 500 errors
Related: Service layer validation commit
Update CreateMediaItem handler:
- Normalize tags for display using NormalizeTags()
- Normalize contributors for display using NormalizeContributors()
- Generate tags_search using NormalizeTagsSearch()
- Generate contributors_search using NormalizeContributorsSearch()
- Pass search fields to database
Update UpdateMediaItem handler:
- Same normalization logic as CreateMediaItem
- Regenerate search fields on updates
Update HandleBulkUpdate handler:
- Add tag normalization with punctuation preference
- Regenerate search fields when tags/contributors updated
All handlers now populate both display and search fields, ensuring
consistent normalization throughout the application.
Relates to Tags & Contributors Migration Phase 6
Changed HandleBulkUpdate to check tags array length before assignment
instead of checking for nil, improving consistency with array handling
and preparing for dual-field normalization implementation.
Convert tags and contributors columns from comma-separated strings to PostgreSQL
TEXT[] arrays for better data normalization and query performance.
Database Changes:
- schema.sql: Change tags/contributors from TEXT to TEXT[]
- schema.sql: Add GIN indexes for fast array searches
- queries.sql: Update search queries to use ANY() operator
- queries.sql: Update fuzzy search with unnest() for arrays
Generated Code (sqlc):
- models.go: Auto-generated with []string types for tags/contributors
- queries.sql.go: Auto-generated with proper array handling
Handler Changes:
- media.go: Update request structs to use []string for tags/contributors
- media.go: Remove pgtype.Text wrapping, use direct array assignment
- media.go: Add tag normalization in CreateMediaItemHandler
- collections.go: Update tags evaluation to join arrays for comparison
- collections.go: Add strings import for Join() function
Service Changes:
- ebook_scanner.go: Update EbookMetadata struct to use []string
- ebook_scanner.go: Remove string Join(), assign arrays directly
- collection_service.go: Update tags rule evaluation to join arrays
- collection_service.go: Add strings import
New Utilities:
- internal/utils/tags.go: Create NormalizeTags(), JoinTags(), SplitTags()
- Normalizes tags by trimming, lowercasing, removing duplicates/empties
API Documentation:
- bruno/media-items/Create Media Item.bru: Update examples to use arrays
- bruno/media-items/Update Media Item.bru: Update examples to use arrays
- Update docs: tags/contributors now array of string
Breaking Change:
- JSON format changes from "tags": "tag1,tag2" to "tags": ["tag1", "tag2"]
- Tests already use array format (no changes needed)
Benefits:
- GIN indexes enable faster array searches
- Normalization prevents data quality issues (case, duplicates)
- Array operations use PostgreSQL native operators (ANY, &&, unnest)
- Better separation of concerns (no string parsing in application)
- Remove 24 duplicate media CRUD methods from ebook.go (884 lines removed)
- Keep 12 scanner/watch/scheduler methods on Handler
- Move request type declarations to media.go:
* CreateMediaItemRequest
* UpdateMediaItemRequest
* CreateMediaNoteRequest
* UpdateMediaNoteRequest
* CreateMediaHighlightRequest
* UpdateMediaHighlightRequest
- Remove unused imports from ebook.go (strconv, pgx)
- Fix library.go to use MediaHandler.ListMediaItems instead of Handler
ebook.go reduced from 1266 lines to 382 lines (70% reduction)
Handler now has focused responsibility: scanner and scheduler operations only
This completes Phase 7 of the ebook.go refactoring plan.
Result: Clean separation of concerns with no duplicate code