POST /api/libraries/:id/issues/:issueId/retry (admin, family-lens gated via the existing gateIssue) re-runs the work that produced an open issue — deliberately NOT a library-wide rescan. Two shapes mirror the two ledger entry kinds: - item-backed issues rescan their media item through the book-detail RescanMediaItem mechanism (self-resolving folders, multi-file audiobook routing, write-after-success guards) and then close the issue (RetryMediaItemIssue); - row-less import_failed issues re-import the single file through the watcher's standalone processMediaFile path (RetryFailedImport: folders resolved RescanMediaItem-style so paths stay library-root-relative), whose create-path self-heal plus an explicit path resolve closes the issue even when the content was already imported by a prior attempt. Failures return the REAL underlying error so the UI can show why the issue still exists; a vanished file is a 409 (ErrRetryFileMissing); already-resolved issues reject re-retry. Docs + bruno mirrors regenerated for the new route (the generator's seq renumbering touches three unrelated bruno files as fallout; openapi gains the path and the date bump), and the docs schema registry gains the retry entry so TestDocsMatchRoutes passes.
68 lines
2.6 KiB
Go
68 lines
2.6 KiB
Go
package router
|
|
|
|
import (
|
|
"bookhoard/internal/handlers"
|
|
|
|
"github.com/labstack/echo/v5"
|
|
)
|
|
|
|
func registerLibraryRoutes(cfg *Config) {
|
|
e := cfg.Echo
|
|
|
|
// JWT middleware for protected routes
|
|
jwtMiddleware := createJWTMiddleware(cfg)
|
|
|
|
// Protected routes group
|
|
protected := e.Group("/api", jwtMiddleware)
|
|
|
|
// Create handler for library-specific convenience routes
|
|
h := cfg.ScannerHandler
|
|
|
|
// Public library types endpoint
|
|
e.GET("/api/libraries/types", cfg.LibraryHandler.GetLibraryTypes)
|
|
|
|
// Library management routes
|
|
library := protected.Group("/libraries")
|
|
|
|
// Admin-only library routes
|
|
adminLibrary := library.Group("", handlers.AdminMiddleware)
|
|
adminLibrary.POST("", cfg.LibraryHandler.CreateLibrary)
|
|
adminLibrary.GET("", cfg.LibraryHandler.ListLibraries)
|
|
adminLibrary.GET("/browse", cfg.LibraryHandler.BrowseDirectories)
|
|
adminLibrary.GET("/:id", cfg.LibraryHandler.GetLibrary)
|
|
adminLibrary.PUT("/:id", cfg.LibraryHandler.UpdateLibrary)
|
|
adminLibrary.DELETE("/:id", cfg.LibraryHandler.DeleteLibrary)
|
|
adminLibrary.POST("/:id/folders", cfg.LibraryHandler.AddLibraryFolder)
|
|
adminLibrary.GET("/:id/folders", cfg.LibraryHandler.GetLibraryFolders)
|
|
adminLibrary.DELETE("/:id/folders", cfg.LibraryHandler.DeleteLibraryFolder)
|
|
adminLibrary.GET("/:id/stats", cfg.LibraryHandler.GetLibraryStats)
|
|
adminLibrary.GET("/:id/issues/list", cfg.ProcessingIssuesHandler.ListProcessingIssues)
|
|
adminLibrary.GET("/:id/issues/stats", cfg.ProcessingIssuesHandler.GetProcessingIssueStats)
|
|
adminLibrary.POST("/:id/issues/:issueId/:mediaItemId/resolve", cfg.ProcessingIssuesHandler.ResolveProcessingIssue)
|
|
adminLibrary.POST("/:id/issues/:issueId/retry", cfg.ProcessingIssuesHandler.RetryProcessingIssue)
|
|
adminLibrary.DELETE("/:id/issues/:issueId", cfg.ProcessingIssuesHandler.DeleteProcessingIssue)
|
|
adminLibrary.POST("/:id/scan", func(c *echo.Context) error {
|
|
libraryID := c.Param("id")
|
|
scanReq := map[string]interface{}{
|
|
"library_id": libraryID,
|
|
}
|
|
c.Set("scan_request", scanReq)
|
|
return h.ScanLibrary(c)
|
|
})
|
|
adminLibrary.GET("/:id/media-items", func(c *echo.Context) error {
|
|
libraryID := c.Param("id")
|
|
c.QueryParams().Set("library_id", libraryID)
|
|
return cfg.MediaHandler.ListMediaItems(c)
|
|
})
|
|
|
|
// System scan settings (admin-only)
|
|
adminLibrary.GET("/scan-settings", cfg.SystemSettingsHandler.GetScanSettings)
|
|
adminLibrary.PUT("/scan-settings", cfg.SystemSettingsHandler.UpdateScanSettings)
|
|
|
|
// User library visibility control
|
|
userLibrary := library.Group("/visibility")
|
|
userLibrary.GET("", cfg.LibraryHandler.GetUserVisibleLibraries)
|
|
userLibrary.POST("", cfg.LibraryHandler.SetLibraryVisibility)
|
|
// Note: Remove endpoint may not exist - check handlers
|
|
}
|