From e87ac72281efeda5b337163e107320ccae212726 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 14 Jan 2025 21:31:10 +0800 Subject: [PATCH] feat: add ucloud ucdn deployer --- internal/deployer/providers.go | 23 +++ internal/domain/access.go | 5 + internal/domain/provider.go | 2 + .../providers/ucloud-ucdn/ucloud_ucdn.go | 132 ++++++++++++++++++ .../providers/ucloud-ucdn/ucloud_ucdn_test.go | 75 ++++++++++ migrations/1736861196_updated_access.go | 113 +++++++++++++++ ui/public/imgs/providers/ucloud.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormUCloudConfig.tsx | 90 ++++++++++++ .../provider/ApplyDNSProviderPicker.tsx | 2 +- .../provider/DeployProviderPicker.tsx | 2 +- .../components/workflow/node/DeployNode.tsx | 8 +- .../workflow/node/DeployNodeConfigForm.tsx | 3 + .../DeployNodeConfigFormUCloudUCDNConfig.tsx | 63 +++++++++ .../components/workflow/node/NotifyNode.tsx | 8 +- ui/src/domain/access.ts | 7 + ui/src/domain/provider.ts | 4 + ui/src/i18n/locales/en/nls.access.json | 9 ++ ui/src/i18n/locales/en/nls.common.json | 2 + .../i18n/locales/en/nls.workflow.nodes.json | 3 + ui/src/i18n/locales/zh/nls.access.json | 9 ++ ui/src/i18n/locales/zh/nls.common.json | 2 + .../i18n/locales/zh/nls.workflow.nodes.json | 3 + 23 files changed, 559 insertions(+), 10 deletions(-) create mode 100644 internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go create mode 100644 internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go create mode 100644 migrations/1736861196_updated_access.go create mode 100644 ui/public/imgs/providers/ucloud.svg create mode 100644 ui/src/components/access/AccessFormUCloudConfig.tsx create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormUCloudUCDNConfig.tsx diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 35a818f4..31d84baf 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -27,6 +27,7 @@ import ( providerTencentCloudCSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-css" providerTencentCloudECDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ecdn" providerTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-eo" + providerUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn" providerVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn" providerVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-clb" providerVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-dcdn" @@ -352,6 +353,28 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, } } + case domain.DeployProviderTypeUCloudUCDN: + { + access := domain.AccessConfigForUCloud{} + if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil { + return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err) + } + + switch options.Provider { + case domain.DeployProviderTypeUCloudUCDN: + deployer, err := providerUCloudUCDN.NewWithLogger(&providerUCloudUCDN.UCloudUCDNDeployerConfig{ + PrivateKey: access.PrivateKey, + PublicKey: access.PublicKey, + ProjectId: maps.GetValueAsString(options.ProviderDeployConfig, "projectId"), + DomainId: maps.GetValueAsString(options.ProviderDeployConfig, "domainId"), + }, logger) + return deployer, logger, err + + default: + break + } + } + case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS: { access := domain.AccessConfigForVolcEngine{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 6ee3b052..e1979ea3 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -115,6 +115,11 @@ type AccessConfigForTencentCloud struct { SecretKey string `json:"secretKey"` } +type AccessConfigForUCloud struct { + PrivateKey string `json:"privateKey"` + PublicKey string `json:"publicKey"` +} + type AccessConfigForVolcEngine struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 7d3f2464..d5b844a9 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -27,6 +27,7 @@ const ( AccessProviderTypeQiniu = AccessProviderType("qiniu") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") + AccessProviderTypeUCloud = AccessProviderType("ucloud") AccessProviderTypeVolcEngine = AccessProviderType("volcengine") AccessProviderTypeWebhook = AccessProviderType("webhook") ) @@ -92,6 +93,7 @@ const ( DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css") DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") + DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go new file mode 100644 index 00000000..ec127f41 --- /dev/null +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go @@ -0,0 +1,132 @@ +package uclouducdn + +import ( + "context" + "errors" + "strconv" + + xerrors "github.com/pkg/errors" + uCdn "github.com/ucloud/ucloud-sdk-go/services/ucdn" + usdk "github.com/ucloud/ucloud-sdk-go/ucloud" + uAuth "github.com/ucloud/ucloud-sdk-go/ucloud/auth" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploaderSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" +) + +type UCloudUCDNDeployerConfig struct { + // 优刻得 API 私钥。 + PrivateKey string `json:"privateKey"` + // 优刻得 API 公钥。 + PublicKey string `json:"publicKey"` + // 优刻得项目 ID。 + ProjectId string `json:"projectId,omitempty"` + // 加速域名 ID。 + DomainId string `json:"domainId"` +} + +type UCloudUCDNDeployer struct { + config *UCloudUCDNDeployerConfig + logger logger.Logger + sdkClient *uCdn.UCDNClient + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*UCloudUCDNDeployer)(nil) + +func New(config *UCloudUCDNDeployerConfig) (*UCloudUCDNDeployer, error) { + return NewWithLogger(config, logger.NewNilLogger()) +} + +func NewWithLogger(config *UCloudUCDNDeployerConfig, logger logger.Logger) (*UCloudUCDNDeployer, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + if logger == nil { + return nil, errors.New("logger is nil") + } + + client, err := createSdkClient(config.PrivateKey, config.PublicKey) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploaderSsl.New(&uploaderSsl.UCloudUSSLUploaderConfig{ + PrivateKey: config.PrivateKey, + PublicKey: config.PublicKey, + ProjectId: config.ProjectId, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &UCloudUCDNDeployer{ + logger: logger, + config: config, + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *UCloudUCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书到 USSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } + + d.logger.Logt("certificate file uploaded", upres) + + // 获取加速域名配置 + // REF: https://docs.ucloud.cn/api/ucdn-api/get_ucdn_domain_config + getUcdnDomainConfigReq := d.sdkClient.NewGetUcdnDomainConfigRequest() + getUcdnDomainConfigReq.DomainId = []string{d.config.DomainId} + if d.config.ProjectId != "" { + getUcdnDomainConfigReq.ProjectId = usdk.String(d.config.ProjectId) + } + getUcdnDomainConfigResp, err := d.sdkClient.GetUcdnDomainConfig(getUcdnDomainConfigReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.GetUcdnDomainConfig'") + } else if len(getUcdnDomainConfigResp.DomainList) == 0 { + return nil, errors.New("no domain found") + } + + d.logger.Logt("已查询到加速域名配置", getUcdnDomainConfigResp) + + // 更新 HTTPS 加速配置 + // REF: https://docs.ucloud.cn/api/ucdn-api/update_ucdn_domain_https_config_v2 + certId, _ := strconv.Atoi(upres.CertId) + updateUcdnDomainHttpsConfigV2Req := d.sdkClient.NewUpdateUcdnDomainHttpsConfigV2Request() + updateUcdnDomainHttpsConfigV2Req.DomainId = usdk.String(d.config.DomainId) + updateUcdnDomainHttpsConfigV2Req.HttpsStatusCn = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusCn) + updateUcdnDomainHttpsConfigV2Req.HttpsStatusAbroad = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusAbroad) + updateUcdnDomainHttpsConfigV2Req.HttpsStatusAbroad = usdk.String(getUcdnDomainConfigResp.DomainList[0].HttpsStatusAbroad) + updateUcdnDomainHttpsConfigV2Req.CertId = usdk.Int(certId) + updateUcdnDomainHttpsConfigV2Req.CertName = usdk.String(upres.CertName) + updateUcdnDomainHttpsConfigV2Req.CertType = usdk.String("ussl") + if d.config.ProjectId != "" { + updateUcdnDomainHttpsConfigV2Req.ProjectId = usdk.String(d.config.ProjectId) + } + updateUcdnDomainHttpsConfigV2Resp, err := d.sdkClient.UpdateUcdnDomainHttpsConfigV2(updateUcdnDomainHttpsConfigV2Req) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.UpdateUcdnDomainHttpsConfigV2'") + } + + d.logger.Logt("已更新 HTTPS 加速配置", updateUcdnDomainHttpsConfigV2Resp) + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(privateKey, publicKey string) (*uCdn.UCDNClient, error) { + cfg := usdk.NewConfig() + + credential := uAuth.NewCredential() + credential.PrivateKey = privateKey + credential.PublicKey = publicKey + + client := uCdn.NewClient(&cfg, &credential) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go new file mode 100644 index 00000000..ba285d71 --- /dev/null +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go @@ -0,0 +1,75 @@ +package uclouducdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fPrivateKey string + fPublicKey string + fDomainId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_UCLOUDCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fPrivateKey, argsPrefix+"PRIVATEKEY", "", "") + flag.StringVar(&fPublicKey, argsPrefix+"PUBLICKEY", "", "") + flag.StringVar(&fDomainId, argsPrefix+"DOMAINID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./ucloud_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_UCLOUDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_UCLOUDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_UCLOUDCDN_PRIVATEKEY="your-private-key" \ + --CERTIMATE_DEPLOYER_UCLOUDCDN_PUBLICKEY="your-public-key" \ + --CERTIMATE_DEPLOYER_UCLOUDCDN_DOMAINID="your-domain-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("PRIVATEKEY: %v", fPrivateKey), + fmt.Sprintf("PUBLICKEY: %v", fPublicKey), + fmt.Sprintf("DOMAIN: %v", fDomainId), + }, "\n")) + + deployer, err := provider.New(&provider.UCloudUCDNDeployerConfig{ + AccessKeyId: fPrivateKey, + AccessKeySecret: fPublicKey, + DomainId: fDomainId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/migrations/1736861196_updated_access.go b/migrations/1736861196_updated_access.go new file mode 100644 index 00000000..8d6023bc --- /dev/null +++ b/migrations/1736861196_updated_access.go @@ -0,0 +1,113 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/models/schema" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // update + edit_provider := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "hwy7m03o", + "name": "provider", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "acmehttpreq", + "aliyun", + "aws", + "azure", + "baiducloud", + "byteplus", + "cloudflare", + "dogecloud", + "godaddy", + "huaweicloud", + "k8s", + "local", + "namedotcom", + "namesilo", + "powerdns", + "qiniu", + "ssh", + "tencentcloud", + "ucloud", + "volcengine", + "webhook" + ] + } + }`), edit_provider); err != nil { + return err + } + collection.Schema.AddField(edit_provider) + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // update + edit_provider := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "hwy7m03o", + "name": "provider", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "acmehttpreq", + "aliyun", + "aws", + "azure", + "baiducloud", + "byteplus", + "cloudflare", + "dogecloud", + "godaddy", + "huaweicloud", + "k8s", + "local", + "namedotcom", + "namesilo", + "powerdns", + "qiniu", + "ssh", + "tencentcloud", + "volcengine", + "webhook" + ] + } + }`), edit_provider); err != nil { + return err + } + collection.Schema.AddField(edit_provider) + + return dao.SaveCollection(collection) + }) +} diff --git a/ui/public/imgs/providers/ucloud.svg b/ui/public/imgs/providers/ucloud.svg new file mode 100644 index 00000000..9e246c65 --- /dev/null +++ b/ui/public/imgs/providers/ucloud.svg @@ -0,0 +1 @@ + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 96f804ed..b398013a 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -27,6 +27,7 @@ import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig"; +import AccessFormUCloudConfig from "./AccessFormUCloudConfig"; import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig"; import AccessFormWebhookConfig from "./AccessFormWebhookConfig"; @@ -118,6 +119,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.TENCENTCLOUD: return ; + case ACCESS_PROVIDERS.UCLOUD: + return ; case ACCESS_PROVIDERS.VOLCENGINE: return ; case ACCESS_PROVIDERS.WEBHOOK: diff --git a/ui/src/components/access/AccessFormUCloudConfig.tsx b/ui/src/components/access/AccessFormUCloudConfig.tsx new file mode 100644 index 00000000..f230e7c2 --- /dev/null +++ b/ui/src/components/access/AccessFormUCloudConfig.tsx @@ -0,0 +1,90 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForUCloud } from "@/domain/access"; + +type AccessFormUCloudConfigFieldValues = Nullish; + +export type AccessFormUCloudConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormUCloudConfigFieldValues; + onValuesChange?: (values: AccessFormUCloudConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormUCloudConfigFieldValues => { + return { + privateKey: "", + publicKey: "", + }; +}; + +const AccessFormUCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUCloudConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + privateKey: z + .string() + .trim() + .min(1, t("access.form.ucloud_private_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })), + publicKey: z + .string() + .min(1, t("access.form.ucloud_public_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + projectId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormUCloudConfig; diff --git a/ui/src/components/provider/ApplyDNSProviderPicker.tsx b/ui/src/components/provider/ApplyDNSProviderPicker.tsx index e72935cc..88d9ebc6 100644 --- a/ui/src/components/provider/ApplyDNSProviderPicker.tsx +++ b/ui/src/components/provider/ApplyDNSProviderPicker.tsx @@ -51,7 +51,7 @@ const ApplyDNSProviderPicker = ({ className, style, placeholder, onSelect }: App > - {t(provider.name)} + {t(provider.name)} diff --git a/ui/src/components/provider/DeployProviderPicker.tsx b/ui/src/components/provider/DeployProviderPicker.tsx index 29ad5f46..159bd53a 100644 --- a/ui/src/components/provider/DeployProviderPicker.tsx +++ b/ui/src/components/provider/DeployProviderPicker.tsx @@ -51,7 +51,7 @@ const DeployProviderPicker = ({ className, style, placeholder, onSelect }: Deplo > - {t(provider.name)} + {t(provider.name)} diff --git a/ui/src/components/workflow/node/DeployNode.tsx b/ui/src/components/workflow/node/DeployNode.tsx index 8a211a67..62a81470 100644 --- a/ui/src/components/workflow/node/DeployNode.tsx +++ b/ui/src/components/workflow/node/DeployNode.tsx @@ -1,6 +1,6 @@ import { memo, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Space, Typography } from "antd"; +import { Avatar, Flex, Typography } from "antd"; import { produce } from "immer"; import { deployProvidersMap } from "@/domain/provider"; @@ -45,10 +45,10 @@ const DeployNode = ({ node, disabled }: DeployNodeProps) => { const config = (node.config as WorkflowNodeConfigForDeploy) ?? {}; const provider = deployProvidersMap.get(config.provider); return ( - + - {t(provider?.name ?? "")} - + {t(provider?.name ?? "")} + ); }, [node]); diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 2643b2c7..99c427f5 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -37,6 +37,7 @@ import DeployNodeConfigFormTencentCloudCOSConfig from "./DeployNodeConfigFormTen import DeployNodeConfigFormTencentCloudCSSConfig from "./DeployNodeConfigFormTencentCloudCSSConfig.tsx"; import DeployNodeConfigFormTencentCloudECDNConfig from "./DeployNodeConfigFormTencentCloudECDNConfig.tsx"; import DeployNodeConfigFormTencentCloudEOConfig from "./DeployNodeConfigFormTencentCloudEOConfig.tsx"; +import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx"; import DeployNodeConfigFormVolcEngineCDNConfig from "./DeployNodeConfigFormVolcEngineCDNConfig.tsx"; import DeployNodeConfigFormVolcEngineCLBConfig from "./DeployNodeConfigFormVolcEngineCLBConfig.tsx"; import DeployNodeConfigFormVolcEngineDCDNConfig from "./DeployNodeConfigFormVolcEngineDCDNConfig.tsx"; @@ -156,6 +157,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOY_PROVIDERS.TENCENTCLOUD_EO: return ; + case DEPLOY_PROVIDERS.UCLOUD_UCDN: + return ; case DEPLOY_PROVIDERS.VOLCENGINE_CDN: return ; case DEPLOY_PROVIDERS.VOLCENGINE_CLB: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUCloudUCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUCloudUCDNConfig.tsx new file mode 100644 index 00000000..41c059fc --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormUCloudUCDNConfig.tsx @@ -0,0 +1,63 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormUCloudUCDNConfigFieldValues = Nullish<{ + domainId: string; +}>; + +export type DeployNodeConfigFormUCloudUCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormUCloudUCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormUCloudUCDNConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormUCloudUCDNConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormUCloudUCDNConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormUCloudUCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domainId: z + .string({ message: t("workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormUCloudUCDNConfig; diff --git a/ui/src/components/workflow/node/NotifyNode.tsx b/ui/src/components/workflow/node/NotifyNode.tsx index 70233c50..1b739e8f 100644 --- a/ui/src/components/workflow/node/NotifyNode.tsx +++ b/ui/src/components/workflow/node/NotifyNode.tsx @@ -1,6 +1,6 @@ import { memo, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Typography } from "antd"; +import { Flex, Typography } from "antd"; import { produce } from "immer"; import { notifyChannelsMap } from "@/domain/settings"; @@ -40,12 +40,12 @@ const NotifyNode = ({ node, disabled }: NotifyNodeProps) => { const config = (node.config as WorkflowNodeConfigForNotify) ?? {}; const channel = notifyChannelsMap.get(config.channel as string); return ( -
- {t(channel?.name ?? " ")} + + {t(channel?.name ?? " ")} {config.subject ?? ""} -
+ ); }, [node]); diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 33002d83..01d97d04 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -26,6 +26,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForQiniu | AccessConfigForSSH | AccessConfigForTencentCloud + | AccessConfigForUCloud | AccessConfigForVolcEngine | AccessConfigForWebhook ); @@ -125,6 +126,12 @@ export type AccessConfigForTencentCloud = { secretKey: string; }; +export type AccessConfigForUCloud = { + privateKey: string; + publicKey: string; + projectId?: string; +}; + export type AccessConfigForVolcEngine = { accessKeyId: string; secretAccessKey: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 0874fbcc..a9876aad 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -22,6 +22,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ QINIU: "qiniu", SSH: "ssh", TENCENTCLOUD: "tencentcloud", + UCLOUD: "ucloud", VOLCENGINE: "volcengine", WEBHOOK: "webhook", } as const); @@ -61,6 +62,7 @@ export const accessProvidersMap: Map [ type, { diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 41f738f8..7f9ead1d 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -121,6 +121,15 @@ "access.form.tencentcloud_secret_key.label": "Tencent Cloud SecretKey", "access.form.tencentcloud_secret_key.placeholder": "Please enter Tencent Cloud SecretKey", "access.form.tencentcloud_secret_key.tooltip": "For more information, see https://cloud.tencent.com/document/product/598/40488?lang=en", + "access.form.ucloud_private_key.label": "UCloud API private key", + "access.form.ucloud_private_key.placeholder": "Please enter UCloud API private key", + "access.form.ucloud_private_key.tooltip": "For more information, see https://console.ucloud.cn/uaccount/api_manage", + "access.form.ucloud_public_key.label": "UCloud API public key", + "access.form.ucloud_public_key.placeholder": "Please enter UCloud API public key", + "access.form.ucloud_public_key.tooltip": "For more information, see https://console.ucloud.cn/uaccount/api_manage", + "access.form.ucloud_project_id.label": "UCloud project ID (Optional)", + "access.form.ucloud_project_id.placeholder": "Please enter UCloud project ID", + "access.form.ucloud_project_id.tooltip": "For more information, see https://console.ucloud.cn/uaccount/iam/project_manage", "access.form.volcengine_access_key_id.label": "VolcEngine AccessKeyId", "access.form.volcengine_access_key_id.placeholder": "Please enter VolcEngine AccessKeyId", "access.form.volcengine_access_key_id.tooltip": "For more information, see https://www.volcengine.com/docs/6291/216571", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index c8c37a7e..0b673e32 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -78,6 +78,8 @@ "common.provider.tencentcloud.dns": "Tencent Cloud - Domain Name Service (DNS)", "common.provider.tencentcloud.ecdn": "Tencent Cloud - Enterprise Content Delivery Network (ECDN)", "common.provider.tencentcloud.eo": "Tencent Cloud - EdgeOne", + "common.provider.ucloud": "UCloud", + "common.provider.ucloud.ucdn": "UCloud - UCloud Content Delivery Network (UCDN)", "common.provider.volcengine": "Volcengine", "common.provider.volcengine.cdn": "Volcengine - Content Delivery Network (CDN)", "common.provider.volcengine.clb": "Volcengine - Cloud Load Balancer (CLB)", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 0ec16538..bbebd5a7 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -301,6 +301,9 @@ "workflow_node.deploy.form.tencentcloud_eo_domain.label": "Tencent Cloud EdgeOne domain", "workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "Please enter Tencent Cloud EdgeOne domain name", "workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "For more information, see https://console.tencentcloud.com/edgeone", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "UCloud UCDN domain ID", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "Please enter UCloud UCDN domain ID", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "For more information, see https://console.ucloud.cn/ucdn", "workflow_node.deploy.form.volcengine_cdn_domain.label": "VolcEngine CDN domain", "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "Please enter VolcEngine CDN domain name", "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "For more information, see https://console.volcengine.com/cdn/homepage", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 342610af..e24c125b 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -121,6 +121,15 @@ "access.form.tencentcloud_secret_key.label": "腾讯云 SecretKey", "access.form.tencentcloud_secret_key.placeholder": "请输入腾讯云 SecretKey", "access.form.tencentcloud_secret_key.tooltip": "这是什么?请参阅 https://cloud.tencent.com/document/product/598/40488", + "access.form.ucloud_private_key.label": "优刻得 API 私钥", + "access.form.ucloud_private_key.placeholder": "请输入优刻得 API 私钥", + "access.form.ucloud_private_key.tooltip": "这是什么?请参阅 https://console.ucloud.cn/uaccount/api_manage", + "access.form.ucloud_public_key.label": "优刻得 API 公钥", + "access.form.ucloud_public_key.placeholder": "请输入优刻得 API 公钥", + "access.form.ucloud_public_key.tooltip": "这是什么?请参阅 https://console.ucloud.cn/uaccount/api_manage", + "access.form.ucloud_project_id.label": "优刻得项目 ID(可选)", + "access.form.ucloud_project_id.placeholder": "请输入优刻得项目 ID", + "access.form.ucloud_project_id.tooltip": "这是什么?请参阅 https://console.ucloud.cn/uaccount/iam/project_manage", "access.form.volcengine_access_key_id.label": "火山引擎 AccessKeyId", "access.form.volcengine_access_key_id.placeholder": "请输入火山引擎 AccessKeyId", "access.form.volcengine_access_key_id.tooltip": "这是什么?请参阅 https://www.volcengine.com/docs/6291/216571", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 0a7d93cf..c35f19a6 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -78,6 +78,8 @@ "common.provider.tencentcloud.dns": "腾讯云 - 云解析 DNS", "common.provider.tencentcloud.ecdn": "腾讯云 - 全站加速网络 ECDN", "common.provider.tencentcloud.eo": "腾讯云 - 边缘安全加速平台 EdgeOne", + "common.provider.ucloud": "优刻得", + "common.provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN", "common.provider.volcengine": "火山引擎", "common.provider.volcengine.cdn": "火山引擎 - 内容分发网络 CDN", "common.provider.volcengine.clb": "火山引擎 - 负载均衡 CLB", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index c06ff102..41450494 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -301,6 +301,9 @@ "workflow_node.deploy.form.tencentcloud_eo_domain.label": "腾讯云 EdgeOne 加速域名", "workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "请输入腾讯云 EdgeOne 加速域名", "workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "这是什么?请参阅 https://console.cloud.tencent.com/edgeone", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "优刻得 UCDN 域名 ID", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "请输入优刻得 UCDN 域名 ID", + "workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "这是什么?请参阅 https://console.ucloud.cn/ucdn", "workflow_node.deploy.form.volcengine_cdn_domain.label": "火山引擎 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "请输入火山引擎 CDN 加速域名", "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 https://console.volcengine.com/cdn/homepage

泛域名表示形式为:*.example.com",