mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00
replace all schema check with go-playground/validator/v10
This commit is contained in:
parent
00f60a6e78
commit
6aefe4d5d9
23 changed files with 149 additions and 250 deletions
|
@ -44,9 +44,6 @@ COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
|||
# copy binary
|
||||
COPY --from=builder /app /app
|
||||
|
||||
# copy schema directory
|
||||
COPY schema/ /app/schema/
|
||||
|
||||
# copy example config
|
||||
COPY config.example.yml /app/config/config.yml
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -14,7 +14,6 @@ require (
|
|||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/puzpuzpuz/xsync/v3 v3.4.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/santhosh-tekuri/jsonschema v1.2.4
|
||||
golang.org/x/net v0.32.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/time v0.8.0
|
||||
|
|
2
go.sum
2
go.sum
|
@ -128,8 +128,6 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
|
|||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis=
|
||||
github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
|
|
@ -27,6 +27,9 @@ var (
|
|||
)
|
||||
|
||||
func NewConfig(cfg *types.AutoCertConfig) *Config {
|
||||
if cfg == nil {
|
||||
cfg = new(types.AutoCertConfig)
|
||||
}
|
||||
if cfg.CertPath == "" {
|
||||
cfg.CertPath = CertFileDefault
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
var (
|
||||
prefixes = []string{"GODOXY_", "GOPROXY_", ""}
|
||||
|
||||
NoSchemaValidation = GetEnvBool("NO_SCHEMA_VALIDATION", true)
|
||||
IsTest = GetEnvBool("TEST", false) || strings.HasSuffix(os.Args[0], ".test")
|
||||
IsDebug = GetEnvBool("DEBUG", IsTest)
|
||||
IsDebugSkipAuth = GetEnvBool("DEBUG_SKIP_AUTH", false)
|
||||
|
|
|
@ -16,11 +16,10 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/notif"
|
||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
"github.com/yusing/go-proxy/internal/watcher"
|
||||
"github.com/yusing/go-proxy/internal/watcher/events"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -68,7 +67,8 @@ func Load() (*Config, E.Error) {
|
|||
}
|
||||
|
||||
func Validate(data []byte) E.Error {
|
||||
return U.ValidateYaml(U.GetSchema(common.ConfigSchemaPath), data)
|
||||
var model *types.Config
|
||||
return utils.DeserializeYAML(data, model)
|
||||
}
|
||||
|
||||
func MatchDomains() []string {
|
||||
|
@ -160,14 +160,8 @@ func (cfg *Config) load() E.Error {
|
|||
E.LogFatal(errMsg, err, &logger)
|
||||
}
|
||||
|
||||
if !common.NoSchemaValidation {
|
||||
if err := Validate(data); err != nil {
|
||||
E.LogFatal(errMsg, err, &logger)
|
||||
}
|
||||
}
|
||||
|
||||
model := types.DefaultConfig()
|
||||
if err := E.From(yaml.Unmarshal(data, model)); err != nil {
|
||||
if err := utils.DeserializeYAML(data, model); err != nil {
|
||||
E.LogFatal(errMsg, err, &logger)
|
||||
}
|
||||
|
||||
|
@ -176,7 +170,7 @@ func (cfg *Config) load() E.Error {
|
|||
errs.Add(entrypoint.SetMiddlewares(model.Entrypoint.Middlewares))
|
||||
errs.Add(entrypoint.SetAccessLogger(cfg.task, model.Entrypoint.AccessLog))
|
||||
errs.Add(cfg.initNotification(model.Providers.Notification))
|
||||
errs.Add(cfg.initAutoCert(&model.AutoCert))
|
||||
errs.Add(cfg.initAutoCert(model.AutoCert))
|
||||
errs.Add(cfg.loadRouteProviders(&model.Providers))
|
||||
|
||||
cfg.value = model
|
||||
|
|
|
@ -2,13 +2,13 @@ package types
|
|||
|
||||
type (
|
||||
AutoCertConfig struct {
|
||||
Email string `json:"email,omitempty" yaml:"email"`
|
||||
Domains []string `json:"domains,omitempty" yaml:",flow"`
|
||||
CertPath string `json:"cert_path,omitempty" yaml:"cert_path"`
|
||||
KeyPath string `json:"key_path,omitempty" yaml:"key_path"`
|
||||
ACMEKeyPath string `json:"acme_key_path,omitempty" yaml:"acme_key_path"`
|
||||
Provider string `json:"provider,omitempty" yaml:"provider"`
|
||||
Options AutocertProviderOpt `json:"options,omitempty" yaml:",flow"`
|
||||
Email string `json:"email,omitempty" validate:"email"`
|
||||
Domains []string `json:"domains,omitempty"`
|
||||
CertPath string `json:"cert_path,omitempty" validate:"omitempty,filepath"`
|
||||
KeyPath string `json:"key_path,omitempty" validate:"omitempty,filepath"`
|
||||
ACMEKeyPath string `json:"acme_key_path,omitempty" validate:"omitempty,filepath"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Options AutocertProviderOpt `json:"options,omitempty"`
|
||||
}
|
||||
AutocertProviderOpt map[string]any
|
||||
)
|
||||
|
|
|
@ -4,21 +4,21 @@ import "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
|||
|
||||
type (
|
||||
Config struct {
|
||||
AutoCert AutoCertConfig `json:"autocert" yaml:",flow"`
|
||||
Entrypoint Entrypoint `json:"entrypoint" yaml:",flow"`
|
||||
Providers Providers `json:"providers" yaml:",flow"`
|
||||
MatchDomains []string `json:"match_domains" yaml:"match_domains"`
|
||||
Homepage HomepageConfig `json:"homepage" yaml:"homepage"`
|
||||
TimeoutShutdown int `json:"timeout_shutdown" yaml:"timeout_shutdown"`
|
||||
AutoCert *AutoCertConfig `json:"autocert"`
|
||||
Entrypoint Entrypoint `json:"entrypoint"`
|
||||
Providers Providers `json:"providers"`
|
||||
MatchDomains []string `json:"match_domains" validate:"dive,fqdn"`
|
||||
Homepage HomepageConfig `json:"homepage"`
|
||||
TimeoutShutdown int `json:"timeout_shutdown" validate:"gte=0"`
|
||||
}
|
||||
Providers struct {
|
||||
Files []string `json:"include" yaml:"include"`
|
||||
Docker map[string]string `json:"docker" yaml:"docker"`
|
||||
Notification []NotificationConfig `json:"notification" yaml:"notification"`
|
||||
Files []string `json:"include" validate:"dive,filepath"`
|
||||
Docker map[string]string `json:"docker" validate:"dive,unix_addr|url"`
|
||||
Notification []NotificationConfig `json:"notification"`
|
||||
}
|
||||
Entrypoint struct {
|
||||
Middlewares []map[string]any `json:"middlewares" yaml:"middlewares"`
|
||||
AccessLog *accesslog.Config `json:"access_log" yaml:"access_log"`
|
||||
Middlewares []map[string]any `json:"middlewares"`
|
||||
AccessLog *accesslog.Config `json:"access_log"`
|
||||
}
|
||||
NotificationConfig map[string]any
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package types
|
||||
|
||||
type HomepageConfig struct {
|
||||
UseDefaultCategories bool `json:"use_default_categories" yaml:"use_default_categories"`
|
||||
UseDefaultCategories bool `json:"use_default_categories"`
|
||||
}
|
||||
|
|
|
@ -15,29 +15,29 @@ type (
|
|||
Container struct {
|
||||
_ U.NoCopy
|
||||
|
||||
DockerHost string `json:"docker_host" yaml:"-"`
|
||||
ContainerName string `json:"container_name" yaml:"-"`
|
||||
ContainerID string `json:"container_id" yaml:"-"`
|
||||
ImageName string `json:"image_name" yaml:"-"`
|
||||
DockerHost string `json:"docker_host"`
|
||||
ContainerName string `json:"container_name"`
|
||||
ContainerID string `json:"container_id"`
|
||||
ImageName string `json:"image_name"`
|
||||
|
||||
Labels map[string]string `json:"-" yaml:"-"`
|
||||
Labels map[string]string `json:"-"`
|
||||
|
||||
PublicPortMapping PortMapping `json:"public_ports" yaml:"-"` // non-zero publicPort:types.Port
|
||||
PrivatePortMapping PortMapping `json:"private_ports" yaml:"-"` // privatePort:types.Port
|
||||
PublicIP string `json:"public_ip" yaml:"-"`
|
||||
PrivateIP string `json:"private_ip" yaml:"-"`
|
||||
NetworkMode string `json:"network_mode" yaml:"-"`
|
||||
PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port
|
||||
PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port
|
||||
PublicIP string `json:"public_ip"`
|
||||
PrivateIP string `json:"private_ip"`
|
||||
NetworkMode string `json:"network_mode"`
|
||||
|
||||
Aliases []string `json:"aliases" yaml:"-"`
|
||||
IsExcluded bool `json:"is_excluded" yaml:"-"`
|
||||
IsExplicit bool `json:"is_explicit" yaml:"-"`
|
||||
IsDatabase bool `json:"is_database" yaml:"-"`
|
||||
IdleTimeout string `json:"idle_timeout,omitempty" yaml:"-"`
|
||||
WakeTimeout string `json:"wake_timeout,omitempty" yaml:"-"`
|
||||
StopMethod string `json:"stop_method,omitempty" yaml:"-"`
|
||||
StopTimeout string `json:"stop_timeout,omitempty" yaml:"-"` // stop_method = "stop" only
|
||||
StopSignal string `json:"stop_signal,omitempty" yaml:"-"` // stop_method = "stop" | "kill" only
|
||||
Running bool `json:"running" yaml:"-"`
|
||||
Aliases []string `json:"aliases"`
|
||||
IsExcluded bool `json:"is_excluded"`
|
||||
IsExplicit bool `json:"is_explicit"`
|
||||
IsDatabase bool `json:"is_database"`
|
||||
IdleTimeout string `json:"idle_timeout,omitempty"`
|
||||
WakeTimeout string `json:"wake_timeout,omitempty"`
|
||||
StopMethod string `json:"stop_method,omitempty"`
|
||||
StopTimeout string `json:"stop_timeout,omitempty"` // stop_method = "stop" only
|
||||
StopSignal string `json:"stop_signal,omitempty"` // stop_method = "stop" | "kill" only
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -6,16 +6,16 @@ type (
|
|||
Category []*Item
|
||||
|
||||
Item struct {
|
||||
Show bool `json:"show" yaml:"show"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Icon string `json:"icon" yaml:"icon"`
|
||||
URL string `json:"url" yaml:"url"` // alias + domain
|
||||
Category string `json:"category" yaml:"category"`
|
||||
Description string `json:"description" yaml:"description" aliases:"desc"`
|
||||
WidgetConfig map[string]any `json:"widget_config" yaml:",flow" aliases:"widget"`
|
||||
Show bool `json:"show"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url"` // alias + domain
|
||||
Category string `json:"category"`
|
||||
Description string `json:"description" aliases:"desc"`
|
||||
WidgetConfig map[string]any `json:"widget_config" aliases:"widget"`
|
||||
|
||||
SourceType string `json:"source_type" yaml:"-"`
|
||||
AltURL string `json:"alt_url" yaml:"-"` // original proxy target
|
||||
SourceType string `json:"source_type"`
|
||||
AltURL string `json:"alt_url"` // original proxy target
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package accesslog
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -37,7 +38,7 @@ const logTimeFormat = "02/Jan/2006:15:04:05 -0700"
|
|||
func NewFileAccessLogger(parent *task.Task, cfg *Config) (*AccessLogger, error) {
|
||||
f, err := os.OpenFile(cfg.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("access log open error: %w", err)
|
||||
}
|
||||
return NewAccessLogger(parent, f, cfg), nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package accesslog_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -13,25 +14,11 @@ import (
|
|||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
type testWritter struct {
|
||||
line string
|
||||
}
|
||||
|
||||
func (w *testWritter) Write(p []byte) (n int, err error) {
|
||||
w.line = string(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *testWritter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tw testWritter
|
||||
|
||||
const (
|
||||
remote = "192.168.1.1"
|
||||
u = "http://example.com/?bar=baz&foo=bar"
|
||||
uRedacted = "http://example.com/?bar=" + RedactedValue + "&foo=" + RedactedValue
|
||||
host = "example.com"
|
||||
uri = "/?bar=baz&foo=bar"
|
||||
uriRedacted = "/?bar=" + RedactedValue + "&foo=" + RedactedValue
|
||||
referer = "https://www.google.com/"
|
||||
proto = "HTTP/1.1"
|
||||
ua = "Go-http-client/1.1"
|
||||
|
@ -41,7 +28,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
testURL = E.Must(url.Parse(u))
|
||||
testURL = E.Must(url.Parse("http://" + host + uri))
|
||||
req = &http.Request{
|
||||
RemoteAddr: remote,
|
||||
Method: method,
|
||||
|
@ -62,17 +49,21 @@ var (
|
|||
ContentLength: contentLength,
|
||||
Header: http.Header{"Content-Type": []string{"text/plain"}},
|
||||
}
|
||||
task = taskPkg.GlobalTask("test logger")
|
||||
)
|
||||
|
||||
func fmtLog(cfg *Config) string {
|
||||
var line bytes.Buffer
|
||||
logger := NewAccessLogger(taskPkg.GlobalTask("test logger"), nil, cfg)
|
||||
logger.Format(&line, req, resp)
|
||||
return line.String()
|
||||
}
|
||||
|
||||
func TestAccessLoggerCommon(t *testing.T) {
|
||||
config := DefaultConfig
|
||||
config.Format = FormatCommon
|
||||
logger := NewAccessLogger(task, &tw, &config)
|
||||
logger.Log(req, resp)
|
||||
ExpectEqual(t, tw.line,
|
||||
fmt.Sprintf("%s - - [%s] \"%s %s %s\" %d %d\n",
|
||||
remote, TestTimeNow, method, u, proto, status, contentLength,
|
||||
ExpectEqual(t, fmtLog(&config),
|
||||
fmt.Sprintf("%s %s - - [%s] \"%s %s %s\" %d %d",
|
||||
host, remote, TestTimeNow, method, uri, proto, status, contentLength,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -80,11 +71,9 @@ func TestAccessLoggerCommon(t *testing.T) {
|
|||
func TestAccessLoggerCombined(t *testing.T) {
|
||||
config := DefaultConfig
|
||||
config.Format = FormatCombined
|
||||
logger := NewAccessLogger(task, &tw, &config)
|
||||
logger.Log(req, resp)
|
||||
ExpectEqual(t, tw.line,
|
||||
fmt.Sprintf("%s - - [%s] \"%s %s %s\" %d %d \"%s\" \"%s\"\n",
|
||||
remote, TestTimeNow, method, u, proto, status, contentLength, referer, ua,
|
||||
ExpectEqual(t, fmtLog(&config),
|
||||
fmt.Sprintf("%s %s - - [%s] \"%s %s %s\" %d %d \"%s\" \"%s\"",
|
||||
host, remote, TestTimeNow, method, uri, proto, status, contentLength, referer, ua,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -93,11 +82,9 @@ func TestAccessLoggerRedactQuery(t *testing.T) {
|
|||
config := DefaultConfig
|
||||
config.Format = FormatCommon
|
||||
config.Fields.Query.DefaultMode = FieldModeRedact
|
||||
logger := NewAccessLogger(task, &tw, &config)
|
||||
logger.Log(req, resp)
|
||||
ExpectEqual(t, tw.line,
|
||||
fmt.Sprintf("%s - - [%s] \"%s %s %s\" %d %d\n",
|
||||
remote, TestTimeNow, method, uRedacted, proto, status, contentLength,
|
||||
ExpectEqual(t, fmtLog(&config),
|
||||
fmt.Sprintf("%s %s - - [%s] \"%s %s %s\" %d %d",
|
||||
host, remote, TestTimeNow, method, uriRedacted, proto, status, contentLength,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -105,10 +92,8 @@ func TestAccessLoggerRedactQuery(t *testing.T) {
|
|||
func getJSONEntry(t *testing.T, config *Config) JSONLogEntry {
|
||||
t.Helper()
|
||||
config.Format = FormatJSON
|
||||
logger := NewAccessLogger(task, &tw, config)
|
||||
logger.Log(req, resp)
|
||||
var entry JSONLogEntry
|
||||
err := json.Unmarshal([]byte(tw.line), &entry)
|
||||
err := json.Unmarshal([]byte(fmtLog(config)), &entry)
|
||||
ExpectNoError(t, err)
|
||||
return entry
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ func TestNewConfig(t *testing.T) {
|
|||
labels := map[string]string{
|
||||
"proxy.buffer_size": "10",
|
||||
"proxy.format": "combined",
|
||||
"proxy.file_path": "/tmp/access.log",
|
||||
"proxy.path": "/tmp/access.log",
|
||||
"proxy.filters.status_codes.values": "200-299",
|
||||
"proxy.filters.method.values": "GET, POST",
|
||||
"proxy.filters.headers.values": "foo=bar, baz",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package types
|
||||
|
||||
type Config struct {
|
||||
Link string `json:"link" yaml:"link"`
|
||||
Mode Mode `json:"mode" yaml:"mode"`
|
||||
Weight Weight `json:"weight" yaml:"weight"`
|
||||
Options map[string]any `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
Link string `json:"link"`
|
||||
Mode Mode `json:"mode"`
|
||||
Weight Weight `json:"weight"`
|
||||
Options map[string]any `json:"options,omitempty"`
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
W "github.com/yusing/go-proxy/internal/watcher"
|
||||
)
|
||||
|
||||
|
@ -31,8 +31,9 @@ func FileProviderImpl(filename string) (ProviderImpl, error) {
|
|||
return impl, nil
|
||||
}
|
||||
|
||||
func Validate(data []byte) E.Error {
|
||||
return U.ValidateYaml(U.GetSchema(common.FileProviderSchemaPath), data)
|
||||
func Validate(data []byte) (err E.Error) {
|
||||
_, err = utils.DeserializeYAMLMap[*route.RawEntry](data)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *FileProvider) String() string {
|
||||
|
@ -45,22 +46,17 @@ func (p *FileProvider) Logger() *zerolog.Logger {
|
|||
|
||||
func (p *FileProvider) loadRoutesImpl() (route.Routes, E.Error) {
|
||||
routes := route.NewRoutes()
|
||||
entries := route.NewProxyEntries()
|
||||
|
||||
data, err := os.ReadFile(p.path)
|
||||
if err != nil {
|
||||
return routes, E.From(err)
|
||||
}
|
||||
|
||||
if err := entries.UnmarshalFromYAML(data); err != nil {
|
||||
return routes, E.From(err)
|
||||
}
|
||||
|
||||
if err := Validate(data); err != nil {
|
||||
E.LogWarn("validation failure", err.Subject(p.fileName))
|
||||
}
|
||||
|
||||
entries, err := utils.DeserializeYAMLMap[*route.RawEntry](data)
|
||||
if err == nil {
|
||||
return route.FromEntries(entries)
|
||||
}
|
||||
return routes, E.From(err)
|
||||
}
|
||||
|
||||
func (p *FileProvider) NewWatcher() W.Watcher {
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
// TODO: support stream load balance
|
||||
// TODO: support stream load balance.
|
||||
type StreamRoute struct {
|
||||
*entry.StreamEntry
|
||||
|
||||
|
|
|
@ -24,20 +24,20 @@ type (
|
|||
|
||||
// raw entry object before validation
|
||||
// loaded from docker labels or yaml file
|
||||
Alias string `json:"-" yaml:"-"`
|
||||
Scheme string `json:"scheme,omitempty" yaml:"scheme"`
|
||||
Host string `json:"host,omitempty" yaml:"host"`
|
||||
Port string `json:"port,omitempty" yaml:"port"`
|
||||
NoTLSVerify bool `json:"no_tls_verify,omitempty" yaml:"no_tls_verify"` // https proxy only
|
||||
PathPatterns []string `json:"path_patterns,omitempty" yaml:"path_patterns"` // http(s) proxy only
|
||||
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty" yaml:"healthcheck"`
|
||||
LoadBalance *loadbalance.Config `json:"load_balance,omitempty" yaml:"load_balance"`
|
||||
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty" yaml:"middlewares"`
|
||||
Homepage *homepage.Item `json:"homepage,omitempty" yaml:"homepage"`
|
||||
AccessLog *accesslog.Config `json:"access_log,omitempty" yaml:"access_log"`
|
||||
Alias string `json:"-"`
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Port string `json:"port,omitempty"`
|
||||
NoTLSVerify bool `json:"no_tls_verify,omitempty"`
|
||||
PathPatterns []string `json:"path_patterns,omitempty"`
|
||||
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"`
|
||||
AccessLog *accesslog.Config `json:"access_log,omitempty"`
|
||||
|
||||
/* Docker only */
|
||||
Container *docker.Container `json:"container,omitempty" yaml:"-"`
|
||||
Container *docker.Container `json:"container,omitempty"`
|
||||
|
||||
finalized bool
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package functional
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
|
@ -195,31 +194,6 @@ func (m Map[KT, VT]) Has(k KT) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
// UnmarshalFromYAML unmarshals a yaml byte slice into the map.
|
||||
//
|
||||
// It overwrites all existing key-value pairs in the map.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// data: yaml byte slice to unmarshal
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// error: if the unmarshaling fails
|
||||
func (m Map[KT, VT]) UnmarshalFromYAML(data []byte) error {
|
||||
if m.Size() != 0 {
|
||||
return errors.New("cannot unmarshal into non-empty map")
|
||||
}
|
||||
tmp := make(map[KT]VT)
|
||||
if err := yaml.Unmarshal(data, tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range tmp {
|
||||
m.Store(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Map[KT, VT]) String() string {
|
||||
tmp := make(map[KT]VT, m.Size())
|
||||
m.RangeAll(func(k KT, v VT) {
|
||||
|
|
|
@ -2,10 +2,8 @@ package utils
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
|
@ -151,19 +149,3 @@ func Copy(dst *ContextWriter, src *ContextReader) (err error) {
|
|||
func Copy2(ctx context.Context, dst io.Writer, src io.Reader) error {
|
||||
return Copy(&ContextWriter{ctx: ctx, Writer: dst}, &ContextReader{ctx: ctx, Reader: src})
|
||||
}
|
||||
|
||||
func LoadJSON[T any](path string, pointer *T) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, pointer)
|
||||
}
|
||||
|
||||
func SaveJSON[T any](path string, pointer *T, perm os.FileMode) error {
|
||||
data, err := json.Marshal(pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, perm)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/santhosh-tekuri/jsonschema"
|
||||
)
|
||||
|
||||
var (
|
||||
schemaCompiler = jsonschema.NewCompiler()
|
||||
schemaStorage = make(map[string]*jsonschema.Schema)
|
||||
schemaMu sync.Mutex
|
||||
)
|
||||
|
||||
func GetSchema(path string) *jsonschema.Schema {
|
||||
if schema, ok := schemaStorage[path]; ok {
|
||||
return schema
|
||||
}
|
||||
schemaMu.Lock()
|
||||
defer schemaMu.Unlock()
|
||||
if schema, ok := schemaStorage[path]; ok {
|
||||
return schema
|
||||
}
|
||||
schema := schemaCompiler.MustCompile(path)
|
||||
schemaStorage[path] = schema
|
||||
return schema
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -12,8 +12,8 @@ import (
|
|||
"unicode"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/santhosh-tekuri/jsonschema"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/functional"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
@ -30,36 +30,6 @@ var (
|
|||
ErrUnknownField = E.New("unknown field")
|
||||
)
|
||||
|
||||
func ValidateYaml(schema *jsonschema.Schema, data []byte) E.Error {
|
||||
var i any
|
||||
|
||||
err := yaml.Unmarshal(data, &i)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
}
|
||||
|
||||
m, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
}
|
||||
|
||||
err = schema.Validate(bytes.NewReader(m))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var valErr *jsonschema.ValidationError
|
||||
if !errors.As(err, &valErr) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b := E.NewBuilder("yaml validation error")
|
||||
for _, e := range valErr.Causes {
|
||||
b.Adds(e.Message)
|
||||
}
|
||||
return b.Error()
|
||||
}
|
||||
|
||||
// Serialize converts the given data into a map[string]any representation.
|
||||
//
|
||||
// It uses reflection to inspect the data type and handle different kinds of data.
|
||||
|
@ -482,10 +452,38 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E
|
|||
return true, Convert(reflect.ValueOf(tmp), dst)
|
||||
}
|
||||
|
||||
func DeserializeJSON(j map[string]string, target any) error {
|
||||
data, err := json.Marshal(j)
|
||||
func DeserializeYAML[T any](data []byte, target T) E.Error {
|
||||
m := make(map[string]any)
|
||||
if err := yaml.Unmarshal(data, m); err != nil {
|
||||
return E.From(err)
|
||||
}
|
||||
return Deserialize(m, target)
|
||||
}
|
||||
|
||||
func DeserializeYAMLMap[V any](data []byte) (_ functional.Map[string, V], err E.Error) {
|
||||
m := make(map[string]any)
|
||||
if err = E.From(yaml.Unmarshal(data, m)); err != nil {
|
||||
return
|
||||
}
|
||||
m2 := make(map[string]V, len(m))
|
||||
if err = Deserialize(m, m2); err != nil {
|
||||
return
|
||||
}
|
||||
return functional.NewMapFrom(m2), nil
|
||||
}
|
||||
|
||||
func LoadJSON[T any](path string, dst *T) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, target)
|
||||
return json.Unmarshal(data, dst)
|
||||
}
|
||||
|
||||
func SaveJSON[T any](path string, src *T, perm os.FileMode) error {
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, perm)
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
)
|
||||
|
||||
type HealthCheckConfig struct {
|
||||
Disable bool `json:"disable,omitempty" yaml:"disable" aliases:"disabled"`
|
||||
Path string `json:"path,omitempty" yaml:"path"`
|
||||
UseGet bool `json:"use_get,omitempty" yaml:"use_get"`
|
||||
Interval time.Duration `json:"interval" yaml:"interval"`
|
||||
Timeout time.Duration `json:"timeout" yaml:"timeout"`
|
||||
Disable bool `json:"disable,omitempty" aliases:"disabled"`
|
||||
Path string `json:"path,omitempty" validate:"omitempty,uri,startswith=/"`
|
||||
UseGet bool `json:"use_get,omitempty"`
|
||||
Interval time.Duration `json:"interval" validate:"omitempty,min=1s"`
|
||||
Timeout time.Duration `json:"timeout" validate:"omitempty,min=1s"`
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue