feat: new deployment provider: aws iam

This commit is contained in:
Fu Diwei 2025-06-03 22:22:54 +08:00
parent 6dc65eea2f
commit 7d55383cf7
16 changed files with 443 additions and 31 deletions

1
go.mod
View File

@ -85,6 +85,7 @@ require (
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // 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/buger/goterm v1.0.4 // indirect
github.com/diskfs/go-diskfs v1.5.0 // indirect

2
go.sum
View File

@ -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/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/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/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=

View File

@ -27,6 +27,7 @@ import (
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf"
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-acm"
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"
pBaiduCloudAppBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-appblb"
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{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@ -350,10 +351,20 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderTypeAWSCloudFront:
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,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
CertificatePath: maputil.GetOrDefaultString(options.ProviderServiceConfig, "certificatePath", "/"),
})
return deployer, err

View File

@ -195,6 +195,7 @@ const (
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam")
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")

View File

@ -14,7 +14,8 @@ import (
"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-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 {
@ -26,6 +27,9 @@ type DeployerConfig struct {
Region string `json:"region"`
// AWS CloudFront 分配 ID。
DistributionId string `json:"distributionId"`
// AWS CloudFront 证书来源。
// 可取值 "ACM"、"IAM"。
CertificateSource string `json:"certificateSource"`
}
type DeployerProvider struct {
@ -47,13 +51,28 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
var uploader uploader.Uploader
if config.CertificateSource == "ACM" {
uploader, err = uploaderspacm.NewUploader(&uploaderspacm.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
})
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{
@ -79,7 +98,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return nil, errors.New("config `distribuitionId` is required")
}
// 上传证书到 ACM
// 上传证书到 ACM/IAM
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
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.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)
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
if err != nil {

View 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
}

View File

@ -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
var listCertificatesNextToken *string = nil
listCertificatesMaxItems := int32(1000)
var listCertificatesMaxItems int32 = 1000
for {
select {
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{
CertificateArn: certSummary.CertificateArn,
}
@ -115,11 +115,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
} else {
oldCertPEM := aws.ToString(getCertificateResp.CertificateChain)
if oldCertPEM == "" {
oldCertPEM = aws.ToString(getCertificateResp.Certificate)
}
oldCertPEM := aws.ToString(getCertificateResp.Certificate)
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
if err != nil {
continue
@ -158,7 +154,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
}
return &uploader.UploadResult{
CertId: *importCertificateResp.CertificateArn,
CertId: aws.ToString(importCertificateResp.CertificateArn),
}, nil
}

View 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
}

View File

@ -35,6 +35,7 @@ import DeployNodeConfigFormAliyunVODConfig from "./DeployNodeConfigFormAliyunVOD
import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig";
import DeployNodeConfigFormAWSACMConfig from "./DeployNodeConfigFormAWSACMConfig";
import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig";
import DeployNodeConfigFormAWSIAMConfig from "./DeployNodeConfigFormAWSIAMConfig";
import DeployNodeConfigFormAzureKeyVaultConfig from "./DeployNodeConfigFormAzureKeyVaultConfig";
import DeployNodeConfigFormBaiduCloudAppBLBConfig from "./DeployNodeConfigFormBaiduCloudAppBLBConfig";
import DeployNodeConfigFormBaiduCloudBLBConfig from "./DeployNodeConfigFormBaiduCloudBLBConfig";
@ -238,6 +239,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.AWS_IAM:
return <DeployNodeConfigFormAWSIAMConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:

View File

@ -1,11 +1,12 @@
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 { z } from "zod";
type DeployNodeConfigFormAWSCloudFrontConfigFieldValues = Nullish<{
region: string;
distributionId: string;
certificateSource: string;
}>;
export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
@ -17,7 +18,9 @@ export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
};
const initFormModel = (): DeployNodeConfigFormAWSCloudFrontConfigFieldValues => {
return {};
return {
certificateSource: "ACM",
};
};
const DeployNodeConfigFormAWSCloudFrontConfig = ({
@ -30,15 +33,9 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
const { t } = useTranslation();
const formSchema = z.object({
region: z
.string({ message: t("workflow_node.deploy.form.aws_cloudfront_region.placeholder") })
.nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.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(),
region: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder")),
distributionId: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")),
certificateSource: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")),
});
const formRule = createSchemaFieldRule(formSchema);
@ -72,6 +69,17 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
>
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")} />
</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>
);
};

View File

@ -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;

View File

@ -389,6 +389,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`,
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`,
AWS_IAM: `${ACCESS_PROVIDERS.AWS}-iam`,
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
BAIDUCLOUD_APPBLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-appblb`,
BAIDUCLOUD_BLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-blb`,
@ -561,6 +562,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud.webhost", DEPLOYMENT_CATEGORIES.WEBSITE],
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOYMENT_CATEGORIES.CDN],
[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.BUNNY_CDN, "provider.bunny.cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOYMENT_CATEGORIES.CDN],

View File

@ -27,6 +27,7 @@
"provider.aws": "AWS",
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
"provider.aws.cloudfront": "AWS - CloudFront",
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
"provider.aws.route53": "AWS - Route53",
"provider.azure": "Azure",
"provider.azure.dns": "Azure - DNS",

View File

@ -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.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_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.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>",

View File

@ -27,6 +27,7 @@
"provider.aws": "AWS",
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
"provider.aws.cloudfront": "AWS - CloudFront",
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
"provider.aws.route53": "AWS - Route53",
"provider.azure": "Azure",
"provider.azure.dns": "Azure - DNS",

View File

@ -296,6 +296,15 @@
"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.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.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>",