From 22d971db4be12037a4bd84ef2cc162ccf9f8e465 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 20 Feb 2025 10:13:02 +0800 Subject: [PATCH] feat: add jdcloud live video deployer --- README.md | 2 +- README_EN.md | 2 +- internal/deployer/providers.go | 11 ++- internal/domain/provider.go | 1 + .../providers/jdcloud-live/jdcloud_live.go | 74 ++++++++++++++++++ .../jdcloud-live/jdcloud_live_test.go | 75 +++++++++++++++++++ internal/pkg/vendors/gname-sdk/models.go | 2 +- ui/src/components/Version.tsx | 4 +- .../workflow/node/DeployNodeConfigForm.tsx | 3 + .../DeployNodeConfigFormJDCloudLiveConfig.tsx | 65 ++++++++++++++++ ui/src/domain/provider.ts | 2 + ui/src/hooks/useVersionChecker.ts | 4 +- ui/src/i18n/locales/en/nls.provider.json | 1 + .../i18n/locales/en/nls.workflow.nodes.json | 7 +- ui/src/i18n/locales/zh/nls.provider.json | 1 + .../i18n/locales/zh/nls.workflow.nodes.json | 3 + 16 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go create mode 100644 internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx diff --git a/README.md b/README.md index 10051a19..f744e1fe 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ make local.run | [百度智能云](https://cloud.baidu.com/) | 可部署到百度智能云 CDN 等服务 | | [华为云](https://www.huaweicloud.com/) | 可部署到华为云 CDN、ELB、WAF 等服务 | | [火山引擎](https://www.volcengine.com/) | 可部署到火山引擎 TOS、CDN、DCDN、CLB、ImageX、Live 等服务 | -| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN | +| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、视频直播等服务 | | [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN、直播云等服务 | | [白山云](https://www.baishan.com/) | 可部署到白山云 CDN | | [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN | diff --git a/README_EN.md b/README_EN.md index b92a6982..05003b60 100644 --- a/README_EN.md +++ b/README_EN.md @@ -129,7 +129,7 @@ The following hosting providers are supported: | [Baidu AI Cloud](https://intl.cloud.baidu.com/) | Supports deployment to Baidu AI CLoud CDN | | [Huawei Cloud](https://www.huaweicloud.com/) | Supports deployment to Huawei Cloud CDN, ELB, WAF | | [Volcengine](https://www.volcengine.com/) | Supports deployment to Volcengine TOS, CDN, DCDN, CLB, ImageX, Live | -| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN | +| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, Live Video | | [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN, Pili | | [Baishan Cloud](https://intl.baishancloud.com/) | Supports deployment to Baishan Cloud CDN | | [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN | diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index c23dbed0..9210a7e5 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -31,6 +31,7 @@ import ( pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb" pHuaweiCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-waf" pJDCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-cdn" + pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" pLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local" pQiniuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-cdn" @@ -416,7 +417,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeJDCloudCDN: + case domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive: { access := domain.AccessConfigForJDCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -432,6 +433,14 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { }) return deployer, err + case domain.DeployProviderTypeJDCloudLive: + deployer, err := pJDCloudLive.NewDeployer(&pJDCloudLive.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + default: break } diff --git a/internal/domain/provider.go b/internal/domain/provider.go index a61a0c54..b39af710 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -128,6 +128,7 @@ const ( DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf") DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn") + DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live") DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") DeployProviderTypeLocal = DeployProviderType("local") DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go new file mode 100644 index 00000000..42a474de --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go @@ -0,0 +1,74 @@ +package jdcloudlive + +import ( + "context" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdLiveApi "github.com/jdcloud-api/jdcloud-sdk-go/services/live/apis" + jdLiveClient "github.com/jdcloud-api/jdcloud-sdk-go/services/live/client" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 直播播放域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdLiveClient.LiveClient +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 设置直播证书 + // REF: https://docs.jdcloud.com/cn/live-video/api/setlivedomaincertificate + setLiveDomainCertificateReq := jdLiveApi.NewSetLiveDomainCertificateRequest(d.config.Domain, "on") + setLiveDomainCertificateReq.SetCert(certPem) + setLiveDomainCertificateReq.SetKey(privkeyPem) + setLiveDomainCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'") + } else { + d.logger.Logt("已设置直播证书", setLiveDomainCertificateResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdLiveClient.LiveClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdLiveClient.NewLiveClient(clientCredentials) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go new file mode 100644 index 00000000..510198cc --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go @@ -0,0 +1,75 @@ +package jdcloudlive_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_live_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/vendors/gname-sdk/models.go b/internal/pkg/vendors/gname-sdk/models.go index c7ade452..997fb2c4 100644 --- a/internal/pkg/vendors/gname-sdk/models.go +++ b/internal/pkg/vendors/gname-sdk/models.go @@ -29,7 +29,7 @@ type AddDomainResolutionRequest struct { type AddDomainResolutionResponse struct { baseResponse - Data int `json:"data"` + Data string `json:"data"` } type ModifyDomainResolutionRequest struct { diff --git a/ui/src/components/Version.tsx b/ui/src/components/Version.tsx index 3d736271..e9245b1c 100644 --- a/ui/src/components/Version.tsx +++ b/ui/src/components/Version.tsx @@ -14,7 +14,7 @@ export type VersionProps = { const Version = ({ className, style }: VersionProps) => { const { t } = useTranslation(); - const { data: hasNewVersions } = useVersionChecker(); + const { hasNewVersion } = useVersionChecker(); return ( @@ -27,7 +27,7 @@ const Version = ({ className, style }: VersionProps) => { - + {version} diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 824a0cfe..3ae859b6 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -39,6 +39,7 @@ import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaw import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig"; import DeployNodeConfigFormHuaweiCloudWAFConfig from "./DeployNodeConfigFormHuaweiCloudWAFConfig"; import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig"; +import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig"; import DeployNodeConfigFormQiniuCDNConfig from "./DeployNodeConfigFormQiniuCDNConfig"; @@ -181,6 +182,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOY_PROVIDERS.JDCLOUD_CDN: return ; + case DEPLOY_PROVIDERS.JDCLOUD_LIVE: + return ; case DEPLOY_PROVIDERS.KUBERNETES_SECRET: return ; case DEPLOY_PROVIDERS.LOCAL: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx new file mode 100644 index 00000000..5e13f5eb --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudLiveConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormJDCloudLiveConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudLiveConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudLiveConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormJDCloudLiveConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudLiveConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudLiveConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.jdcloud_live_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormJDCloudLiveConfig; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index c4757c22..a2b09baf 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -222,6 +222,7 @@ export const DEPLOY_PROVIDERS = Object.freeze({ HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`, HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`, JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`, + JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`, KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`, LOCAL: `${ACCESS_PROVIDERS.LOCAL}`, QINIU_CDN: `${ACCESS_PROVIDERS.QINIU}-cdn`, @@ -309,6 +310,7 @@ export const deployProvidersMap: Map void; }; @@ -62,7 +62,7 @@ const useVersionChecker = () => { ); return { - data: !!data, + hasNewVersion: !!data, check: refresh, }; }; diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index b95f40b3..cccd18c2 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -56,6 +56,7 @@ "provider.jdcloud": "JD Cloud", "provider.jdcloud.cdn": "JD Cloud - CDN (Content Delivery Network)", "provider.jdcloud.dns": "JD Cloud - DNS", + "provider.jdcloud.live": "JD Cloud - Live Video", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.local": "Local deployment", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 90a48bf8..2ce432d3 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -270,6 +270,9 @@ "workflow_node.deploy.form.jdcloud_cdn_domain.label": "JD Cloud CDN domain", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "Please enter JD Cloud CDN domain name", "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "For more information, see https://cdn-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_live_domain.label": "JD Cloud Live Video play domain", + "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "Please enter JD Cloud Live Video play domain name", + "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "For more information, see https://docs.jdcloud.com/en/live-video/domain-management", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.placeholder": "Please enter Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.tooltip": "For more information, see https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", @@ -401,8 +404,8 @@ "workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS domain", "workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS domain name", "workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "For more information, see https://console.tencentcloud.com/cos", - "workflow_node.deploy.form.tencentcloud_css_domain.label": "Tencent Cloud CSS playing domain", - "workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "Please enter Tencent Cloud CSS playing domain name", + "workflow_node.deploy.form.tencentcloud_css_domain.label": "Tencent Cloud CSS play domain", + "workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "Please enter Tencent Cloud CSS play domain name", "workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "For more information, see https://console.cloud.tencent.com/live/livestat", "workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "Tencent Cloud ECDN domain", "workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "Please enter Tencent Cloud ECDN domain name", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 87edb0f1..0d8e2911 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -56,6 +56,7 @@ "provider.jdcloud": "京东云", "provider.jdcloud.cdn": "京东云 - 内容分发网络 CDN", "provider.jdcloud.dns": "京东云 - 云解析 DNS", + "provider.jdcloud.live": "京东云 - 视频直播", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.local": "本地部署", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 3dcf31a5..a7331d08 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -270,6 +270,9 @@ "workflow_node.deploy.form.jdcloud_cdn_domain.label": "京东云 CDN 加速域名", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "请输入京东云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_live_domain.label": "京东云视频直播播放域名", + "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "请输入京东云视频直播播放域名", + "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/live-video/domain-management", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.placeholder": "请输入 Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces/",