package main import ( "encoding/json" "net/http" "strings" "github.com/gin-gonic/gin" ) // handleFoldersProxy proxies PhotoPrism's /api/v1/folders/originals and // post-filters by the caller's BasePath so the folder tree only shows // folders under the user's library root. // // Route: GET /api/sidecar/folders (behind requireSession) func handleFoldersProxy(pp *ppClient) gin.HandlerFunc { return func(c *gin.Context) { token := ctxToken(c) basePath := ctxBasePath(c) // Forward query params to PhotoPrism. query := c.Request.URL.RawQuery if query == "" { query = "recursive=true&uncached=true&files=false" } resp, err := pp.call(c.Request.Context(), http.MethodGet, "/api/v1/folders/originals?"+query, token, nil) if err != nil || !resp.OK { c.JSON(http.StatusBadGateway, gin.H{"error": "upstream folders request failed"}) return } // Decode the response — PhotoPrism returns { folders: [...] }. var payload struct { Folders []map[string]any `json:"folders"` } if err := json.Unmarshal(resp.Body, &payload); err != nil { c.Data(resp.Status, "application/json", resp.Body) return } // If the user has no BasePath (admin/empty), return as-is. if basePath == "" { c.JSON(http.StatusOK, payload) return } prefix := basePath + "/" // Post-filter folders by Path field only — frontend handles BasePath // prefix stripping via toUserPath(). filtered := make([]map[string]any, 0, len(payload.Folders)) for _, f := range payload.Folders { rawPath, ok := f["Path"] if !ok { continue } pathStr, ok := rawPath.(string) if !ok { continue } // Keep only folders under the user's base path. if pathStr == basePath || strings.HasPrefix(pathStr, prefix) { filtered = append(filtered, f) } } c.JSON(http.StatusOK, gin.H{"folders": filtered}) } }