mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-07 21:19:51 +00:00
commit
65cd1dc850
1
go.mod
1
go.mod
@ -85,6 +85,7 @@ require (
|
|||||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
||||||
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/iam v1.42.0 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -235,6 +235,8 @@ github.com/aws/aws-sdk-go-v2/service/acm v1.32.0/go.mod h1:3sKYAgRbuBa2QMYGh/WEc
|
|||||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1 h1:6xZNYtuVwzBs8k+TmraERt0vL68Ppg9aUi+aTQmPaVM=
|
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1 h1:6xZNYtuVwzBs8k+TmraERt0vL68Ppg9aUi+aTQmPaVM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po=
|
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po=
|
||||||
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
|
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/iam v1.42.0 h1:G6+UzGvubaet9QOh0664E9JeT+b6Zvop3AChozRqkrA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/iam v1.42.0/go.mod h1:mPJkGQzeCoPs82ElNILor2JzZgYENr4UaSKUT8K27+c=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
|
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
|
||||||
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
|
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
|
||||||
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
|
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
|
||||||
|
pConstellix "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/constellix"
|
||||||
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
|
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
|
||||||
pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean"
|
pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean"
|
||||||
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
||||||
@ -38,6 +39,7 @@ import (
|
|||||||
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
|
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
|
||||||
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
|
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
|
||||||
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo"
|
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo"
|
||||||
|
pUCloudUDNR "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ucloud-udnr"
|
||||||
pVercel "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/vercel"
|
pVercel "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/vercel"
|
||||||
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
|
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
|
||||||
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
|
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
|
||||||
@ -234,6 +236,22 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeConstellix:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForConstellix{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pConstellix.NewChallengeProvider(&pConstellix.ChallengeProviderConfig{
|
||||||
|
ApiKey: access.ApiKey,
|
||||||
|
SecretKey: access.SecretKey,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
case domain.ACMEDns01ProviderTypeDeSEC:
|
case domain.ACMEDns01ProviderTypeDeSEC:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForDeSEC{}
|
access := domain.AccessConfigForDeSEC{}
|
||||||
@ -579,6 +597,22 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeUCloudUDNR:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForUCloud{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pUCloudUDNR.NewChallengeProvider(&pUCloudUDNR.ChallengeProviderConfig{
|
||||||
|
PrivateKey: access.PrivateKey,
|
||||||
|
PublicKey: access.PublicKey,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
case domain.ACMEDns01ProviderTypeVercel:
|
case domain.ACMEDns01ProviderTypeVercel:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForVercel{}
|
access := domain.AccessConfigForVercel{}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf"
|
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf"
|
||||||
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-acm"
|
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-acm"
|
||||||
pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront"
|
pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront"
|
||||||
|
pAWSIAM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-iam"
|
||||||
pAzureKeyVault "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/azure-keyvault"
|
pAzureKeyVault "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/azure-keyvault"
|
||||||
pBaiduCloudAppBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-appblb"
|
pBaiduCloudAppBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-appblb"
|
||||||
pBaiduCloudBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-blb"
|
pBaiduCloudBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-blb"
|
||||||
@ -331,7 +332,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront:
|
case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront, domain.DeploymentProviderTypeAWSIAM:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAWS{}
|
access := domain.AccessConfigForAWS{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -350,10 +351,20 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
|
|
||||||
case domain.DeploymentProviderTypeAWSCloudFront:
|
case domain.DeploymentProviderTypeAWSCloudFront:
|
||||||
deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{
|
deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{
|
||||||
|
AccessKeyId: access.AccessKeyId,
|
||||||
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
|
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||||
|
DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
|
||||||
|
CertificateSource: maputil.GetOrDefaultString(options.ProviderServiceConfig, "certificateSource", "ACM"),
|
||||||
|
})
|
||||||
|
return deployer, err
|
||||||
|
|
||||||
|
case domain.DeploymentProviderTypeAWSIAM:
|
||||||
|
deployer, err := pAWSIAM.NewDeployer(&pAWSIAM.DeployerConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
SecretAccessKey: access.SecretAccessKey,
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||||
DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
|
CertificatePath: maputil.GetOrDefaultString(options.ProviderServiceConfig, "certificatePath", "/"),
|
||||||
})
|
})
|
||||||
return deployer, err
|
return deployer, err
|
||||||
|
|
||||||
@ -986,6 +997,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
jumpServers[i] = pSSH.JumpServerConfig{
|
jumpServers[i] = pSSH.JumpServerConfig{
|
||||||
SshHost: jumpServer.Host,
|
SshHost: jumpServer.Host,
|
||||||
SshPort: jumpServer.Port,
|
SshPort: jumpServer.Port,
|
||||||
|
SshAuthMethod: jumpServer.AuthMethod,
|
||||||
SshUsername: jumpServer.Username,
|
SshUsername: jumpServer.Username,
|
||||||
SshPassword: jumpServer.Password,
|
SshPassword: jumpServer.Password,
|
||||||
SshKey: jumpServer.Key,
|
SshKey: jumpServer.Key,
|
||||||
@ -996,6 +1008,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
|
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
|
||||||
SshHost: access.Host,
|
SshHost: access.Host,
|
||||||
SshPort: access.Port,
|
SshPort: access.Port,
|
||||||
|
SshAuthMethod: access.AuthMethod,
|
||||||
SshUsername: access.Username,
|
SshUsername: access.Username,
|
||||||
SshPassword: access.Password,
|
SshPassword: access.Password,
|
||||||
SshKey: access.Key,
|
SshKey: access.Key,
|
||||||
|
@ -109,6 +109,11 @@ type AccessConfigForCMCCCloud struct {
|
|||||||
AccessKeySecret string `json:"accessKeySecret"`
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForConstellix struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForDeSEC struct {
|
type AccessConfigForDeSEC struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
@ -310,14 +315,16 @@ type AccessConfigForSlackBot struct {
|
|||||||
type AccessConfigForSSH struct {
|
type AccessConfigForSSH struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
Username string `json:"username"`
|
AuthMethod string `json:"authMethod,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
JumpServers []struct {
|
JumpServers []struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
Username string `json:"username"`
|
AuthMethod string `json:"authMethod,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
|
@ -28,6 +28,7 @@ const (
|
|||||||
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
|
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
|
||||||
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
|
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
|
||||||
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
||||||
|
AccessProviderTypeConstellix = AccessProviderType("constellix")
|
||||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留)
|
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留)
|
||||||
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留)
|
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留)
|
||||||
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
||||||
@ -131,6 +132,7 @@ const (
|
|||||||
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
||||||
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
||||||
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
|
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
|
||||||
|
ACMEDns01ProviderTypeConstellix = ACMEDns01ProviderType(AccessProviderTypeConstellix)
|
||||||
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||||
ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean)
|
ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean)
|
||||||
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||||
@ -156,6 +158,7 @@ const (
|
|||||||
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||||
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
||||||
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||||
|
ACMEDns01ProviderTypeUCloudUDNR = ACMEDns01ProviderType(AccessProviderTypeUCloud + "-udnr")
|
||||||
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||||
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||||
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||||
@ -192,6 +195,7 @@ const (
|
|||||||
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
|
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
|
||||||
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
|
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
|
||||||
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
|
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
|
||||||
|
DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam")
|
||||||
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
|
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
|
||||||
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
|
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
|
||||||
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
|
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package lego_aliyunesa
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -102,13 +101,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
siteName := strings.TrimRight(authZone, ".")
|
siteName := dns01.UnFqdn(authZone)
|
||||||
siteId, err := d.getSiteId(siteName)
|
siteId, err := d.getSiteId(siteName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.addOrUpdateDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil {
|
if err := d.addOrUpdateDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: %w", err)
|
return fmt.Errorf("alicloud-esa: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,13 +122,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
siteName := strings.TrimRight(authZone, ".")
|
siteName := dns01.UnFqdn(authZone)
|
||||||
siteId, err := d.getSiteId(siteName)
|
siteId, err := d.getSiteId(siteName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.removeDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, ".")); err != nil {
|
if err := d.removeDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: %w", err)
|
return fmt.Errorf("alicloud-esa: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package lego_baiducloud
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package cloudns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/constellix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := constellix.NewDefaultConfig()
|
||||||
|
providerConfig.APIKey = config.ApiKey
|
||||||
|
providerConfig.SecretKey = config.SecretKey
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := constellix.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package lego_dnsla
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package lego_dynv6
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package lego_gname
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package lego_jdcloud
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package lego_tencentcloudeo
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
@ -91,7 +90,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
if err := d.addOrUpdateDNSRecord(strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil {
|
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
if err := d.removeDNSRecord(strings.TrimRight(info.EffectiveFQDN, ".")); err != nil {
|
if err := d.removeDNSRecord(dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
|
||||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,165 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/sdk3rd/ucloud/udnr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "UCLOUDUDNR_"
|
||||||
|
|
||||||
|
EnvPublicKey = envNamespace + "PUBLIC_KEY"
|
||||||
|
EnvPrivateKey = envNamespace + "PRIVATE_KEY"
|
||||||
|
|
||||||
|
EnvTTL = envNamespace + "TTL"
|
||||||
|
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||||
|
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
PrivateKey string
|
||||||
|
PublicKey string
|
||||||
|
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int32
|
||||||
|
HTTPTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSProvider struct {
|
||||||
|
client *udnr.UDNRClient
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||||
|
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(EnvPrivateKey, EnvPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.PrivateKey = values[EnvPrivateKey]
|
||||||
|
config.PublicKey = values[EnvPublicKey]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("ucloud-udnr: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := ucloud.NewConfig()
|
||||||
|
credential := auth.NewCredential()
|
||||||
|
credential.PrivateKey = config.PrivateKey
|
||||||
|
credential.PublicKey = config.PublicKey
|
||||||
|
client := udnr.NewClient(&cfg, &credential)
|
||||||
|
|
||||||
|
return &DNSProvider{
|
||||||
|
client: client,
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||||
|
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||||
|
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
} else {
|
||||||
|
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||||
|
if record.DnsType == "TXT" && record.RecordName == subDomain {
|
||||||
|
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||||
|
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||||
|
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||||
|
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||||
|
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||||
|
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
udnrDomainDNSAddReq := d.client.NewAddDomainDNSRequest()
|
||||||
|
udnrDomainDNSAddReq.Dn = ucloud.String(authZone)
|
||||||
|
udnrDomainDNSAddReq.DnsType = ucloud.String("TXT")
|
||||||
|
udnrDomainDNSAddReq.RecordName = ucloud.String(subDomain)
|
||||||
|
udnrDomainDNSAddReq.Content = ucloud.String(info.Value)
|
||||||
|
udnrDomainDNSAddReq.TTL = ucloud.Int(int(d.config.TTL))
|
||||||
|
if _, err := d.client.AddDomainDNS(udnrDomainDNSAddReq); err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||||
|
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||||
|
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||||
|
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||||
|
} else {
|
||||||
|
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||||
|
if record.DnsType == "TXT" && record.RecordName == subDomain {
|
||||||
|
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||||
|
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||||
|
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||||
|
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||||
|
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||||
|
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package ucloududnr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ucloud-udnr/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
PrivateKey string `json:"privateKey"`
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := internal.NewDefaultConfig()
|
||||||
|
providerConfig.PrivateKey = config.PrivateKey
|
||||||
|
providerConfig.PublicKey = config.PublicKey
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = config.DnsTTL
|
||||||
|
}
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
@ -14,7 +14,8 @@ import (
|
|||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm"
|
uploaderspacm "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm"
|
||||||
|
uploaderspiam "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-iam"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@ -26,6 +27,9 @@ type DeployerConfig struct {
|
|||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
// AWS CloudFront 分配 ID。
|
// AWS CloudFront 分配 ID。
|
||||||
DistributionId string `json:"distributionId"`
|
DistributionId string `json:"distributionId"`
|
||||||
|
// AWS CloudFront 证书来源。
|
||||||
|
// 可取值 "ACM"、"IAM"。
|
||||||
|
CertificateSource string `json:"certificateSource"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
@ -47,13 +51,28 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
var uploader uploader.Uploader
|
||||||
AccessKeyId: config.AccessKeyId,
|
if config.CertificateSource == "ACM" {
|
||||||
SecretAccessKey: config.SecretAccessKey,
|
uploader, err = uploaderspacm.NewUploader(&uploaderspacm.UploaderConfig{
|
||||||
Region: config.Region,
|
AccessKeyId: config.AccessKeyId,
|
||||||
})
|
SecretAccessKey: config.SecretAccessKey,
|
||||||
if err != nil {
|
Region: config.Region,
|
||||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
} else if config.CertificateSource == "IAM" {
|
||||||
|
uploader, err = uploaderspiam.NewUploader(&uploaderspiam.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
SecretAccessKey: config.SecretAccessKey,
|
||||||
|
Region: config.Region,
|
||||||
|
CertificatePath: "/cloudfront/",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("unsupported certificate source: '%s'", config.CertificateSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
@ -79,7 +98,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return nil, errors.New("config `distribuitionId` is required")
|
return nil, errors.New("config `distribuitionId` is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书到 ACM
|
// 上传证书到 ACM/IAM
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
@ -109,7 +128,19 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
updateDistributionReq.DistributionConfig.ViewerCertificate = &types.ViewerCertificate{}
|
updateDistributionReq.DistributionConfig.ViewerCertificate = &types.ViewerCertificate{}
|
||||||
}
|
}
|
||||||
updateDistributionReq.DistributionConfig.ViewerCertificate.CloudFrontDefaultCertificate = aws.Bool(false)
|
updateDistributionReq.DistributionConfig.ViewerCertificate.CloudFrontDefaultCertificate = aws.Bool(false)
|
||||||
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = aws.String(upres.CertId)
|
if d.config.CertificateSource == "ACM" {
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = aws.String(upres.CertId)
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.IAMCertificateId = nil
|
||||||
|
} else if d.config.CertificateSource == "IAM" {
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = nil
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.IAMCertificateId = aws.String(upres.CertId)
|
||||||
|
if updateDistributionReq.DistributionConfig.ViewerCertificate.MinimumProtocolVersion == "" {
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.MinimumProtocolVersion = types.MinimumProtocolVersionTLSv1
|
||||||
|
}
|
||||||
|
if updateDistributionReq.DistributionConfig.ViewerCertificate.SSLSupportMethod == "" {
|
||||||
|
updateDistributionReq.DistributionConfig.ViewerCertificate.SSLSupportMethod = types.SSLSupportMethodSniOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq)
|
updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq)
|
||||||
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
|
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
75
internal/pkg/core/deployer/providers/aws-iam/aws_iam.go
Normal file
75
internal/pkg/core/deployer/providers/aws-iam/aws_iam.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package awsiam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-iam"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// AWS AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// AWS SecretAccessKey。
|
||||||
|
SecretAccessKey string `json:"secretAccessKey"`
|
||||||
|
// AWS 区域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// IAM 证书路径。
|
||||||
|
// 选填。
|
||||||
|
CertificatePath string `json:"certificatePath,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
SecretAccessKey: config.SecretAccessKey,
|
||||||
|
Region: config.Region,
|
||||||
|
CertificatePath: config.CertificatePath,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.New(slog.DiscardHandler)
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 IAM
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
"github.com/povsister/scp"
|
"github.com/povsister/scp"
|
||||||
@ -24,7 +25,12 @@ type JumpServerConfig struct {
|
|||||||
// SSH 端口。
|
// SSH 端口。
|
||||||
// 零值时默认值 22。
|
// 零值时默认值 22。
|
||||||
SshPort int32 `json:"sshPort,omitempty"`
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
|
// SSH 认证方式。
|
||||||
|
// 可取值 "none"、"password"、"key"。
|
||||||
|
// 零值时根据有无密码或私钥字段决定。
|
||||||
|
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||||
// SSH 登录用户名。
|
// SSH 登录用户名。
|
||||||
|
// 零值时默认值 "root"。
|
||||||
SshUsername string `json:"sshUsername,omitempty"`
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
// SSH 登录密码。
|
// SSH 登录密码。
|
||||||
SshPassword string `json:"sshPassword,omitempty"`
|
SshPassword string `json:"sshPassword,omitempty"`
|
||||||
@ -41,7 +47,12 @@ type DeployerConfig struct {
|
|||||||
// SSH 端口。
|
// SSH 端口。
|
||||||
// 零值时默认值 22。
|
// 零值时默认值 22。
|
||||||
SshPort int32 `json:"sshPort,omitempty"`
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
|
// SSH 认证方式。
|
||||||
|
// 可取值 "none"、"password" 或 "key"。
|
||||||
|
// 零值时根据有无密码或私钥字段决定。
|
||||||
|
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||||
// SSH 登录用户名。
|
// SSH 登录用户名。
|
||||||
|
// 零值时默认值 "root"。
|
||||||
SshUsername string `json:"sshUsername,omitempty"`
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
// SSH 登录密码。
|
// SSH 登录密码。
|
||||||
SshPassword string `json:"sshPassword,omitempty"`
|
SshPassword string `json:"sshPassword,omitempty"`
|
||||||
@ -141,6 +152,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
jumpConn,
|
jumpConn,
|
||||||
jumpServerConf.SshHost,
|
jumpServerConf.SshHost,
|
||||||
jumpServerConf.SshPort,
|
jumpServerConf.SshPort,
|
||||||
|
jumpServerConf.SshAuthMethod,
|
||||||
jumpServerConf.SshUsername,
|
jumpServerConf.SshUsername,
|
||||||
jumpServerConf.SshPassword,
|
jumpServerConf.SshPassword,
|
||||||
jumpServerConf.SshKey,
|
jumpServerConf.SshKey,
|
||||||
@ -174,6 +186,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
targetConn,
|
targetConn,
|
||||||
d.config.SshHost,
|
d.config.SshHost,
|
||||||
d.config.SshPort,
|
d.config.SshPort,
|
||||||
|
d.config.SshAuthMethod,
|
||||||
d.config.SshUsername,
|
d.config.SshUsername,
|
||||||
d.config.SshPassword,
|
d.config.SshPassword,
|
||||||
d.config.SshKey,
|
d.config.SshKey,
|
||||||
@ -262,7 +275,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSshClient(conn net.Conn, host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
|
func createSshClient(conn net.Conn, host string, port int32, authMethod string, username, password, key, keyPassphrase string) (*ssh.Client, error) {
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
}
|
}
|
||||||
@ -271,28 +284,65 @@ func createSshClient(conn net.Conn, host string, port int32, username string, pa
|
|||||||
port = 22
|
port = 22
|
||||||
}
|
}
|
||||||
|
|
||||||
var authMethod ssh.AuthMethod
|
if username == "" {
|
||||||
if key != "" {
|
username = "root"
|
||||||
var signer ssh.Signer
|
}
|
||||||
var err error
|
|
||||||
|
|
||||||
if keyPassphrase != "" {
|
const AUTH_METHOD_NONE = "none"
|
||||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
const AUTH_METHOD_PASSWORD = "password"
|
||||||
|
const AUTH_METHOD_KEY = "key"
|
||||||
|
if authMethod == "" {
|
||||||
|
if key != "" {
|
||||||
|
authMethod = AUTH_METHOD_KEY
|
||||||
|
} else if password != "" {
|
||||||
|
authMethod = AUTH_METHOD_PASSWORD
|
||||||
} else {
|
} else {
|
||||||
signer, err = ssh.ParsePrivateKey([]byte(key))
|
authMethod = AUTH_METHOD_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authentications := make([]ssh.AuthMethod, 0)
|
||||||
|
switch authMethod {
|
||||||
|
case AUTH_METHOD_NONE:
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
case AUTH_METHOD_PASSWORD:
|
||||||
return nil, err
|
{
|
||||||
|
authentications = append(authentications, ssh.Password(password))
|
||||||
|
authentications = append(authentications, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
|
||||||
|
if len(questions) == 1 {
|
||||||
|
return []string{password}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected keyboard interactive question [%s]", strings.Join(questions, ", "))
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
authMethod = ssh.PublicKeys(signer)
|
|
||||||
} else {
|
case AUTH_METHOD_KEY:
|
||||||
authMethod = ssh.Password(password)
|
{
|
||||||
|
var signer ssh.Signer
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if keyPassphrase != "" {
|
||||||
|
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
||||||
|
} else {
|
||||||
|
signer, err = ssh.ParsePrivateKey([]byte(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
authentications = append(authentications, ssh.PublicKeys(signer))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
||||||
User: username,
|
User: username,
|
||||||
Auth: []ssh.AuthMethod{authMethod},
|
Auth: authentications,
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,7 +74,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
// 获取证书列表,避免重复上传
|
// 获取证书列表,避免重复上传
|
||||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html
|
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html
|
||||||
var listCertificatesNextToken *string = nil
|
var listCertificatesNextToken *string = nil
|
||||||
listCertificatesMaxItems := int32(1000)
|
var listCertificatesMaxItems int32 = 1000
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@ -107,7 +107,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 最后对比证书内容
|
// 最后对比证书内容
|
||||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListTagsForCertificate.html
|
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_GetCertificate.html
|
||||||
getCertificateReq := &awsacm.GetCertificateInput{
|
getCertificateReq := &awsacm.GetCertificateInput{
|
||||||
CertificateArn: certSummary.CertificateArn,
|
CertificateArn: certSummary.CertificateArn,
|
||||||
}
|
}
|
||||||
@ -115,11 +115,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
|
||||||
} else {
|
} else {
|
||||||
oldCertPEM := aws.ToString(getCertificateResp.CertificateChain)
|
oldCertPEM := aws.ToString(getCertificateResp.Certificate)
|
||||||
if oldCertPEM == "" {
|
|
||||||
oldCertPEM = aws.ToString(getCertificateResp.Certificate)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
|
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@ -158,7 +154,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &uploader.UploadResult{
|
return &uploader.UploadResult{
|
||||||
CertId: *importCertificateResp.CertificateArn,
|
CertId: aws.ToString(importCertificateResp.CertificateArn),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
185
internal/pkg/core/uploader/providers/aws-iam/aws_iam.go
Normal file
185
internal/pkg/core/uploader/providers/aws-iam/aws_iam.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package awsiam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
aws "github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
awscfg "github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
awscred "github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
|
awsiam "github.com/aws/aws-sdk-go-v2/service/iam"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploaderConfig struct {
|
||||||
|
// AWS AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// AWS SecretAccessKey。
|
||||||
|
SecretAccessKey string `json:"secretAccessKey"`
|
||||||
|
// AWS 区域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// IAM 证书路径。
|
||||||
|
// 选填。
|
||||||
|
CertificatePath string `json:"certificatePath,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploaderProvider struct {
|
||||||
|
config *UploaderConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *awsiam.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
||||||
|
|
||||||
|
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UploaderProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader {
|
||||||
|
if logger == nil {
|
||||||
|
u.logger = slog.New(slog.DiscardHandler)
|
||||||
|
} else {
|
||||||
|
u.logger = logger
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (*uploader.UploadResult, error) {
|
||||||
|
// 解析证书内容
|
||||||
|
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取服务器证书
|
||||||
|
serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to extract certs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取证书列表,避免重复上传
|
||||||
|
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_ListServerCertificates.html
|
||||||
|
var listServerCertificatesMarker *string = nil
|
||||||
|
var listServerCertificatesMaxItems int32 = 1000
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listServerCertificatesReq := &awsiam.ListServerCertificatesInput{
|
||||||
|
Marker: listServerCertificatesMarker,
|
||||||
|
MaxItems: aws.Int32(listServerCertificatesMaxItems),
|
||||||
|
}
|
||||||
|
if u.config.CertificatePath != "" {
|
||||||
|
listServerCertificatesReq.PathPrefix = aws.String(u.config.CertificatePath)
|
||||||
|
}
|
||||||
|
listServerCertificatesResp, err := u.sdkClient.ListServerCertificates(context.TODO(), listServerCertificatesReq)
|
||||||
|
u.logger.Debug("sdk request 'iam.ListServerCertificates'", slog.Any("request", listServerCertificatesReq), slog.Any("response", listServerCertificatesResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'iam.ListServerCertificates': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, certMeta := range listServerCertificatesResp.ServerCertificateMetadataList {
|
||||||
|
// 先对比证书路径
|
||||||
|
if u.config.CertificatePath != "" && aws.ToString(certMeta.Path) != u.config.CertificatePath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先对比证书有效期
|
||||||
|
if certMeta.Expiration == nil || !certMeta.Expiration.Equal(certX509.NotAfter) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后对比证书内容
|
||||||
|
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_GetServerCertificate.html
|
||||||
|
getServerCertificateReq := &awsiam.GetServerCertificateInput{
|
||||||
|
ServerCertificateName: certMeta.ServerCertificateName,
|
||||||
|
}
|
||||||
|
getServerCertificateResp, err := u.sdkClient.GetServerCertificate(context.TODO(), getServerCertificateReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'iam.GetServerCertificate': %w", err)
|
||||||
|
} else {
|
||||||
|
oldCertPEM := aws.ToString(getServerCertificateResp.ServerCertificate.CertificateBody)
|
||||||
|
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !certutil.EqualCertificate(certX509, oldCertX509) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||||
|
u.logger.Info("ssl certificate already exists")
|
||||||
|
return &uploader.UploadResult{
|
||||||
|
CertId: aws.ToString(certMeta.ServerCertificateId),
|
||||||
|
CertName: aws.ToString(certMeta.ServerCertificateName),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if listServerCertificatesResp.Marker == nil || len(listServerCertificatesResp.ServerCertificateMetadataList) < int(listServerCertificatesMaxItems) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listServerCertificatesMarker = listServerCertificatesResp.Marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成新证书名(需符合 AWS IAM 命名规则)
|
||||||
|
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
|
// 导入证书
|
||||||
|
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_UploadServerCertificate.html
|
||||||
|
uploadServerCertificateReq := &awsiam.UploadServerCertificateInput{
|
||||||
|
ServerCertificateName: aws.String(certName),
|
||||||
|
Path: aws.String(u.config.CertificatePath),
|
||||||
|
CertificateBody: aws.String(serverCertPEM),
|
||||||
|
CertificateChain: aws.String(intermediaCertPEM),
|
||||||
|
PrivateKey: aws.String(privkeyPEM),
|
||||||
|
}
|
||||||
|
if u.config.CertificatePath == "" {
|
||||||
|
uploadServerCertificateReq.Path = aws.String("/")
|
||||||
|
}
|
||||||
|
uploadServerCertificateResp, err := u.sdkClient.UploadServerCertificate(context.TODO(), uploadServerCertificateReq)
|
||||||
|
u.logger.Debug("sdk request 'iam.UploadServerCertificate'", slog.Any("request", uploadServerCertificateReq), slog.Any("response", uploadServerCertificateResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'iam.UploadServerCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &uploader.UploadResult{
|
||||||
|
CertId: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateId),
|
||||||
|
CertName: certName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, secretAccessKey, region string) (*awsiam.Client, error) {
|
||||||
|
cfg, err := awscfg.LoadDefaultConfig(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := awsiam.NewFromConfig(cfg, func(o *awsiam.Options) {
|
||||||
|
o.Region = region
|
||||||
|
o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
|
||||||
|
})
|
||||||
|
return client, nil
|
||||||
|
}
|
115
internal/pkg/sdk3rd/ucloud/udnr/apis.go
Normal file
115
internal/pkg/sdk3rd/ucloud/udnr/apis.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package udnr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryDomainDNSRequest struct {
|
||||||
|
request.CommonBase
|
||||||
|
|
||||||
|
Dn *string `required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDomainDNSResponse struct {
|
||||||
|
response.CommonBase
|
||||||
|
|
||||||
|
Data []DomainDNSRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) NewQueryDomainDNSRequest() *QueryDomainDNSRequest {
|
||||||
|
req := &QueryDomainDNSRequest{}
|
||||||
|
|
||||||
|
c.Client.SetupRequest(req)
|
||||||
|
|
||||||
|
req.SetRetryable(false)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) QueryDomainDNS(req *QueryDomainDNSRequest) (*QueryDomainDNSResponse, error) {
|
||||||
|
var err error
|
||||||
|
var res QueryDomainDNSResponse
|
||||||
|
|
||||||
|
reqCopier := *req
|
||||||
|
|
||||||
|
err = c.Client.InvokeAction("UdnrDomainDNSQuery", &reqCopier, &res)
|
||||||
|
if err != nil {
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddDomainDNSRequest struct {
|
||||||
|
request.CommonBase
|
||||||
|
|
||||||
|
Dn *string `required:"true"`
|
||||||
|
DnsType *string `required:"true"`
|
||||||
|
RecordName *string `required:"true"`
|
||||||
|
Content *string `required:"true"`
|
||||||
|
TTL *int `required:"true"`
|
||||||
|
Prio *int `required:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddDomainDNSResponse struct {
|
||||||
|
response.CommonBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) NewAddDomainDNSRequest() *AddDomainDNSRequest {
|
||||||
|
req := &AddDomainDNSRequest{}
|
||||||
|
|
||||||
|
c.Client.SetupRequest(req)
|
||||||
|
|
||||||
|
req.SetRetryable(false)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) AddDomainDNS(req *AddDomainDNSRequest) (*AddDomainDNSResponse, error) {
|
||||||
|
var err error
|
||||||
|
var res AddDomainDNSResponse
|
||||||
|
|
||||||
|
reqCopier := *req
|
||||||
|
|
||||||
|
err = c.Client.InvokeAction("UdnrDomainDNSAdd", &reqCopier, &res)
|
||||||
|
if err != nil {
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteDomainDNSRequest struct {
|
||||||
|
request.CommonBase
|
||||||
|
|
||||||
|
Dn *string `required:"true"`
|
||||||
|
DnsType *string `required:"true"`
|
||||||
|
RecordName *string `required:"true"`
|
||||||
|
Content *string `required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteDomainDNSResponse struct {
|
||||||
|
response.CommonBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) NewDeleteDomainDNSRequest() *DeleteDomainDNSRequest {
|
||||||
|
req := &DeleteDomainDNSRequest{}
|
||||||
|
|
||||||
|
c.Client.SetupRequest(req)
|
||||||
|
|
||||||
|
req.SetRetryable(false)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDNRClient) DeleteDomainDNS(req *DeleteDomainDNSRequest) (*DeleteDomainDNSResponse, error) {
|
||||||
|
var err error
|
||||||
|
var res DeleteDomainDNSResponse
|
||||||
|
|
||||||
|
reqCopier := *req
|
||||||
|
|
||||||
|
err = c.Client.InvokeAction("UdnrDeleteDnsRecord", &reqCopier, &res)
|
||||||
|
if err != nil {
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
18
internal/pkg/sdk3rd/ucloud/udnr/client.go
Normal file
18
internal/pkg/sdk3rd/ucloud/udnr/client.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package udnr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||||
|
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UDNRClient struct {
|
||||||
|
*ucloud.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(config *ucloud.Config, credential *auth.Credential) *UDNRClient {
|
||||||
|
meta := ucloud.ClientMeta{Product: "UDNR"}
|
||||||
|
client := ucloud.NewClientWithMeta(config, credential, meta)
|
||||||
|
return &UDNRClient{
|
||||||
|
client,
|
||||||
|
}
|
||||||
|
}
|
9
internal/pkg/sdk3rd/ucloud/udnr/models.go
Normal file
9
internal/pkg/sdk3rd/ucloud/udnr/models.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package udnr
|
||||||
|
|
||||||
|
type DomainDNSRecord struct {
|
||||||
|
DnsType string
|
||||||
|
RecordName string
|
||||||
|
Content string
|
||||||
|
TTL int
|
||||||
|
Prio int
|
||||||
|
}
|
62
migrations/1748959200_upgrade.go
Normal file
62
migrations/1748959200_upgrade.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(app core.App) error {
|
||||||
|
tracer := NewTracer("(v0.3)1748959200")
|
||||||
|
tracer.Printf("go ...")
|
||||||
|
|
||||||
|
// migrate data
|
||||||
|
{
|
||||||
|
collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := app.FindAllRecords(collection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
changed := false
|
||||||
|
|
||||||
|
if record.GetString("provider") == "ssh" {
|
||||||
|
config := make(map[string]any)
|
||||||
|
if err := record.UnmarshalJSONField("config", &config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config["authMethod"] == nil || config["authMethod"] == "" {
|
||||||
|
if config["key"] != nil && config["key"] != "" {
|
||||||
|
config["authMethod"] = "key"
|
||||||
|
} else if config["password"] != nil && config["password"] != "" {
|
||||||
|
config["authMethod"] = "password"
|
||||||
|
} else {
|
||||||
|
config["authMethod"] = "none"
|
||||||
|
}
|
||||||
|
record.Set("config", config)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
if err := app.Save(record); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("done")
|
||||||
|
return nil
|
||||||
|
}, func(app core.App) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
BIN
ui/public/imgs/providers/constellix.png
Normal file
BIN
ui/public/imgs/providers/constellix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
@ -28,6 +28,7 @@ import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig";
|
|||||||
import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
|
import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
|
||||||
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
||||||
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
||||||
|
import AccessFormConstellixConfig from "./AccessFormConstellixConfig";
|
||||||
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
|
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
|
||||||
import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig";
|
import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig";
|
||||||
import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig";
|
import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig";
|
||||||
@ -219,6 +220,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
return <AccessFormClouDNSConfig {...nestedFormProps} />;
|
return <AccessFormClouDNSConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.CMCCCLOUD:
|
case ACCESS_PROVIDERS.CMCCCLOUD:
|
||||||
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.CONSTELLIX:
|
||||||
|
return <AccessFormConstellixConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.DESEC:
|
case ACCESS_PROVIDERS.DESEC:
|
||||||
return <AccessFormDeSECConfig {...nestedFormProps} />;
|
return <AccessFormDeSECConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.DIGITALOCEAN:
|
case ACCESS_PROVIDERS.DIGITALOCEAN:
|
||||||
|
67
ui/src/components/access/AccessFormConstellixConfig.tsx
Normal file
67
ui/src/components/access/AccessFormConstellixConfig.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { type AccessConfigForConstellix } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormConstellixConfigFieldValues = Nullish<AccessConfigForConstellix>;
|
||||||
|
|
||||||
|
export type AccessFormConstellixConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormConstellixConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormConstellixConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormConstellixConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
apiKey: "",
|
||||||
|
secretKey: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormConstellixConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormConstellixConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
apiKey: z.string().trim().nonempty(t("access.form.constellix_api_key.placeholder")),
|
||||||
|
secretKey: z.string().trim().nonempty(t("access.form.constellix_secret_key.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="apiKey"
|
||||||
|
label={t("access.form.constellix_api_key.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.constellix_api_key.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input autoComplete="new-password" placeholder={t("access.form.constellix_api_key.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="secretKey"
|
||||||
|
label={t("access.form.constellix_secret_key.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.constellix_secret_key.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input.Password autoComplete="new-password" placeholder={t("access.form.constellix_secret_key.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormConstellixConfig;
|
@ -1,9 +1,10 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined } from "@ant-design/icons";
|
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
import { Button, Collapse, Form, type FormInstance, Input, InputNumber, Space } from "antd";
|
import { Button, Collapse, Form, type FormInstance, Input, InputNumber, Select, Space } from "antd";
|
||||||
import { createSchemaFieldRule } from "antd-zod";
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import Show from "@/components/Show";
|
||||||
import TextFileInput from "@/components/TextFileInput";
|
import TextFileInput from "@/components/TextFileInput";
|
||||||
import { type AccessConfigForSSH } from "@/domain/access";
|
import { type AccessConfigForSSH } from "@/domain/access";
|
||||||
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
||||||
@ -18,10 +19,15 @@ export type AccessFormSSHConfigProps = {
|
|||||||
onValuesChange?: (values: AccessFormSSHConfigFieldValues) => void;
|
onValuesChange?: (values: AccessFormSSHConfigFieldValues) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AUTH_METHOD_NONE = "none" as const;
|
||||||
|
const AUTH_METHOD_PASSWORD = "password" as const;
|
||||||
|
const AUTH_METHOD_KEY = "key" as const;
|
||||||
|
|
||||||
const initFormModel = (): AccessFormSSHConfigFieldValues => {
|
const initFormModel = (): AccessFormSSHConfigFieldValues => {
|
||||||
return {
|
return {
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 22,
|
port: 22,
|
||||||
|
authMethod: AUTH_METHOD_PASSWORD,
|
||||||
username: "root",
|
username: "root",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -38,6 +44,9 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
.int(t("access.form.ssh_port.placeholder"))
|
.int(t("access.form.ssh_port.placeholder"))
|
||||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
),
|
),
|
||||||
|
authMethod: z.union([z.literal(AUTH_METHOD_NONE), z.literal(AUTH_METHOD_PASSWORD), z.literal(AUTH_METHOD_KEY)], {
|
||||||
|
message: t("access.form.ssh_auth_method.placeholder"),
|
||||||
|
}),
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, t("access.form.ssh_username.placeholder"))
|
.min(1, t("access.form.ssh_username.placeholder"))
|
||||||
@ -45,11 +54,13 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
password: z
|
password: z
|
||||||
.string()
|
.string()
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||||
.nullish(),
|
.nullish()
|
||||||
|
.refine((v) => fieldAuthMethod !== AUTH_METHOD_PASSWORD || !!v?.trim(), t("access.form.ssh_password.placeholder")),
|
||||||
key: z
|
key: z
|
||||||
.string()
|
.string()
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.nullish(),
|
.nullish()
|
||||||
|
.refine((v) => fieldAuthMethod !== AUTH_METHOD_KEY || !!v?.trim(), t("access.form.ssh_key.placeholder")),
|
||||||
keyPassphrase: z
|
keyPassphrase: z
|
||||||
.string()
|
.string()
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
@ -57,47 +68,43 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
.refine((v) => !v || formInst.getFieldValue("key"), t("access.form.ssh_key.placeholder")),
|
.refine((v) => !v || formInst.getFieldValue("key"), t("access.form.ssh_key.placeholder")),
|
||||||
jumpServers: z
|
jumpServers: z
|
||||||
.array(
|
.array(
|
||||||
z
|
z.object({
|
||||||
.object({
|
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||||
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
port: z.preprocess(
|
||||||
port: z.preprocess(
|
(v) => Number(v),
|
||||||
(v) => Number(v),
|
z
|
||||||
z
|
.number()
|
||||||
.number()
|
.int(t("access.form.ssh_port.placeholder"))
|
||||||
.int(t("access.form.ssh_port.placeholder"))
|
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
),
|
||||||
),
|
authMethod: z.union([z.literal(AUTH_METHOD_NONE), z.literal(AUTH_METHOD_PASSWORD), z.literal(AUTH_METHOD_KEY)], {
|
||||||
username: z
|
message: t("access.form.ssh_auth_method.placeholder"),
|
||||||
.string()
|
}),
|
||||||
.min(1, t("access.form.ssh_username.placeholder"))
|
username: z
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
.string()
|
||||||
password: z
|
.min(1, t("access.form.ssh_username.placeholder"))
|
||||||
.string()
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
password: z
|
||||||
.nullish(),
|
.string()
|
||||||
key: z
|
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||||
.string()
|
.nullish(),
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
key: z
|
||||||
.nullish(),
|
.string()
|
||||||
keyPassphrase: z
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.string()
|
.nullish(),
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
keyPassphrase: z
|
||||||
.nullish(),
|
.string()
|
||||||
})
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.superRefine((data, ctx) => {
|
.nullish(),
|
||||||
if (data.keyPassphrase && !data.key) {
|
}),
|
||||||
ctx.addIssue({
|
{ message: t("access.form.ssh_jump_servers.errmsg.invalid") }
|
||||||
path: ["keyPassphrase"],
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t("access.form.ssh_key.placeholder"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
.nullish(),
|
.nullish(),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const fieldAuthMethod = Form.useWatch("authMethod", formInst);
|
||||||
|
|
||||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
onValuesChange?.(values);
|
onValuesChange?.(values);
|
||||||
};
|
};
|
||||||
@ -125,36 +132,39 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Form.Item name="authMethod" label={t("access.form.ssh_auth_method.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("access.form.ssh_auth_method.placeholder")}>
|
||||||
|
<Select.Option key={AUTH_METHOD_NONE} value={AUTH_METHOD_NONE}>
|
||||||
|
{t("access.form.ssh_auth_method.option.none.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_PASSWORD} value={AUTH_METHOD_PASSWORD}>
|
||||||
|
{t("access.form.ssh_auth_method.option.password.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_KEY} value={AUTH_METHOD_KEY}>
|
||||||
|
{t("access.form.ssh_auth_method.option.key.label")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
<Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
||||||
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Show when={fieldAuthMethod === AUTH_METHOD_PASSWORD}>
|
||||||
name="password"
|
<Form.Item name="password" label={t("access.form.ssh_password.label")} rules={[formRule]}>
|
||||||
label={t("access.form.ssh_password.label")}
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
||||||
rules={[formRule]}
|
</Form.Item>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
|
</Show>
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Show when={fieldAuthMethod === AUTH_METHOD_KEY}>
|
||||||
name="key"
|
<Form.Item name="key" label={t("access.form.ssh_key.label")} rules={[formRule]}>
|
||||||
label={t("access.form.ssh_key.label")}
|
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
||||||
rules={[formRule]}
|
</Form.Item>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item name="keyPassphrase" label={t("access.form.ssh_key_passphrase.label")} rules={[formRule]}>
|
||||||
name="keyPassphrase"
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
||||||
label={t("access.form.ssh_key_passphrase.label")}
|
</Form.Item>
|
||||||
rules={[formRule]}
|
</Show>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item name="jumpServers" label={t("access.form.ssh_jump_servers.label")} rules={[formRule]}>
|
<Form.Item name="jumpServers" label={t("access.form.ssh_jump_servers.label")} rules={[formRule]}>
|
||||||
<Form.List name="jumpServers">
|
<Form.List name="jumpServers">
|
||||||
@ -174,6 +184,60 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Fields = () => {
|
||||||
|
const authMethod = Form.useWatch(["jumpServers", field.name, "authMethod"], formInst);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<div className="w-2/3">
|
||||||
|
<Form.Item name={[field.name, "host"]} label={t("access.form.ssh_host.label")} rules={[formRule]}>
|
||||||
|
<Input placeholder={t("access.form.ssh_host.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
<div className="w-1/3">
|
||||||
|
<Form.Item name={[field.name, "port"]} label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
||||||
|
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "authMethod"]} label={t("access.form.ssh_auth_method.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("access.form.ssh_auth_method.placeholder")}>
|
||||||
|
<Select.Option key={AUTH_METHOD_NONE} value={AUTH_METHOD_NONE}>
|
||||||
|
{t("access.form.ssh_auth_method.option.none.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_PASSWORD} value={AUTH_METHOD_PASSWORD}>
|
||||||
|
{t("access.form.ssh_auth_method.option.password.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_KEY} value={AUTH_METHOD_KEY}>
|
||||||
|
{t("access.form.ssh_auth_method.option.key.label")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "username"]} label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
||||||
|
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Show when={authMethod === AUTH_METHOD_PASSWORD}>
|
||||||
|
<Form.Item name={[field.name, "password"]} label={t("access.form.ssh_password.label")} rules={[formRule]}>
|
||||||
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={authMethod === AUTH_METHOD_KEY}>
|
||||||
|
<Form.Item name={[field.name, "key"]} label={t("access.form.ssh_key.label")} rules={[formRule]}>
|
||||||
|
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "keyPassphrase"]} label={t("access.form.ssh_key_passphrase.label")} rules={[formRule]}>
|
||||||
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: field.key,
|
key: field.key,
|
||||||
label: <Label />,
|
label: <Label />,
|
||||||
@ -214,58 +278,12 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
/>
|
/>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
),
|
),
|
||||||
children: (
|
children: <Fields />,
|
||||||
<>
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
<div className="w-2/3">
|
|
||||||
<Form.Item name={[field.name, "host"]} label={t("access.form.ssh_host.label")} rules={[formRule]}>
|
|
||||||
<Input placeholder={t("access.form.ssh_host.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<div className="w-1/3">
|
|
||||||
<Form.Item name={[field.name, "port"]} label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
|
||||||
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Form.Item name={[field.name, "username"]} label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
|
||||||
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "password"]}
|
|
||||||
label={t("access.form.ssh_password.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "key"]}
|
|
||||||
label={t("access.form.ssh_key.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "keyPassphrase"]}
|
|
||||||
label={t("access.form.ssh_key_passphrase.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Button className="w-full" type="dashed" icon={<PlusOutlined />} onClick={() => add()}>
|
<Button className="w-full" type="dashed" icon={<PlusOutlined />} onClick={() => add(initFormModel())}>
|
||||||
{t("access.form.ssh_jump_servers.add")}
|
{t("access.form.ssh_jump_servers.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -35,6 +35,7 @@ import DeployNodeConfigFormAliyunVODConfig from "./DeployNodeConfigFormAliyunVOD
|
|||||||
import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig";
|
import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig";
|
||||||
import DeployNodeConfigFormAWSACMConfig from "./DeployNodeConfigFormAWSACMConfig";
|
import DeployNodeConfigFormAWSACMConfig from "./DeployNodeConfigFormAWSACMConfig";
|
||||||
import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig";
|
import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig";
|
||||||
|
import DeployNodeConfigFormAWSIAMConfig from "./DeployNodeConfigFormAWSIAMConfig";
|
||||||
import DeployNodeConfigFormAzureKeyVaultConfig from "./DeployNodeConfigFormAzureKeyVaultConfig";
|
import DeployNodeConfigFormAzureKeyVaultConfig from "./DeployNodeConfigFormAzureKeyVaultConfig";
|
||||||
import DeployNodeConfigFormBaiduCloudAppBLBConfig from "./DeployNodeConfigFormBaiduCloudAppBLBConfig";
|
import DeployNodeConfigFormBaiduCloudAppBLBConfig from "./DeployNodeConfigFormBaiduCloudAppBLBConfig";
|
||||||
import DeployNodeConfigFormBaiduCloudBLBConfig from "./DeployNodeConfigFormBaiduCloudBLBConfig";
|
import DeployNodeConfigFormBaiduCloudBLBConfig from "./DeployNodeConfigFormBaiduCloudBLBConfig";
|
||||||
@ -238,6 +239,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
|
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
|
||||||
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
|
||||||
|
case DEPLOYMENT_PROVIDERS.AWS_IAM:
|
||||||
|
return <DeployNodeConfigFormAWSIAMConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
|
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
|
||||||
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:
|
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Form, type FormInstance, Input } from "antd";
|
import { Form, type FormInstance, Input, Select } from "antd";
|
||||||
import { createSchemaFieldRule } from "antd-zod";
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
type DeployNodeConfigFormAWSCloudFrontConfigFieldValues = Nullish<{
|
type DeployNodeConfigFormAWSCloudFrontConfigFieldValues = Nullish<{
|
||||||
region: string;
|
region: string;
|
||||||
distributionId: string;
|
distributionId: string;
|
||||||
|
certificateSource: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
|
export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
|
||||||
@ -17,7 +18,9 @@ export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initFormModel = (): DeployNodeConfigFormAWSCloudFrontConfigFieldValues => {
|
const initFormModel = (): DeployNodeConfigFormAWSCloudFrontConfigFieldValues => {
|
||||||
return {};
|
return {
|
||||||
|
certificateSource: "ACM",
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
||||||
@ -30,15 +33,9 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
region: z
|
region: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder")),
|
||||||
.string({ message: t("workflow_node.deploy.form.aws_cloudfront_region.placeholder") })
|
distributionId: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")),
|
||||||
.nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder"))
|
certificateSource: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")),
|
||||||
.trim(),
|
|
||||||
distributionId: z
|
|
||||||
.string({ message: t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder") })
|
|
||||||
.nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder"))
|
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
|
||||||
.trim(),
|
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
@ -72,6 +69,17 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
|||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")} />
|
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="certificateSource" label={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")}>
|
||||||
|
<Select.Option key="ACM" value="ACM">
|
||||||
|
ACM
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key="IAM" value="IAM">
|
||||||
|
IAM
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
type DeployNodeConfigFormAWSIAMConfigFieldValues = Nullish<{
|
||||||
|
region: string;
|
||||||
|
certificatePath?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type DeployNodeConfigFormAWSIAMConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: DeployNodeConfigFormAWSIAMConfigFieldValues;
|
||||||
|
onValuesChange?: (values: DeployNodeConfigFormAWSIAMConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): DeployNodeConfigFormAWSIAMConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
certificatePath: "/",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployNodeConfigFormAWSIAMConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormAWSIAMConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
region: z
|
||||||
|
.string({ message: t("workflow_node.deploy.form.aws_iam_region.placeholder") })
|
||||||
|
.nonempty(t("workflow_node.deploy.form.aws_iam_region.placeholder"))
|
||||||
|
.trim(),
|
||||||
|
certificatePath: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (!v) return true;
|
||||||
|
return v.startsWith("/") && v.endsWith("/");
|
||||||
|
}, t("workflow_node.deploy.form.aws_iam_certificate_path.errmsg.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.aws_iam_region.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_region.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("workflow_node.deploy.form.aws_iam_region.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="certificatePath"
|
||||||
|
label={t("workflow_node.deploy.form.aws_iam_certificate_path.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_certificate_path.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input allowClear placeholder={t("workflow_node.deploy.form.aws_iam_certificate_path.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeployNodeConfigFormAWSIAMConfig;
|
@ -23,6 +23,7 @@ export interface AccessModel extends BaseModel {
|
|||||||
| AccessConfigForCloudflare
|
| AccessConfigForCloudflare
|
||||||
| AccessConfigForClouDNS
|
| AccessConfigForClouDNS
|
||||||
| AccessConfigForCMCCCloud
|
| AccessConfigForCMCCCloud
|
||||||
|
| AccessConfigForConstellix
|
||||||
| AccessConfigForDeSEC
|
| AccessConfigForDeSEC
|
||||||
| AccessConfigForDigitalOcean
|
| AccessConfigForDigitalOcean
|
||||||
| AccessConfigForDingTalkBot
|
| AccessConfigForDingTalkBot
|
||||||
@ -172,6 +173,11 @@ export type AccessConfigForCMCCCloud = {
|
|||||||
accessKeySecret: string;
|
accessKeySecret: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AccessConfigForConstellix = {
|
||||||
|
apiKey: string;
|
||||||
|
secretKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type AccessConfigForDeSEC = {
|
export type AccessConfigForDeSEC = {
|
||||||
token: string;
|
token: string;
|
||||||
};
|
};
|
||||||
@ -373,7 +379,8 @@ export type AccessConfigForSlackBot = {
|
|||||||
export type AccessConfigForSSH = {
|
export type AccessConfigForSSH = {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
authMethod?: string;
|
||||||
|
username?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
keyPassphrase?: string;
|
keyPassphrase?: string;
|
||||||
|
@ -22,6 +22,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
|||||||
CLOUDFLARE: "cloudflare",
|
CLOUDFLARE: "cloudflare",
|
||||||
CLOUDNS: "cloudns",
|
CLOUDNS: "cloudns",
|
||||||
CMCCCLOUD: "cmcccloud",
|
CMCCCLOUD: "cmcccloud",
|
||||||
|
CONSTELLIX: "constellix",
|
||||||
DESEC: "desec",
|
DESEC: "desec",
|
||||||
DIGITALOCEAN: "digitalocean",
|
DIGITALOCEAN: "digitalocean",
|
||||||
DINGTALKBOT: "dingtalkbot",
|
DINGTALKBOT: "dingtalkbot",
|
||||||
@ -120,6 +121,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
|||||||
[ACCESS_PROVIDERS.GCORE, "provider.gcore", "/imgs/providers/gcore.png", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.GCORE, "provider.gcore", "/imgs/providers/gcore.png", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.NETLIFY, "provider.netlify", "/imgs/providers/netlify.png", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.NETLIFY, "provider.netlify", "/imgs/providers/netlify.png", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.RAINYUN, "provider.rainyun", "/imgs/providers/rainyun.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.RAINYUN, "provider.rainyun", "/imgs/providers/rainyun.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
||||||
|
[ACCESS_PROVIDERS.UCLOUD, "provider.ucloud", "/imgs/providers/ucloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
|
||||||
|
|
||||||
[ACCESS_PROVIDERS.QINIU, "provider.qiniu", "/imgs/providers/qiniu.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.QINIU, "provider.qiniu", "/imgs/providers/qiniu.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.UPYUN, "provider.upyun", "/imgs/providers/upyun.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.UPYUN, "provider.upyun", "/imgs/providers/upyun.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
@ -127,7 +129,6 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
|||||||
[ACCESS_PROVIDERS.WANGSU, "provider.wangsu", "/imgs/providers/wangsu.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.WANGSU, "provider.wangsu", "/imgs/providers/wangsu.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.DOGECLOUD, "provider.dogecloud", "/imgs/providers/dogecloud.png", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.DOGECLOUD, "provider.dogecloud", "/imgs/providers/dogecloud.png", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.UCLOUD, "provider.ucloud", "/imgs/providers/ucloud.svg", [ACCESS_USAGES.HOSTING]],
|
|
||||||
[ACCESS_PROVIDERS.UNICLOUD, "provider.unicloud", "/imgs/providers/unicloud.png", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.UNICLOUD, "provider.unicloud", "/imgs/providers/unicloud.png", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS["1PANEL"], "provider.1panel", "/imgs/providers/1panel.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS["1PANEL"], "provider.1panel", "/imgs/providers/1panel.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
@ -144,6 +145,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
|||||||
|
|
||||||
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.DNS]],
|
||||||
[ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.png", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.png", [ACCESS_USAGES.DNS]],
|
||||||
|
[ACCESS_PROVIDERS.CONSTELLIX, "provider.constellix", "/imgs/providers/constellix.png", [ACCESS_USAGES.DNS]],
|
||||||
[ACCESS_PROVIDERS.DESEC, "provider.desec", "/imgs/providers/desec.svg", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.DESEC, "provider.desec", "/imgs/providers/desec.svg", [ACCESS_USAGES.DNS]],
|
||||||
[ACCESS_PROVIDERS.DIGITALOCEAN, "provider.digitalocean", "/imgs/providers/digitalocean.svg", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.DIGITALOCEAN, "provider.digitalocean", "/imgs/providers/digitalocean.svg", [ACCESS_USAGES.DNS]],
|
||||||
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.DNS]],
|
||||||
@ -264,6 +266,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
|||||||
CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`,
|
CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`,
|
||||||
CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`,
|
CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`,
|
||||||
CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`,
|
CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`,
|
||||||
|
CONSTELLIX: `${ACCESS_PROVIDERS.CONSTELLIX}`,
|
||||||
DESEC: `${ACCESS_PROVIDERS.DESEC}`,
|
DESEC: `${ACCESS_PROVIDERS.DESEC}`,
|
||||||
DIGITALOCEAN: `${ACCESS_PROVIDERS.DIGITALOCEAN}`,
|
DIGITALOCEAN: `${ACCESS_PROVIDERS.DIGITALOCEAN}`,
|
||||||
DNSLA: `${ACCESS_PROVIDERS.DNSLA}`,
|
DNSLA: `${ACCESS_PROVIDERS.DNSLA}`,
|
||||||
@ -286,6 +289,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
|||||||
PORKBUN: `${ACCESS_PROVIDERS.PORKBUN}`,
|
PORKBUN: `${ACCESS_PROVIDERS.PORKBUN}`,
|
||||||
POWERDNS: `${ACCESS_PROVIDERS.POWERDNS}`,
|
POWERDNS: `${ACCESS_PROVIDERS.POWERDNS}`,
|
||||||
RAINYUN: `${ACCESS_PROVIDERS.RAINYUN}`,
|
RAINYUN: `${ACCESS_PROVIDERS.RAINYUN}`,
|
||||||
|
UCLOUD_UDNR: `${ACCESS_PROVIDERS.UCLOUD}-udnr`,
|
||||||
TENCENTCLOUD: `${ACCESS_PROVIDERS.TENCENTCLOUD}`, // 兼容旧值,等同于 `TENCENTCLOUD_DNS`
|
TENCENTCLOUD: `${ACCESS_PROVIDERS.TENCENTCLOUD}`, // 兼容旧值,等同于 `TENCENTCLOUD_DNS`
|
||||||
TENCENTCLOUD_DNS: `${ACCESS_PROVIDERS.TENCENTCLOUD}-dns`,
|
TENCENTCLOUD_DNS: `${ACCESS_PROVIDERS.TENCENTCLOUD}-dns`,
|
||||||
TENCENTCLOUD_EO: `${ACCESS_PROVIDERS.TENCENTCLOUD}-eo`,
|
TENCENTCLOUD_EO: `${ACCESS_PROVIDERS.TENCENTCLOUD}-eo`,
|
||||||
@ -323,6 +327,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
|||||||
[ACME_DNS01_PROVIDERS.BUNNY, "provider.bunny"],
|
[ACME_DNS01_PROVIDERS.BUNNY, "provider.bunny"],
|
||||||
[ACME_DNS01_PROVIDERS.CLOUDFLARE, "provider.cloudflare"],
|
[ACME_DNS01_PROVIDERS.CLOUDFLARE, "provider.cloudflare"],
|
||||||
[ACME_DNS01_PROVIDERS.CLOUDNS, "provider.cloudns"],
|
[ACME_DNS01_PROVIDERS.CLOUDNS, "provider.cloudns"],
|
||||||
|
[ACME_DNS01_PROVIDERS.CONSTELLIX, "provider.constellix"],
|
||||||
[ACME_DNS01_PROVIDERS.DESEC, "provider.desec"],
|
[ACME_DNS01_PROVIDERS.DESEC, "provider.desec"],
|
||||||
[ACME_DNS01_PROVIDERS.DIGITALOCEAN, "provider.digitalocean"],
|
[ACME_DNS01_PROVIDERS.DIGITALOCEAN, "provider.digitalocean"],
|
||||||
[ACME_DNS01_PROVIDERS.DNSLA, "provider.dnsla"],
|
[ACME_DNS01_PROVIDERS.DNSLA, "provider.dnsla"],
|
||||||
@ -342,6 +347,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
|||||||
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
|
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
|
||||||
[ACME_DNS01_PROVIDERS.CMCCCLOUD, "provider.cmcccloud"],
|
[ACME_DNS01_PROVIDERS.CMCCCLOUD, "provider.cmcccloud"],
|
||||||
[ACME_DNS01_PROVIDERS.RAINYUN, "provider.rainyun"],
|
[ACME_DNS01_PROVIDERS.RAINYUN, "provider.rainyun"],
|
||||||
|
[ACME_DNS01_PROVIDERS.UCLOUD_UDNR, "provider.ucloud.udnr"],
|
||||||
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
|
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
|
||||||
[ACME_DNS01_PROVIDERS.POWERDNS, "provider.powerdns"],
|
[ACME_DNS01_PROVIDERS.POWERDNS, "provider.powerdns"],
|
||||||
[ACME_DNS01_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq"],
|
[ACME_DNS01_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq"],
|
||||||
@ -383,6 +389,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
|||||||
ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`,
|
ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`,
|
||||||
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
|
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
|
||||||
AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`,
|
AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`,
|
||||||
|
AWS_IAM: `${ACCESS_PROVIDERS.AWS}-iam`,
|
||||||
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
|
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
|
||||||
BAIDUCLOUD_APPBLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-appblb`,
|
BAIDUCLOUD_APPBLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-appblb`,
|
||||||
BAIDUCLOUD_BLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-blb`,
|
BAIDUCLOUD_BLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-blb`,
|
||||||
@ -555,6 +562,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
|||||||
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud.webhost", DEPLOYMENT_CATEGORIES.WEBSITE],
|
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud.webhost", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||||
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOYMENT_CATEGORIES.CDN],
|
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOYMENT_CATEGORIES.CDN],
|
||||||
[DEPLOYMENT_PROVIDERS.AWS_ACM, "provider.aws.acm", DEPLOYMENT_CATEGORIES.SSL],
|
[DEPLOYMENT_PROVIDERS.AWS_ACM, "provider.aws.acm", DEPLOYMENT_CATEGORIES.SSL],
|
||||||
|
[DEPLOYMENT_PROVIDERS.AWS_IAM, "provider.aws.iam", DEPLOYMENT_CATEGORIES.SSL],
|
||||||
[DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT, "provider.azure.keyvault", DEPLOYMENT_CATEGORIES.SSL],
|
[DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT, "provider.azure.keyvault", DEPLOYMENT_CATEGORIES.SSL],
|
||||||
[DEPLOYMENT_PROVIDERS.BUNNY_CDN, "provider.bunny.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
[DEPLOYMENT_PROVIDERS.BUNNY_CDN, "provider.bunny.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||||
[DEPLOYMENT_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOYMENT_CATEGORIES.CDN],
|
[DEPLOYMENT_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOYMENT_CATEGORIES.CDN],
|
||||||
|
@ -146,6 +146,12 @@
|
|||||||
"access.form.cmcccloud_access_key_secret.label": "CMCC ECloud AccessKeySecret",
|
"access.form.cmcccloud_access_key_secret.label": "CMCC ECloud AccessKeySecret",
|
||||||
"access.form.cmcccloud_access_key_secret.placeholder": "Please enter CMCC ECloud AccessKeySecret",
|
"access.form.cmcccloud_access_key_secret.placeholder": "Please enter CMCC ECloud AccessKeySecret",
|
||||||
"access.form.cmcccloud_access_key_secret.tooltip": "For more information, see <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>",
|
"access.form.cmcccloud_access_key_secret.tooltip": "For more information, see <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>",
|
||||||
|
"access.form.constellix_api_key.label": "Constellix API key",
|
||||||
|
"access.form.constellix_api_key.placeholder": "Please enter Constellix API key",
|
||||||
|
"access.form.constellix_api_key.tooltip": "For more information, see <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||||
|
"access.form.constellix_secret_key.label": "Constellix API secret key",
|
||||||
|
"access.form.constellix_secret_key.placeholder": "Please enter Constellix API secret key",
|
||||||
|
"access.form.constellix_secret_key.tooltip": "For more information, see <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||||
"access.form.desec_token.label": "deSEC token",
|
"access.form.desec_token.label": "deSEC token",
|
||||||
"access.form.desec_token.placeholder": "Please enter deSEC token",
|
"access.form.desec_token.placeholder": "Please enter deSEC token",
|
||||||
"access.form.desec_token.tooltip": "For more information, see <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
"access.form.desec_token.tooltip": "For more information, see <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
||||||
@ -372,18 +378,21 @@
|
|||||||
"access.form.ssh_host.placeholder": "Please enter server host",
|
"access.form.ssh_host.placeholder": "Please enter server host",
|
||||||
"access.form.ssh_port.label": "Server port",
|
"access.form.ssh_port.label": "Server port",
|
||||||
"access.form.ssh_port.placeholder": "Please enter server port",
|
"access.form.ssh_port.placeholder": "Please enter server port",
|
||||||
|
"access.form.ssh_auth_method.label": "Authentication method",
|
||||||
|
"access.form.ssh_auth_method.placeholder": "Please select authentication method",
|
||||||
|
"access.form.ssh_auth_method.option.none.label": "None",
|
||||||
|
"access.form.ssh_auth_method.option.password.label": "Password",
|
||||||
|
"access.form.ssh_auth_method.option.key.label": "SSH key",
|
||||||
"access.form.ssh_username.label": "Username",
|
"access.form.ssh_username.label": "Username",
|
||||||
"access.form.ssh_username.placeholder": "Please enter username",
|
"access.form.ssh_username.placeholder": "Please enter username",
|
||||||
"access.form.ssh_password.label": "Password (Optional)",
|
"access.form.ssh_password.label": "Password",
|
||||||
"access.form.ssh_password.placeholder": "Please enter password",
|
"access.form.ssh_password.placeholder": "Please enter password",
|
||||||
"access.form.ssh_password.tooltip": "Required when using password to connect to SSH.",
|
"access.form.ssh_key.label": "SSH key",
|
||||||
"access.form.ssh_key.label": "SSH key (Optional)",
|
|
||||||
"access.form.ssh_key.placeholder": "Please enter SSH key",
|
"access.form.ssh_key.placeholder": "Please enter SSH key",
|
||||||
"access.form.ssh_key.tooltip": "Required when using key to connect to SSH.",
|
|
||||||
"access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
|
"access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
|
||||||
"access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
|
"access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
|
||||||
"access.form.ssh_key_passphrase.tooltip": "Optional when using key to connect to SSH.",
|
|
||||||
"access.form.ssh_jump_servers.label": "SSH jump server (Optional)",
|
"access.form.ssh_jump_servers.label": "SSH jump server (Optional)",
|
||||||
|
"access.form.ssh_jump_servers.errmsg.invalid": "Please configure a valid jump server",
|
||||||
"access.form.ssh_jump_servers.item.label": "Jump server",
|
"access.form.ssh_jump_servers.item.label": "Jump server",
|
||||||
"access.form.ssh_jump_servers.add": "Add jump server",
|
"access.form.ssh_jump_servers.add": "Add jump server",
|
||||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||||
|
@ -27,13 +27,14 @@
|
|||||||
"provider.aws": "AWS",
|
"provider.aws": "AWS",
|
||||||
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
||||||
"provider.aws.cloudfront": "AWS - CloudFront",
|
"provider.aws.cloudfront": "AWS - CloudFront",
|
||||||
|
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
|
||||||
"provider.aws.route53": "AWS - Route53",
|
"provider.aws.route53": "AWS - Route53",
|
||||||
"provider.azure": "Azure",
|
"provider.azure": "Azure",
|
||||||
"provider.azure.dns": "Azure - DNS",
|
"provider.azure.dns": "Azure - DNS",
|
||||||
"provider.azure.keyvault": "Azure - KeyVault",
|
"provider.azure.keyvault": "Azure - KeyVault",
|
||||||
"provider.baiducloud": "Baidu Cloud",
|
"provider.baiducloud": "Baidu Cloud",
|
||||||
"provider.baiducloud.appblb": "Baidu Cloud - AppBLB (Application Baidu Load Balancer)",
|
"provider.baiducloud.appblb": "Baidu Cloud - AppBLB (Application Baidu Load Balancer)",
|
||||||
"provider.baiducloud.blb": "Baidu Cloud - BLB (Baidu Load Balancer)",
|
"provider.baiducloud.blb": "Baidu Cloud - BLB (Load Balancer)",
|
||||||
"provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)",
|
"provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)",
|
||||||
"provider.baiducloud.cert_upload": "Baidu Cloud - Upload to SSL Certificate Service",
|
"provider.baiducloud.cert_upload": "Baidu Cloud - Upload to SSL Certificate Service",
|
||||||
"provider.baiducloud.dns": "Baidu Cloud - DNS (Domain Name Service)",
|
"provider.baiducloud.dns": "Baidu Cloud - DNS (Domain Name Service)",
|
||||||
@ -55,6 +56,7 @@
|
|||||||
"provider.cloudflare": "Cloudflare",
|
"provider.cloudflare": "Cloudflare",
|
||||||
"provider.cloudns": "ClouDNS",
|
"provider.cloudns": "ClouDNS",
|
||||||
"provider.cmcccloud": "China Mobile Cloud (ECloud)",
|
"provider.cmcccloud": "China Mobile Cloud (ECloud)",
|
||||||
|
"provider.constellix": "Constellix",
|
||||||
"provider.ctcccloud": "China Telecom Cloud (State Cloud)",
|
"provider.ctcccloud": "China Telecom Cloud (State Cloud)",
|
||||||
"provider.cucccloud": "China Unicom Cloud",
|
"provider.cucccloud": "China Unicom Cloud",
|
||||||
"provider.desec": "deSEC",
|
"provider.desec": "deSEC",
|
||||||
@ -113,7 +115,7 @@
|
|||||||
"provider.qiniu.kodo": "Qiniu - Kodo",
|
"provider.qiniu.kodo": "Qiniu - Kodo",
|
||||||
"provider.qiniu.pili": "Qiniu - Pili",
|
"provider.qiniu.pili": "Qiniu - Pili",
|
||||||
"provider.rainyun": "Rain Yun",
|
"provider.rainyun": "Rain Yun",
|
||||||
"provider.rainyun.rcdn": "Rain Yun - RCDN (Rain Content Delivery Network)",
|
"provider.rainyun.rcdn": "Rain Yun - RCDN (Content Delivery Network)",
|
||||||
"provider.ratpanel": "RatPanel",
|
"provider.ratpanel": "RatPanel",
|
||||||
"provider.ratpanel.console": "RatPanel - Console",
|
"provider.ratpanel.console": "RatPanel - Console",
|
||||||
"provider.ratpanel.site": "RatPanel - Website",
|
"provider.ratpanel.site": "RatPanel - Website",
|
||||||
@ -136,8 +138,9 @@
|
|||||||
"provider.tencentcloud.vod": "Tencent Cloud - VOD (Video on Demand)",
|
"provider.tencentcloud.vod": "Tencent Cloud - VOD (Video on Demand)",
|
||||||
"provider.tencentcloud.waf": "Tencent Cloud - WAF (Web Application Firewall)",
|
"provider.tencentcloud.waf": "Tencent Cloud - WAF (Web Application Firewall)",
|
||||||
"provider.ucloud": "UCloud",
|
"provider.ucloud": "UCloud",
|
||||||
"provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)",
|
"provider.ucloud.ucdn": "UCloud - UCDN (Content Delivery Network)",
|
||||||
"provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)",
|
"provider.ucloud.udnr": "UCloud - UDNR (Domain Name Registrar)",
|
||||||
|
"provider.ucloud.us3": "UCloud - US3 (Object-based Storage)",
|
||||||
"provider.unicloud": "uniCloud (DCloud)",
|
"provider.unicloud": "uniCloud (DCloud)",
|
||||||
"provider.unicloud.webhost": "uniCloud (DCloud) - Web Host",
|
"provider.unicloud.webhost": "uniCloud (DCloud) - Web Host",
|
||||||
"provider.upyun": "UPYUN",
|
"provider.upyun": "UPYUN",
|
||||||
|
@ -297,6 +297,15 @@
|
|||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront distribution ID",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront distribution ID",
|
||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "Please enter AWS CloudFront distribution ID",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "Please enter AWS CloudFront distribution ID",
|
||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
||||||
|
"workflow_node.deploy.form.aws_cloudfront_certificate_source.label": "AWS CloudFront certificate source",
|
||||||
|
"workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder": "Please select AWS CloudFront certificate source",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.label": "AWS IAM Region",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.placeholder": "Please enter AWS IAM region (e.g. us-east-1)",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints</a>",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.label": "AWS IAM certificate path (Optional)",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.placeholder": "Please enter AWS IAM certificate path",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid": "Please enter a valid AWS IAM certificate path",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/reference_identifiers.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/reference_identifiers.html</a>",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault name",
|
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault name",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "Please enter Azure KeyVault name",
|
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "Please enter Azure KeyVault name",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||||
|
@ -146,6 +146,12 @@
|
|||||||
"access.form.cmcccloud_access_key_secret.label": "移动云 AccessKeySecret",
|
"access.form.cmcccloud_access_key_secret.label": "移动云 AccessKeySecret",
|
||||||
"access.form.cmcccloud_access_key_secret.placeholder": "请输入移动云 AccessKeySecret",
|
"access.form.cmcccloud_access_key_secret.placeholder": "请输入移动云 AccessKeySecret",
|
||||||
"access.form.cmcccloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>",
|
"access.form.cmcccloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>",
|
||||||
|
"access.form.constellix_api_key.label": "Constellix API Key",
|
||||||
|
"access.form.constellix_api_key.placeholder": "请输入 Constellix API Key",
|
||||||
|
"access.form.constellix_api_key.tooltip": "这是什么?请参阅 <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||||
|
"access.form.constellix_secret_key.label": "Constellix Secret Key",
|
||||||
|
"access.form.constellix_secret_key.placeholder": "请输入 Constellix Secret Key",
|
||||||
|
"access.form.constellix_secret_key.tooltip": "这是什么?请参阅 <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||||
"access.form.desec_token.label": "deSEC Token",
|
"access.form.desec_token.label": "deSEC Token",
|
||||||
"access.form.desec_token.placeholder": "请输入 deSEC Token",
|
"access.form.desec_token.placeholder": "请输入 deSEC Token",
|
||||||
"access.form.desec_token.tooltip": "这是什么?请参阅 <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
"access.form.desec_token.tooltip": "这是什么?请参阅 <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
||||||
@ -372,18 +378,21 @@
|
|||||||
"access.form.ssh_host.placeholder": "请输入服务器地址",
|
"access.form.ssh_host.placeholder": "请输入服务器地址",
|
||||||
"access.form.ssh_port.label": "服务器端口",
|
"access.form.ssh_port.label": "服务器端口",
|
||||||
"access.form.ssh_port.placeholder": "请输入服务器端口",
|
"access.form.ssh_port.placeholder": "请输入服务器端口",
|
||||||
|
"access.form.ssh_auth_method.label": "认证方式",
|
||||||
|
"access.form.ssh_auth_method.placeholder": "请选择认证方式",
|
||||||
|
"access.form.ssh_auth_method.option.none.label": "无",
|
||||||
|
"access.form.ssh_auth_method.option.password.label": "密码",
|
||||||
|
"access.form.ssh_auth_method.option.key.label": "密钥",
|
||||||
"access.form.ssh_username.label": "用户名",
|
"access.form.ssh_username.label": "用户名",
|
||||||
"access.form.ssh_username.placeholder": "请输入用户名",
|
"access.form.ssh_username.placeholder": "请输入用户名",
|
||||||
"access.form.ssh_password.label": "密码(可选)",
|
"access.form.ssh_password.label": "密码",
|
||||||
"access.form.ssh_password.placeholder": "请输入密码",
|
"access.form.ssh_password.placeholder": "请输入密码",
|
||||||
"access.form.ssh_password.tooltip": "使用密码连接到 SSH 时必填。<br>该字段与密钥文件字段二选一,如果同时填写优先使用 SSH 密钥登录。",
|
"access.form.ssh_key.label": "SSH 密钥",
|
||||||
"access.form.ssh_key.label": "SSH 密钥(可选)",
|
|
||||||
"access.form.ssh_key.placeholder": "请输入 SSH 密钥文件内容",
|
"access.form.ssh_key.placeholder": "请输入 SSH 密钥文件内容",
|
||||||
"access.form.ssh_key.tooltip": "使用 SSH 密钥连接到 SSH 时必填。<br>该字段与密码字段二选一,如果同时填写优先使用 SSH 密钥登录。",
|
|
||||||
"access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
|
"access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
|
||||||
"access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
|
"access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
|
||||||
"access.form.ssh_key_passphrase.tooltip": "使用 SSH 密钥连接到 SSH 时选填。",
|
|
||||||
"access.form.ssh_jump_servers.label": "SSH 跳板机(可选)",
|
"access.form.ssh_jump_servers.label": "SSH 跳板机(可选)",
|
||||||
|
"access.form.ssh_jump_servers.errmsg.invalid": "请配置有效的 SSH 跳板机",
|
||||||
"access.form.ssh_jump_servers.item.label": "跳板机",
|
"access.form.ssh_jump_servers.item.label": "跳板机",
|
||||||
"access.form.ssh_jump_servers.add": "添加跳板机",
|
"access.form.ssh_jump_servers.add": "添加跳板机",
|
||||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"provider.aws": "AWS",
|
"provider.aws": "AWS",
|
||||||
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
||||||
"provider.aws.cloudfront": "AWS - CloudFront",
|
"provider.aws.cloudfront": "AWS - CloudFront",
|
||||||
|
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
|
||||||
"provider.aws.route53": "AWS - Route53",
|
"provider.aws.route53": "AWS - Route53",
|
||||||
"provider.azure": "Azure",
|
"provider.azure": "Azure",
|
||||||
"provider.azure.dns": "Azure - DNS",
|
"provider.azure.dns": "Azure - DNS",
|
||||||
@ -55,6 +56,7 @@
|
|||||||
"provider.cloudflare": "Cloudflare",
|
"provider.cloudflare": "Cloudflare",
|
||||||
"provider.cloudns": "ClouDNS",
|
"provider.cloudns": "ClouDNS",
|
||||||
"provider.cmcccloud": "移动云",
|
"provider.cmcccloud": "移动云",
|
||||||
|
"provider.constellix": "Constellix",
|
||||||
"provider.ctcccloud": "联通云",
|
"provider.ctcccloud": "联通云",
|
||||||
"provider.cucccloud": "天翼云",
|
"provider.cucccloud": "天翼云",
|
||||||
"provider.desec": "deSEC",
|
"provider.desec": "deSEC",
|
||||||
@ -137,6 +139,7 @@
|
|||||||
"provider.tencentcloud.waf": "腾讯云 - Web 应用防火墙 WAF",
|
"provider.tencentcloud.waf": "腾讯云 - Web 应用防火墙 WAF",
|
||||||
"provider.ucloud": "优刻得",
|
"provider.ucloud": "优刻得",
|
||||||
"provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN",
|
"provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN",
|
||||||
|
"provider.ucloud.udnr": "优刻得 - 域名服务 UDNR",
|
||||||
"provider.ucloud.us3": "优刻得 - 对象存储 US3",
|
"provider.ucloud.us3": "优刻得 - 对象存储 US3",
|
||||||
"provider.unicloud": "uniCloud (DCloud)",
|
"provider.unicloud": "uniCloud (DCloud)",
|
||||||
"provider.unicloud.webhost": "uniCloud (DCloud) - 前端网页托管",
|
"provider.unicloud.webhost": "uniCloud (DCloud) - 前端网页托管",
|
||||||
|
@ -296,6 +296,15 @@
|
|||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront 分配 ID",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront 分配 ID",
|
||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "请输入 AWS CloudFront 分配 ID",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "请输入 AWS CloudFront 分配 ID",
|
||||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
||||||
|
"workflow_node.deploy.form.aws_cloudfront_certificate_source.label": "AWS CloudFront 证书来源",
|
||||||
|
"workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder": "请选择 AWS CloudFront 证书来源",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.label": "AWS IAM 服务区域",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.placeholder": "请输入 AWS IAM 服务区域(例如:us-east-1)",
|
||||||
|
"workflow_node.deploy.form.aws_iam_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints</a>",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.label": "AWS IAM 证书路径(可选)",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.placeholder": "请输入 AWS IAM 证书路径",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid": "请输入正确的 AWS IAM 证书路径",
|
||||||
|
"workflow_node.deploy.form.aws_iam_certificate_path.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_identifiers.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_identifiers.html</a>",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault 名称",
|
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault 名称",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "请输入 Azure KeyVault 名称",
|
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "请输入 Azure KeyVault 名称",
|
||||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user