mirror of
https://github.com/navidrome/navidrome.git
synced 2025-12-23 23:18:05 -05:00
* refactor: improve URL path handling in local storage system Enhanced the local storage implementation to properly handle URL-decoded paths and fix issues with file paths containing special characters. Added decodedPath field to localStorage struct to separate URL parsing concerns from file system operations. Key changes: - Added decodedPath field to localStorage struct for proper URL decoding - Modified newLocalStorage to use decoded path instead of modifying original URL - Fixed Windows path handling to work with decoded paths - Improved URL escaping in storage.For() to handle special characters - Added comprehensive test suite covering URL decoding, symlink resolution, Windows paths, and edge cases - Refactored test extractor to use mockTestExtractor for better isolation This ensures that file paths with spaces, hash symbols, and other special characters are handled correctly throughout the storage system. Signed-off-by: Deluan <deluan@navidrome.org> * fix(tests): fix test file permissions and add missing tests.Init call * refactor(tests): remove redundant test Signed-off-by: Deluan <deluan@navidrome.org> * fix: URL building for Windows and remove redundant variable Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify URL path escaping in local storage Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
61 lines
1.3 KiB
Go
61 lines
1.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/navidrome/navidrome/utils/slice"
|
|
)
|
|
|
|
const LocalSchemaID = "file"
|
|
|
|
type constructor func(url.URL) Storage
|
|
|
|
var (
|
|
registry = map[string]constructor{}
|
|
lock sync.RWMutex
|
|
)
|
|
|
|
func Register(schema string, c constructor) {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
registry[schema] = c
|
|
}
|
|
|
|
// For returns a Storage implementation for the given URI.
|
|
// It uses the schema part of the URI to find the correct registered
|
|
// Storage constructor.
|
|
// If the URI does not contain a schema, it is treated as a file:// URI.
|
|
func For(uri string) (Storage, error) {
|
|
lock.RLock()
|
|
defer lock.RUnlock()
|
|
parts := strings.Split(uri, "://")
|
|
|
|
// Paths without schema are treated as file:// and use the default LocalStorage implementation
|
|
if len(parts) < 2 {
|
|
uri, _ = filepath.Abs(uri)
|
|
uri = filepath.ToSlash(uri)
|
|
|
|
// Properly escape each path component using URL standards
|
|
pathParts := strings.Split(uri, "/")
|
|
escapedParts := slice.Map(pathParts, func(s string) string {
|
|
return url.PathEscape(s)
|
|
})
|
|
|
|
uri = LocalSchemaID + "://" + strings.Join(escapedParts, "/")
|
|
}
|
|
|
|
u, err := url.Parse(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c, ok := registry[u.Scheme]
|
|
if !ok {
|
|
return nil, errors.New("schema '" + u.Scheme + "' not registered")
|
|
}
|
|
return c(*u), nil
|
|
}
|