mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-23 16:58:31 +02:00
Merge branch 'main' into dev
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
@@ -43,10 +44,22 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.URL.Scheme = ""
|
// Strip the {API_BASE}/proxy/http prefix while preserving URL escaping.
|
||||||
r.URL.Host = ""
|
//
|
||||||
r.URL.Path = r.URL.Path[agent.HTTPProxyURLPrefixLen:] // strip the {API_BASE}/proxy/http prefix
|
// NOTE: `r.URL.Path` is decoded. If we rewrite it without keeping `RawPath`
|
||||||
r.RequestURI = r.URL.String()
|
// 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{
|
rp := &httputil.ReverseProxy{
|
||||||
Director: func(r *http.Request) {
|
Director: func(r *http.Request) {
|
||||||
|
|||||||
@@ -4555,6 +4555,11 @@
|
|||||||
"x-nullable": false,
|
"x-nullable": false,
|
||||||
"x-omitempty": false
|
"x-omitempty": false
|
||||||
},
|
},
|
||||||
|
"is_excluded": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-nullable": false,
|
||||||
|
"x-omitempty": false
|
||||||
|
},
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
|||||||
@@ -1056,6 +1056,8 @@ definitions:
|
|||||||
type: number
|
type: number
|
||||||
is_docker:
|
is_docker:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
is_excluded:
|
||||||
|
type: boolean
|
||||||
statuses:
|
statuses:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/RouteStatus'
|
$ref: '#/definitions/RouteStatus'
|
||||||
|
|||||||
@@ -146,11 +146,18 @@ func findRouteAnyDomain(host string) types.HTTPRoute {
|
|||||||
if r, ok := routes.HTTP.Get(host); ok {
|
if r, ok := routes.HTTP.Get(host); ok {
|
||||||
return r
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findRouteByDomains(domains []string) func(host string) types.HTTPRoute {
|
func findRouteByDomains(domains []string) func(host string) types.HTTPRoute {
|
||||||
return func(host string) types.HTTPRoute {
|
return func(host string) types.HTTPRoute {
|
||||||
|
host, _, _ = strings.Cut(host, ":") // strip the trailing :port
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
if target, ok := strings.CutSuffix(host, domain); ok {
|
if target, ok := strings.CutSuffix(host, domain); ok {
|
||||||
if r, ok := routes.HTTP.Get(target); ok {
|
if r, ok := routes.HTTP.Get(target); ok {
|
||||||
|
|||||||
@@ -128,3 +128,47 @@ func TestFindRouteByDomainsExactMatch(t *testing.T) {
|
|||||||
|
|
||||||
run(t, tests, testsNoMatch)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/yusing/ds/ordered"
|
"github.com/yusing/ds/ordered"
|
||||||
"github.com/yusing/godoxy/internal/homepage/widgets"
|
"github.com/yusing/godoxy/internal/homepage/widgets"
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
|
strutils "github.com/yusing/goutils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -146,13 +147,13 @@ func (c *Category) sortByClicks() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
// fallback to alphabetical
|
// 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() {
|
func (c *Category) sortByAlphabetical() {
|
||||||
slices.SortStableFunc(c.Items, func(a, b *Item) int {
|
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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type (
|
|||||||
Idle float32 `json:"idle"`
|
Idle float32 `json:"idle"`
|
||||||
AvgLatency float32 `json:"avg_latency"`
|
AvgLatency float32 `json:"avg_latency"`
|
||||||
IsDocker bool `json:"is_docker"`
|
IsDocker bool `json:"is_docker"`
|
||||||
|
IsExcluded bool `json:"is_excluded"`
|
||||||
CurrentStatus types.HealthStatus `json:"current_status" swaggertype:"string" enums:"healthy,unhealthy,unknown,napping,starting"`
|
CurrentStatus types.HealthStatus `json:"current_status" swaggertype:"string" enums:"healthy,unhealthy,unknown,napping,starting"`
|
||||||
Statuses []Status `json:"statuses"`
|
Statuses []Status `json:"statuses"`
|
||||||
} // @name RouteUptimeAggregate
|
} // @name RouteUptimeAggregate
|
||||||
@@ -156,6 +157,7 @@ func (rs RouteStatuses) aggregate(limit int, offset int) Aggregated {
|
|||||||
CurrentStatus: status,
|
CurrentStatus: status,
|
||||||
Statuses: statuses,
|
Statuses: statuses,
|
||||||
IsDocker: r != nil && r.IsDocker(),
|
IsDocker: r != nil && r.IsDocker(),
|
||||||
|
IsExcluded: r == nil || r.ShouldExclude(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user