Add library-based scanning and media item endpoints
- Add POST /api/libraries/:id/scan endpoint for admin library scanning - Add GET /api/libraries/:id/media-items endpoint for library media items - Move /api/libraries/types to public endpoint (no auth required) - Update ScanEbooks handler to support library_id parameter
This commit is contained in:
+20
-4
@@ -115,6 +115,13 @@ func main() {
|
|||||||
|
|
||||||
// Protected routes
|
// Protected routes
|
||||||
protected := e.Group("/api", jwtMiddleware)
|
protected := e.Group("/api", jwtMiddleware)
|
||||||
|
|
||||||
|
// Setup ebook handler routes first (so we can use it for library scan)
|
||||||
|
h := handlers.SetupRoutes(protected, queries)
|
||||||
|
|
||||||
|
// Public library types endpoint (no authentication required)
|
||||||
|
e.GET("/api/libraries/types", libraryHandler.GetLibraryTypes)
|
||||||
|
|
||||||
protected.GET("/auth/profile", authHandler.GetProfile)
|
protected.GET("/auth/profile", authHandler.GetProfile)
|
||||||
protected.PUT("/auth/profile", authHandler.UpdateProfile)
|
protected.PUT("/auth/profile", authHandler.UpdateProfile)
|
||||||
protected.POST("/auth/refresh", authHandler.RefreshAccessToken)
|
protected.POST("/auth/refresh", authHandler.RefreshAccessToken)
|
||||||
@@ -126,7 +133,6 @@ func main() {
|
|||||||
|
|
||||||
// Library management routes
|
// Library management routes
|
||||||
library := protected.Group("/libraries")
|
library := protected.Group("/libraries")
|
||||||
library.GET("/types", libraryHandler.GetLibraryTypes)
|
|
||||||
|
|
||||||
// Admin-only library routes
|
// Admin-only library routes
|
||||||
adminLibrary := library.Group("", handlers.AdminMiddleware)
|
adminLibrary := library.Group("", handlers.AdminMiddleware)
|
||||||
@@ -139,6 +145,19 @@ func main() {
|
|||||||
adminLibrary.GET("/:id/folders", libraryHandler.GetLibraryFolders)
|
adminLibrary.GET("/:id/folders", libraryHandler.GetLibraryFolders)
|
||||||
adminLibrary.DELETE("/:id/folders", libraryHandler.DeleteLibraryFolder)
|
adminLibrary.DELETE("/:id/folders", libraryHandler.DeleteLibraryFolder)
|
||||||
adminLibrary.GET("/:id/stats", libraryHandler.GetLibraryStats)
|
adminLibrary.GET("/:id/stats", libraryHandler.GetLibraryStats)
|
||||||
|
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.ScanEbooks(c)
|
||||||
|
})
|
||||||
|
adminLibrary.GET("/:id/media-items", func(c echo.Context) error {
|
||||||
|
libraryID := c.Param("id")
|
||||||
|
c.QueryParams().Set("library_id", libraryID)
|
||||||
|
return h.ListMediaItems(c)
|
||||||
|
})
|
||||||
|
|
||||||
// User library visibility control
|
// User library visibility control
|
||||||
protected.POST("/libraries/visibility", libraryHandler.SetLibraryVisibility)
|
protected.POST("/libraries/visibility", libraryHandler.SetLibraryVisibility)
|
||||||
@@ -173,9 +192,6 @@ func main() {
|
|||||||
// Static files
|
// Static files
|
||||||
e.Static("/static", "web/static")
|
e.Static("/static", "web/static")
|
||||||
|
|
||||||
// Routes
|
|
||||||
h := handlers.SetupRoutes(protected, queries)
|
|
||||||
|
|
||||||
// Start scheduler for auto-scanning
|
// Start scheduler for auto-scanning
|
||||||
go h.StartScheduler()
|
go h.StartScheduler()
|
||||||
defer h.StopScheduler()
|
defer h.StopScheduler()
|
||||||
|
|||||||
@@ -124,12 +124,22 @@ func SetupRoutes(g *echo.Group, db *database.Queries) *Handler {
|
|||||||
// ScanEbooksRequest represents the request for scanning ebooks
|
// ScanEbooksRequest represents the request for scanning ebooks
|
||||||
type ScanEbooksRequest struct {
|
type ScanEbooksRequest struct {
|
||||||
FolderPaths []string `json:"folder_paths,omitempty"`
|
FolderPaths []string `json:"folder_paths,omitempty"`
|
||||||
|
LibraryID string `json:"library_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanEbooks handles POST /api/scanner/scan (now runs in background)
|
// ScanEbooks handles POST /api/scanner/scan (now runs in background)
|
||||||
func (h *Handler) ScanEbooks(c echo.Context) error {
|
func (h *Handler) ScanEbooks(c echo.Context) error {
|
||||||
var req ScanEbooksRequest
|
var req ScanEbooksRequest
|
||||||
if err := c.Bind(&req); err != nil {
|
|
||||||
|
// Check if scan_request is set in context (from library scan route)
|
||||||
|
if scanReq, ok := c.Get("scan_request").(map[string]interface{}); ok {
|
||||||
|
if libraryID, ok := scanReq["library_id"].(string); ok {
|
||||||
|
req.LibraryID = libraryID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind request body if provided (for direct scanner/scan calls)
|
||||||
|
if err := c.Bind(&req); err != nil && req.LibraryID == "" {
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,14 +152,33 @@ func (h *Handler) ScanEbooks(c echo.Context) error {
|
|||||||
|
|
||||||
var folderPaths []string
|
var folderPaths []string
|
||||||
|
|
||||||
// If folder paths provided in request, use them
|
// If library_id is provided, fetch folders from library
|
||||||
// Otherwise, use user's saved folders
|
if req.LibraryID != "" {
|
||||||
if len(req.FolderPaths) > 0 {
|
libraryUUID, err := uuid.Parse(req.LibraryID)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library id"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch library folders from database
|
||||||
|
libraryFolders, err := h.db.GetLibraryFolders(c.Request().Context(), pgtype.UUID{Bytes: [16]byte(libraryUUID), Valid: true})
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusNotFound, map[string]string{"error": "library not found or has no folders"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract folder paths
|
||||||
|
for _, folder := range libraryFolders {
|
||||||
|
folderPaths = append(folderPaths, folder.FolderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(folderPaths) == 0 {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library has no folders configured"})
|
||||||
|
}
|
||||||
|
} else if len(req.FolderPaths) > 0 {
|
||||||
|
// Use folder paths from request
|
||||||
folderPaths = req.FolderPaths
|
folderPaths = req.FolderPaths
|
||||||
} else {
|
} else {
|
||||||
// TODO: Replace with library-based folder scanning
|
// Neither library_id nor folder_paths provided
|
||||||
// For now, require folder paths in request
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "either library_id or folder_paths required for scanning"})
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "folder_paths required for scanning"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jobID := uuid.New().String()
|
jobID := uuid.New().String()
|
||||||
@@ -158,7 +187,7 @@ func (h *Handler) ScanEbooks(c echo.Context) error {
|
|||||||
ID: jobID,
|
ID: jobID,
|
||||||
Type: services.JobTypeScan,
|
Type: services.JobTypeScan,
|
||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
"library_id": userID,
|
"library_id": req.LibraryID,
|
||||||
"folders": folderPaths,
|
"folders": folderPaths,
|
||||||
"admin_id": userUUID.String(),
|
"admin_id": userUUID.String(),
|
||||||
"db": h.db,
|
"db": h.db,
|
||||||
|
|||||||
Reference in New Issue
Block a user