From 3dbbde164b4ecd4dabcdf427ac9a691a7cf876cd Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 30 Dec 2025 22:46:38 +0800 Subject: [PATCH 1/4] fix(route): enhance host parsing with port suffix support - Added logic to strip the trailing :port from the host when searching for routes. - Updated findRouteByDomains function to ensure consistent host formatting. - Added related tests --- internal/entrypoint/entrypoint.go | 7 ++++ internal/entrypoint/entrypoint_test.go | 44 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/internal/entrypoint/entrypoint.go b/internal/entrypoint/entrypoint.go index ecaadd73..e7a69426 100644 --- a/internal/entrypoint/entrypoint.go +++ b/internal/entrypoint/entrypoint.go @@ -146,11 +146,18 @@ func findRouteAnyDomain(host string) types.HTTPRoute { if r, ok := routes.HTTP.Get(host); ok { return r } + // try striping the trailing :port from the host + if before, _, ok := strings.Cut(host, ":"); ok { + if r, ok := routes.HTTP.Get(before); ok { + return r + } + } return nil } func findRouteByDomains(domains []string) func(host string) types.HTTPRoute { return func(host string) types.HTTPRoute { + host, _, _ = strings.Cut(host, ":") // strip the trailing :port for _, domain := range domains { if target, ok := strings.CutSuffix(host, domain); ok { if r, ok := routes.HTTP.Get(target); ok { diff --git a/internal/entrypoint/entrypoint_test.go b/internal/entrypoint/entrypoint_test.go index d36f4879..526e878a 100644 --- a/internal/entrypoint/entrypoint_test.go +++ b/internal/entrypoint/entrypoint_test.go @@ -128,3 +128,47 @@ func TestFindRouteByDomainsExactMatch(t *testing.T) { run(t, tests, testsNoMatch) } + +func TestFindRouteWithPort(t *testing.T) { + t.Run("AnyDomain", func(t *testing.T) { + addRoute("app1") + addRoute("app2.com") + + tests := []string{ + "app1:8080", + "app1.domain.com:8080", + "app2.com:8080", + } + testsNoMatch := []string{ + "app11", + "app2.co", + "app2.co:8080", + } + run(t, tests, testsNoMatch) + }) + + t.Run("ByDomains", func(t *testing.T) { + ep.SetFindRouteDomains([]string{ + ".domain.com", + }) + addRoute("app1") + addRoute("app2") + addRoute("app3.domain.com") + + tests := []string{ + "app1.domain.com:8080", + "app2:8080", // exact match fallback + "app3.domain.com:8080", + } + testsNoMatch := []string{ + "app11", + "app1.domain.co", + "app1.domain.co:8080", + "app2.co", + "app2.co:8080", + "app3.domain.co", + "app3.domain.co:8080", + } + run(t, tests, testsNoMatch) + }) +} From 89a4ca767def40b2399fd6015dcec0419c5001f5 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 1 Jan 2026 12:31:36 +0800 Subject: [PATCH 2/4] fix(homepage): improve alphabetical sorting by normalizing item names (#181) - Updated the sorting function to use Title case for item names to ensure consistent alphabetical ordering. --- internal/homepage/homepage.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/homepage/homepage.go b/internal/homepage/homepage.go index e4a0c5d2..739496c6 100644 --- a/internal/homepage/homepage.go +++ b/internal/homepage/homepage.go @@ -7,6 +7,7 @@ import ( "github.com/yusing/ds/ordered" "github.com/yusing/godoxy/internal/homepage/widgets" "github.com/yusing/godoxy/internal/serialization" + strutils "github.com/yusing/goutils/strings" ) type ( @@ -146,13 +147,13 @@ func (c *Category) sortByClicks() { return 1 } // fallback to alphabetical - return strings.Compare(a.Name, b.Name) + return strings.Compare(strutils.Title(a.Name), strutils.Title(b.Name)) }) } func (c *Category) sortByAlphabetical() { slices.SortStableFunc(c.Items, func(a, b *Item) int { - return strings.Compare(a.Name, b.Name) + return strings.Compare(strutils.Title(a.Name), strutils.Title(b.Name)) }) } From a47170da39d5a4e50a631c630e47ad8fc6d2853e Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 1 Jan 2026 13:20:17 +0800 Subject: [PATCH 3/4] feat(metrics): add IsExcluded field to RouteUptimeAggregate for enhanced status tracking - updated swagger --- internal/api/v1/docs/swagger.json | 5 +++++ internal/api/v1/docs/swagger.yaml | 2 ++ internal/metrics/uptime/uptime.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/internal/api/v1/docs/swagger.json b/internal/api/v1/docs/swagger.json index 9cea7db6..290a8eee 100644 --- a/internal/api/v1/docs/swagger.json +++ b/internal/api/v1/docs/swagger.json @@ -4555,6 +4555,11 @@ "x-nullable": false, "x-omitempty": false }, + "is_excluded": { + "type": "boolean", + "x-nullable": false, + "x-omitempty": false + }, "statuses": { "type": "array", "items": { diff --git a/internal/api/v1/docs/swagger.yaml b/internal/api/v1/docs/swagger.yaml index e6744d88..f6065c92 100644 --- a/internal/api/v1/docs/swagger.yaml +++ b/internal/api/v1/docs/swagger.yaml @@ -1056,6 +1056,8 @@ definitions: type: number is_docker: type: boolean + is_excluded: + type: boolean statuses: items: $ref: '#/definitions/RouteStatus' diff --git a/internal/metrics/uptime/uptime.go b/internal/metrics/uptime/uptime.go index 11dc0718..18baf000 100644 --- a/internal/metrics/uptime/uptime.go +++ b/internal/metrics/uptime/uptime.go @@ -33,6 +33,7 @@ type ( Idle float32 `json:"idle"` AvgLatency float32 `json:"avg_latency"` IsDocker bool `json:"is_docker"` + IsExcluded bool `json:"is_excluded"` CurrentStatus types.HealthStatus `json:"current_status" swaggertype:"string" enums:"healthy,unhealthy,unknown,napping,starting"` Statuses []Status `json:"statuses"` } // @name RouteUptimeAggregate @@ -156,6 +157,7 @@ func (rs RouteStatuses) aggregate(limit int, offset int) Aggregated { CurrentStatus: status, Statuses: statuses, IsDocker: r != nil && r.IsDocker(), + IsExcluded: r == nil || r.ShouldExclude(), } } return result From fd74bfedf0d51030b72aaa31f12af348644aee67 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 1 Jan 2026 18:25:27 +0800 Subject: [PATCH 4/4] fix(agent): improve url handling to not break urls with encoded characters --- agent/pkg/handler/proxy_http.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/agent/pkg/handler/proxy_http.go b/agent/pkg/handler/proxy_http.go index d840d456..79fa2c60 100644 --- a/agent/pkg/handler/proxy_http.go +++ b/agent/pkg/handler/proxy_http.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "net/http/httputil" + "strings" "time" "github.com/yusing/godoxy/agent/pkg/agent" @@ -43,10 +44,22 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) { return } - r.URL.Scheme = "" - r.URL.Host = "" - r.URL.Path = r.URL.Path[agent.HTTPProxyURLPrefixLen:] // strip the {API_BASE}/proxy/http prefix - r.RequestURI = r.URL.String() + // Strip the {API_BASE}/proxy/http prefix while preserving URL escaping. + // + // NOTE: `r.URL.Path` is decoded. If we rewrite it without keeping `RawPath` + // in sync, Go may re-escape the path (e.g. turning "%5B" into "%255B"), + // which breaks urls with percent-encoded characters, like Next.js static chunk URLs. + prefix := agent.APIEndpointBase + agent.EndpointProxyHTTP + r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix) + if r.URL.RawPath != "" { + if after, ok := strings.CutPrefix(r.URL.RawPath, prefix); ok { + r.URL.RawPath = after + } else { + // RawPath is no longer a valid encoding for Path; force Go to re-derive it. + r.URL.RawPath = "" + } + } + r.RequestURI = "" rp := &httputil.ReverseProxy{ Director: func(r *http.Request) {