fix: use path to handle urls in weed admin file browser (#7858)
* fix: use path instead of filepath to handle urls in weed admin file browser * test: add comprehensive tests for file browser path handling - Test breadcrumb generation for various path scenarios - Test path handling with forward slashes (URL compatibility) - Test parent path calculation for Windows compatibility - Test file extension handling using path.Ext - Test bucket path detection logic These tests verify that the switch from filepath to path package works correctly and handles URLs properly across all platforms. * refactor: simplify fullPath construction using path.Join Replace verbose manual path construction with path.Join which: - Handles trailing slashes automatically - Is more concise and readable - Is more robust for edge cases * fix: normalize path in ShowFileBrowser and rename generateBreadcrumbs parameter Critical fix: - Add util.CleanWindowsPath() normalization to path parameter in ShowFileBrowser handler, matching the pattern used in other file operation handlers (lines 273, 464) - This ensures Windows-style backslashes are converted to forward slashes before processing, fixing path handling issues on Windows Consistency improvement: - Rename path parameter to dir in generateBreadcrumbs function - Aligns with parameter rename in GetFileBrowser for consistent naming throughout the file * test: improve coverage for Windows path handling and production code behavior Address reviewer feedback by enhancing test quality: 1. Improved test documentation: - Added clear comments explaining what each test validates - Clarified that some tests validate expected behavior vs production code - Documented the Windows path normalization flow 2. Enhanced actual production code testing: - TestGenerateBreadcrumbs: Calls actual production function - TestBreadcrumbPathFormatting: Validates production output format - TestDirectoryNavigation: Integration-style test for complete flow 3. Added new test functions for better coverage: - TestPathJoinHandlesEdgeCases: Verifies path.Join behavior - TestWindowsPathNormalizationBehavior: Documents expected normalization - TestDirectoryNavigation: Complete navigation flow test 4. Improved test organization: - Fixed duplicate field naming issues - Better test names for clarity - More comprehensive edge case coverage These improvements ensure the fix for issue #7628 (Windows path handling) is properly validated across the complete flow from handler to path logic. * test: use actual util.CleanWindowsPath function in Windows path normalization test Address reviewer feedback by testing the actual production function: - Import util package for CleanWindowsPath - Call the real util.CleanWindowsPath() instead of reimplementing logic - Ensures test validates actual implementation, not just expected behavior - Added more test cases for edge cases (simple path, deep nesting) This change validates that the Windows path normalization in the ShowFileBrowser handler (handlers/file_browser_handlers.go:64) works correctly with the actual util.CleanWindowsPath function. * style: fix indentation in TestPathJoinHandlesEdgeCases Align t.Errorf statement inside the if block with proper indentation. The error message now correctly aligns with the if block body, maintaining consistent indentation throughout the function. * test: restore backslash validation check in TestPathJoinHandlesEdgeCases --------- Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
@@ -2,7 +2,7 @@ package dash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -47,9 +47,9 @@ type FileBrowserData struct {
|
||||
}
|
||||
|
||||
// GetFileBrowser retrieves file browser data for a given path
|
||||
func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
if path == "" {
|
||||
path = "/"
|
||||
func (s *AdminServer) GetFileBrowser(dir string) (*FileBrowserData, error) {
|
||||
if dir == "" {
|
||||
dir = "/"
|
||||
}
|
||||
|
||||
var entries []FileEntry
|
||||
@@ -58,7 +58,7 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
// Get directory listing from filer
|
||||
err := s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||
stream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
|
||||
Directory: path,
|
||||
Directory: dir,
|
||||
Prefix: "",
|
||||
Limit: 1000,
|
||||
InclusiveStartFrom: false,
|
||||
@@ -81,11 +81,7 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
fullPath := path
|
||||
if !strings.HasSuffix(fullPath, "/") {
|
||||
fullPath += "/"
|
||||
}
|
||||
fullPath += entry.Name
|
||||
fullPath := path.Join(dir, entry.Name)
|
||||
|
||||
var modTime time.Time
|
||||
if entry.Attributes != nil && entry.Attributes.Mtime > 0 {
|
||||
@@ -121,7 +117,7 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
if entry.IsDirectory {
|
||||
mime = "inode/directory"
|
||||
} else {
|
||||
ext := strings.ToLower(filepath.Ext(entry.Name))
|
||||
ext := strings.ToLower(path.Ext(entry.Name))
|
||||
switch ext {
|
||||
case ".txt", ".log":
|
||||
mime = "text/plain"
|
||||
@@ -195,12 +191,12 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
})
|
||||
|
||||
// Generate breadcrumbs
|
||||
breadcrumbs := s.generateBreadcrumbs(path)
|
||||
breadcrumbs := s.generateBreadcrumbs(dir)
|
||||
|
||||
// Calculate parent path
|
||||
parentPath := "/"
|
||||
if path != "/" {
|
||||
parentPath = filepath.Dir(path)
|
||||
if dir != "/" {
|
||||
parentPath = path.Dir(dir)
|
||||
if parentPath == "." {
|
||||
parentPath = "/"
|
||||
}
|
||||
@@ -209,16 +205,16 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
// Check if this is a bucket path
|
||||
isBucketPath := false
|
||||
bucketName := ""
|
||||
if strings.HasPrefix(path, "/buckets/") {
|
||||
if strings.HasPrefix(dir, "/buckets/") {
|
||||
isBucketPath = true
|
||||
pathParts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
pathParts := strings.Split(strings.Trim(dir, "/"), "/")
|
||||
if len(pathParts) >= 2 {
|
||||
bucketName = pathParts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return &FileBrowserData{
|
||||
CurrentPath: path,
|
||||
CurrentPath: dir,
|
||||
ParentPath: parentPath,
|
||||
Breadcrumbs: breadcrumbs,
|
||||
Entries: entries,
|
||||
@@ -231,7 +227,7 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) {
|
||||
}
|
||||
|
||||
// generateBreadcrumbs creates breadcrumb navigation for the current path
|
||||
func (s *AdminServer) generateBreadcrumbs(path string) []BreadcrumbItem {
|
||||
func (s *AdminServer) generateBreadcrumbs(dir string) []BreadcrumbItem {
|
||||
var breadcrumbs []BreadcrumbItem
|
||||
|
||||
// Always start with root
|
||||
@@ -240,12 +236,12 @@ func (s *AdminServer) generateBreadcrumbs(path string) []BreadcrumbItem {
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
if path == "/" {
|
||||
if dir == "/" {
|
||||
return breadcrumbs
|
||||
}
|
||||
|
||||
// Split path and build breadcrumbs
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
parts := strings.Split(strings.Trim(dir, "/"), "/")
|
||||
currentPath := ""
|
||||
|
||||
for _, part := range parts {
|
||||
@@ -258,7 +254,7 @@ func (s *AdminServer) generateBreadcrumbs(path string) []BreadcrumbItem {
|
||||
displayName := part
|
||||
if len(breadcrumbs) == 1 && part == "buckets" {
|
||||
displayName = "Object Store Buckets"
|
||||
} else if len(breadcrumbs) == 2 && strings.HasPrefix(path, "/buckets/") {
|
||||
} else if len(breadcrumbs) == 2 && strings.HasPrefix(dir, "/buckets/") {
|
||||
displayName = "📦 " + part // Add bucket icon to bucket name
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user