mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
feat: enhance route handling with agent support and refactor port selection mapping
This commit is contained in:
parent
fbb07011f1
commit
7ef8354eb0
7 changed files with 336 additions and 162 deletions
79
internal/route/port_selection.go
Normal file
79
internal/route/port_selection.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package route
|
||||
|
||||
var (
|
||||
ImageNamePortMapTCP = map[string]int{
|
||||
"mssql": 1433,
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"postgres": 5432,
|
||||
"rabbitmq": 5672,
|
||||
"redis": 6379,
|
||||
"memcached": 11211,
|
||||
"mongo": 27017,
|
||||
"minecraft-server": 25565,
|
||||
}
|
||||
|
||||
ImageNamePortMapHTTP = map[string]int{
|
||||
"adguardhome": 3000,
|
||||
"bazarr": 6767,
|
||||
"calibre-web": 8083,
|
||||
"changedetection.io": 3000,
|
||||
"dockge": 5001,
|
||||
"gitea": 3000,
|
||||
"gogs": 3000,
|
||||
"grafana": 3000,
|
||||
"home-assistant": 8123,
|
||||
"homebridge": 8581,
|
||||
"httpd": 80,
|
||||
"immich": 3001,
|
||||
"jellyfin": 8096,
|
||||
"lidarr": 8686,
|
||||
"microbin": 8080,
|
||||
"nginx": 80,
|
||||
"nginx-proxy-manager": 81,
|
||||
"open-webui": 8080,
|
||||
"plex": 32400,
|
||||
"prometheus": 9090,
|
||||
"prowlarr": 9696,
|
||||
"radarr": 7878,
|
||||
"radarr-sma": 7878,
|
||||
"rsshub": 1200,
|
||||
"rss-bridge": 80,
|
||||
"sonarr": 8989,
|
||||
"sonarr-sma": 8989,
|
||||
"uptime-kuma": 3001,
|
||||
"whisparr": 6969,
|
||||
}
|
||||
ImageNamePortMapHTTPS = map[string]int{
|
||||
"portainer-be": 9443,
|
||||
"portainer-ce": 9443,
|
||||
}
|
||||
AliasPortMapHTTP = map[string]int{}
|
||||
AliasPortMapHTTPS = map[string]int{
|
||||
"portainer": 9443,
|
||||
"crafty": 8080,
|
||||
}
|
||||
)
|
||||
|
||||
func getSchemePortByImageName(imageName string) (scheme string, port int, ok bool) {
|
||||
if port, ok := ImageNamePortMapHTTP[imageName]; ok {
|
||||
return "http", port, true
|
||||
}
|
||||
if port, ok := ImageNamePortMapHTTPS[imageName]; ok {
|
||||
return "https", port, true
|
||||
}
|
||||
if port, ok := ImageNamePortMapTCP[imageName]; ok {
|
||||
return "tcp", port, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getSchemePortByAlias(alias string) (scheme string, port int, ok bool) {
|
||||
if port, ok := AliasPortMapHTTP[alias]; ok {
|
||||
return "http", port, true
|
||||
}
|
||||
if port, ok := AliasPortMapHTTPS[alias]; ok {
|
||||
return "https", port, true
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,21 +1,24 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||
"github.com/yusing/go-proxy/internal/api/v1/favicon"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/docker/idlewatcher"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
"github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
metricslogger "github.com/yusing/go-proxy/internal/net/http/middleware/metrics_logger"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
metricslogger "github.com/yusing/go-proxy/internal/net/gphttp/middleware/metrics_logger"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
|
@ -40,17 +43,25 @@ type (
|
|||
|
||||
func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
|
||||
httpConfig := base.HTTPConfig
|
||||
proxyURL := base.ProxyURL
|
||||
|
||||
if httpConfig.NoTLSVerify {
|
||||
trans = gphttp.DefaultTransportNoTLS
|
||||
}
|
||||
if httpConfig.ResponseHeaderTimeout > 0 {
|
||||
trans = trans.Clone()
|
||||
trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout
|
||||
var trans *http.Transport
|
||||
a := base.Agent()
|
||||
if a != nil {
|
||||
trans = a.Transport()
|
||||
proxyURL = agent.HTTPProxyURL
|
||||
} else {
|
||||
trans = gphttp.NewTransport()
|
||||
if httpConfig.NoTLSVerify {
|
||||
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
if httpConfig.ResponseHeaderTimeout > 0 {
|
||||
trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout
|
||||
}
|
||||
}
|
||||
|
||||
service := base.TargetName()
|
||||
rp := reverseproxy.NewReverseProxy(service, base.ProxyURL, trans)
|
||||
rp := reverseproxy.NewReverseProxy(service, proxyURL, trans)
|
||||
|
||||
if len(base.Middlewares) > 0 {
|
||||
err := middleware.PatchReverseProxy(rp, base.Middlewares)
|
||||
|
@ -59,6 +70,20 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
|
|||
}
|
||||
}
|
||||
|
||||
if a != nil {
|
||||
headers := &agentproxy.AgentProxyHeaders{
|
||||
Host: base.ProxyURL.Host,
|
||||
IsHTTPS: base.ProxyURL.Scheme == "https",
|
||||
SkipTLSVerify: httpConfig.NoTLSVerify,
|
||||
ResponseHeaderTimeout: int(httpConfig.ResponseHeaderTimeout.Seconds()),
|
||||
}
|
||||
ori := rp.HandlerFunc
|
||||
rp.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||
agentproxy.SetAgentProxyHeaders(r, headers)
|
||||
ori(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
r := &ReveseProxyRoute{
|
||||
Route: base,
|
||||
rp: rp,
|
||||
|
@ -72,6 +97,9 @@ func (r *ReveseProxyRoute) String() string {
|
|||
|
||||
// Start implements task.TaskStarter.
|
||||
func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
||||
if existing, ok := routes.GetHTTPRoute(r.TargetName()); ok && !r.UseLoadBalance() {
|
||||
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
|
||||
}
|
||||
r.task = parent.Subtask("http."+r.TargetName(), false)
|
||||
|
||||
switch {
|
||||
|
@ -85,15 +113,15 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
|||
r.HealthMon = waker
|
||||
case r.UseHealthCheck():
|
||||
if r.IsDocker() {
|
||||
client, err := docker.ConnectClient(r.Idlewatcher.DockerHost)
|
||||
client, err := docker.NewClient(r.Container.DockerHost)
|
||||
if err == nil {
|
||||
fallback := monitor.NewHTTPHealthChecker(r.rp.TargetURL, r.HealthCheck)
|
||||
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Idlewatcher.ContainerID, r.TargetName(), r.HealthCheck, fallback)
|
||||
fallback := r.newHealthMonitor()
|
||||
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Container.ContainerID, r.TargetName(), r.HealthCheck, fallback)
|
||||
r.task.OnCancel("close_docker_client", client.Close)
|
||||
}
|
||||
}
|
||||
if r.HealthMon == nil {
|
||||
r.HealthMon = monitor.NewHTTPHealthMonitor(r.rp.TargetURL, r.HealthCheck)
|
||||
r.HealthMon = r.newHealthMonitor()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +205,17 @@ func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor {
|
|||
return r.HealthMon
|
||||
}
|
||||
|
||||
func (r *ReveseProxyRoute) newHealthMonitor() interface {
|
||||
health.HealthMonitor
|
||||
health.HealthChecker
|
||||
} {
|
||||
if a := r.Agent(); a != nil {
|
||||
target := monitor.AgentTargetFromURL(r.ProxyURL)
|
||||
return monitor.NewAgentProxiedMonitor(a, r.HealthCheck, target)
|
||||
}
|
||||
return monitor.NewHTTPHealthMonitor(r.ProxyURL, r.HealthCheck)
|
||||
}
|
||||
|
||||
func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
|
||||
var lb *loadbalancer.LoadBalancer
|
||||
cfg := r.LoadBalance
|
||||
|
@ -186,7 +225,7 @@ func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
|
|||
linked = l.(*ReveseProxyRoute)
|
||||
lb = linked.loadBalancer
|
||||
lb.UpdateConfigIfNeeded(cfg)
|
||||
if linked.Homepage.IsEmpty() && !r.Homepage.IsEmpty() {
|
||||
if linked.Homepage == nil {
|
||||
linked.Homepage = r.Homepage
|
||||
}
|
||||
} else {
|
||||
|
@ -205,7 +244,7 @@ func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
|
|||
}
|
||||
r.loadBalancer = lb
|
||||
|
||||
server := loadbalance.NewServer(r.task.Name(), r.rp.TargetURL, r.LoadBalance.Weight, r.handler, r.HealthMon)
|
||||
server := loadbalance.NewServer(r.task.Name(), r.ProxyURL, r.LoadBalance.Weight, r.handler, r.HealthMon)
|
||||
lb.AddServer(server)
|
||||
r.task.OnCancel("lb_remove_server", func() {
|
||||
lb.RemoveServer(server)
|
||||
|
|
|
@ -2,21 +2,24 @@ package route
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/route/rules"
|
||||
"github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -38,7 +41,7 @@ type (
|
|||
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
|
||||
LoadBalance *loadbalance.Config `json:"load_balance,omitempty"`
|
||||
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
|
||||
Homepage *homepage.Item `json:"homepage,omitempty"`
|
||||
Homepage *homepage.ItemConfig `json:"homepage,omitempty"`
|
||||
AccessLog *accesslog.Config `json:"access_log,omitempty"`
|
||||
|
||||
Metadata `deserialize:"-"`
|
||||
|
@ -160,6 +163,17 @@ func (r *Route) Type() types.RouteType {
|
|||
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
|
||||
}
|
||||
|
||||
func (r *Route) Agent() *agent.AgentConfig {
|
||||
if r.Container == nil {
|
||||
return nil
|
||||
}
|
||||
return r.Container.Agent
|
||||
}
|
||||
|
||||
func (r *Route) IsAgent() bool {
|
||||
return r.Container != nil && r.Container.Agent != nil
|
||||
}
|
||||
|
||||
func (r *Route) HealthMonitor() health.HealthMonitor {
|
||||
return r.impl.HealthMonitor()
|
||||
}
|
||||
|
@ -176,8 +190,16 @@ func (r *Route) LoadBalanceConfig() *loadbalance.Config {
|
|||
return r.LoadBalance
|
||||
}
|
||||
|
||||
func (r *Route) HomepageConfig() *homepage.Item {
|
||||
return r.Homepage
|
||||
func (r *Route) HomepageConfig() *homepage.ItemConfig {
|
||||
return r.Homepage.GetOverride(r.Alias)
|
||||
}
|
||||
|
||||
func (r *Route) HomepageItem() *homepage.Item {
|
||||
return &homepage.Item{
|
||||
Alias: r.Alias,
|
||||
Provider: r.Provider,
|
||||
ItemConfig: r.HomepageConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Route) ContainerInfo() *docker.Container {
|
||||
|
@ -202,7 +224,7 @@ func (r *Route) ShouldExclude() bool {
|
|||
return true
|
||||
case r.IsZeroPort() && !r.UseIdleWatcher():
|
||||
return true
|
||||
case r.Container.IsDatabase && !r.Container.IsExplicit:
|
||||
case !r.Container.IsExplicit && r.Container.IsBlacklisted():
|
||||
return true
|
||||
case strings.HasPrefix(r.Container.ContainerName, "buildx_"):
|
||||
return true
|
||||
|
@ -244,76 +266,77 @@ func (r *Route) Finalize() {
|
|||
switch {
|
||||
case !isDocker:
|
||||
r.Host = "localhost"
|
||||
case cont.PrivateIP != "":
|
||||
r.Host = cont.PrivateIP
|
||||
case cont.PublicIP != "":
|
||||
r.Host = cont.PublicIP
|
||||
case cont.PrivateHostname != "":
|
||||
r.Host = cont.PrivateHostname
|
||||
case cont.PublicHostname != "":
|
||||
r.Host = cont.PublicHostname
|
||||
}
|
||||
}
|
||||
|
||||
lp, pp := r.Port.Listening, r.Port.Proxy
|
||||
|
||||
if isDocker {
|
||||
if port, ok := common.ServiceNamePortMapTCP[cont.ImageName]; ok {
|
||||
scheme, port, ok := getSchemePortByImageName(cont.Image.Name)
|
||||
if ok {
|
||||
if r.Scheme == "" {
|
||||
r.Scheme = types.Scheme(scheme)
|
||||
}
|
||||
if pp == 0 {
|
||||
pp = port
|
||||
}
|
||||
if r.Scheme == "" {
|
||||
r.Scheme = "tcp"
|
||||
}
|
||||
} else if port, ok := common.ImageNamePortMap[cont.ImageName]; ok {
|
||||
if pp == 0 {
|
||||
pp = port
|
||||
}
|
||||
if r.Scheme == "" {
|
||||
r.Scheme = "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scheme, port, ok := getSchemePortByAlias(r.Alias); ok {
|
||||
if r.Scheme == "" {
|
||||
r.Scheme = types.Scheme(scheme)
|
||||
}
|
||||
if pp == 0 {
|
||||
pp = port
|
||||
}
|
||||
}
|
||||
|
||||
if pp == 0 {
|
||||
switch {
|
||||
case r.Scheme == "https":
|
||||
pp = 443
|
||||
case !isDocker:
|
||||
pp = 80
|
||||
default:
|
||||
case isDocker:
|
||||
pp = lowestPort(cont.PrivatePortMapping)
|
||||
if pp == 0 {
|
||||
pp = lowestPort(cont.PublicPortMapping)
|
||||
}
|
||||
case r.Scheme == "https":
|
||||
pp = 443
|
||||
default:
|
||||
pp = 80
|
||||
}
|
||||
}
|
||||
|
||||
if isDocker {
|
||||
if r.Scheme == "" {
|
||||
for _, p := range cont.PublicPortMapping {
|
||||
if p.PrivatePort == uint16(pp) && p.Type == "udp" {
|
||||
r.Scheme = "udp"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// replace private port with public port if using public IP.
|
||||
if r.Host == cont.PublicIP {
|
||||
if r.Host == cont.PublicHostname {
|
||||
if p, ok := cont.PrivatePortMapping[pp]; ok {
|
||||
pp = int(p.PublicPort)
|
||||
}
|
||||
}
|
||||
// replace public port with private port if using private IP.
|
||||
if r.Host == cont.PrivateIP {
|
||||
} else {
|
||||
// replace public port with private port if using private IP.
|
||||
if p, ok := cont.PublicPortMapping[pp]; ok {
|
||||
pp = int(p.PrivatePort)
|
||||
}
|
||||
}
|
||||
|
||||
if r.Scheme == "" {
|
||||
switch {
|
||||
case r.Host == cont.PublicIP && cont.PublicPortMapping[pp].Type == "udp":
|
||||
r.Scheme = "udp"
|
||||
case r.Host == cont.PrivateIP && cont.PrivatePortMapping[pp].Type == "udp":
|
||||
r.Scheme = "udp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.Scheme == "" {
|
||||
switch {
|
||||
case lp != 0:
|
||||
r.Scheme = "tcp"
|
||||
case strings.HasSuffix(strconv.Itoa(pp), "443"):
|
||||
case pp%1000 == 443:
|
||||
r.Scheme = "https"
|
||||
default: // assume its http
|
||||
r.Scheme = "http"
|
||||
|
@ -323,7 +346,7 @@ func (r *Route) Finalize() {
|
|||
r.Port.Listening, r.Port.Proxy = lp, pp
|
||||
|
||||
if r.HealthCheck == nil {
|
||||
r.HealthCheck = health.DefaultHealthConfig
|
||||
r.HealthCheck = health.DefaultHealthConfig()
|
||||
}
|
||||
|
||||
if !r.HealthCheck.Disable {
|
||||
|
@ -346,13 +369,68 @@ func (r *Route) Finalize() {
|
|||
cont.StopMethod = common.StopMethodDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.Homepage.IsEmpty() {
|
||||
r.Homepage = homepage.NewItem(r.Alias)
|
||||
func (r *Route) FinalizeHomepageConfig() {
|
||||
if r.Alias == "" {
|
||||
panic("alias is empty")
|
||||
}
|
||||
|
||||
isDocker := r.Container != nil
|
||||
|
||||
if r.Homepage == nil {
|
||||
r.Homepage = &homepage.ItemConfig{Show: true}
|
||||
}
|
||||
r.Homepage = r.Homepage.GetOverride(r.Alias)
|
||||
|
||||
hp := r.Homepage
|
||||
|
||||
var key string
|
||||
if hp.Name == "" {
|
||||
if r.Container != nil {
|
||||
key = r.Container.Image.Name
|
||||
} else {
|
||||
key = r.Alias
|
||||
}
|
||||
displayName, ok := internal.GetDisplayName(key)
|
||||
if ok {
|
||||
hp.Name = displayName
|
||||
} else {
|
||||
hp.Name = strutils.Title(
|
||||
strings.ReplaceAll(
|
||||
strings.ReplaceAll(key, "-", " "),
|
||||
"_", " ",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if hp.Category == "" {
|
||||
if config.GetInstance().Value().Homepage.UseDefaultCategories {
|
||||
if isDocker {
|
||||
key = r.Container.Image.Name
|
||||
} else {
|
||||
key = strings.ToLower(r.Alias)
|
||||
}
|
||||
if category, ok := homepage.PredefinedCategories[key]; ok {
|
||||
hp.Category = category
|
||||
}
|
||||
}
|
||||
|
||||
if hp.Category == "" {
|
||||
switch {
|
||||
case r.UseLoadBalance():
|
||||
hp.Category = "Load-balanced"
|
||||
case isDocker:
|
||||
hp.Category = "Docker"
|
||||
default:
|
||||
hp.Category = "Others"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lowestPort(ports map[int]dockertypes.Port) (res int) {
|
||||
func lowestPort(ports map[int]container.Port) (res int) {
|
||||
cmp := (uint16)(65535)
|
||||
for port, v := range ports {
|
||||
if v.PrivatePort < cmp {
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package routequery
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
provider "github.com/yusing/go-proxy/internal/route/provider/types"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
func getHealthInfo(r route.Route) map[string]string {
|
||||
|
@ -28,13 +25,37 @@ func getHealthInfo(r route.Route) map[string]string {
|
|||
}
|
||||
}
|
||||
|
||||
type HealthInfoRaw struct {
|
||||
Status health.Status
|
||||
Latency time.Duration
|
||||
}
|
||||
|
||||
func getHealthInfoRaw(r route.Route) *HealthInfoRaw {
|
||||
mon := r.HealthMonitor()
|
||||
if mon == nil {
|
||||
return &HealthInfoRaw{
|
||||
Status: health.StatusUnknown,
|
||||
Latency: time.Duration(0),
|
||||
}
|
||||
}
|
||||
return &HealthInfoRaw{
|
||||
Status: mon.Status(),
|
||||
Latency: mon.Latency(),
|
||||
}
|
||||
}
|
||||
|
||||
func HealthMap() map[string]map[string]string {
|
||||
healthMap := make(map[string]map[string]string)
|
||||
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
|
||||
healthMap := make(map[string]map[string]string, routes.NumRoutes())
|
||||
routes.RangeRoutes(func(alias string, r route.Route) {
|
||||
healthMap[alias] = getHealthInfo(r)
|
||||
})
|
||||
routes.GetStreamRoutes().RangeAll(func(alias string, r route.StreamRoute) {
|
||||
healthMap[alias] = getHealthInfo(r)
|
||||
return healthMap
|
||||
}
|
||||
|
||||
func HealthInfo() map[string]*HealthInfoRaw {
|
||||
healthMap := make(map[string]*HealthInfoRaw, routes.NumRoutes())
|
||||
routes.RangeRoutes(func(alias string, r route.Route) {
|
||||
healthMap[alias] = getHealthInfoRaw(r)
|
||||
})
|
||||
return healthMap
|
||||
}
|
||||
|
@ -43,105 +64,33 @@ func HomepageCategories() []string {
|
|||
check := make(map[string]struct{})
|
||||
categories := make([]string, 0)
|
||||
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
|
||||
homepage := r.HomepageConfig()
|
||||
if homepage.IsEmpty() || homepage.Category == "" {
|
||||
item := r.HomepageConfig()
|
||||
if item == nil || item.Category == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := check[homepage.Category]; ok {
|
||||
if _, ok := check[item.Category]; ok {
|
||||
return
|
||||
}
|
||||
check[homepage.Category] = struct{}{}
|
||||
categories = append(categories, homepage.Category)
|
||||
check[item.Category] = struct{}{}
|
||||
categories = append(categories, item.Category)
|
||||
})
|
||||
return categories
|
||||
}
|
||||
|
||||
func HomepageConfig(useDefaultCategories bool, categoryFilter, providerFilter string) homepage.Categories {
|
||||
hpCfg := homepage.NewHomePageConfig()
|
||||
func HomepageConfig(categoryFilter, providerFilter string) homepage.Homepage {
|
||||
hp := make(homepage.Homepage)
|
||||
|
||||
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
|
||||
item := r.HomepageConfig()
|
||||
|
||||
if item.IsEmpty() {
|
||||
item = homepage.NewItem(alias)
|
||||
}
|
||||
|
||||
if override := item.GetOverride(); override != item {
|
||||
if providerFilter != "" && override.Provider != providerFilter ||
|
||||
categoryFilter != "" && override.Category != categoryFilter {
|
||||
return
|
||||
}
|
||||
hpCfg.Add(override)
|
||||
if providerFilter != "" && r.ProviderName() != providerFilter {
|
||||
return
|
||||
}
|
||||
|
||||
item.Alias = alias
|
||||
item.Provider = r.ProviderName()
|
||||
|
||||
if providerFilter != "" && item.Provider != providerFilter {
|
||||
return
|
||||
}
|
||||
|
||||
if item.Name == "" {
|
||||
reference := r.TargetName()
|
||||
cont := r.ContainerInfo()
|
||||
if cont != nil {
|
||||
reference = cont.ImageName
|
||||
}
|
||||
name, ok := internal.GetDisplayName(reference)
|
||||
if ok {
|
||||
item.Name = name
|
||||
} else {
|
||||
item.Name = strutils.Title(
|
||||
strings.ReplaceAll(
|
||||
strings.ReplaceAll(alias, "-", " "),
|
||||
"_", " ",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if useDefaultCategories {
|
||||
container := r.ContainerInfo()
|
||||
if container != nil && item.Category == "" {
|
||||
if category, ok := homepage.PredefinedCategories[container.ImageName]; ok {
|
||||
item.Category = category
|
||||
}
|
||||
}
|
||||
|
||||
if item.Category == "" {
|
||||
if category, ok := homepage.PredefinedCategories[strings.ToLower(alias)]; ok {
|
||||
item.Category = category
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item := r.HomepageItem()
|
||||
if categoryFilter != "" && item.Category != categoryFilter {
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case r.IsDocker():
|
||||
if item.Category == "" {
|
||||
item.Category = "Docker"
|
||||
}
|
||||
item.SourceType = string(provider.ProviderTypeDocker)
|
||||
case r.UseLoadBalance():
|
||||
if item.Category == "" {
|
||||
item.Category = "Load-balanced"
|
||||
}
|
||||
item.SourceType = "loadbalancer"
|
||||
default:
|
||||
if item.Category == "" {
|
||||
item.Category = "Others"
|
||||
}
|
||||
item.SourceType = string(provider.ProviderTypeFile)
|
||||
}
|
||||
|
||||
item.AltURL = r.TargetURL().String()
|
||||
hpCfg.Add(item)
|
||||
hp.Add(item)
|
||||
})
|
||||
return hpCfg
|
||||
return hp
|
||||
}
|
||||
|
||||
func RoutesByAlias(typeFilter ...route.RouteType) map[string]route.Route {
|
||||
|
|
|
@ -10,6 +10,19 @@ var (
|
|||
streamRoutes = F.NewMapOf[string, types.StreamRoute]()
|
||||
)
|
||||
|
||||
func RangeRoutes(callback func(alias string, r types.Route)) {
|
||||
httpRoutes.RangeAll(func(alias string, r types.HTTPRoute) {
|
||||
callback(alias, r)
|
||||
})
|
||||
streamRoutes.RangeAll(func(alias string, r types.StreamRoute) {
|
||||
callback(alias, r)
|
||||
})
|
||||
}
|
||||
|
||||
func NumRoutes() int {
|
||||
return httpRoutes.Size() + streamRoutes.Size()
|
||||
}
|
||||
|
||||
func GetHTTPRoutes() F.Map[string, types.HTTPRoute] {
|
||||
return httpRoutes
|
||||
}
|
||||
|
@ -35,6 +48,14 @@ func GetStreamRoute(alias string) (types.StreamRoute, bool) {
|
|||
return streamRoutes.Load(alias)
|
||||
}
|
||||
|
||||
func GetRoute(alias string) (types.Route, bool) {
|
||||
r, ok := httpRoutes.Load(alias)
|
||||
if ok {
|
||||
return r, true
|
||||
}
|
||||
return streamRoutes.Load(alias)
|
||||
}
|
||||
|
||||
func SetHTTPRoute(alias string, r types.HTTPRoute) {
|
||||
httpRoutes.Store(alias, r)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/docker/idlewatcher"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
|
@ -47,6 +47,9 @@ func (r *StreamRoute) String() string {
|
|||
|
||||
// Start implements task.TaskStarter.
|
||||
func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
|
||||
if existing, ok := routes.GetStreamRoute(r.TargetName()); ok {
|
||||
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
|
||||
}
|
||||
r.task = parent.Subtask("stream." + r.TargetName())
|
||||
r.Stream = NewStream(r)
|
||||
parent.OnCancel("finish", func() {
|
||||
|
@ -64,10 +67,10 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
|
|||
r.HealthMon = waker
|
||||
case r.UseHealthCheck():
|
||||
if r.IsDocker() {
|
||||
client, err := docker.ConnectClient(r.IdlewatcherConfig().DockerHost)
|
||||
client, err := docker.NewClient(r.Container.DockerHost)
|
||||
if err == nil {
|
||||
fallback := monitor.NewRawHealthChecker(r.TargetURL(), r.HealthCheck)
|
||||
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.IdlewatcherConfig().ContainerID, r.TargetName(), r.HealthCheck, fallback)
|
||||
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Container.ContainerID, r.TargetName(), r.HealthCheck, fallback)
|
||||
r.task.OnCancel("close_docker_client", client.Close)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package types
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -28,10 +29,14 @@ type (
|
|||
IdlewatcherConfig() *idlewatcher.Config
|
||||
HealthCheckConfig() *health.HealthCheckConfig
|
||||
LoadBalanceConfig() *loadbalance.Config
|
||||
HomepageConfig() *homepage.Item
|
||||
HomepageConfig() *homepage.ItemConfig
|
||||
HomepageItem() *homepage.Item
|
||||
ContainerInfo() *docker.Container
|
||||
|
||||
Agent() *agent.AgentConfig
|
||||
|
||||
IsDocker() bool
|
||||
IsAgent() bool
|
||||
UseLoadBalance() bool
|
||||
UseIdleWatcher() bool
|
||||
UseHealthCheck() bool
|
||||
|
|
Loading…
Add table
Reference in a new issue