diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index fec6d3bf..4562fda9 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -252,6 +252,7 @@ const (
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
DeploymentProviderTypeHuaweiCloudELB = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-elb")
DeploymentProviderTypeHuaweiCloudSCM = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-scm")
+ DeploymentProviderTypeHuaweiCloudOBS = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-obs")
DeploymentProviderTypeHuaweiCloudWAF = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-waf")
DeploymentProviderTypeJDCloudALB = DeploymentProviderType(AccessProviderTypeJDCloud + "-alb")
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")
diff --git a/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs.go b/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs.go
new file mode 100644
index 00000000..6e8c2fa5
--- /dev/null
+++ b/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs.go
@@ -0,0 +1,124 @@
+package huaweicloudobs
+
+import (
+ "bytes"
+ "context"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/sha1"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/certimate-go/certimate/pkg/core"
+)
+
+type SSLDeployerProviderConfig struct {
+ // 华为云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 华为云 SecretAccessKey。
+ SecretAccessKey string `json:"secretAccessKey"`
+ // 华为云 Bucket 对应的 Endpoint。
+ Endpoint string `json:"endpoint"`
+ // 华为云 OBS 桶名。
+ Bucket string `json:"bucket"`
+ // 自定义域名。
+ Domain string `json:"domain"`
+}
+
+type SSLDeployerProvider struct {
+ config *SSLDeployerProviderConfig
+ logger *slog.Logger
+}
+
+var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
+
+func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
+ if config == nil {
+ return nil, errors.New("the configuration of the ssl deployer provider is nil")
+ }
+
+ config.Endpoint = strings.TrimPrefix(strings.TrimPrefix(config.Endpoint, "http://"), "https://")
+
+ return &SSLDeployerProvider{
+ config: config,
+ logger: slog.Default(),
+ }, nil
+}
+
+func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
+ if logger == nil {
+ d.logger = slog.New(slog.DiscardHandler)
+ } else {
+ d.logger = logger
+ }
+}
+
+// REF: https://support.huaweicloud.com/usermanual-obs/obs_06_3200.html
+// REF: https://support.huaweicloud.com/api-obs/obs_04_0059.html
+func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
+ if d.config.Domain == "" {
+ return nil, fmt.Errorf("config `domain` is required")
+ }
+
+ url := fmt.Sprintf("https://%s.%s/?customdomain=%s", d.config.Bucket, d.config.Endpoint, d.config.Domain)
+ bodyXML := fmt.Sprintf(`
+
+ %s
+ %s
+ %s
+ %s
+`,
+ d.config.Bucket+"_"+d.config.Domain, certPEM, certPEM, privkeyPEM,
+ )
+
+ // 计算 Content-MD5(Base64 编码)
+ md5sum := md5.Sum([]byte(bodyXML))
+ contentMD5 := base64.StdEncoding.EncodeToString(md5sum[:])
+
+ // 日期
+ date := time.Now().UTC().Format(http.TimeFormat)
+
+ // 构造签名字符串
+ method := "PUT"
+ contentType := "application/xml"
+ canonicalizedResource := fmt.Sprintf("/%s/?customdomain=%s", d.config.Bucket, d.config.Domain)
+ stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s\n%s", method, contentMD5, contentType, date, canonicalizedResource)
+
+ // HMAC-SHA1 签名
+ h := hmac.New(sha1.New, []byte(d.config.SecretAccessKey))
+ h.Write([]byte(stringToSign))
+ signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+ // Authorization
+ authHeader := fmt.Sprintf("OBS %s:%s", d.config.AccessKeyId, signature)
+
+ // 创建请求
+ req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(bodyXML)))
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Date", date)
+ req.Header.Set("Authorization", authHeader)
+ req.Header.Set("Content-MD5", contentMD5)
+ req.Header.Set("Content-Type", contentType)
+
+ // 请求
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ body := new(bytes.Buffer)
+ body.ReadFrom(resp.Body)
+ return nil, fmt.Errorf("HTTP request failed with status %d: %s", resp.StatusCode, body.String())
+ }
+ return &core.SSLDeployResult{}, nil
+}
diff --git a/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs_test.go b/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs_test.go
new file mode 100644
index 00000000..a1d06f00
--- /dev/null
+++ b/pkg/core/ssl-deployer/providers/huaweicloud-obs/huaweicloud_obs_test.go
@@ -0,0 +1,85 @@
+package huaweicloudobs_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/huaweicloud-obs"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fSecretAccessKey string
+ fEndpoint string
+ fBucket string
+ fDomain string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
+ flag.StringVar(&fEndpoint, argsPrefix+"ENDPOINT", "", "")
+ flag.StringVar(&fBucket, argsPrefix+"BUCKET", "", "")
+ flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./huaweicloud_obs_test.go -args \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_SECRETACCESSKEY="your-secret-access-key" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_ENDPOINT="https://your-endpoint" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_BUCKET="your-bucket" \
+ --CERTIMATE_SSLDEPLOYER_HUAWEICLOUDCDN_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("SECRETACCESSKEY: %v", fSecretAccessKey),
+ fmt.Sprintf("ENDPOINT: %v", fEndpoint),
+ fmt.Sprintf("BUCKET: %v", fBucket),
+ fmt.Sprintf("DOMAIN: %v", fDomain),
+ }, "\n"))
+
+ deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
+ AccessKeyId: fAccessKeyId,
+ SecretAccessKey: fSecretAccessKey,
+ Endpoint: fEndpoint,
+ Bucket: fBucket,
+ Domain: fDomain,
+ })
+ if err != nil {
+ t.Errorf("err: %+v", err)
+ return
+ }
+
+ fInputCertData, _ := os.ReadFile(fInputCertPath)
+ fInputKeyData, _ := os.ReadFile(fInputKeyPath)
+ res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
+ if err != nil {
+ t.Errorf("err: %+v", err)
+ return
+ }
+
+ t.Logf("ok: %v", res)
+ })
+}
diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderHuaweiCloudOBS.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderHuaweiCloudOBS.tsx
new file mode 100644
index 00000000..4406cf1f
--- /dev/null
+++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderHuaweiCloudOBS.tsx
@@ -0,0 +1,84 @@
+import { getI18n, useTranslation } from "react-i18next";
+import { Form, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { validDomainName } from "@/utils/validators";
+
+import { useFormNestedFieldsContext } from "./_context";
+
+const BizDeployNodeConfigFieldsProviderHuaweiCloudOBS = () => {
+ const { i18n, t } = useTranslation();
+
+ const { parentNamePath } = useFormNestedFieldsContext();
+ const formSchema = z.object({
+ [parentNamePath]: getSchema({ i18n }),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+ const initialValues = getInitialValues();
+
+ return (
+ <>
+
}
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+ >
+ );
+};
+
+const getInitialValues = (): Nullish>> => {
+ return {
+ endpoint: "",
+ bucket: "",
+ domain: "",
+ };
+};
+
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
+ const { t } = i18n;
+
+ return z.object({
+ endpoint: z
+ .string(t("workflow_node.deploy.form.huaweicloud_obs_endpoint.placeholder"))
+ .nonempty(t("workflow_node.deploy.form.huaweicloud_obs_endpoint.placeholder")),
+ bucket: z
+ .string(t("workflow_node.deploy.form.huaweicloud_obs_bucket.placeholder"))
+ .nonempty(t("workflow_node.deploy.form.huaweicloud_obs_bucket.placeholder")),
+ domain: z
+ .string(t("workflow_node.deploy.form.huaweicloud_obs_domain.placeholder"))
+ .refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
+ });
+};
+
+const _default = Object.assign(BizDeployNodeConfigFieldsProviderHuaweiCloudOBS, {
+ getInitialValues,
+ getSchema,
+});
+
+export default _default;
diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigForm.tsx
index 005618d2..6cd8f9f7 100644
--- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigForm.tsx
@@ -64,6 +64,7 @@ import BizDeployNodeConfigFieldsProviderGcoreCDN from "./BizDeployNodeConfigFiel
import BizDeployNodeConfigFieldsProviderGoEdge from "./BizDeployNodeConfigFieldsProviderGoEdge";
import BizDeployNodeConfigFieldsProviderHuaweiCloudCDN from "./BizDeployNodeConfigFieldsProviderHuaweiCloudCDN";
import BizDeployNodeConfigFieldsProviderHuaweiCloudELB from "./BizDeployNodeConfigFieldsProviderHuaweiCloudELB";
+import BizDeployNodeConfigFieldsProviderHuaweiCloudOBS from "./BizDeployNodeConfigFieldsProviderHuaweiCloudOBS";
import BizDeployNodeConfigFieldsProviderHuaweiCloudWAF from "./BizDeployNodeConfigFieldsProviderHuaweiCloudWAF";
import BizDeployNodeConfigFieldsProviderJDCloudALB from "./BizDeployNodeConfigFieldsProviderJDCloudALB";
import BizDeployNodeConfigFieldsProviderJDCloudCDN from "./BizDeployNodeConfigFieldsProviderJDCloudCDN";
@@ -322,6 +323,9 @@ const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProp
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_ELB: {
return BizDeployNodeConfigFieldsProviderHuaweiCloudELB;
}
+ case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_OBS: {
+ return BizDeployNodeConfigFieldsProviderHuaweiCloudOBS;
+ }
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF: {
return BizDeployNodeConfigFieldsProviderHuaweiCloudWAF;
}
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index 8a934aa9..e5370d3c 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -485,6 +485,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`,
HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`,
HUAWEICLOUD_SCM: `${ACCESS_PROVIDERS.HUAWEICLOUD}-scm`,
+ HUAWEICLOUD_OBS: `${ACCESS_PROVIDERS.HUAWEICLOUD}-obs`,
HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`,
JDCLOUD_ALB: `${ACCESS_PROVIDERS.JDCLOUD}-alb`,
JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`,
@@ -605,6 +606,7 @@ export const deploymentProvidersMap: Mapcrontab rules.",
"workflow_node.start.form.trigger_cron.help": "Expected execution time for the last 5 times (the actual time zone is based on the server):",
"workflow_node.start.form.trigger_cron.guide": "If you have multiple workflows, it is recommended to set them to run at different times of the day instead of always running at a specific time. And please don't always set it to midnight every day to avoid spikes in traffic.
Reference links:
1. Let’s Encrypt rate limits
2. Why should my Let’s Encrypt (ACME) client run at a random time?",
-
"workflow_node.apply.label": "Request certificate",
"workflow_node.apply.help": "Apply for SSL certificate issuance from the certificate authority.",
"workflow_node.apply.default_name": "Application",
@@ -126,7 +125,6 @@
"workflow_node.apply.form.skip_before_expiry_days.suffix": ", skip to re-apply.",
"workflow_node.apply.form.skip_before_expiry_days.unit": "days",
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "Be careful not to exceed the validity period limit of the issued certificate, otherwise the certificate may never be renewed.",
-
"workflow_node.upload.label": "Upload certificate",
"workflow_node.upload.help": "Upload the user's existing SSL certificate.",
"workflow_node.upload.default_name": "Uploading",
@@ -137,7 +135,6 @@
"workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
"workflow_node.upload.form.private_key.label": "Private key (PEM format)",
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
-
"workflow_node.monitor.label": "Monitor certificate",
"workflow_node.monitor.help": "Obtain the SSL certificate of the website through HTTPS protocol.",
"workflow_node.monitor.default_name": "Monitoring",
@@ -152,7 +149,6 @@
"workflow_node.monitor.form.domain.help": "Notes: It is only required when the host is an IP address.",
"workflow_node.monitor.form.request_path.label": "Request path (Optional)",
"workflow_node.monitor.form.request_path.placeholder": "Please enter request path",
-
"workflow_node.deploy.label": "Deploy certificate",
"workflow_node.deploy.help": "Invoke the APIs of the service provider to deploy the SSL certificate.",
"workflow_node.deploy.default_name": "Deployment",
@@ -522,6 +518,15 @@
"workflow_node.deploy.form.huaweicloud_cdn_domain.label": "Huawei Cloud CDN domain",
"workflow_node.deploy.form.huaweicloud_cdn_domain.placeholder": "Please enter Huawei Cloud CDN domain name",
"workflow_node.deploy.form.huaweicloud_cdn_domain.tooltip": "For more information, see https://console-intl.huaweicloud.com/cdn",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.label": "Huawei Cloud OBS Endpoint",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.placeholder": "Please enter the Huawei Cloud OBS endpoint (e.g., obs.cn-north-4.myhuaweicloud.com)",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.tooltip": "What is this? Please refer to https://support.huaweicloud.com/productdesc-obs/obs_03_0152.html",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.label": "Huawei Cloud COS Bucket Name",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.placeholder": "Please enter the Huawei Cloud COS bucket name",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.tooltip": "What is this? Please refer to https://support.huaweicloud.com/productdesc-obs/obs_03_0207.html",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.label": "Huawei Cloud COS Custom Domain",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.placeholder": "Please enter the Huawei Cloud COS custom domain",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.tooltip": "What is this? Please refer to https://support.huaweicloud.com/productdesc-obs/obs_03_0207.html",
"workflow_node.deploy.form.huaweicloud_elb_region.label": "Huawei Cloud ELB region",
"workflow_node.deploy.form.huaweicloud_elb_region.placeholder": "Please enter Huawei Cloud ELB region (e.g. cn-north-1)",
"workflow_node.deploy.form.huaweicloud_elb_region.tooltip": "For more information, see https://console-intl.huaweicloud.com/apiexplorer/#/endpoint",
@@ -1021,7 +1026,6 @@
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": " to re-deploy.",
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "skip",
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "not skip",
-
"workflow_node.notify.label": "Send notification",
"workflow_node.notify.help": "Invoke the APIs of the service provider to push message notifications.",
"workflow_node.notify.default_name": "Notification",
@@ -1083,7 +1087,6 @@
"workflow_node.condition.default_name": "Parallel",
"workflow_node.condition.default_name.template_certtest_on_expire_soon": "If the certificate will be expiring soon ...",
"workflow_node.condition.default_name.template_certtest_on_expired": "If the certificate has expired ...",
-
"workflow_node.branch_block.label": "Branch",
"workflow_node.branch_block.default_name": "Branch",
"workflow_node.branch_block.state.no": "Enter Unconditionally",
@@ -1112,7 +1115,6 @@
"workflow_node.branch_block.form.expression.value.option.true.label": "True",
"workflow_node.branch_block.form.expression.value.option.false.label": "False",
"workflow_node.branch_block.form.expression.add_condition.button": "Add condition",
-
"workflow_node.try_catch.label": "Execution result branch",
"workflow_node.try_catch.help": "Attempt to execute subsequent nodes, and when any node fails to execute, interrupt and enter the execution failure branch.",
"workflow_node.try_catch.default_name": "Try to ...",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 85f1e578..a9be81f0 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -1,4 +1,4 @@
-{
+{
"provider.1panel": "1Panel",
"provider.1panel.console": "1Panel - 面板自身",
"provider.1panel.site": "1Panel - 网站",
@@ -96,6 +96,7 @@
"provider.huaweicloud.dns": "华为云 - 云解析 DNS",
"provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB",
"provider.huaweicloud.scm_upload": "华为云 - 上传到云证书管理服务 SCM",
+ "provider.huaweicloud.obs": "华为云 - 对象储存 OBS",
"provider.huaweicloud.waf": "华为云 - Web 应用防火墙 WAF",
"provider.jdcloud": "京东云",
"provider.jdcloud.alb": "京东云 - 应用负载均衡 ALB",
@@ -181,7 +182,6 @@
"provider.wecombot": "企业微信群机器人",
"provider.westcn": "西部数码",
"provider.zerossl": "ZeroSSL",
-
"provider.category.all": "全部",
"provider.category.cdn": "CDN",
"provider.category.storage": "文件存储",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index 19bdbcaf..090f18a3 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -17,7 +17,6 @@
"workflow_node.start.form.trigger_cron.tooltip": "五段式表达式,使用 crontab 标准语法规则。
支持使用任意值(即 *)、值列表分隔符(即 ,)、值的范围(即 -)、步骤值(即 /)等四种表达式。",
"workflow_node.start.form.trigger_cron.help": "预计最近 5 次运行时间(实际时区以服务器设置为准):",
"workflow_node.start.form.trigger_cron.guide": "如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。也不要总是设置为每日零时,以免遭遇证书颁发机构的流量高峰。
参考链接:
1. Let’s Encrypt 速率限制
2. 为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?",
-
"workflow_node.apply.label": "申请签发证书",
"workflow_node.apply.help": "从证书颁发机构申请签发 SSL 证书。",
"workflow_node.apply.default_name": "申请",
@@ -125,7 +124,6 @@
"workflow_node.apply.form.skip_before_expiry_days.suffix": "时,再次运行工作流时跳过此申请节点。",
"workflow_node.apply.form.skip_before_expiry_days.unit": "天",
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "注意不要超过颁发的证书最大有效期,否则证书可能永远不会续期。",
-
"workflow_node.upload.label": "上传自有证书",
"workflow_node.upload.help": "上传用户已有的本地 SSL 证书。",
"workflow_node.upload.default_name": "上传",
@@ -136,7 +134,6 @@
"workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
"workflow_node.upload.form.private_key.label": "私钥文件(PEM 格式)",
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
-
"workflow_node.monitor.label": "监控网站证书",
"workflow_node.monitor.help": "通过 HTTPS 协议获取指定网站的 SSL 证书。",
"workflow_node.monitor.default_name": "监控",
@@ -151,7 +148,6 @@
"workflow_node.monitor.form.domain.help": "提示:仅当主机地址为 IP 时需要输入。",
"workflow_node.monitor.form.request_path.label": "请求路径(可选)",
"workflow_node.monitor.form.request_path.placeholder": "请输入请求路径",
-
"workflow_node.deploy.label": "部署证书到 ...",
"workflow_node.deploy.help": "调用服务提供商相关 API,将 SSL 证书部署到指定的目标。",
"workflow_node.deploy.default_name": "部署",
@@ -520,6 +516,15 @@
"workflow_node.deploy.form.huaweicloud_cdn_domain.label": "华为云 CDN 加速域名",
"workflow_node.deploy.form.huaweicloud_cdn_domain.placeholder": "请输入华为云 CDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.huaweicloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/cdn",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.label": "华为云 OBS 接口端点",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.placeholder": "请输入华为云 OBS 接口端点(例如:obs.cn-north-4.myhuaweicloud.com)",
+ "workflow_node.deploy.form.huaweicloud_obs_endpoint.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/productdesc-obs/obs_03_0152.html",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.label": "华为云 COS 存储桶名",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.placeholder": "请输入华为云 COS 存储桶名",
+ "workflow_node.deploy.form.huaweicloud_obs_bucket.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/productdesc-obs/obs_03_0207.html",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.label": "华为云 COS 自定义域名",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.placeholder": "请输入华为云 COS 自定义域名",
+ "workflow_node.deploy.form.huaweicloud_obs_domain.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/productdesc-obs/obs_03_0207.html",
"workflow_node.deploy.form.huaweicloud_elb_region.label": "华为云 ELB 服务区域",
"workflow_node.deploy.form.huaweicloud_elb_region.placeholder": "请输入华为云 ELB 服务区域(例如:cn-north-1)",
"workflow_node.deploy.form.huaweicloud_elb_region.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/apiexplorer/#/endpoint",
@@ -1019,7 +1024,6 @@
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": "此部署节点。",
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "跳过",
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "不跳过",
-
"workflow_node.notify.label": "推送通知",
"workflow_node.notify.help": "调用服务提供商相关 API,将消息通知推送到指定的目标。",
"workflow_node.notify.default_name": "通知",
@@ -1110,14 +1114,11 @@
"workflow_node.branch_block.form.expression.value.option.true.label": "真",
"workflow_node.branch_block.form.expression.value.option.false.label": "假",
"workflow_node.branch_block.form.expression.add_condition.button": "添加条件",
-
"workflow_node.try_catch.label": "执行结果分支",
"workflow_node.try_catch.help": "尝试执行后续节点,当任一节点执行失败后,中断并进入执行失败分支。",
"workflow_node.try_catch.default_name": "尝试执行…",
-
"workflow_node.catch_block.label": "执行失败分支",
"workflow_node.catch_block.default_name": "若执行失败…",
-
"workflow_node.end.label": "结束",
"workflow_node.end.help": "中止工作流运行并退出。通常是一个工作流的最后一个节点。",
"workflow_node.end.default_name": "结束"