From 2db6d5c163737de35d904028f85d1a04afd46b04 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 25 Jun 2025 20:50:18 +0800 Subject: [PATCH] feat: new deployment provider: kong --- go.mod | 7 + go.sum | 26 +-- internal/deployer/providers.go | 19 +++ internal/domain/access.go | 6 + internal/domain/provider.go | 2 + .../ssl-deployer/providers/kong/consts.go | 8 + pkg/core/ssl-deployer/providers/kong/kong.go | 149 ++++++++++++++++++ .../ssl-deployer/providers/kong/kong_test.go | 77 +++++++++ ui/public/imgs/providers/kong.png | Bin 0 -> 4820 bytes ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormKongConfig.tsx | 71 +++++++++ .../workflow/node/DeployNodeConfigForm.tsx | 3 + .../node/DeployNodeConfigFormKongConfig.tsx | 89 +++++++++++ ui/src/domain/access.ts | 7 + ui/src/domain/provider.ts | 4 + ui/src/i18n/locales/en/nls.access.json | 5 + ui/src/i18n/locales/en/nls.provider.json | 1 + .../i18n/locales/en/nls.workflow.nodes.json | 9 ++ ui/src/i18n/locales/zh/nls.access.json | 5 + ui/src/i18n/locales/zh/nls.provider.json | 1 + .../i18n/locales/zh/nls.workflow.nodes.json | 9 ++ 21 files changed, 491 insertions(+), 10 deletions(-) create mode 100644 pkg/core/ssl-deployer/providers/kong/consts.go create mode 100644 pkg/core/ssl-deployer/providers/kong/kong.go create mode 100644 pkg/core/ssl-deployer/providers/kong/kong_test.go create mode 100644 ui/public/imgs/providers/kong.png create mode 100644 ui/src/components/access/AccessFormKongConfig.tsx create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormKongConfig.tsx diff --git a/go.mod b/go.mod index f9bd4fb6..712a9904 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.3.0 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.155 github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 + github.com/kong/go-kong v0.66.1 github.com/libdns/dynv6 v1.0.0 github.com/libdns/libdns v0.2.3 github.com/luthermonson/go-proxmox v0.2.2 @@ -109,12 +110,15 @@ require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/imdario/mergo v0.3.12 // indirect github.com/jinzhu/copier v0.3.4 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/kong/semver/v4 v4.0.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/magefile/mage v1.14.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect @@ -127,6 +131,9 @@ require ( github.com/qiniu/x v1.10.5 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.17.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/go.sum b/go.sum index e574635c..149f9151 100644 --- a/go.sum +++ b/go.sum @@ -552,6 +552,8 @@ github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.155/go.mod h1:Y/+YLCFCJtS29i2M github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= @@ -596,6 +598,10 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kong/go-kong v0.66.1 h1:UVdemzcCpfXEl6O/VHdf0rT2bXdIO5ykuJbf2z1JTko= +github.com/kong/go-kong v0.66.1/go.mod h1:wRMPAXGOB3kn53TF6zN4l2JhIWPUfXDFKNHkMHBB3iQ= +github.com/kong/semver/v4 v4.0.1 h1:DIcNR8W3gfx0KabFBADPalxxsp+q/5COwIFkkhrFQ2Y= +github.com/kong/semver/v4 v4.0.1/go.mod h1:LImQ0oT15pJvSns/hs2laLca2zcYoHu5EsSNY0J6/QA= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= @@ -656,6 +662,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -786,6 +794,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= +github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -829,8 +839,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1187 h1:x2q6BAFm2f+9YaE7/lGPWXL7HzRkovjoqOMbdtRdpBw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1187/go.mod h1:GoIHP0ayv0QOWN4c9aUEaKi74lY/tbeJz7h5i8y2gdU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193 h1:zOWZKDVA3kvA5/b+AwKzDtz5ewdiibeKxVqtCFJSTNI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193/go.mod h1:ufxDBGyS3X/9QKkZzuOFKLNra9FmSfgAHBO/FlFZaTU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1188 h1:zzaIE12soTfyAgRvBYhb5bYxFXRCelvYXDEfvtkT5Y4= @@ -840,26 +848,18 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1163/go.mod github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1182/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1183/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1187/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1188/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1189/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1191/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1192 h1:3K6aJXXkjBLxqFYnBqAqFW5YqxmwMT0HR2F4gxQiNMU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1192/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193 h1:anxhOjL4WrQDqUcX7eT8VEaQITiKWllKwsH1fEt6lBw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163 h1:putqrH5n1SVRqFWHOylVqYI5yLQUjRTkHqZPLT2yeVY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163/go.mod h1:aEWRXlAvovPUUoS3kVB/LVWEQ19WqzTj2lXGvR1YArY= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1192 h1:2430drceaOXASJZyVZ+e7QSzgBfgwSjDEDM5rh4046M= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1192/go.mod h1:JHZLo95Fde/0et2Ag2E5P6VmCZQIq74MClUtanJ4JcY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193 h1:VtXqRnzGz3KheXu2msNPvA/fUYQGsVVRC30WgyAUEqg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193/go.mod h1:42I1OwaedHR6Yvg7J6UYoOjNYUYfFqwaeEkvx3x+NZc= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 h1:6SUO0hTie3zxnUEMxmhnS1iRIXpAukSZV27Nrx4NwIk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172/go.mod h1:tmN4zfu70SD0iee3qfpc09NRLel30zGoAuzIs4X0Kfs= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1189 h1:Db7gmkey7On70PAohvrna6RMLZzLHRjbALxPlH5JC3c= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1189/go.mod h1:x+WlMCjbePO7M3R0qzKmrpmieUWrtsRpcKBDpxJNQ5A= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193 h1:tmACSthp5JLjrdxzng6XFs4gfQcZHBTTVlXR0tO6hSk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193/go.mod h1:LWf5UPUl41EQICrq0jswgQEO/BtRQY+CxAI6X+i709o= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1191 h1:4l1Db+yFh9HgqNynYbG93khxLtXSBwnXZgNmc88jOE0= @@ -868,6 +868,12 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1183 h1:3fvxkF github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1183/go.mod h1:d47RTYqj/2xjIk/lmq8bQ9deUwfEQcWhPQxUgqZnz24= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182 h1:2DaykFM5mXvQBvuhQEU/aOG5amissS31XI1wZh+FeMA= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182/go.mod h1:pTAgdVcS28xFIARJXhg10hx2+g/Q9FqVkAkal3ARNfc= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 0b2a77bd..0f33f57f 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -63,6 +63,7 @@ import ( pJDCloudLive "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-live" pJDCloudVOD "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-vod" pK8sSecret "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/k8s-secret" + pKong "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/kong" pLeCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/lecdn" pLocal "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/local" pNetlifySite "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/netlify-site" @@ -924,6 +925,24 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy return deployer, err } + case domain.DeploymentProviderTypeKong: + { + access := domain.AccessConfigForKong{} + if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pKong.NewSSLDeployerProvider(&pKong.SSLDeployerProviderConfig{ + ServerUrl: access.ServerUrl, + ApiToken: access.ApiToken, + AllowInsecureConnections: access.AllowInsecureConnections, + ResourceType: pKong.ResourceType(xmaps.GetString(options.ProviderServiceConfig, "resourceType")), + Workspace: xmaps.GetString(options.ProviderServiceConfig, "workspace"), + CertificateId: xmaps.GetString(options.ProviderServiceConfig, "certificateId"), + }) + return deployer, err + } + case domain.DeploymentProviderTypeKubernetesSecret: { access := domain.AccessConfigForKubernetes{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 178a9d57..db34cf8a 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -227,6 +227,12 @@ type AccessConfigForJDCloud struct { AccessKeySecret string `json:"accessKeySecret"` } +type AccessConfigForKong struct { + ServerUrl string `json:"serverUrl"` + ApiToken string `json:"apiToken"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + type AccessConfigForKubernetes struct { KubeConfig string `json:"kubeConfig,omitempty"` } diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 6deedee8..d86faa07 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -52,6 +52,7 @@ const ( AccessProviderTypeHetzner = AccessProviderType("hetzner") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeJDCloud = AccessProviderType("jdcloud") + AccessProviderTypeKong = AccessProviderType("kong") AccessProviderTypeKubernetes = AccessProviderType("k8s") AccessProviderTypeLarkBot = AccessProviderType("larkbot") AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt") @@ -236,6 +237,7 @@ const ( DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn") DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live") DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod") + DeploymentProviderTypeKong = DeploymentProviderType(AccessProviderTypeKong) DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret") DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal) diff --git a/pkg/core/ssl-deployer/providers/kong/consts.go b/pkg/core/ssl-deployer/providers/kong/consts.go new file mode 100644 index 00000000..91b462bb --- /dev/null +++ b/pkg/core/ssl-deployer/providers/kong/consts.go @@ -0,0 +1,8 @@ +package kong + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/pkg/core/ssl-deployer/providers/kong/kong.go b/pkg/core/ssl-deployer/providers/kong/kong.go new file mode 100644 index 00000000..bd325240 --- /dev/null +++ b/pkg/core/ssl-deployer/providers/kong/kong.go @@ -0,0 +1,149 @@ +package kong + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/http" + "net/url" + "strings" + + "github.com/kong/go-kong/kong" + + "github.com/certimate-go/certimate/pkg/core" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" + xhttp "github.com/certimate-go/certimate/pkg/utils/http" +) + +type SSLDeployerProviderConfig struct { + // Kong 服务地址。 + ServerUrl string `json:"serverUrl"` + // Kong Admin API Token。 + ApiToken string `json:"apiToken"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 工作空间。 + // 选填。 + Workspace string `json:"workspace,omitempty"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId string `json:"certificateId,omitempty"` +} + +type SSLDeployerProvider struct { + config *SSLDeployerProviderConfig + logger *slog.Logger + sdkClient *kong.Client +} + +var _ core.SSLDeployer = (*SSLDeployerProvider)(nil) + +func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) { + if config == nil { + return nil, errors.New("the configuration of the ssl deployer provider is nil") + } + + client, err := createSDKClient(config.ServerUrl, config.Workspace, config.ApiToken, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("could not create sdk client: %w", err) + } + + return &SSLDeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) { + if logger == nil { + d.logger = slog.New(slog.DiscardHandler) + } else { + d.logger = logger + } +} + +func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &core.SSLDeployResult{}, nil +} + +func (d *SSLDeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.CertificateId == "" { + return errors.New("config `certificateId` is required") + } + + // 解析证书内容 + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return err + } + + if d.config.Workspace == "" { + // 更新证书 + // REF: https://developer.konghq.com/api/gateway/admin-ee/3.10/#/operations/upsert-certificate + updateCertificateReq := &kong.Certificate{ + ID: kong.String(d.config.CertificateId), + Cert: kong.String(certPEM), + Key: kong.String(privkeyPEM), + SNIs: kong.StringSlice(certX509.DNSNames...), + } + updateCertificateResp, err := d.sdkClient.Certificates.Update(context.TODO(), updateCertificateReq) + d.logger.Debug("sdk request 'kong.UpdateCertificate'", slog.String("sslId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'kong.UpdateCertificate': %w", err) + } + } else { + // 更新证书 + // REF: https://developer.konghq.com/api/gateway/admin-ee/3.10/#/operations/upsert-certificate-in-workspace + updateCertificateReq := &kong.Certificate{ + ID: kong.String(d.config.CertificateId), + Cert: kong.String(certPEM), + Key: kong.String(privkeyPEM), + SNIs: kong.StringSlice(certX509.DNSNames...), + } + updateCertificateResp, err := d.sdkClient.Certificates.Update(context.TODO(), updateCertificateReq) + d.logger.Debug("sdk request 'kong.UpdateCertificate'", slog.String("sslId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'kong.UpdateCertificate': %w", err) + } + } + + return nil +} + +func createSDKClient(serverUrl, workspace, apiKey string, skipTlsVerify bool) (*kong.Client, error) { + httpClient := &http.Client{ + Transport: xhttp.NewDefaultTransport(), + Timeout: http.DefaultClient.Timeout, + } + if skipTlsVerify { + transport := xhttp.NewDefaultTransport() + if transport.TLSClientConfig == nil { + transport.TLSClientConfig = &tls.Config{} + } + transport.TLSClientConfig.InsecureSkipVerify = true + httpClient.Transport = transport + } + + baseUrl := strings.TrimRight(serverUrl, "/") + if workspace != "" { + baseUrl = fmt.Sprintf("%s/%s", baseUrl, url.PathEscape(workspace)) + } + + return kong.NewClient(kong.String(baseUrl), httpClient) +} diff --git a/pkg/core/ssl-deployer/providers/kong/kong_test.go b/pkg/core/ssl-deployer/providers/kong/kong_test.go new file mode 100644 index 00000000..4d3c2aff --- /dev/null +++ b/pkg/core/ssl-deployer/providers/kong/kong_test.go @@ -0,0 +1,77 @@ +package kong_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/kong" +) + +var ( + fInputCertPath string + fInputKeyPath string + fServerUrl string + fApiToken string + fCertificateId string +) + +func init() { + argsPrefix := "CERTIMATE_SSLDEPLOYER_KONG_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./kong_test.go -args \ + --CERTIMATE_SSLDEPLOYER_KONG_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_SSLDEPLOYER_KONG_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_SSLDEPLOYER_KONG_SERVERURL="http://127.0.0.1:9080" \ + --CERTIMATE_SSLDEPLOYER_KONG_APITOKEN="your-admin-token" \ + --CERTIMATE_SSLDEPLOYER_KONG_CERTIFICATEID="your-cerficiate-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("SERVERURL: %v", fServerUrl), + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{ + ServerUrl: fServerUrl, + ApiToken: fApiToken, + AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: fCertificateId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/ui/public/imgs/providers/kong.png b/ui/public/imgs/providers/kong.png new file mode 100644 index 0000000000000000000000000000000000000000..2b95e955b8bc2d19f0986dcd57a6a6bc3c76ad36 GIT binary patch literal 4820 zcmb7{3p|tW`^UFA%qE7moN^2~Bok7Z)0`q^SR`Q%m1Bt;HdV*Y16;>w4dx``(^s&+Azt7GuH#6@dZ(03Oq$ zhSmT8P;dLi#m1_vV~&#m05$;D9A{+WXD&>475O9ntbY{+9^C%j6>V{2>3=eaN1?x& zzW^<+B0s16>cVTYe`7oA(tqp!E?WM}Lchs>?t!(?&ih&0QIpLY1%Afw3jAmB-{~*x z&%hs*zX5+vS%&{h`sda!{Wt!v!2Q3be{uf5v{>edh8|YHANH{LU;1z3uEySN;O;ht z_^G$knxE;*tZclZ~kJvFA4&3e=T7&7f4l)aLoQmgDEkgibOf3?DUQo`k}1PykQ%#tL?M>)}(>_o1vRL z)y}J@qCOryz#(<4!qYYJQ07|rxcg{VZw_%e&$3oz?`a;t!(D4pxN#+S-}U;}!j*9T*_18lSD>P!RgBi#H#In4a&$WTJwPN7SQIQy$%;d>mB5n+5~ zJT7M?m-5bpe=^Oz-|V{9W_-Vu*VenMqogr1UM$H(lwOGIj!Y1XJ!5lF>8src-8Bcl zH1+v=Rm>FT9>R4gc*I4k#=X9OAb11=^LahwkIC0B%fV|+&2jV7@1~y(ZSxx_YQk&P z09;<`9%t7|{jwYJ7Lzk?UXv*sh~T~yR1%2QoKH?ud?d;5oSI!vbd(BKvK}0Zc9cGN zmZP?bGsguk7DKLF_!aymUDw{ zD!71>*w^Ow_O`tfv6d!k&S$jQ##QCuM%DbqHmZ*Cjv7o#&A4bLbFUS zce>cs*Nk{SKMGeO?kVpsHl*J$g{&A<^HcQ*=2g0R6>!75b}I?*_#ZiT;9M972k}vYMaxc5ig&JyKC*0MZ{0Tz@;MLMySzO4jhsco1FMTZt*_ICUCEq{ z2B@zci3-<`TXh_fZ&q;i5DKkCx?0KQ*hKB2PJmo@@TjRKwm~ByRPO6vA)>V{Q)Hru zA40uRWGgY{S+*D3OOG7mR&z6*g*X}c+@5Q7S@wPzb(iK54W5vQt9SwYCaAV|Qs8l% zId0vek4MUcX5G zVf@_NCorS1^n=YY#=sK3>I4tIE8o&InyI$f#6zF;oamZIp8~Yv^)$F^-ghdwXW!Ye zcn30EeLTh8%qiL&D4k+SVcfXAD$MSoy)%l!qsH&@j3Yz`YPkMA@ygo)pyE!@<;$Zw zII-T%PeG=Iy`8|Ihd|*AC<7WV!9DcA;;Q%1UHkf0ZzgxD4pEFT`V6=x;sa=wV+4GLS62xc55v47>m5$@q_ zS!E+;?pAXUSs1~+Oey`r90S&h<(3X(h&s#5TUKK3k5sGN&{6wGIK8%tc{#&fL%crw z6=5J(D4M6l42RUzylnN|7MJj}BSIaFQw0S^0||v{9KK+rNn2Ioyk_v(jE^t)V+SMh zePa)%O-GILQnRvLBz+zv)Ez^)p7A7QDRjBE`1R1dEuJsDuy5IXyuKcJ$i&O*96u)} z_l}5K`EjOh`+2*kOM@@o4BGqR+>#ew>jP%#ikC|&&OsiWQ9tuoMo5hYE|r|;-3y57 zQQpxI>NmzKf*p5)gb*4Q1$A^q)ST0_}QOYYT%F>TXrVGB0<; zMqBER>I1Hf)l8R%Jd9tqk&tREU?R0!e0P0cq`SXCy{Rb#jW)r`~W2RcVMm z@nuxxt|to$~L_^1BvgidOp@c2vyggD&K|(O$h_C#Y*n?^aAhiJ2elraq|5)NXQ8_Pc+ z{zEe*XrOA^>;p(aT0ujUga&Y3G7WLlmfP@(rpK zi=1SZMZplKtmKnH(%QUubWI!q=|~dlM4NVYL2&2a6o{w^!XVd3+2(qQGV%VM4Kh{9 zSXd#)keYy*+zm?zTQFDI9{9sjOGeo-BBUW%(G9RNl!9|BwDq8mZ-7YB@8;3Eg@iPG zFIn8&*R_im*)t=3FL{<$Mh(sPB#ST0HlvQ&wCMFgmN_5xHpb8UdMc z$@dbjMM)~=uz1`$A+Q}KLBgiwT2qX*NhM{ZNS#Fz#g1kLD`YN3U)aASKm71Jx2&*e zCRUs}v=7R75IT(u)2TO>z0qUNuQZH6opH)(4cU#6ml$=xThT0a9}s#QK5|t`cQjTZ zI>`w?eGB%f0C*@GT36AV9{B}sXBtImi%*+6Zet_WJqi@-6r+>kcvzd+C~8skHxS^u zKv7-#EJwFVnhXjm(K66W+`V5wX;}W;^jSBcM_MUxSKRu23fRrv2Tn^HM3}{iF!1w+ zuVhoxk3O|h9HxQYI(FV@)_a0VM~CAUt#e^OZtiL#mSTKCOR4W5zU4!q9c?YN%2TlF z-I9&c@P_t@A%R7yR{k=qjlD^Z50qhs0}%sz=bOqyVK-+@0oozGi}J%yLORS}arcsI z#T15*tl-ZHR>izunN2wI9dDzC2UZ-ir7;%huHgb@u=$uXke-xZSFxt@9HCcp!-{PTUyO0IW8>xLe6&dJ$t;*T(1Nhi6g*1-hQ@V=U=F*oO4|F{$p?eh{OMY98D5O8_dr= zxeg3B(&uhOUkgQkgw4zgoC*s{+3LrP>f(d)RJ}T&8j-M%cSr%s#I%$5R0GPovtdHK zT>dBEv*|_q)}p9QP^T)i#dyqMq~2KBL_CJFmHRMM@1`;&2C$KELC{YVITM~Vo#e}U z$my^49fRtM_BC0BkJSb(QpA_s?ZS_BVokQ}izKw(X*V0qm0P1kV%qH>9q(da1xKZ4 zKD`+rb6w#^p&H0vgTnjy`|gP;Hd~3*KH>HcRFJO%bbaN$99+fP1zYfgYs#vK!ed-?Q0+RChhl8|UJ<k)VSH=xMW^kp4zKOkuepd8+mL(%8rQIW zYGptlW~ zl>{dCJ&8S1YZC={U5>ZSxaJSN7CVA6r+ohmKx*&dX#$c}|CE}_*O#qP;R||7ce7;S*eaN?cs^To> zG`dT8)^1_JDcs_c>(PeI9S!%OtCaLrC}T-t5_lW2L)uzdo?G{#z|L8)PCFaBl92f> zXNAQCj4OI`Bs?Sv&%q{Qrj)FEv0M2|+>fE0(#wEcl;Oz?H8byht)g4`1{@*jwL2E4 zjHAm%u-Ts+Woee^<1rFpXm&0fGRnumGo!v)2Z~-;l8^$iD+)FHIeEUe*MI#jxYlK& z+A!*zxU|tw_krWljccl^JEquV6onibobA_Qo1GeCggCb^xTJEgRh@T=(XaVp2~!MZ zelj)TsCHQCE7_`?*2ehoAF|SV6H)>b)wk|lnPXq^q}4erp4O6(bfBL&6RcE>l&`IH z+7ln`5!DRkwCh$Xu9T@N&4ZwM*rNN3E9|-TBJ>>&zEHe{wlu`6Y_4x`eNKM)7OrbN l7Q+VI{tGF|d})k5{`&xUeWAz*^_fz)d!|Mh!wNK>_%E1O>GS{q literal 0 HcmV?d00001 diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index d49fe661..ba9e1318 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -50,6 +50,7 @@ import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServices import AccessFormHetznerConfig from "./AccessFormHetznerConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; +import AccessFormKongConfig from "./AccessFormKongConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig"; import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig"; @@ -266,6 +267,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.JDCLOUD: return ; + case ACCESS_PROVIDERS.KONG: + return ; case ACCESS_PROVIDERS.KUBERNETES: return ; case ACCESS_PROVIDERS.LARKBOT: diff --git a/ui/src/components/access/AccessFormKongConfig.tsx b/ui/src/components/access/AccessFormKongConfig.tsx new file mode 100644 index 00000000..36669161 --- /dev/null +++ b/ui/src/components/access/AccessFormKongConfig.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod/v4"; + +import { type AccessConfigForKong } from "@/domain/access"; + +type AccessFormKongConfigFieldValues = Nullish; + +export type AccessFormKongConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormKongConfigFieldValues; + onValuesChange?: (values: AccessFormKongConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormKongConfigFieldValues => { + return { + serverUrl: "http://:8001/", + apiToken: "", + }; +}; + +const AccessFormKongConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormKongConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + serverUrl: z.url(t("common.errmsg.url_invalid")), + apiToken: z.string().nonempty(t("access.form.kong_api_token.placeholder")), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormKongConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index baf4dcf4..10fb1765 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -65,6 +65,7 @@ import DeployNodeConfigFormJDCloudALBConfig from "./DeployNodeConfigFormJDCloudA import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig"; import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig"; import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig"; +import DeployNodeConfigFormKongConfig from "./DeployNodeConfigFormKongConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; import DeployNodeConfigFormLeCDNConfig from "./DeployNodeConfigFormLeCDNConfig"; import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig"; @@ -304,6 +305,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.JDCLOUD_VOD: return ; + case DEPLOYMENT_PROVIDERS.KONG: + return ; case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET: return ; case DEPLOYMENT_PROVIDERS.LECDN: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormKongConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormKongConfig.tsx new file mode 100644 index 00000000..e023c014 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormKongConfig.tsx @@ -0,0 +1,89 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod/v4"; + +import Show from "@/components/Show"; + +type DeployNodeConfigFormKongConfigFieldValues = Nullish<{ + resourceType: string; + certificateId?: string; +}>; + +export type DeployNodeConfigFormKongConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormKongConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormKongConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; + +const initFormModel = (): DeployNodeConfigFormKongConfigFieldValues => { + return { + resourceType: RESOURCE_TYPE_CERTIFICATE, + certificateId: "", + }; +}; + +const DeployNodeConfigFormKongConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormKongConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, t("workflow_node.deploy.form.kong_resource_type.placeholder")), + workspace: z.string().nullish(), + certificateId: z + .string() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_CERTIFICATE || !!v?.trim(), t("workflow_node.deploy.form.kong_certificate_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + + } + > + + + +
+ ); +}; + +export default DeployNodeConfigFormKongConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index df0e8a06..51555f07 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -45,6 +45,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForHetzner | AccessConfigForHuaweiCloud | AccessConfigForJDCloud + | AccessConfigForKong | AccessConfigForKubernetes | AccessConfigForLarkBot | AccessConfigForLeCDN @@ -293,6 +294,12 @@ export type AccessConfigForJDCloud = { accessKeySecret: string; }; +export type AccessConfigForKong = { + serverUrl: string; + apiToken: string; + allowInsecureConnections?: boolean; +}; + export type AccessConfigForKubernetes = { kubeConfig?: string; }; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 12511f81..9c098fc5 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -44,6 +44,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ HETZNER: "hetzner", HUAWEICLOUD: "huaweicloud", JDCLOUD: "jdcloud", + KONG: "kong", KUBERNETES: "k8s", LARKBOT: "larkbot", LECDN: "lecdn", @@ -146,6 +147,7 @@ export const accessProvidersMap: Map [ type, diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 76be1cfd..b5b79af7 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -286,6 +286,11 @@ "access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file", "access.form.k8s_kubeconfig.tooltip": "For more information, see https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/

Leave it blank to use the Pod's ServiceAccount.", + "access.form.kong_server_url.label": "Kong admin API server URL", + "access.form.kong_server_url.placeholder": "Please enter Kong admin API server URL", + "access.form.kong_api_key.label": "Kong admin API token", + "access.form.kong_api_key.placeholder": "Please enter Kong admin API token", + "access.form.kong_api_key.tooltip": "For more information, see https://developer.konghq.com/", "access.form.larkbot_webhook_url.label": "Lark bot Webhook URL", "access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL", "access.form.larkbot_webhook_url.tooltip": "For more information, see https://www.feishu.cn/hc/en-US/articles/807992406756", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index fe76a727..9c7d0053 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -101,6 +101,7 @@ "provider.jdcloud.dns": "JD Cloud - DNS", "provider.jdcloud.live": "JD Cloud - Live Video", "provider.jdcloud.vod": "JD Cloud - VOD (Video on Demand)", + "provider.kong": "Kong", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.larkbot": "Lark Bot", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index caf55597..0eba1c73 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -528,6 +528,15 @@ "workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key", "workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key", "workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see https://kubernetes.io/docs/concepts/configuration/secret/", + "workflow_node.deploy.form.kong_resource_type.label": "Resource type", + "workflow_node.deploy.form.kong_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.kong_resource_type.option.certificate.label": "SSL certificate", + "workflow_node.deploy.form.kong_workspace.label": "Kong workspace (Optional)", + "workflow_node.deploy.form.kong_workspace.placeholder": "Please enter Kong workspace", + "workflow_node.deploy.form.kong_workspace.tooltip": "You can find it on Kong dashboard.", + "workflow_node.deploy.form.kong_certificate_id.label": "Kong certificate ID", + "workflow_node.deploy.form.kong_certificate_id.placeholder": "Please enter Kong certificate ID", + "workflow_node.deploy.form.kong_certificate_id.tooltip": "You can find it on Kong dashboard.", "workflow_node.deploy.form.lecdn_resource_type.label": "Resource type", "workflow_node.deploy.form.lecdn_resource_type.placeholder": "Please select resource type", "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "Certificate", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 837ccc17..8299c3a4 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -286,6 +286,11 @@ "access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.placeholder": "请输入 KubeConfig 文件内容", "access.form.k8s_kubeconfig.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/

为空时,将使用 Pod 的 ServiceAccount 作为凭证。", + "access.form.kong_server_url.label": "Kong Admin API 服务地址", + "access.form.kong_server_url.placeholder": "请输入 Kong Admin API 服务地址", + "access.form.kong_api_key.label": "Kong Admin API Token", + "access.form.kong_api_key.placeholder": "请输入 Kong Admin API Token", + "access.form.kong_api_key.tooltip": "这是什么?请参阅 https://developer.konghq.com/", "access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 https://www.feishu.cn/hc/zh-CN/articles/807992406756", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index fa16398a..73a3b63c 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -101,6 +101,7 @@ "provider.jdcloud.dns": "京东云 - 云解析 DNS", "provider.jdcloud.live": "京东云 - 视频直播", "provider.jdcloud.vod": "京东云 - 视频点播", + "provider.kong": "Kong", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.larkbot": "飞书群机器人", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 4e20f18c..cd571174 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -526,6 +526,15 @@ "workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)", "workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键", "workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/", + "workflow_node.deploy.form.kong_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.kong_resource_type.placeholder": "请选择证书部署方式", + "workflow_node.deploy.form.kong_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.kong_workspace.label": "Kong 工作空间(可选)", + "workflow_node.deploy.form.kong_workspace.placeholder": "请输入 Kong 工作空间", + "workflow_node.deploy.form.kong_workspace.tooltip": "请登录 Kong 控制台查看。", + "workflow_node.deploy.form.kong_certificate_id.label": "Kong 证书 ID", + "workflow_node.deploy.form.kong_certificate_id.placeholder": "请输入 Kong 证书 ID", + "workflow_node.deploy.form.kong_certificate_id.tooltip": "请登录 Kong 控制台查看。", "workflow_node.deploy.form.lecdn_resource_type.label": "证书部署方式", "workflow_node.deploy.form.lecdn_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "替换指定证书",