} else {
@@ -238,7 +238,7 @@ templ FileBrowser(data dash.FileBrowserData) {
if data.HasNextPage {
-
+
Next
} else {
diff --git a/weed/admin/view/app/file_browser_templ.go b/weed/admin/view/app/file_browser_templ.go
index 0747e933f..ddcdda4d7 100644
--- a/weed/admin/view/app/file_browser_templ.go
+++ b/weed/admin/view/app/file_browser_templ.go
@@ -140,7 +140,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.SafeURL
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s", crumb.Path)))
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL(crumb.Path)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 62, Col: 73}
}
@@ -171,7 +171,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.SafeURL
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s", crumb.Path)))
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL(crumb.Path)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 68, Col: 73}
}
@@ -214,7 +214,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 templ.SafeURL
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, "/files?path=/"))
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL("/")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 86, Col: 45}
}
@@ -232,7 +232,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 templ.SafeURL
- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, "/files?path=/buckets"))
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL("/buckets")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 88, Col: 58}
}
@@ -263,7 +263,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 templ.SafeURL
- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s", data.CurrentPath)))
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL(data.CurrentPath)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 93, Col: 77}
}
@@ -356,7 +356,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 templ.SafeURL
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s&lastFileName=%s&limit=%d", data.CurrentPath, data.LastFileName, data.PageSize)))
+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPageURL(data.CurrentPath, data.LastFileName, data.PageSize)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 110, Col: 138}
}
@@ -380,7 +380,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 templ.SafeURL
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s", data.ParentPath)))
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL(data.ParentPath)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 119, Col: 78}
}
@@ -426,7 +426,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 templ.SafeURL
- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s", entry.FullPath)))
+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPathURL(entry.FullPath)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 153, Col: 83}
}
@@ -731,7 +731,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var35 templ.SafeURL
- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fmt.Sprintf("/files?path=%s&lastFileName=%s&limit=%d", data.CurrentPath, data.LastFileName, data.PageSize)))
+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinURLErrs(dash.PUrl(ctx, fileBrowserPageURL(data.CurrentPath, data.LastFileName, data.PageSize)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 241, Col: 138}
}
diff --git a/weed/admin/view/app/file_browser_urls.go b/weed/admin/view/app/file_browser_urls.go
new file mode 100644
index 000000000..570489fde
--- /dev/null
+++ b/weed/admin/view/app/file_browser_urls.go
@@ -0,0 +1,18 @@
+package app
+
+import (
+ "fmt"
+ "net/url"
+)
+
+func fileBrowserPathURL(path string) string {
+ return "/files?path=" + url.QueryEscape(path)
+}
+
+func fileBrowserPageURL(path, lastFileName string, limit int) string {
+ return fmt.Sprintf("%s&lastFileName=%s&limit=%d",
+ fileBrowserPathURL(path),
+ url.QueryEscape(lastFileName),
+ limit,
+ )
+}
diff --git a/weed/admin/view/app/file_browser_urls_test.go b/weed/admin/view/app/file_browser_urls_test.go
new file mode 100644
index 000000000..8d7f1d5a3
--- /dev/null
+++ b/weed/admin/view/app/file_browser_urls_test.go
@@ -0,0 +1,21 @@
+package app
+
+import "testing"
+
+func TestFileBrowserPathURLEscapesQueryPath(t *testing.T) {
+ path := "/buckets/airflow-logs/logs/run_id=backfill__2025-01-31T15:00:00+00:00"
+ want := "/files?path=%2Fbuckets%2Fairflow-logs%2Flogs%2Frun_id%3Dbackfill__2025-01-31T15%3A00%3A00%2B00%3A00"
+
+ if got := fileBrowserPathURL(path); got != want {
+ t.Fatalf("fileBrowserPathURL() = %q, want %q", got, want)
+ }
+}
+
+func TestFileBrowserPageURLEscapesCursor(t *testing.T) {
+ got := fileBrowserPageURL("/buckets/airflow-logs/logs/run_id=a+b", "task:1+next", 50)
+ want := "/files?path=%2Fbuckets%2Fairflow-logs%2Flogs%2Frun_id%3Da%2Bb&lastFileName=task%3A1%2Bnext&limit=50"
+
+ if got != want {
+ t.Fatalf("fileBrowserPageURL() = %q, want %q", got, want)
+ }
+}
diff --git a/weed/server/filer_ui/breadcrumb_test.go b/weed/server/filer_ui/breadcrumb_test.go
index 6e42541cb..153a6b7ab 100644
--- a/weed/server/filer_ui/breadcrumb_test.go
+++ b/weed/server/filer_ui/breadcrumb_test.go
@@ -84,3 +84,21 @@ func TestToBreadcrumb(t *testing.T) {
})
}
}
+
+func TestPrintPathPreservesPathSafeTimestampChars(t *testing.T) {
+ got := printpath("/logs/run_id=backfill__2025-01-31T15:00:00+00:00/")
+ want := "/logs/run_id=backfill__2025-01-31T15:00:00+00:00/"
+
+ if got != want {
+ t.Fatalf("printpath() = %q, want %q", got, want)
+ }
+}
+
+func TestQueryEscapePreservesPlusInLoadMoreCursor(t *testing.T) {
+ got := funcMap["queryEscape"].(func(string) string)("task:1+next")
+ want := "task%3A1%2Bnext"
+
+ if got != want {
+ t.Fatalf("queryEscape() = %q, want %q", got, want)
+ }
+}
diff --git a/weed/server/filer_ui/filer.html b/weed/server/filer_ui/filer.html
index be0eea8fd..935f7f808 100644
--- a/weed/server/filer_ui/filer.html
+++ b/weed/server/filer_ui/filer.html
@@ -170,7 +170,7 @@
{{ if .ShouldDisplayLoadMore }}
diff --git a/weed/server/filer_ui/templates.go b/weed/server/filer_ui/templates.go
index 7faf01007..689d2e61f 100644
--- a/weed/server/filer_ui/templates.go
+++ b/weed/server/filer_ui/templates.go
@@ -18,6 +18,7 @@ func printpath(parts ...string) string {
var funcMap = template.FuncMap{
"humanizeBytes": humanize.Bytes,
"printpath": printpath,
+ "queryEscape": url.QueryEscape,
}
//go:embed filer.html