mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
v0.5.0-rc4: initial support for ovh, provider generator implementation update, replaced all interface{} to any
This commit is contained in:
parent
82f06374f7
commit
21fcceb391
21 changed files with 286 additions and 64 deletions
|
@ -114,6 +114,8 @@ See [providers.example.yml](providers.example.yml) for examples
|
|||
|
||||
- Cert "renewal" is actually obtaining a new cert instead of renewing the existing one
|
||||
|
||||
- `autocert` config is not hot-reloadable
|
||||
|
||||
[🔼Back to top](#table-of-content)
|
||||
|
||||
## Build it yourself
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# Supported DNS Providers
|
||||
|
||||
<!-- TOC -->
|
||||
- [Cloudflare](#cloudflare)
|
||||
- [CloudDNS](#clouddns)
|
||||
- [DuckDNS](#duckdns)
|
||||
- [Implement other DNS providers](#implement-other-dns-providers)
|
||||
<!-- /TOC -->
|
||||
|
||||
- [Supported DNS Providers](#supported-dns-providers)
|
||||
- [Cloudflare](#cloudflare)
|
||||
- [CloudDNS](#clouddns)
|
||||
- [DuckDNS](#duckdns)
|
||||
- [OVHCloud](#ovhcloud)
|
||||
- [Implement other DNS providers](#implement-other-dns-providers)
|
||||
|
||||
## Cloudflare
|
||||
|
||||
|
@ -23,10 +25,29 @@ Follow [this guide](https://cloudkul.com/blog/automcatic-renew-and-generate-ssl-
|
|||
|
||||
## DuckDNS
|
||||
|
||||
`token`: DuckDNS Token
|
||||
- `token`: DuckDNS Token
|
||||
|
||||
Tested by [earvingad](https://github.com/earvingad)
|
||||
|
||||
## OVHCloud
|
||||
|
||||
_Note, `application_key` and `oauth2_config` **CANNOT** be used together_
|
||||
|
||||
- `api_endpoint`: Endpoint URL, or one of
|
||||
- `ovh-eu`,
|
||||
- `ovh-ca`,
|
||||
- `ovh-us`,
|
||||
- `kimsufi-eu`,
|
||||
- `kimsufi-ca`,
|
||||
- `soyoustart-eu`,
|
||||
- `soyoustart-ca`
|
||||
- `application_secret`
|
||||
- `application_key`
|
||||
- `consumer_key`
|
||||
- `oauth2_config`: Client ID and Client Secret
|
||||
- `client_id`
|
||||
- `client_secret`
|
||||
|
||||
## Implement other DNS providers
|
||||
|
||||
See [add_dns_provider.md](docs/add_dns_provider.md)
|
||||
|
|
2
go.work
2
go.work
|
@ -1,4 +1,4 @@
|
|||
go 1.22
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
"title": "DNS Challenge Provider",
|
||||
"default": "local",
|
||||
"type": "string",
|
||||
"enum": ["local", "cloudflare", "clouddns", "duckdns"]
|
||||
"enum": ["local", "cloudflare", "clouddns", "duckdns", "ovh"]
|
||||
},
|
||||
"options": {
|
||||
"title": "Provider specific options",
|
||||
|
@ -135,6 +135,82 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"provider": {
|
||||
"const": "ovh"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"options": {
|
||||
"required": ["application_secret", "consumer_key"],
|
||||
"additionalProperties": false,
|
||||
"oneOf": [
|
||||
{
|
||||
"required": ["application_key"]
|
||||
},
|
||||
{
|
||||
"required": ["oauth2_config"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"api_endpoint": {
|
||||
"description": "OVH API endpoint",
|
||||
"default": "ovh-eu",
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"ovh-eu",
|
||||
"ovh-ca",
|
||||
"ovh-us",
|
||||
"kimsufi-eu",
|
||||
"kimsufi-ca",
|
||||
"soyoustart-eu",
|
||||
"soyoustart-ca"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
]
|
||||
},
|
||||
"application_secret": {
|
||||
"description": "OVH Application Secret",
|
||||
"type": "string"
|
||||
},
|
||||
"consumer_key": {
|
||||
"description": "OVH Consumer Key",
|
||||
"type": "string"
|
||||
},
|
||||
"application_key": {
|
||||
"description": "OVH Application Key",
|
||||
"type": "string"
|
||||
},
|
||||
"oauth2_config": {
|
||||
"description": "OVH OAuth2 config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"description": "OVH Client ID",
|
||||
"type": "string"
|
||||
},
|
||||
"client_secret": {
|
||||
"description": "OVH Client Secret",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["client_id", "client_secret"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func Stats(cfg *config.Config, w http.ResponseWriter, r *http.Request) {
|
||||
stats := map[string]interface{}{
|
||||
stats := map[string]any{
|
||||
"proxies": cfg.Statistics(),
|
||||
"uptime": utils.FormatDuration(server.GetProxyServer().Uptime()),
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/go-acme/lego/v4/providers/dns/clouddns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/duckdns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ovh"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -19,6 +20,7 @@ const (
|
|||
ProviderCloudflare = "cloudflare"
|
||||
ProviderClouddns = "clouddns"
|
||||
ProviderDuckdns = "duckdns"
|
||||
ProviderOVH = "ovh"
|
||||
)
|
||||
|
||||
var providersGenMap = map[string]ProviderGenerator{
|
||||
|
@ -26,6 +28,7 @@ var providersGenMap = map[string]ProviderGenerator{
|
|||
ProviderCloudflare: providerGenerator(cloudflare.NewDefaultConfig, cloudflare.NewDNSProviderConfig),
|
||||
ProviderClouddns: providerGenerator(clouddns.NewDefaultConfig, clouddns.NewDNSProviderConfig),
|
||||
ProviderDuckdns: providerGenerator(duckdns.NewDefaultConfig, duckdns.NewDNSProviderConfig),
|
||||
ProviderOVH: providerGenerator(ovh.NewDefaultConfig, ovh.NewDNSProviderConfig),
|
||||
}
|
||||
|
||||
var logger = logrus.WithField("module", "autocert")
|
||||
|
|
|
@ -272,23 +272,13 @@ func getCertExpiries(cert *tls.Certificate) (CertExpiries, E.NestedError) {
|
|||
return r, E.Nil()
|
||||
}
|
||||
|
||||
func setOptions[T interface{}](cfg *T, opt M.AutocertProviderOpt) E.NestedError {
|
||||
for k, v := range opt {
|
||||
err := U.SetFieldFromSnake(cfg, k, v)
|
||||
if err.HasError() {
|
||||
return E.Failure("set autocert option").Subject(k).With(err)
|
||||
}
|
||||
}
|
||||
return E.Nil()
|
||||
}
|
||||
|
||||
func providerGenerator[CT any, PT challenge.Provider](
|
||||
defaultCfg func() *CT,
|
||||
newProvider func(*CT) (PT, error),
|
||||
) ProviderGenerator {
|
||||
return func(opt M.AutocertProviderOpt) (challenge.Provider, E.NestedError) {
|
||||
cfg := defaultCfg()
|
||||
err := setOptions(cfg, opt)
|
||||
err := U.Deserialize(opt, cfg)
|
||||
if err.HasError() {
|
||||
return nil, err
|
||||
}
|
||||
|
|
49
src/autocert/provider_test/ovh_test.go
Normal file
49
src/autocert/provider_test/ovh_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package provider_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/ovh"
|
||||
. "github.com/yusing/go-proxy/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// type Config struct {
|
||||
// APIEndpoint string
|
||||
|
||||
// ApplicationKey string
|
||||
// ApplicationSecret string
|
||||
// ConsumerKey string
|
||||
|
||||
// OAuth2Config *OAuth2Config
|
||||
|
||||
// PropagationTimeout time.Duration
|
||||
// PollingInterval time.Duration
|
||||
// TTL int
|
||||
// HTTPClient *http.Client
|
||||
// }
|
||||
|
||||
func TestOVH(t *testing.T) {
|
||||
cfg := &ovh.Config{}
|
||||
testYaml := `
|
||||
api_endpoint: https://eu.api.ovh.com
|
||||
application_key: <application_key>
|
||||
application_secret: <application_secret>
|
||||
consumer_key: <consumer_key>
|
||||
oauth2_config:
|
||||
client_id: <client_id>
|
||||
client_secret: <client_secret>
|
||||
`
|
||||
cfgExpected := &ovh.Config{
|
||||
APIEndpoint: "https://eu.api.ovh.com",
|
||||
ApplicationKey: "<application_key>",
|
||||
ApplicationSecret: "<application_secret>",
|
||||
ConsumerKey: "<consumer_key>",
|
||||
OAuth2Config: &ovh.OAuth2Config{ClientID: "<client_id>", ClientSecret: "<client_secret>"},
|
||||
}
|
||||
testYaml = testYaml[1:] // remove first \n
|
||||
opt := make(map[string]any)
|
||||
ExpectNoError(t, yaml.Unmarshal([]byte(testYaml), opt))
|
||||
ExpectNoError(t, Deserialize(opt, cfg))
|
||||
ExpectEqual(t, cfg, cfgExpected)
|
||||
}
|
|
@ -95,7 +95,7 @@ func (cfg *Config) RoutesByAlias() map[string]U.SerializedObject {
|
|||
prName := p.GetName()
|
||||
p.GetCurrentRoutes().EachKV(func(a string, r R.Route) {
|
||||
obj, err := U.Serialize(r)
|
||||
if err != nil {
|
||||
if err.HasError() {
|
||||
cfg.l.Error(err)
|
||||
return
|
||||
}
|
||||
|
@ -114,13 +114,13 @@ func (cfg *Config) RoutesByAlias() map[string]U.SerializedObject {
|
|||
return routes
|
||||
}
|
||||
|
||||
func (cfg *Config) Statistics() map[string]interface{} {
|
||||
func (cfg *Config) Statistics() map[string]any {
|
||||
nTotalStreams := 0
|
||||
nTotalRPs := 0
|
||||
providerStats := make(map[string]interface{})
|
||||
providerStats := make(map[string]any)
|
||||
|
||||
cfg.proxyProviders.Each(func(p *PR.Provider) {
|
||||
stats := make(map[string]interface{})
|
||||
stats := make(map[string]any)
|
||||
nStreams := 0
|
||||
nRPs := 0
|
||||
p.GetCurrentRoutes().EachKV(func(a string, r R.Route) {
|
||||
|
@ -141,7 +141,7 @@ func (cfg *Config) Statistics() map[string]interface{} {
|
|||
providerStats[p.GetName()] = stats
|
||||
})
|
||||
|
||||
return map[string]interface{}{
|
||||
return map[string]any{
|
||||
"num_total_streams": nTotalStreams,
|
||||
"num_total_reverse_proxies": nTotalRPs,
|
||||
"providers": providerStats,
|
||||
|
|
|
@ -9,7 +9,7 @@ type (
|
|||
Icon string
|
||||
Category string
|
||||
Description string
|
||||
WidgetConfig map[string]interface{}
|
||||
WidgetConfig map[string]any
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestHomePageLabel(t *testing.T) {
|
|||
field := "ip"
|
||||
v := "bar"
|
||||
pl, err := ParseLabel(makeLabel(NSHomePage, alias, field), v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
if pl.Target != alias {
|
||||
t.Errorf("Expected alias=%s, got %s", alias, pl.Target)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func TestHomePageLabel(t *testing.T) {
|
|||
func TestStringProxyLabel(t *testing.T) {
|
||||
v := "bar"
|
||||
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "ip"), v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, pl.Value, v)
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func TestBoolProxyLabelValid(t *testing.T) {
|
|||
|
||||
for k, v := range tests {
|
||||
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "no_tls_verify"), k)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, pl.Value, v)
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ X-Custom-Header2: boo`
|
|||
}
|
||||
|
||||
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "set_headers"), v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
hGot := ExpectType[map[string]string](t, pl.Value)
|
||||
if hGot != nil && !reflect.DeepEqual(h, hGot) {
|
||||
t.Errorf("Expected %v, got %v", h, hGot)
|
||||
|
@ -109,7 +109,7 @@ func TestHideHeadersProxyLabel(t *testing.T) {
|
|||
`
|
||||
v = strings.TrimPrefix(v, "\n")
|
||||
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "hide_headers"), v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
sGot := ExpectType[[]string](t, pl.Value)
|
||||
sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
|
||||
if sGot != nil {
|
||||
|
@ -120,7 +120,7 @@ func TestHideHeadersProxyLabel(t *testing.T) {
|
|||
func TestCommaSepProxyLabelSingle(t *testing.T) {
|
||||
v := "a"
|
||||
pl, err := ParseLabel("proxy.aliases", v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
sGot := ExpectType[[]string](t, pl.Value)
|
||||
sWant := []string{"a"}
|
||||
if sGot != nil {
|
||||
|
@ -132,7 +132,7 @@ func TestCommaSepProxyLabelSingle(t *testing.T) {
|
|||
func TestCommaSepProxyLabelMulti(t *testing.T) {
|
||||
v := "X-Custom-Header1, X-Custom-Header2,X-Custom-Header3"
|
||||
pl, err := ParseLabel("proxy.aliases", v)
|
||||
ExpectErrNil(t, err)
|
||||
ExpectNoError(t, err)
|
||||
sGot := ExpectType[[]string](t, pl.Value)
|
||||
sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
|
||||
if sGot != nil {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
module github.com/yusing/go-proxy
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.1
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/docker/cli v27.2.1+incompatible
|
||||
|
@ -36,6 +34,7 @@ require (
|
|||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/ovh/go-ovh v1.6.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
|
||||
go.opentelemetry.io/otel v1.30.0 // indirect
|
||||
|
@ -45,10 +44,12 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.30.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.25.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
)
|
||||
|
|
10
src/go.sum
10
src/go.sum
|
@ -45,12 +45,16 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
|
|||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
|
||||
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
|
@ -63,6 +67,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
|||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
|
||||
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -110,6 +116,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -149,6 +157,8 @@ google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -9,5 +9,5 @@ type (
|
|||
Provider string `json:"provider"`
|
||||
Options AutocertProviderOpt `yaml:",flow" json:"options"`
|
||||
}
|
||||
AutocertProviderOpt map[string]string
|
||||
AutocertProviderOpt map[string]any
|
||||
)
|
||||
|
|
|
@ -37,7 +37,6 @@ func NewStreamRoute(entry *P.StreamEntry) (*StreamRoute, E.NestedError) {
|
|||
base := &StreamRoute{
|
||||
StreamEntry: entry,
|
||||
wg: sync.WaitGroup{},
|
||||
stopCh: make(chan struct{}, 1),
|
||||
connCh: make(chan any),
|
||||
}
|
||||
if entry.Scheme.ListeningScheme.IsTCP() {
|
||||
|
@ -57,6 +56,7 @@ func (r *StreamRoute) Start() E.NestedError {
|
|||
if r.started.Load() {
|
||||
return E.Invalid("state", "already started")
|
||||
}
|
||||
r.stopCh = make(chan struct{}, 1)
|
||||
r.wg.Wait()
|
||||
if err := r.Setup(); err != nil {
|
||||
return E.Failure("setup").With(err)
|
||||
|
|
|
@ -38,11 +38,11 @@ func (route *TCPRoute) Setup() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (route *TCPRoute) Accept() (interface{}, error) {
|
||||
func (route *TCPRoute) Accept() (any, error) {
|
||||
return route.listener.Accept()
|
||||
}
|
||||
|
||||
func (route *TCPRoute) Handle(c interface{}) error {
|
||||
func (route *TCPRoute) Handle(c any) error {
|
||||
clientConn := c.(net.Conn)
|
||||
|
||||
defer clientConn.Close()
|
||||
|
|
|
@ -55,7 +55,7 @@ func (route *UDPRoute) Setup() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (route *UDPRoute) Accept() (interface{}, error) {
|
||||
func (route *UDPRoute) Accept() (any, error) {
|
||||
in := route.listeningConn
|
||||
|
||||
buffer := make([]byte, udpBufferSize)
|
||||
|
@ -103,7 +103,7 @@ func (route *UDPRoute) Accept() (interface{}, error) {
|
|||
return conn, err
|
||||
}
|
||||
|
||||
func (route *UDPRoute) Handle(c interface{}) error {
|
||||
func (route *UDPRoute) Handle(c any) error {
|
||||
return c.(*UDPConn).Start()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,25 +2,25 @@ package functional
|
|||
|
||||
import "sync"
|
||||
|
||||
func ForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
|
||||
func ForEachKey[K comparable, V any](obj map[K]V, do func(K)) {
|
||||
for k := range obj {
|
||||
do(k)
|
||||
}
|
||||
}
|
||||
|
||||
func ForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
|
||||
func ForEachValue[K comparable, V any](obj map[K]V, do func(V)) {
|
||||
for _, v := range obj {
|
||||
do(v)
|
||||
}
|
||||
}
|
||||
|
||||
func ForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
|
||||
func ForEachKV[K comparable, V any](obj map[K]V, do func(K, V)) {
|
||||
for k, v := range obj {
|
||||
do(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func ParallelForEach[T interface{}](obj []T, do func(T)) {
|
||||
func ParallelForEach[T any](obj []T, do func(T)) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(obj))
|
||||
for _, v := range obj {
|
||||
|
@ -32,7 +32,7 @@ func ParallelForEach[T interface{}](obj []T, do func(T)) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func ParallelForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
|
||||
func ParallelForEachKey[K comparable, V any](obj map[K]V, do func(K)) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(obj))
|
||||
for k := range obj {
|
||||
|
@ -44,7 +44,7 @@ func ParallelForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func ParallelForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
|
||||
func ParallelForEachValue[K comparable, V any](obj map[K]V, do func(V)) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(obj))
|
||||
for _, v := range obj {
|
||||
|
@ -56,7 +56,7 @@ func ParallelForEachValue[K comparable, V interface{}](obj map[K]V, do func(V))
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func ParallelForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
|
||||
func ParallelForEachKV[K comparable, V any](obj map[K]V, do func(K, V)) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(obj))
|
||||
for k, v := range obj {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
E "github.com/yusing/go-proxy/error"
|
||||
)
|
||||
|
||||
type Map[KT comparable, VT interface{}] struct {
|
||||
type Map[KT comparable, VT any] struct {
|
||||
m map[KT]VT
|
||||
defVals map[KT]VT
|
||||
sync.RWMutex
|
||||
|
@ -22,7 +22,7 @@ type Map[KT comparable, VT interface{}] struct {
|
|||
//
|
||||
// Return:
|
||||
// - *Map[KT, VT]: a pointer to the newly created Map.
|
||||
func NewMap[KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
|
||||
func NewMap[KT comparable, VT any](dv ...map[KT]VT) *Map[KT, VT] {
|
||||
return NewMapFrom(make(map[KT]VT), dv...)
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ func NewMap[KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
|
|||
//
|
||||
// Return:
|
||||
// - *Map[KT, VT]: a pointer to the newly created Map.
|
||||
func NewMapOf[M Map[KT, VT], KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
|
||||
func NewMapOf[M Map[KT, VT], KT comparable, VT any](dv ...map[KT]VT) *Map[KT, VT] {
|
||||
return NewMapFrom(make(map[KT]VT), dv...)
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func NewMapOf[M Map[KT, VT], KT comparable, VT interface{}](dv ...map[KT]VT) *Ma
|
|||
//
|
||||
// Return:
|
||||
// - *Map[KT, VT]: a pointer to the newly created Map.
|
||||
func NewMapFrom[KT comparable, VT interface{}](from map[KT]VT, dv ...map[KT]VT) *Map[KT, VT] {
|
||||
func NewMapFrom[KT comparable, VT any](from map[KT]VT, dv ...map[KT]VT) *Map[KT, VT] {
|
||||
if len(dv) > 0 {
|
||||
return &Map[KT, VT]{m: from, defVals: dv[0]}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/santhosh-tekuri/jsonschema"
|
||||
E "github.com/yusing/go-proxy/error"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func ValidateYaml(schema *jsonschema.Schema, data []byte) E.NestedError {
|
||||
var i interface{}
|
||||
var i any
|
||||
|
||||
err := yaml.Unmarshal(data, &i)
|
||||
if err != nil {
|
||||
|
@ -55,7 +56,7 @@ func TryJsonStringify(o any) string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
// Serialize converts the given data into a map[string]interface{} representation.
|
||||
// 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.
|
||||
// For a struct, it extracts the fields using the json tag if present, or the field name if not.
|
||||
|
@ -66,9 +67,9 @@ func TryJsonStringify(o any) string {
|
|||
// - data: The data to be converted into a map.
|
||||
//
|
||||
// Returns:
|
||||
// - result: The resulting map[string]interface{} representation of the data.
|
||||
// - result: The resulting map[string]any representation of the data.
|
||||
// - error: An error if the data type is unsupported or if there is an error during conversion.
|
||||
func Serialize(data interface{}) (SerializedObject, error) {
|
||||
func Serialize(data any) (SerializedObject, E.NestedError) {
|
||||
result := make(map[string]any)
|
||||
|
||||
// Use reflection to inspect the data type
|
||||
|
@ -76,7 +77,7 @@ func Serialize(data interface{}) (SerializedObject, error) {
|
|||
|
||||
// Check if the value is valid
|
||||
if !value.IsValid() {
|
||||
return nil, fmt.Errorf("invalid data")
|
||||
return nil, E.Invalid("data", fmt.Sprintf("type: %T", data))
|
||||
}
|
||||
|
||||
// Dereference pointers if necessary
|
||||
|
@ -107,7 +108,7 @@ func Serialize(data interface{}) (SerializedObject, error) {
|
|||
} else if field.Anonymous {
|
||||
// If the field is an embedded struct, add its fields to the result
|
||||
fieldMap, err := Serialize(value.Field(i).Interface())
|
||||
if err != nil {
|
||||
if err.HasError() {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range fieldMap {
|
||||
|
@ -118,10 +119,72 @@ func Serialize(data interface{}) (SerializedObject, error) {
|
|||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %s", value.Kind())
|
||||
// return nil, fmt.Errorf("unsupported type: %s", value.Kind())
|
||||
return nil, E.Unsupported("type", value.Kind())
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, E.Nil()
|
||||
}
|
||||
|
||||
type SerializedObject map[string]any
|
||||
func Deserialize(src map[string]any, target any) E.NestedError {
|
||||
// convert data fields to lower no-snake
|
||||
// convert target fields to lower
|
||||
// then check if the field of data is in the target
|
||||
mapping := make(map[string]string)
|
||||
t := reflect.TypeOf(target).Elem()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
snakeCaseField := strings.ToLower(field.Name)
|
||||
mapping[snakeCaseField] = field.Name
|
||||
}
|
||||
for k, v := range src {
|
||||
kCleaned := toLowerNoSnake(k)
|
||||
if fieldName, ok := mapping[kCleaned]; ok {
|
||||
prop := reflect.ValueOf(target).Elem().FieldByName(fieldName)
|
||||
propType := prop.Type()
|
||||
isPtr := prop.Kind() == reflect.Ptr
|
||||
if prop.CanSet() {
|
||||
val := reflect.ValueOf(v)
|
||||
vType := val.Type()
|
||||
switch {
|
||||
case isPtr && vType.ConvertibleTo(propType.Elem()):
|
||||
ptr := reflect.New(propType.Elem())
|
||||
ptr.Elem().Set(val.Convert(propType.Elem()))
|
||||
prop.Set(ptr)
|
||||
case vType.ConvertibleTo(propType):
|
||||
prop.Set(val.Convert(propType))
|
||||
case isPtr:
|
||||
var vSerialized SerializedObject
|
||||
vSerialized, ok = v.(SerializedObject)
|
||||
if !ok {
|
||||
if vType.ConvertibleTo(reflect.TypeFor[SerializedObject]()) {
|
||||
vSerialized = val.Convert(reflect.TypeFor[SerializedObject]()).Interface().(SerializedObject)
|
||||
} else {
|
||||
return E.Failure(fmt.Sprintf("convert %s (%T) to %s", k, v, reflect.TypeFor[SerializedObject]()))
|
||||
}
|
||||
}
|
||||
propNew := reflect.New(propType.Elem())
|
||||
err := Deserialize(vSerialized, propNew.Interface())
|
||||
if err.HasError() {
|
||||
return E.Failure("set field").With(k).With(err)
|
||||
}
|
||||
prop.Set(propNew)
|
||||
default:
|
||||
return E.Unsupported("field", k).Extraf("type=%s", propType)
|
||||
}
|
||||
} else {
|
||||
return E.Unsupported("field", k).Extraf("type=%s", propType)
|
||||
}
|
||||
} else {
|
||||
return E.Failure("unknown field").With(k)
|
||||
}
|
||||
}
|
||||
|
||||
return E.Nil()
|
||||
}
|
||||
|
||||
func toLowerNoSnake(s string) string {
|
||||
return strings.ToLower(strings.ReplaceAll(s, "_", ""))
|
||||
}
|
||||
|
||||
type SerializedObject = map[string]any
|
||||
|
|
|
@ -7,9 +7,16 @@ import (
|
|||
E "github.com/yusing/go-proxy/error"
|
||||
)
|
||||
|
||||
func ExpectErrNil(t *testing.T, err E.NestedError) {
|
||||
func ExpectNoError(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
if err.HasError() {
|
||||
var noError bool
|
||||
switch t := err.(type) {
|
||||
case E.NestedError:
|
||||
noError = t.NoError()
|
||||
default:
|
||||
noError = err == nil
|
||||
}
|
||||
if !noError {
|
||||
t.Errorf("expected err=nil, got %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue