Merge pull request #416 from fudiwei/feat/new-workflow

feat: more providers
This commit is contained in:
Yoan.liu 2025-01-15 09:11:58 +08:00 committed by GitHub
commit 23f83b9377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 1509 additions and 26 deletions

View File

@ -125,6 +125,7 @@ make local.run
| [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN |
| [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN |
| [BytePlus](https://www.byteplus.com/) | 可部署到 BytePlus CDN 等服务 |
| [优刻得](https://www.ucloud.cn/) | 可部署到优刻得 US3、UCDN 等服务 |
</details>

View File

@ -124,6 +124,7 @@ The following hosting providers are supported:
| [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN |
| [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN |
| [BytePlus](https://www.byteplus.com/) | Supports deployment to BytePlus CDN |
| [UCloud](https://www.ucloud-global.com/) | Supports deployment to UCloud US3, UCDN |
</details>

2
go.mod
View File

@ -34,6 +34,7 @@ require (
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1080
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1065
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1065
github.com/ucloud/ucloud-sdk-go v0.22.31
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.8
github.com/volcengine/volc-sdk-golang v1.0.189
github.com/volcengine/volcengine-go-sdk v1.0.177
@ -80,6 +81,7 @@ require (
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.mongodb.org/mongo-driver v1.12.0 // indirect

5
go.sum
View File

@ -783,10 +783,13 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
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=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -847,6 +850,8 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ucloud/ucloud-sdk-go v0.22.31 h1:izZK+Re9ZkJAd1fHSVpFzgh8uKda4f5G6++iUw4n/mE=
github.com/ucloud/ucloud-sdk-go v0.22.31/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=

View File

@ -27,6 +27,8 @@ import (
providerTencentCloudCSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-css"
providerTencentCloudECDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ecdn"
providerTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-eo"
providerUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn"
providerUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3"
providerVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn"
providerVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-clb"
providerVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-dcdn"
@ -352,6 +354,39 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger,
}
}
case domain.DeployProviderTypeUCloudUCDN, domain.DeployProviderTypeUCloudUS3:
{
access := domain.AccessConfigForUCloud{}
if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
}
switch options.Provider {
case domain.DeployProviderTypeUCloudUCDN:
deployer, err := providerUCloudUCDN.NewWithLogger(&providerUCloudUCDN.UCloudUCDNDeployerConfig{
PrivateKey: access.PrivateKey,
PublicKey: access.PublicKey,
ProjectId: access.ProjectId,
DomainId: maps.GetValueAsString(options.ProviderDeployConfig, "domainId"),
}, logger)
return deployer, logger, err
case domain.DeployProviderTypeUCloudUS3:
deployer, err := providerUCloudUS3.NewWithLogger(&providerUCloudUS3.UCloudUS3DeployerConfig{
PrivateKey: access.PrivateKey,
PublicKey: access.PublicKey,
ProjectId: access.ProjectId,
Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"),
Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger)
return deployer, logger, err
default:
break
}
}
case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS:
{
access := domain.AccessConfigForVolcEngine{}

View File

@ -25,9 +25,9 @@ func (a *Access) UnmarshalConfigToMap() (map[string]any, error) {
type AccessConfigForACMEHttpReq struct {
Endpoint string `json:"endpoint"`
Mode string `json:"mode"`
Username string `json:"username"`
Password string `json:"password"`
Mode string `json:"mode,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
type AccessConfigForAliyun struct {
@ -79,7 +79,7 @@ type AccessConfigForHuaweiCloud struct {
type AccessConfigForLocal struct{}
type AccessConfigForKubernetes struct {
KubeConfig string `json:"kubeConfig"`
KubeConfig string `json:"kubeConfig,omitempty"`
}
type AccessConfigForNameDotCom struct {
@ -105,9 +105,9 @@ type AccessConfigForSSH struct {
Host string `json:"host"`
Port int32 `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Key string `json:"key"`
KeyPassphrase string `json:"keyPassphrase"`
Password string `json:"password,omitempty"`
Key string `json:"key,omitempty"`
KeyPassphrase string `json:"keyPassphrase,omitempty"`
}
type AccessConfigForTencentCloud struct {
@ -115,6 +115,12 @@ type AccessConfigForTencentCloud struct {
SecretKey string `json:"secretKey"`
}
type AccessConfigForUCloud struct {
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
ProjectId string `json:"projectId,omitempty"`
}
type AccessConfigForVolcEngine struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`

View File

@ -27,6 +27,7 @@ const (
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeSSH = AccessProviderType("ssh")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeWebhook = AccessProviderType("webhook")
)
@ -92,6 +93,8 @@ const (
DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css")
DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn")
DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo")
DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn")
DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3")
DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn")
DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb")
DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn")

View File

@ -0,0 +1,132 @@
package uclouducdn
import (
"context"
"errors"
"strconv"
xerrors "github.com/pkg/errors"
uCdn "github.com/ucloud/ucloud-sdk-go/services/ucdn"
usdk "github.com/ucloud/ucloud-sdk-go/ucloud"
uAuth "github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploaderSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl"
)
type UCloudUCDNDeployerConfig struct {
// 优刻得 API 私钥。
PrivateKey string `json:"privateKey"`
// 优刻得 API 公钥。
PublicKey string `json:"publicKey"`
// 优刻得项目 ID。
ProjectId string `json:"projectId,omitempty"`
// 加速域名 ID。
DomainId string `json:"domainId"`
}
type UCloudUCDNDeployer struct {
config *UCloudUCDNDeployerConfig
logger logger.Logger
sdkClient *uCdn.UCDNClient
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*UCloudUCDNDeployer)(nil)
func New(config *UCloudUCDNDeployerConfig) (*UCloudUCDNDeployer, error) {
return NewWithLogger(config, logger.NewNilLogger())
}
func NewWithLogger(config *UCloudUCDNDeployerConfig, logger logger.Logger) (*UCloudUCDNDeployer, error) {
if config == nil {
return nil, errors.New("config is nil")
}
if logger == nil {
return nil, errors.New("logger is nil")
}
client, err := createSdkClient(config.PrivateKey, config.PublicKey)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := uploaderSsl.New(&uploaderSsl.UCloudUSSLUploaderConfig{
PrivateKey: config.PrivateKey,
PublicKey: config.PublicKey,
ProjectId: config.ProjectId,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &UCloudUCDNDeployer{
logger: logger,
config: config,
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *UCloudUCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 USSL
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
}
d.logger.Logt("certificate file uploaded", upres)
// 获取加速域名配置
// REF: https://docs.ucloud.cn/api/ucdn-api/get_ucdn_domain_config
getUcdnDomainConfigReq := d.sdkClient.NewGetUcdnDomainConfigRequest()
getUcdnDomainConfigReq.DomainId = []string{d.config.DomainId}
if d.config.ProjectId != "" {
getUcdnDomainConfigReq.ProjectId = usdk.String(d.config.ProjectId)
}
getUcdnDomainConfigResp, err := d.sdkClient.GetUcdnDomainConfig(getUcdnDomainConfigReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.GetUcdnDomainConfig'")
} else if len(getUcdnDomainConfigResp.DomainList) == 0 {
return nil, errors.New("no domain found")
}
d.logger.Logt("已查询到加速域名配置", getUcdnDomainConfigResp)
// 更新 HTTPS 加速配置
// REF: https://docs.ucloud.cn/api/ucdn-api/update_ucdn_domain_https_config_v2
certId, _ := strconv.Atoi(upres.CertId)
updateUcdnDomainHttpsConfigV2Req := d.sdkClient.NewUpdateUcdnDomainHttpsConfigV2Request()
updateUcdnDomainHttpsConfigV2Req.DomainId = usdk.String(d.config.DomainId)
updateUcdnDomainHttpsConfigV2Req.HttpsStatusCn = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusCn)
updateUcdnDomainHttpsConfigV2Req.HttpsStatusAbroad = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusAbroad)
updateUcdnDomainHttpsConfigV2Req.HttpsStatusAbroad = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusAbroad)
updateUcdnDomainHttpsConfigV2Req.CertId = usdk.Int(certId)
updateUcdnDomainHttpsConfigV2Req.CertName = usdk.String(upres.CertName)
updateUcdnDomainHttpsConfigV2Req.CertType = usdk.String("ussl")
if d.config.ProjectId != "" {
updateUcdnDomainHttpsConfigV2Req.ProjectId = usdk.String(d.config.ProjectId)
}
updateUcdnDomainHttpsConfigV2Resp, err := d.sdkClient.UpdateUcdnDomainHttpsConfigV2(updateUcdnDomainHttpsConfigV2Req)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.UpdateUcdnDomainHttpsConfigV2'")
}
d.logger.Logt("已更新 HTTPS 加速配置", updateUcdnDomainHttpsConfigV2Resp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(privateKey, publicKey string) (*uCdn.UCDNClient, error) {
cfg := usdk.NewConfig()
credential := uAuth.NewCredential()
credential.PrivateKey = privateKey
credential.PublicKey = publicKey
client := uCdn.NewClient(&cfg, &credential)
return client, nil
}

View File

@ -0,0 +1,75 @@
package uclouducdn_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn"
)
var (
fInputCertPath string
fInputKeyPath string
fPrivateKey string
fPublicKey string
fDomainId string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_UCLOUDUCDN_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fPrivateKey, argsPrefix+"PRIVATEKEY", "", "")
flag.StringVar(&fPublicKey, argsPrefix+"PUBLICKEY", "", "")
flag.StringVar(&fDomainId, argsPrefix+"DOMAINID", "", "")
}
/*
Shell command to run this test:
go test -v ./ucloud_ucdn_test.go -args \
--CERTIMATE_DEPLOYER_UCLOUDUCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_UCLOUDUCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_UCLOUDUCDN_PRIVATEKEY="your-private-key" \
--CERTIMATE_DEPLOYER_UCLOUDUCDN_PUBLICKEY="your-public-key" \
--CERTIMATE_DEPLOYER_UCLOUDUCDN_DOMAINID="your-domain-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("PRIVATEKEY: %v", fPrivateKey),
fmt.Sprintf("PUBLICKEY: %v", fPublicKey),
fmt.Sprintf("DOMAIN: %v", fDomainId),
}, "\n"))
deployer, err := provider.New(&provider.UCloudUCDNDeployerConfig{
PrivateKey: fPrivateKey,
PublicKey: fPublicKey,
DomainId: fDomainId,
})
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)
})
}

View File

@ -0,0 +1,116 @@
package ucloudus3
import (
"context"
"errors"
xerrors "github.com/pkg/errors"
usdk "github.com/ucloud/ucloud-sdk-go/ucloud"
uAuth "github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploaderSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl"
usdkFile "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ufile"
)
type UCloudUS3DeployerConfig struct {
// 优刻得 API 私钥。
PrivateKey string `json:"privateKey"`
// 优刻得 API 公钥。
PublicKey string `json:"publicKey"`
// 优刻得项目 ID。
ProjectId string `json:"projectId,omitempty"`
// 优刻得地域。
Region string `json:"region"`
// 存储桶名。
Bucket string `json:"bucket"`
// 自定义域名(不支持泛域名)。
Domain string `json:"domain"`
}
type UCloudUS3Deployer struct {
config *UCloudUS3DeployerConfig
logger logger.Logger
sdkClient *usdkFile.UFileClient
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*UCloudUS3Deployer)(nil)
func New(config *UCloudUS3DeployerConfig) (*UCloudUS3Deployer, error) {
return NewWithLogger(config, logger.NewNilLogger())
}
func NewWithLogger(config *UCloudUS3DeployerConfig, logger logger.Logger) (*UCloudUS3Deployer, error) {
if config == nil {
return nil, errors.New("config is nil")
}
if logger == nil {
return nil, errors.New("logger is nil")
}
client, err := createSdkClient(config.PrivateKey, config.PublicKey, config.Region)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := uploaderSsl.New(&uploaderSsl.UCloudUSSLUploaderConfig{
PrivateKey: config.PrivateKey,
PublicKey: config.PublicKey,
ProjectId: config.ProjectId,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &UCloudUS3Deployer{
logger: logger,
config: config,
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *UCloudUS3Deployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 USSL
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
}
d.logger.Logt("certificate file uploaded", upres)
// 添加 SSL 证书
// REF: https://docs.ucloud.cn/api/ufile-api/add_ufile_ssl_cert
addUFileSSLCertReq := d.sdkClient.NewAddUFileSSLCertRequest()
addUFileSSLCertReq.BucketName = usdk.String(d.config.Bucket)
addUFileSSLCertReq.Domain = usdk.String(d.config.Domain)
addUFileSSLCertReq.USSLId = usdk.String(upres.CertId)
addUFileSSLCertReq.CertificateName = usdk.String(upres.CertName)
if d.config.ProjectId != "" {
addUFileSSLCertReq.ProjectId = usdk.String(d.config.ProjectId)
}
addUFileSSLCertResp, err := d.sdkClient.AddUFileSSLCert(addUFileSSLCertReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.AddUFileSSLCert'")
}
d.logger.Logt("添加 SSL 证书", addUFileSSLCertResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(privateKey, publicKey, region string) (*usdkFile.UFileClient, error) {
cfg := usdk.NewConfig()
cfg.Region = region
credential := uAuth.NewCredential()
credential.PrivateKey = privateKey
credential.PublicKey = publicKey
client := usdkFile.NewClient(&cfg, &credential)
return client, nil
}

View File

@ -0,0 +1,85 @@
package ucloudus3_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3"
)
var (
fInputCertPath string
fInputKeyPath string
fPrivateKey string
fPublicKey string
fRegion string
fBucket string
fDomain string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_UCLOUDUS3_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fPrivateKey, argsPrefix+"PRIVATEKEY", "", "")
flag.StringVar(&fPublicKey, argsPrefix+"PUBLICKEY", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fBucket, argsPrefix+"BUCKET", "", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}
/*
Shell command to run this test:
go test -v ./ucloud_us3_test.go -args \
--CERTIMATE_DEPLOYER_UCLOUDUS3_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_PRIVATEKEY="your-private-key" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_PUBLICKEY="your-public-key" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_REGION="cn-bj2" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_BUCKET="your-us3-bucket" \
--CERTIMATE_DEPLOYER_UCLOUDUS3_DOMAIN="example.com"
*/
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("PRIVATEKEY: %v", fPrivateKey),
fmt.Sprintf("PUBLICKEY: %v", fPublicKey),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("BUCKET: %v", fBucket),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.New(&provider.UCloudUS3DeployerConfig{
PrivateKey: fPrivateKey,
PublicKey: fPublicKey,
Region: fRegion,
Bucket: fBucket,
Domain: fDomain,
})
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)
})
}

View File

@ -0,0 +1,220 @@
package ucloudussl
import (
"context"
"crypto/md5"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
xerrors "github.com/pkg/errors"
usdk "github.com/ucloud/ucloud-sdk-go/ucloud"
uAuth "github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
x509util "github.com/usual2970/certimate/internal/pkg/utils/x509"
usdkSsl "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ussl"
)
type UCloudUSSLUploaderConfig struct {
// 优刻得 API 私钥。
PrivateKey string `json:"privateKey"`
// 优刻得 API 公钥。
PublicKey string `json:"publicKey"`
// 优刻得项目 ID。
ProjectId string `json:"projectId,omitempty"`
}
type UCloudUSSLUploader struct {
config *UCloudUSSLUploaderConfig
sdkClient *usdkSsl.USSLClient
}
var _ uploader.Uploader = (*UCloudUSSLUploader)(nil)
func New(config *UCloudUSSLUploaderConfig) (*UCloudUSSLUploader, error) {
if config == nil {
return nil, errors.New("config is nil")
}
client, err := createSdkClient(config.PrivateKey, config.PublicKey)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
return &UCloudUSSLUploader{
config: config,
sdkClient: client,
}, nil
}
func (u *UCloudUSSLUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
// 生成新证书名(需符合优刻得命名规则)
var certId, certName string
certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
// 生成优刻得所需的证书参数
certPemBase64 := base64.StdEncoding.EncodeToString([]byte(certPem))
privkeyPemBase64 := base64.StdEncoding.EncodeToString([]byte(privkeyPem))
certMd5 := md5.Sum([]byte(certPemBase64 + privkeyPemBase64))
certMd5Hex := hex.EncodeToString(certMd5[:])
// 上传托管证书
// REF: https://docs.ucloud.cn/api/usslcertificate-api/upload_normal_certificate
uploadNormalCertificateReq := u.sdkClient.NewUploadNormalCertificateRequest()
uploadNormalCertificateReq.CertificateName = usdk.String(certName)
uploadNormalCertificateReq.SslPublicKey = usdk.String(certPemBase64)
uploadNormalCertificateReq.SslPrivateKey = usdk.String(privkeyPemBase64)
uploadNormalCertificateReq.SslMD5 = usdk.String(certMd5Hex)
if u.config.ProjectId != "" {
uploadNormalCertificateReq.ProjectId = usdk.String(u.config.ProjectId)
}
uploadNormalCertificateResp, err := u.sdkClient.UploadNormalCertificate(uploadNormalCertificateReq)
if err != nil {
if uploadNormalCertificateResp != nil && uploadNormalCertificateResp.GetRetCode() == 80035 {
return u.getExistCert(ctx, certPem, privkeyPem)
}
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.UploadNormalCertificate'")
}
certId = fmt.Sprintf("%d", uploadNormalCertificateResp.CertificateID)
return &uploader.UploadResult{
CertId: certId,
CertName: certName,
ExtendedData: map[string]interface{}{
"resourceId": uploadNormalCertificateResp.LongResourceID,
},
}, nil
}
func (u *UCloudUSSLUploader) getExistCert(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
// 解析证书内容
certX509, err := x509util.ParseCertificateFromPEM(certPem)
if err != nil {
return nil, err
}
// 遍历获取用户证书列表,避免重复上传
// REF: https://docs.ucloud.cn/api/usslcertificate-api/get_certificate_list
// REF: https://docs.ucloud.cn/api/usslcertificate-api/download_certificate
getCertificateListPage := int(1)
getCertificateListLimit := int(1000)
for {
getCertificateListReq := u.sdkClient.NewGetCertificateListRequest()
getCertificateListReq.Mode = usdk.String("trust")
getCertificateListReq.Domain = usdk.String(certX509.Subject.CommonName)
getCertificateListReq.Sort = usdk.String("2")
getCertificateListReq.Page = usdk.Int(getCertificateListPage)
getCertificateListReq.PageSize = usdk.Int(getCertificateListLimit)
if u.config.ProjectId != "" {
getCertificateListReq.ProjectId = usdk.String(u.config.ProjectId)
}
getCertificateListResp, err := u.sdkClient.GetCertificateList(getCertificateListReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.GetCertificateList'")
}
if getCertificateListResp.CertificateList != nil {
for _, certInfo := range getCertificateListResp.CertificateList {
// 优刻得未提供可唯一标识证书的字段,只能通过多个字段尝试匹配来判断是否为同一证书
// 先分别匹配证书的域名、品牌、有效期,再匹配签名算法
if len(certX509.DNSNames) == 0 || certInfo.Domains != strings.Join(certX509.DNSNames, ",") {
continue
}
if len(certX509.Issuer.Organization) == 0 || certInfo.Brand != certX509.Issuer.Organization[0] {
continue
}
if int64(certInfo.NotBefore) != certX509.NotBefore.UnixMilli() || int64(certInfo.NotAfter) != certX509.NotAfter.UnixMilli() {
continue
}
getCertificateDetailInfoReq := u.sdkClient.NewGetCertificateDetailInfoRequest()
getCertificateDetailInfoReq.CertificateID = usdk.Int(certInfo.CertificateID)
if u.config.ProjectId != "" {
getCertificateDetailInfoReq.ProjectId = usdk.String(u.config.ProjectId)
}
getCertificateDetailInfoResp, err := u.sdkClient.GetCertificateDetailInfo(getCertificateDetailInfoReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.GetCertificateDetailInfo'")
}
switch certX509.SignatureAlgorithm {
case x509.SHA256WithRSA:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA256-RSA") {
continue
}
case x509.SHA384WithRSA:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA384-RSA") {
continue
}
case x509.SHA512WithRSA:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA512-RSA") {
continue
}
case x509.SHA256WithRSAPSS:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA256-RSAPSS") {
continue
}
case x509.SHA384WithRSAPSS:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA384-RSAPSS") {
continue
}
case x509.SHA512WithRSAPSS:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "SHA512-RSAPSS") {
continue
}
case x509.ECDSAWithSHA256:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "ECDSA-SHA256") {
continue
}
case x509.ECDSAWithSHA384:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "ECDSA-SHA384") {
continue
}
case x509.ECDSAWithSHA512:
if !strings.EqualFold(getCertificateDetailInfoResp.CertificateInfo.Algorithm, "ECDSA-SHA512") {
continue
}
default:
// 未知签名算法,跳过
continue
}
return &uploader.UploadResult{
CertId: fmt.Sprintf("%d", certInfo.CertificateID),
CertName: certInfo.Name,
ExtendedData: map[string]interface{}{
"resourceId": certInfo.CertificateSN,
},
}, nil
}
}
if getCertificateListResp.CertificateList == nil || len(getCertificateListResp.CertificateList) < int(getCertificateListLimit) {
break
} else {
getCertificateListPage++
}
}
return nil, errors.New("no certificate found")
}
func createSdkClient(privateKey, publicKey string) (*usdkSsl.USSLClient, error) {
cfg := usdk.NewConfig()
credential := uAuth.NewCredential()
credential.PrivateKey = privateKey
credential.PublicKey = publicKey
client := usdkSsl.NewClient(&cfg, &credential)
return client, nil
}

View File

@ -0,0 +1,72 @@
package ucloudussl_test
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl"
)
var (
fInputCertPath string
fInputKeyPath string
fPrivateKey string
fPublicKey string
)
func init() {
argsPrefix := "CERTIMATE_UPLOADER_UCLOUDUSSL_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fPrivateKey, argsPrefix+"PRIVATEKEY", "", "")
flag.StringVar(&fPublicKey, argsPrefix+"PUBLICKEY", "", "")
}
/*
Shell command to run this test:
go test -v ./ucloud_ussl_test.go -args \
--CERTIMATE_UPLOADER_UCLOUDUSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_UPLOADER_UCLOUDUSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_UPLOADER_UCLOUDUSSL_PRIVATEKEY="your-private-key" \
--CERTIMATE_UPLOADER_UCLOUDUSSL_PUBLICKEY="your-public-key"
*/
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("PRIVATEKEY: %v", fPrivateKey),
fmt.Sprintf("PUBLICKEY: %v", fPublicKey),
}, "\n"))
uploader, err := provider.New(&provider.UCloudUSSLUploaderConfig{
PrivateKey: fPrivateKey,
PublicKey: fPublicKey,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
fInputCertData, _ := os.ReadFile(fInputCertPath)
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
if err != nil {
t.Errorf("err: %+v", err)
return
}
sres, _ := json.Marshal(res)
t.Logf("ok: %s", string(sres))
})
}

View File

@ -9,7 +9,7 @@ import (
veSession "github.com/volcengine/volcengine-go-sdk/volcengine/session"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
veCertCenter "github.com/usual2970/certimate/internal/pkg/vendors/volcengine-sdk/certcenter"
vesdkCc "github.com/usual2970/certimate/internal/pkg/vendors/volcengine-sdk/certcenter"
)
type VolcEngineCertCenterUploaderConfig struct {
@ -23,7 +23,7 @@ type VolcEngineCertCenterUploaderConfig struct {
type VolcEngineCertCenterUploader struct {
config *VolcEngineCertCenterUploaderConfig
sdkClient *veCertCenter.CertCenter
sdkClient *vesdkCc.CertCenter
}
var _ uploader.Uploader = (*VolcEngineCertCenterUploader)(nil)
@ -47,8 +47,8 @@ func New(config *VolcEngineCertCenterUploaderConfig) (*VolcEngineCertCenterUploa
func (u *VolcEngineCertCenterUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
// 上传证书
// REF: https://www.volcengine.com/docs/6638/1365580
importCertificateReq := &veCertCenter.ImportCertificateInput{
CertificateInfo: &veCertCenter.ImportCertificateInputCertificateInfo{
importCertificateReq := &vesdkCc.ImportCertificateInput{
CertificateInfo: &vesdkCc.ImportCertificateInputCertificateInfo{
CertificateChain: ve.String(certPem),
PrivateKey: ve.String(privkeyPem),
},
@ -71,7 +71,7 @@ func (u *VolcEngineCertCenterUploader) Upload(ctx context.Context, certPem strin
}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*veCertCenter.CertCenter, error) {
func createSdkClient(accessKeyId, accessKeySecret, region string) (*vesdkCc.CertCenter, error) {
if region == "" {
region = "cn-beijing" // 证书中心默认区域:北京
}
@ -83,6 +83,6 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*veCertCenter
return nil, err
}
client := veCertCenter.New(session)
client := vesdkCc.New(session)
return client, nil
}

View File

@ -0,0 +1,42 @@
package ufile
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
type AddUFileSSLCertRequest struct {
request.CommonBase
BucketName *string `required:"true"`
Domain *string `required:"true"`
CertificateName *string `required:"true"`
USSLId *string `required:"false"`
}
type AddUFileSSLCertResponse struct {
response.CommonBase
}
func (c *UFileClient) NewAddUFileSSLCertRequest() *AddUFileSSLCertRequest {
req := &AddUFileSSLCertRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(false)
return req
}
func (c *UFileClient) AddUFileSSLCert(req *AddUFileSSLCertRequest) (*AddUFileSSLCertResponse, error) {
var err error
var res AddUFileSSLCertResponse
reqCopier := *req
err = c.Client.InvokeAction("AddUFileSSLCert", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,18 @@
package ufile
import (
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
)
type UFileClient struct {
*ucloud.Client
}
func NewClient(config *ucloud.Config, credential *auth.Credential) *UFileClient {
meta := ucloud.ClientMeta{Product: "UFile"}
client := ucloud.NewClientWithMeta(config, credential, meta)
return &UFileClient{
client,
}
}

View File

@ -0,0 +1,161 @@
package ussl
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
type UploadNormalCertificateRequest struct {
request.CommonBase
CertificateName *string `required:"true"`
SslPublicKey *string `required:"true"`
SslPrivateKey *string `required:"true"`
SslMD5 *string `required:"true"`
SslCaKey *string `required:"false"`
}
type UploadNormalCertificateResponse struct {
response.CommonBase
CertificateID int
LongResourceID string
}
func (c *USSLClient) NewUploadNormalCertificateRequest() *UploadNormalCertificateRequest {
req := &UploadNormalCertificateRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(false)
return req
}
func (c *USSLClient) UploadNormalCertificate(req *UploadNormalCertificateRequest) (*UploadNormalCertificateResponse, error) {
var err error
var res UploadNormalCertificateResponse
reqCopier := *req
err = c.Client.InvokeAction("UploadNormalCertificate", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
type GetCertificateListRequest struct {
request.CommonBase
Mode *string `required:"true"`
StateCode *string `required:"false"`
Brand *string `required:"false"`
CaOrganization *string `required:"false"`
Domain *string `required:"false"`
Sort *string `required:"false"`
Page *int `required:"false"`
PageSize *int `required:"false"`
}
type GetCertificateListResponse struct {
response.CommonBase
CertificateList []*CertificateListItem
TotalCount int
}
func (c *USSLClient) NewGetCertificateListRequest() *GetCertificateListRequest {
req := &GetCertificateListRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(false)
return req
}
func (c *USSLClient) GetCertificateList(req *GetCertificateListRequest) (*GetCertificateListResponse, error) {
var err error
var res GetCertificateListResponse
reqCopier := *req
err = c.Client.InvokeAction("GetCertificateList", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
type GetCertificateDetailInfoRequest struct {
request.CommonBase
CertificateID *int `required:"true"`
}
type GetCertificateDetailInfoResponse struct {
response.CommonBase
CertificateInfo *CertificateInfo
}
func (c *USSLClient) NewGetCertificateDetailInfoRequest() *GetCertificateDetailInfoRequest {
req := &GetCertificateDetailInfoRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(false)
return req
}
func (c *USSLClient) GetCertificateDetailInfo(req *GetCertificateDetailInfoRequest) (*GetCertificateDetailInfoResponse, error) {
var err error
var res GetCertificateDetailInfoResponse
reqCopier := *req
err = c.Client.InvokeAction("GetCertificateDetailInfo", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
type DownloadCertificateRequest struct {
request.CommonBase
CertificateID *int `required:"true"`
}
type DownloadCertificateResponse struct {
response.CommonBase
CertificateUrl string
CertCA *CertificateDownloadInfo
Certificate *CertificateDownloadInfo
}
func (c *USSLClient) NewDownloadCertificateRequest() *DownloadCertificateRequest {
req := &DownloadCertificateRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(false)
return req
}
func (c *USSLClient) DownloadCertificate(req *DownloadCertificateRequest) (*DownloadCertificateResponse, error) {
var err error
var res DownloadCertificateResponse
reqCopier := *req
err = c.Client.InvokeAction("DownloadCertificate", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,18 @@
package ussl
import (
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
)
type USSLClient struct {
*ucloud.Client
}
func NewClient(config *ucloud.Config, credential *auth.Credential) *USSLClient {
meta := ucloud.ClientMeta{Product: "USSL"}
client := ucloud.NewClientWithMeta(config, credential, meta)
return &USSLClient{
client,
}
}

View File

@ -0,0 +1,61 @@
package ussl
type CertificateListItem struct {
CertificateID int
CertificateSN string
CertificateCat string
Mode string
Domains string
Brand string
ValidityPeriod int
Type string
NotBefore int
NotAfter int
AlarmState int
State string
StateCode string
Name string
MaxDomainsCount int
DomainsCount int
CaChannel string
CSRAlgorithms []CSRAlgorithmInfo
TopOrganizationID int
OrganizationID int
IsFree int
YearOfValidity int
Channel int
CreateTime int
CertificateUrl string
}
type CSRAlgorithmInfo struct {
Algorithm string
AlgorithmOption []string
}
type CertificateInfo struct {
Type string
CertificateID int
CertificateType string
CaOrganization string
Algorithm string
ValidityPeriod int
State string
StateCode string
Name string
Brand string
Domains string
DomainsCount int
Mode string
CSROnline int
CSR string
CSRKeyParameter string
CSREncryptAlgo string
IssuedDate int
ExpiredDate int
}
type CertificateDownloadInfo struct {
FileData string
FileName string
}

View File

@ -0,0 +1,113 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e")
if err != nil {
return err
}
// update
edit_provider := &schema.SchemaField{}
if err := json.Unmarshal([]byte(`{
"system": false,
"id": "hwy7m03o",
"name": "provider",
"type": "select",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSelect": 1,
"values": [
"acmehttpreq",
"aliyun",
"aws",
"azure",
"baiducloud",
"byteplus",
"cloudflare",
"dogecloud",
"godaddy",
"huaweicloud",
"k8s",
"local",
"namedotcom",
"namesilo",
"powerdns",
"qiniu",
"ssh",
"tencentcloud",
"ucloud",
"volcengine",
"webhook"
]
}
}`), edit_provider); err != nil {
return err
}
collection.Schema.AddField(edit_provider)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e")
if err != nil {
return err
}
// update
edit_provider := &schema.SchemaField{}
if err := json.Unmarshal([]byte(`{
"system": false,
"id": "hwy7m03o",
"name": "provider",
"type": "select",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSelect": 1,
"values": [
"acmehttpreq",
"aliyun",
"aws",
"azure",
"baiducloud",
"byteplus",
"cloudflare",
"dogecloud",
"godaddy",
"huaweicloud",
"k8s",
"local",
"namedotcom",
"namesilo",
"powerdns",
"qiniu",
"ssh",
"tencentcloud",
"volcengine",
"webhook"
]
}
}`), edit_provider); err != nil {
return err
}
collection.Schema.AddField(edit_provider)
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M912.192 1024H111.808A111.808 111.808 0 0 1 0 912.192V111.808C0 50.048 50.048 0 111.808 0h800.384C973.952 0 1024 50.048 1024 111.808v800.384c0 61.76-50.048 111.808-111.808 111.808" fill="#3860F4"></path><path d="M822.208 435.2c0 64.128 0.128 128.32 0 192.448-0.256 134.592-81.216 229.376-214.336 252.416a462.08 462.08 0 0 1-187.136-5.632c-110.528-26.048-187.392-117.76-189.632-230.912-1.536-79.04 0.64-158.144-1.024-237.184-0.512-24.576 9.728-30.272 30.144-26.88 30.336 5.056 42.56-6.848 37.248-37.248-4.288-24.704 7.68-33.408 30.912-29.952 25.984 3.968 38.848-5.056 34.24-33.152-3.008-18.432 3.712-26.304 24.128-26.368 20.416-0.064 24.576 7.68 24.448 26.24-0.64 120.32-0.384 240.64-0.192 360.96 0 30.016 2.304 59.84 19.968 85.568 24 35.008 78.464 50.688 131.072 36.736 42.368-11.2 69.312-37.696 76.16-82.88 5.824-37.76 3.328-75.52 3.456-113.216 0.448-116.864 0.896-233.728-0.384-350.592-0.256-24.32 6.72-31.488 30.72-30.4 41.088 1.92 82.368 1.6 123.52 0.064 20.736-0.704 27.648 4.736 27.2 26.56-1.344 74.432-0.512 148.928-0.512 223.36" fill="#FFFFFF"></path><path d="M249.152 318.976c16.768-2.304 24 2.88 24.064 19.84 0 16.768-4.032 24.448-22.528 24.128-16.768-0.256-21.12-7.168-20.672-22.4 0.384-14.528 2.304-25.472 19.2-21.568M384.64 227.904c-15.808 2.304-22.784-3.392-22.656-20.352 0.128-15.488 3.84-22.656 20.928-22.4 15.168 0.32 22.912 3.52 22.4 20.8-0.448 14.976-3.072 24.832-20.736 21.952M317.76 253.44c16-2.368 22.72 3.648 22.72 20.672s-7.04 21.504-22.656 21.248c-14.272-0.192-20.8-4.48-20.864-19.648-0.128-15.168 3.008-25.088 20.8-22.272M319.36 192.64c13.312-1.536 21.056 1.984 20.928 18.112-0.128 16.896-9.856 17.28-22.208 17.6-13.504 0.32-21.12-2.88-21.056-18.752 0-17.024 9.856-17.92 22.4-16.96M272.192 272.896c2.176 15.36-7.424 15.744-18.944 16.192-14.976 0.64-16.128-7.552-16.448-19.328-0.384-14.848 7.68-16.192 19.52-16.512 14.912-0.448 17.216 7.68 15.872 19.648M190.848 288.896c-12.288 1.664-15.616-4.992-15.168-16 0.32-8.64 1.728-15.616 12.608-16 10.816-0.32 17.152 3.392 17.472 15.232 0.256 10.624-2.24 17.92-14.912 16.768M333.888 150.016c0.128 10.368-6.144 13.44-15.296 13.312-10.432-0.128-17.728-4.032-17.024-15.744 0.64-11.52 9.664-10.304 17.472-10.56 8.96-0.32 15.744 1.92 14.848 12.992M252.8 220.928c-8.64 0.448-15.616-1.152-15.616-12.096 0-8.192 0.128-15.808 11.52-16.128 9.728-0.256 17.6 0.64 17.28 13.696-0.256 9.28-2.816 15.04-13.184 14.528M266.112 150.272c-0.32 7.68-3.712 12.544-12.032 12.032-8.32-0.512-10.88-5.952-11.008-13.44-0.064-8.32 4.352-11.52 12.288-11.392 8.576 0.192 11.328 4.992 10.752 12.8M186.496 198.336c6.976 0 13.376 1.024 14.208 9.856 0.768 8.064-3.2 12.352-11.008 12.992-7.168 0.512-13.248-0.64-13.632-9.792-0.384-7.424 1.28-13.184 10.432-13.056M191.36 143.552c2.752 2.432 6.784 4.288 6.784 6.208 0 5.504-4.48 7.68-9.216 6.336-2.24-0.64-3.584-4.48-5.312-6.912l7.68-5.632" fill="#FFFFFF"></path></svg>

View File

@ -27,6 +27,7 @@ import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig";
import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormSSHConfig from "./AccessFormSSHConfig";
import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig";
import AccessFormWebhookConfig from "./AccessFormWebhookConfig";
@ -118,6 +119,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormSSHConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.TENCENTCLOUD:
return <AccessFormTencentCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.UCLOUD:
return <AccessFormUCloudConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.VOLCENGINE:
return <AccessFormVolcEngineConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.WEBHOOK:

View File

@ -0,0 +1,90 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForUCloud } from "@/domain/access";
type AccessFormUCloudConfigFieldValues = Nullish<AccessConfigForUCloud>;
export type AccessFormUCloudConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormUCloudConfigFieldValues;
onValuesChange?: (values: AccessFormUCloudConfigFieldValues) => void;
};
const initFormModel = (): AccessFormUCloudConfigFieldValues => {
return {
privateKey: "",
publicKey: "",
};
};
const AccessFormUCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUCloudConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
privateKey: z
.string()
.trim()
.min(1, t("access.form.ucloud_private_key.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })),
publicKey: z
.string()
.min(1, t("access.form.ucloud_public_key.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
projectId: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim()
.nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="privateKey"
label={t("access.form.ucloud_private_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ucloud_private_key.tooltip") }}></span>}
>
<Input autoComplete="new-password" placeholder={t("access.form.ucloud_private_key.placeholder")} />
</Form.Item>
<Form.Item
name="publicKey"
label={t("access.form.ucloud_public_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ucloud_public_key.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.ucloud_public_key.placeholder")} />
</Form.Item>
<Form.Item
name="projectId"
label={t("access.form.ucloud_project_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ucloud_project_id.tooltip") }}></span>}
>
<Input autoComplete="new-password" placeholder={t("access.form.ucloud_project_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormUCloudConfig;

View File

@ -40,7 +40,7 @@ const ApplyDNSProviderPicker = ({ className, style, placeholder, onSelect }: App
<Row gutter={[16, 16]}>
{filteredProviders.map((provider, index) => {
return (
<Col key={index} span={12}>
<Col key={index} xs={24} md={12} span={12}>
<Card
className="h-16 w-full overflow-hidden shadow-sm"
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
@ -51,7 +51,7 @@ const ApplyDNSProviderPicker = ({ className, style, placeholder, onSelect }: App
>
<Flex className="size-full overflow-hidden" align="center" gap={8}>
<Avatar src={provider.icon} size="small" />
<Typography.Text className="line-clamp-2">{t(provider.name)}</Typography.Text>
<Typography.Text className="line-clamp-2 flex-1">{t(provider.name)}</Typography.Text>
</Flex>
</Card>
</Col>

View File

@ -40,7 +40,7 @@ const DeployProviderPicker = ({ className, style, placeholder, onSelect }: Deplo
<Row gutter={[16, 16]}>
{filteredProviders.map((provider, index) => {
return (
<Col key={index} span={12}>
<Col key={index} xs={24} md={12} span={12}>
<Card
className="h-16 w-full overflow-hidden shadow-sm"
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
@ -51,7 +51,7 @@ const DeployProviderPicker = ({ className, style, placeholder, onSelect }: Deplo
>
<Flex className="size-full overflow-hidden" align="center" gap={8}>
<Avatar src={provider.icon} size="small" />
<Typography.Text className="line-clamp-2">{t(provider.name)}</Typography.Text>
<Typography.Text className="line-clamp-2 flex-1">{t(provider.name)}</Typography.Text>
</Flex>
</Card>
</Col>

View File

@ -1,6 +1,6 @@
import { memo, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Avatar, Space, Typography } from "antd";
import { Avatar, Flex, Typography } from "antd";
import { produce } from "immer";
import { deployProvidersMap } from "@/domain/provider";
@ -45,10 +45,10 @@ const DeployNode = ({ node, disabled }: DeployNodeProps) => {
const config = (node.config as WorkflowNodeConfigForDeploy) ?? {};
const provider = deployProvidersMap.get(config.provider);
return (
<Space className="max-w-full">
<Flex className="size-full overflow-hidden" align="center" gap={8}>
<Avatar src={provider?.icon} size="small" />
<Typography.Text className="truncate">{t(provider?.name ?? "")}</Typography.Text>
</Space>
<Typography.Text className="flex-1 truncate">{t(provider?.name ?? "")}</Typography.Text>
</Flex>
);
}, [node]);

View File

@ -37,6 +37,8 @@ import DeployNodeConfigFormTencentCloudCOSConfig from "./DeployNodeConfigFormTen
import DeployNodeConfigFormTencentCloudCSSConfig from "./DeployNodeConfigFormTencentCloudCSSConfig.tsx";
import DeployNodeConfigFormTencentCloudECDNConfig from "./DeployNodeConfigFormTencentCloudECDNConfig.tsx";
import DeployNodeConfigFormTencentCloudEOConfig from "./DeployNodeConfigFormTencentCloudEOConfig.tsx";
import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx";
import DeployNodeConfigFormUCloudUS3Config from "./DeployNodeConfigFormUCloudUS3Config.tsx";
import DeployNodeConfigFormVolcEngineCDNConfig from "./DeployNodeConfigFormVolcEngineCDNConfig.tsx";
import DeployNodeConfigFormVolcEngineCLBConfig from "./DeployNodeConfigFormVolcEngineCLBConfig.tsx";
import DeployNodeConfigFormVolcEngineDCDNConfig from "./DeployNodeConfigFormVolcEngineDCDNConfig.tsx";
@ -156,6 +158,10 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
return <DeployNodeConfigFormTencentCloudECDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_EO:
return <DeployNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.UCLOUD_UCDN:
return <DeployNodeConfigFormUCloudUCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.UCLOUD_US3:
return <DeployNodeConfigFormUCloudUS3Config {...nestedFormProps} />;
case DEPLOY_PROVIDERS.VOLCENGINE_CDN:
return <DeployNodeConfigFormVolcEngineCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.VOLCENGINE_CLB:

View File

@ -0,0 +1,63 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type DeployNodeConfigFormUCloudUCDNConfigFieldValues = Nullish<{
domainId: string;
}>;
export type DeployNodeConfigFormUCloudUCDNConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: DeployNodeConfigFormUCloudUCDNConfigFieldValues;
onValuesChange?: (values: DeployNodeConfigFormUCloudUCDNConfigFieldValues) => void;
};
const initFormModel = (): DeployNodeConfigFormUCloudUCDNConfigFieldValues => {
return {};
};
const DeployNodeConfigFormUCloudUCDNConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: DeployNodeConfigFormUCloudUCDNConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
domainId: z
.string({ message: t("workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder") })
.nonempty(t("workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder")),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="domainId"
label={t("workflow_node.deploy.form.ucloud_ucdn_domain_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default DeployNodeConfigFormUCloudUCDNConfig;

View File

@ -0,0 +1,93 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
type DeployNodeConfigFormUCloudUS3ConfigFieldValues = Nullish<{
region: string;
bucket: string;
domain: string;
}>;
export type DeployNodeConfigFormUCloudUS3ConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: DeployNodeConfigFormUCloudUS3ConfigFieldValues;
onValuesChange?: (values: DeployNodeConfigFormUCloudUS3ConfigFieldValues) => void;
};
const initFormModel = (): DeployNodeConfigFormUCloudUS3ConfigFieldValues => {
return {};
};
const DeployNodeConfigFormUCloudUS3Config = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: DeployNodeConfigFormUCloudUS3ConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
region: z
.string({ message: t("workflow_node.deploy.form.ucloud_us3_region.placeholder") })
.nonempty(t("workflow_node.deploy.form.ucloud_us3_region.placeholder"))
.trim(),
bucket: z
.string({ message: t("workflow_node.deploy.form.ucloud_us3_bucket.placeholder") })
.nonempty(t("workflow_node.deploy.form.ucloud_us3_bucket.placeholder"))
.trim(),
domain: z
.string({ message: t("workflow_node.deploy.form.ucloud_us3_domain.placeholder") })
.refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="region"
label={t("workflow_node.deploy.form.ucloud_us3_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_us3_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_us3_region.placeholder")} />
</Form.Item>
<Form.Item
name="bucket"
label={t("workflow_node.deploy.form.ucloud_us3_bucket.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_us3_bucket.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_us3_bucket.placeholder")} />
</Form.Item>
<Form.Item
name="domain"
label={t("workflow_node.deploy.form.ucloud_us3_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_us3_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_us3_domain.placeholder")} />
</Form.Item>
</Form>
);
};
export default DeployNodeConfigFormUCloudUS3Config;

View File

@ -1,6 +1,6 @@
import { memo, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Typography } from "antd";
import { Flex, Typography } from "antd";
import { produce } from "immer";
import { notifyChannelsMap } from "@/domain/settings";
@ -40,12 +40,12 @@ const NotifyNode = ({ node, disabled }: NotifyNodeProps) => {
const config = (node.config as WorkflowNodeConfigForNotify) ?? {};
const channel = notifyChannelsMap.get(config.channel as string);
return (
<div className="flex items-center justify-between space-x-2">
<Typography.Text className="truncate">{t(channel?.name ?? " ")}</Typography.Text>
<Flex className="size-full overflow-hidden" align="center" gap={8}>
<Typography.Text className="flex-1 truncate">{t(channel?.name ?? " ")}</Typography.Text>
<Typography.Text className="truncate" type="secondary">
{config.subject ?? ""}
</Typography.Text>
</div>
</Flex>
);
}, [node]);

View File

@ -26,6 +26,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForQiniu
| AccessConfigForSSH
| AccessConfigForTencentCloud
| AccessConfigForUCloud
| AccessConfigForVolcEngine
| AccessConfigForWebhook
);
@ -125,6 +126,12 @@ export type AccessConfigForTencentCloud = {
secretKey: string;
};
export type AccessConfigForUCloud = {
privateKey: string;
publicKey: string;
projectId?: string;
};
export type AccessConfigForVolcEngine = {
accessKeyId: string;
secretAccessKey: string;

View File

@ -22,6 +22,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
QINIU: "qiniu",
SSH: "ssh",
TENCENTCLOUD: "tencentcloud",
UCLOUD: "ucloud",
VOLCENGINE: "volcengine",
WEBHOOK: "webhook",
} as const);
@ -61,6 +62,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.DOGECLOUD, "common.provider.dogecloud", "/imgs/providers/dogecloud.svg", ACCESS_USAGES.DEPLOY],
[ACCESS_PROVIDERS.VOLCENGINE, "common.provider.volcengine", "/imgs/providers/volcengine.svg", ACCESS_USAGES.ALL],
[ACCESS_PROVIDERS.BYTEPLUS, "common.provider.byteplus", "/imgs/providers/byteplus.svg", ACCESS_USAGES.DEPLOY],
[ACCESS_PROVIDERS.UCLOUD, "common.provider.ucloud", "/imgs/providers/ucloud.svg", ACCESS_USAGES.DEPLOY],
[ACCESS_PROVIDERS.AWS, "common.provider.aws", "/imgs/providers/aws.svg", ACCESS_USAGES.APPLY],
[ACCESS_PROVIDERS.AZURE, "common.provider.azure", "/imgs/providers/azure.svg", ACCESS_USAGES.APPLY],
[ACCESS_PROVIDERS.CLOUDFLARE, "common.provider.cloudflare", "/imgs/providers/cloudflare.svg", ACCESS_USAGES.APPLY],
@ -173,6 +175,8 @@ export const DEPLOY_PROVIDERS = Object.freeze({
TENCENTCLOUD_CSS: `${ACCESS_PROVIDERS.TENCENTCLOUD}-css`,
TENCENTCLOUD_ECDN: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ecdn`,
TENCENTCLOUD_EO: `${ACCESS_PROVIDERS.TENCENTCLOUD}-eo`,
UCLOUD_UCDN: `${ACCESS_PROVIDERS.UCLOUD}-ucdn`,
UCLOUD_US3: `${ACCESS_PROVIDERS.UCLOUD}-us3`,
VOLCENGINE_CDN: `${ACCESS_PROVIDERS.VOLCENGINE}-cdn`,
VOLCENGINE_CLB: `${ACCESS_PROVIDERS.VOLCENGINE}-clb`,
VOLCENGINE_DCDN: `${ACCESS_PROVIDERS.VOLCENGINE}-dcdn`,
@ -224,6 +228,8 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv
[DEPLOY_PROVIDERS.QINIU_CDN, "common.provider.qiniu.cdn"],
[DEPLOY_PROVIDERS.DOGECLOUD_CDN, "common.provider.dogecloud.cdn"],
[DEPLOY_PROVIDERS.BYTEPLUS_CDN, "common.provider.byteplus.cdn"],
[DEPLOY_PROVIDERS.UCLOUD_US3, "common.provider.ucloud.us3"],
[DEPLOY_PROVIDERS.UCLOUD_UCDN, "common.provider.ucloud.ucdn"],
].map(([type, name]) => [
type,
{

View File

@ -121,6 +121,15 @@
"access.form.tencentcloud_secret_key.label": "Tencent Cloud SecretKey",
"access.form.tencentcloud_secret_key.placeholder": "Please enter Tencent Cloud SecretKey",
"access.form.tencentcloud_secret_key.tooltip": "For more information, see <a href=\"https://cloud.tencent.com/document/product/598/40488?lang=en\" target=\"_blank\">https://cloud.tencent.com/document/product/598/40488?lang=en</a>",
"access.form.ucloud_private_key.label": "UCloud API private key",
"access.form.ucloud_private_key.placeholder": "Please enter UCloud API private key",
"access.form.ucloud_private_key.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/uaccount/api_manage\" target=\"_blank\">https://console.ucloud-global.com/uaccount/api_manage</a>",
"access.form.ucloud_public_key.label": "UCloud API public key",
"access.form.ucloud_public_key.placeholder": "Please enter UCloud API public key",
"access.form.ucloud_public_key.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/uaccount/api_manage\" target=\"_blank\">https://console.ucloud-global.com/uaccount/api_manage</a>",
"access.form.ucloud_project_id.label": "UCloud project ID (Optional)",
"access.form.ucloud_project_id.placeholder": "Please enter UCloud project ID",
"access.form.ucloud_project_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/uaccount/iam/project_manage\" target=\"_blank\">https://console.ucloud-global.com/uaccount/iam/project_manage</a>",
"access.form.volcengine_access_key_id.label": "VolcEngine AccessKeyId",
"access.form.volcengine_access_key_id.placeholder": "Please enter VolcEngine AccessKeyId",
"access.form.volcengine_access_key_id.tooltip": "For more information, see <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",

View File

@ -78,6 +78,9 @@
"common.provider.tencentcloud.dns": "Tencent Cloud - Domain Name Service (DNS)",
"common.provider.tencentcloud.ecdn": "Tencent Cloud - Enterprise Content Delivery Network (ECDN)",
"common.provider.tencentcloud.eo": "Tencent Cloud - EdgeOne",
"common.provider.ucloud": "UCloud",
"common.provider.ucloud.ucdn": "UCloud - UCloud Content Delivery Network (UCDN)",
"common.provider.ucloud.us3": "UCloud - UCloud Object-based Storage (US3)",
"common.provider.volcengine": "Volcengine",
"common.provider.volcengine.cdn": "Volcengine - Content Delivery Network (CDN)",
"common.provider.volcengine.clb": "Volcengine - Cloud Load Balancer (CLB)",

View File

@ -301,6 +301,18 @@
"workflow_node.deploy.form.tencentcloud_eo_domain.label": "Tencent Cloud EdgeOne domain",
"workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "Please enter Tencent Cloud EdgeOne domain name",
"workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/edgeone\" target=\"_blank\">https://console.tencentcloud.com/edgeone</a>",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "UCloud UCDN domain ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "Please enter UCloud UCDN domain ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ucdn\" target=\"_blank\">https://console.ucloud-global.com/ucdn</a>",
"workflow_node.deploy.form.ucloud_us3_region.label": "UCloud region",
"workflow_node.deploy.form.ucloud_us3_region.placeholder": "Please enter VolcEngine region (e.g. cn-bj2)",
"workflow_node.deploy.form.ucloud_us3_region.tooltip": "For more information, see <a href=\"https://www.ucloud-global.com/en/docs/api/summary/regionlist\" target=\"_blank\">https://www.ucloud-global.com/en/docs/api/summary/regionlist</a>",
"workflow_node.deploy.form.ucloud_us3_bucket.label": "UCloud US3 bucket",
"workflow_node.deploy.form.ucloud_us3_bucket.placeholder": "Please enter UCloud US3 bucket name",
"workflow_node.deploy.form.ucloud_us3_bucket.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
"workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain",
"workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name",
"workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
"workflow_node.deploy.form.volcengine_cdn_domain.label": "VolcEngine CDN domain",
"workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "Please enter VolcEngine CDN domain name",
"workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a>",

View File

@ -121,6 +121,15 @@
"access.form.tencentcloud_secret_key.label": "腾讯云 SecretKey",
"access.form.tencentcloud_secret_key.placeholder": "请输入腾讯云 SecretKey",
"access.form.tencentcloud_secret_key.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/598/40488\" target=\"_blank\">https://cloud.tencent.com/document/product/598/40488</a>",
"access.form.ucloud_private_key.label": "优刻得 API 私钥",
"access.form.ucloud_private_key.placeholder": "请输入优刻得 API 私钥",
"access.form.ucloud_private_key.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/uaccount/api_manage\" target=\"_blank\">https://console.ucloud.cn/uaccount/api_manage</a>",
"access.form.ucloud_public_key.label": "优刻得 API 公钥",
"access.form.ucloud_public_key.placeholder": "请输入优刻得 API 公钥",
"access.form.ucloud_public_key.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/uaccount/api_manage\" target=\"_blank\">https://console.ucloud.cn/uaccount/api_manage</a>",
"access.form.ucloud_project_id.label": "优刻得项目 ID可选",
"access.form.ucloud_project_id.placeholder": "请输入优刻得项目 ID",
"access.form.ucloud_project_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/uaccount/iam/project_manage\" target=\"_blank\">https://console.ucloud.cn/uaccount/iam/project_manage</a>",
"access.form.volcengine_access_key_id.label": "火山引擎 AccessKeyId",
"access.form.volcengine_access_key_id.placeholder": "请输入火山引擎 AccessKeyId",
"access.form.volcengine_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",

View File

@ -78,6 +78,9 @@
"common.provider.tencentcloud.dns": "腾讯云 - 云解析 DNS",
"common.provider.tencentcloud.ecdn": "腾讯云 - 全站加速网络 ECDN",
"common.provider.tencentcloud.eo": "腾讯云 - 边缘安全加速平台 EdgeOne",
"common.provider.ucloud": "优刻得",
"common.provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN",
"common.provider.ucloud.us3": "优刻得 - 对象存储 US3",
"common.provider.volcengine": "火山引擎",
"common.provider.volcengine.cdn": "火山引擎 - 内容分发网络 CDN",
"common.provider.volcengine.clb": "火山引擎 - 负载均衡 CLB",

View File

@ -301,6 +301,18 @@
"workflow_node.deploy.form.tencentcloud_eo_domain.label": "腾讯云 EdgeOne 加速域名",
"workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "请输入腾讯云 EdgeOne 加速域名",
"workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "优刻得 UCDN 域名 ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "请输入优刻得 UCDN 域名 ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ucdn\" target=\"_blank\">https://console.ucloud.cn/ucdn</a>",
"workflow_node.deploy.form.ucloud_us3_region.label": "优刻得地域",
"workflow_node.deploy.form.ucloud_us3_region.placeholder": "优刻得地域例如cn-bj2",
"workflow_node.deploy.form.ucloud_us3_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.ucloud.cn/api/summary/regionlist\" target=\"_blank\">https://docs.ucloud.cn/api/summary/regionlist</a>",
"workflow_node.deploy.form.ucloud_us3_bucket.label": "优刻得 US3 存储桶名",
"workflow_node.deploy.form.ucloud_us3_bucket.placeholder": "请输入优刻得 US3 存储桶名",
"workflow_node.deploy.form.ucloud_us3_bucket.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ufile\" target=\"_blank\">https://console.ucloud.cn/ufile</a>",
"workflow_node.deploy.form.ucloud_us3_domain.label": "优刻得 US3 自定义域名",
"workflow_node.deploy.form.ucloud_us3_domain.placeholder": "请输入优刻得 US3 自定义域名",
"workflow_node.deploy.form.ucloud_us3_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ufile\" target=\"_blank\">https://console.ucloud.cn/ufile</a>",
"workflow_node.deploy.form.volcengine_cdn_domain.label": "火山引擎 CDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "请输入火山引擎 CDN 加速域名",
"workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a><br><br>泛域名表示形式为:*.example.com",

View File

@ -300,7 +300,7 @@ const WorkflowDetail = () => {
</Space>
</div>
</div>
<div className="px-12 py-8">
<div className="px-12 py-8 max-md:px-4">
<WorkflowElements />
</div>
</div>