mirror of
https://github.com/certimate-go/certimate.git
synced 2026-10-16 07:05:59 +08:00
feat(provider): new purge provider: ucloud ussl
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package purgers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
pgrimpl "github.com/certimate-go/certimate/pkg/core/purger/providers/ucloud-ussl"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.PurgeProviderTypeUCloudUSSL, func(options *ProviderFactoryOptions) (core.Purger, error) {
|
||||
credentials := domain.AccessConfigForUCloud{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := pgrimpl.NewPurger(&pgrimpl.PurgerConfig{
|
||||
PrivateKey: credentials.PrivateKey,
|
||||
PublicKey: credentials.PublicKey,
|
||||
ProjectId: credentials.ProjectId,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -500,6 +500,7 @@ const (
|
||||
PurgeProviderTypeTencentCloudGAAP = PurgeProviderType(AccessProviderTypeTencentCloud + "-gaap")
|
||||
PurgeProviderTypeTencentCloudSSL = PurgeProviderType(AccessProviderTypeTencentCloud + "-ssl")
|
||||
PurgeProviderTypeTencentCloudTSE = PurgeProviderType(AccessProviderTypeTencentCloud + "-tse")
|
||||
PurgeProviderTypeUCloudUSSL = PurgeProviderType(AccessProviderTypeUCloud + "-ussl")
|
||||
)
|
||||
|
||||
type NotificationProviderType string
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/ucloud/ucloud-sdk-go/services/ulb"
|
||||
@@ -260,7 +259,7 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudLoadba
|
||||
return fmt.Errorf("failed to execute sdk request 'ulb.AddSSLBinding': %w", err)
|
||||
}
|
||||
|
||||
// 找出需要删除绑定的扩展证书
|
||||
// 获取扩展证书信息,并找出需要删除绑定的同域名证书
|
||||
// REF: https://docs.ucloud.cn/api/ulb-api/describe_sslv2
|
||||
sslIdsToDelete := make([]string, 0)
|
||||
for _, certItem := range cloudListenerInfo.Certificates {
|
||||
@@ -283,9 +282,6 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudLoadba
|
||||
if sslItem.Domains == d.config.Domain {
|
||||
sslIdsToDelete = append(sslIdsToDelete, sslItem.SSLId) // 同域名证书需要删除
|
||||
continue
|
||||
} else if sslItem.NotAfter != 0 && int64(sslItem.NotAfter) < time.Now().Unix() {
|
||||
sslIdsToDelete = append(sslIdsToDelete, sslItem.SSLId) // 过期证书需要删除。TODO: remove on v0.5
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
//go:build tester
|
||||
|
||||
package ucloudussl_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tester "github.com/certimate-go/certimate/pkg/core/purger/providers-tester"
|
||||
impl "github.com/certimate-go/certimate/pkg/core/purger/providers/ucloud-ussl"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = tester.InitArgs("UCLOUDUSSL_")
|
||||
fTestCertPath string
|
||||
fTestKeyPath string
|
||||
fPrivateKey string
|
||||
fPublicKey string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fPrivateKey, "PRIVATEKEY")
|
||||
fp.DefineString(&fPublicKey, "PUBLICKEY")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -tags=tester -v ./ucloud_ussl_test.go -args \
|
||||
--UCLOUDUSSL_PRIVATEKEY="your-private-key" \
|
||||
--UCLOUDUSSL_PUBLICKEY="your-public-key"
|
||||
*/
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Purge", func(t *testing.T) {
|
||||
provider, err := impl.NewPurger(&impl.PurgerConfig{
|
||||
PrivateKey: fPrivateKey,
|
||||
PublicKey: fPublicKey,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tester.Purge(t, provider, tester.PurgeInput{})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package ucloudussl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
ucloudsdk "github.com/certimate-go/certimate/pkg/sdk3rd/ucloud/ussl"
|
||||
xloop "github.com/certimate-go/certimate/pkg/utils/loop"
|
||||
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
|
||||
)
|
||||
|
||||
type (
|
||||
Provider = core.Purger
|
||||
PurgeResult = core.PurgerPurgeResult
|
||||
)
|
||||
|
||||
type PurgerConfig struct {
|
||||
// 优刻得 API 私钥。
|
||||
PrivateKey string `json:"privateKey"`
|
||||
// 优刻得 API 公钥。
|
||||
PublicKey string `json:"publicKey"`
|
||||
// 优刻得项目 ID。
|
||||
ProjectId string `json:"projectId,omitempty"`
|
||||
// 优刻得接口端点。
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
}
|
||||
|
||||
type Purger struct {
|
||||
config *PurgerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *ucloudsdk.USSLClient
|
||||
}
|
||||
|
||||
var _ Provider = (*Purger)(nil)
|
||||
|
||||
func NewPurger(config *PurgerConfig) (*Purger, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("the configuration of the purger provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.PrivateKey, config.PublicKey, config.ProjectId, config.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
return &Purger{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Purger) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
p.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
p.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Purger) Purge(ctx context.Context, expiry time.Duration) (*PurgeResult, error) {
|
||||
purgingCertIds := make([]int, 0)
|
||||
purgingCertId2ModeMap := make(map[int]string)
|
||||
|
||||
// 查询用户托管证书列表
|
||||
// REF: https://docs.ucloud.cn/api/usslcertificate-api/get_certificate_list
|
||||
getCertificateListPage := 1
|
||||
getCertificateListPageSize := 1000
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
getCertificateListReq := p.sdkClient.NewGetCertificateListRequest()
|
||||
getCertificateListReq.Mode = ucloud.String("trust")
|
||||
getCertificateListReq.Sort = ucloud.String("2")
|
||||
getCertificateListReq.Page = ucloud.Int(getCertificateListPage)
|
||||
getCertificateListReq.PageSize = ucloud.Int(getCertificateListPageSize)
|
||||
getCertificateListResp, err := p.sdkClient.GetCertificateList(getCertificateListReq)
|
||||
p.logger.Debug("sdk request 'ussl.GetCertificateList'", slog.Any("request", getCertificateListReq), slog.Any("response", getCertificateListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ussl.GetCertificateList': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range getCertificateListResp.CertificateList {
|
||||
certNotAfter := time.UnixMilli(certItem.NotAfter)
|
||||
|
||||
if time.Since(certNotAfter) >= expiry {
|
||||
purgingCertIds = append(purgingCertIds, certItem.CertificateID)
|
||||
purgingCertId2ModeMap[certItem.CertificateID] = certItem.Mode
|
||||
}
|
||||
}
|
||||
|
||||
if len(getCertificateListResp.CertificateList) < getCertificateListPageSize ||
|
||||
getCertificateListPage*getCertificateListPageSize >= getCertificateListResp.TotalCount {
|
||||
break
|
||||
}
|
||||
|
||||
getCertificateListPage++
|
||||
}
|
||||
|
||||
// 查询用户购买证书列表
|
||||
// REF: https://docs.ucloud.cn/api/usslcertificate-api/get_certificate_list
|
||||
getCertificateListPage = 1
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
getCertificateListReq := p.sdkClient.NewGetCertificateListRequest()
|
||||
getCertificateListReq.Mode = ucloud.String("purchase")
|
||||
getCertificateListReq.Sort = ucloud.String("2")
|
||||
getCertificateListReq.Page = ucloud.Int(getCertificateListPage)
|
||||
getCertificateListReq.PageSize = ucloud.Int(getCertificateListPageSize)
|
||||
getCertificateListResp, err := p.sdkClient.GetCertificateList(getCertificateListReq)
|
||||
p.logger.Debug("sdk request 'ussl.GetCertificateList'", slog.Any("request", getCertificateListReq), slog.Any("response", getCertificateListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ussl.GetCertificateList': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range getCertificateListResp.CertificateList {
|
||||
certNotAfter := time.UnixMilli(certItem.NotAfter)
|
||||
|
||||
if time.Since(certNotAfter) >= expiry {
|
||||
purgingCertIds = append(purgingCertIds, certItem.CertificateID)
|
||||
purgingCertId2ModeMap[certItem.CertificateID] = certItem.Mode
|
||||
}
|
||||
}
|
||||
|
||||
if len(getCertificateListResp.CertificateList) < getCertificateListPageSize ||
|
||||
getCertificateListPage*getCertificateListPageSize >= getCertificateListResp.TotalCount {
|
||||
break
|
||||
}
|
||||
|
||||
getCertificateListPage++
|
||||
}
|
||||
|
||||
// 删除证书
|
||||
// REF: https://docs.ucloud.cn/api/usslcertificate-api/delete_ssl_certificate
|
||||
purgedCount := 0
|
||||
if len(purgingCertIds) > 0 {
|
||||
p.logger.Info("found certificates to purge", slog.Any("certIds", purgingCertIds))
|
||||
|
||||
if err := xloop.ForRangeAllWithContext(ctx, purgingCertIds, func(ctx context.Context, certId int, i int) error {
|
||||
if i > 0 {
|
||||
if err := xwait.DelayWithContext(ctx, 1*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
deleteSSLCertificateReq := p.sdkClient.NewDeleteSSLCertificateRequest()
|
||||
deleteSSLCertificateReq.CertificateMode = ucloud.String(purgingCertId2ModeMap[certId])
|
||||
deleteSSLCertificateReq.CertificateID = ucloud.Int(certId)
|
||||
deleteSSLCertificateResp, err := p.sdkClient.DeleteSSLCertificate(deleteSSLCertificateReq)
|
||||
p.logger.Debug("sdk request 'ussl.DeleteSSLCertificate'", slog.Any("request", deleteSSLCertificateReq), slog.Any("response", deleteSSLCertificateResp))
|
||||
if err != nil {
|
||||
if deleteSSLCertificateResp != nil && deleteSSLCertificateResp.GetRetCode() == 80011 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'ussl.DeleteSSLCertificate': %w", err)
|
||||
}
|
||||
|
||||
purgedCount++
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &PurgeResult{
|
||||
Count: purgedCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(privateKey, publicKey, projectId, endpoint string) (*ucloudsdk.USSLClient, error) {
|
||||
if privateKey == "" {
|
||||
return nil, fmt.Errorf("ucloud: invalid private key")
|
||||
}
|
||||
if publicKey == "" {
|
||||
return nil, fmt.Errorf("ucloud: invalid public key")
|
||||
}
|
||||
|
||||
cfg := ucloud.NewConfig()
|
||||
if projectId != "" {
|
||||
cfg.ProjectId = projectId
|
||||
}
|
||||
if endpoint != "" {
|
||||
if strings.Contains(endpoint, "://") {
|
||||
cfg.BaseUrl = endpoint
|
||||
} else {
|
||||
cfg.BaseUrl = "https://" + endpoint
|
||||
}
|
||||
}
|
||||
|
||||
credential := auth.NewCredential()
|
||||
credential.PrivateKey = privateKey
|
||||
credential.PublicKey = publicKey
|
||||
|
||||
client := ucloudsdk.NewClient(&cfg, &credential)
|
||||
return client, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ussl
|
||||
|
||||
import (
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
|
||||
)
|
||||
|
||||
type DeleteSSLCertificateRequest struct {
|
||||
request.CommonBase
|
||||
|
||||
CertificateMode *string `required:"true"`
|
||||
CertificateID *int `required:"true"`
|
||||
}
|
||||
|
||||
type DeleteSSLCertificateResponse struct {
|
||||
response.CommonBase
|
||||
}
|
||||
|
||||
func (c *USSLClient) NewDeleteSSLCertificateRequest() *DeleteSSLCertificateRequest {
|
||||
req := &DeleteSSLCertificateRequest{}
|
||||
|
||||
c.Client.SetupRequest(req)
|
||||
|
||||
req.SetRetryable(true)
|
||||
req.SetEncoder(request.NewJSONEncoder(c.GetConfig(), c.GetCredential()))
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *USSLClient) DeleteSSLCertificate(req *DeleteSSLCertificateRequest) (*DeleteSSLCertificateResponse, error) {
|
||||
var err error
|
||||
var res DeleteSSLCertificateResponse
|
||||
|
||||
reqCopier := *req
|
||||
|
||||
err = c.Client.InvokeAction("DeleteSSLCertificate", &reqCopier, &res)
|
||||
if err != nil {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import BizPurgeNodeConfigFieldsProviderHuaweiCloudELB from "./BizPurgeNodeConfig
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudGAAP from "./BizPurgeNodeConfigFieldsProviderTencentCloudGAAP";
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudSSL from "./BizPurgeNodeConfigFieldsProviderTencentCloudSSL";
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudTSE from "./BizPurgeNodeConfigFieldsProviderTencentCloudTSE";
|
||||
import BizPurgeNodeConfigFieldsProviderUCloudUSSL from "./BizPurgeNodeConfigFieldsProviderUCloudUSSL";
|
||||
|
||||
const providerComponentMap: Partial<Record<PurgeProviderType, React.ComponentType<any>>> = {
|
||||
/*
|
||||
@@ -22,6 +23,7 @@ const providerComponentMap: Partial<Record<PurgeProviderType, React.ComponentTyp
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_GAAP]: BizPurgeNodeConfigFieldsProviderTencentCloudGAAP,
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_SSL]: BizPurgeNodeConfigFieldsProviderTencentCloudSSL,
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_TSE]: BizPurgeNodeConfigFieldsProviderTencentCloudTSE,
|
||||
[PURGE_PROVIDERS.UCLOUD_USSL]: BizPurgeNodeConfigFieldsProviderUCloudUSSL,
|
||||
};
|
||||
|
||||
const useComponent = (provider: string, { initProps, deps = [] }: { initProps?: (provider: string) => any; deps?: unknown[] }) => {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizPurgeNodeConfigFieldsProviderUCloudUSSL = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "endpoint"]}
|
||||
initialValue={initialValues.endpoint}
|
||||
label={t("workflow_node.purge.form.ucloud_ussl_endpoint.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.purge.form.ucloud_ussl_endpoint.tooltip") }}></span>}
|
||||
>
|
||||
<Input allowClear placeholder={t("workflow_node.purge.form.ucloud_ussl_endpoint.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
endpoint: z.string().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizPurgeNodeConfigFieldsProviderUCloudUSSL, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -994,6 +994,7 @@ export const PURGE_PROVIDERS = Object.freeze(
|
||||
TENCENTCLOUD_GAAP: `${ACCESS_PROVIDERS.TENCENTCLOUD}-gaap`,
|
||||
TENCENTCLOUD_SSL: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ssl`,
|
||||
TENCENTCLOUD_TSE: `${ACCESS_PROVIDERS.TENCENTCLOUD}-tse`,
|
||||
UCLOUD_USSL: `${ACCESS_PROVIDERS.UCLOUD}-ussl`,
|
||||
} as const
|
||||
);
|
||||
|
||||
@@ -1016,6 +1017,7 @@ export const purgeProvidersMap: Map<PurgeProvider["type"] | string, PurgeProvide
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_TSE, "provider.tencentcloud_tse"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_SCM, "provider.huaweicloud_scm"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud_elb"],
|
||||
[PURGE_PROVIDERS.UCLOUD_USSL, "provider.ucloud_ussl"],
|
||||
] satisfies Array<[PurgeProviderType, string, "builtin"] | [PurgeProviderType, string]>
|
||||
).map(([type, name]) => [
|
||||
type,
|
||||
|
||||
@@ -275,6 +275,7 @@
|
||||
"ucloud_uewaf": "UCloud - UEWAF (Enterprise Web Application Firewall)",
|
||||
"ucloud_upathx": "UCloud - UPathX (Global Dynamic Acceleration)",
|
||||
"ucloud_us3": "UCloud - US3 (Object-based Storage)",
|
||||
"ucloud_ussl": "UCloud - USSL (SSL Certificates)",
|
||||
"todaynic": "TodayNIC.com",
|
||||
"unicloud": "uniCloud (DCloud)",
|
||||
"unicloud_webhost": "uniCloud - Web Host",
|
||||
|
||||
@@ -3090,6 +3090,11 @@
|
||||
"label": "Tencent Cloud TSE Cloud-native API gateway ID",
|
||||
"placeholder": "Please enter Tencent Cloud TSE Cloud-native API gateway ID",
|
||||
"tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/rcc/governance\" target=\"_blank\">https://console.tencentcloud.com/rcc/governance</a>"
|
||||
},
|
||||
"ucloud_ussl_endpoint": {
|
||||
"label": "UCloud API endpoint (Optional)",
|
||||
"placeholder": "Please enter UCloud USSL API endpoint (e.g. api.ucloud-global.com)",
|
||||
"tooltip": "<ul style=\"list-style: disc;\"><li><strong>api.ucloud-global.com</strong> for UCloud Global</li><li><strong>api.ucloud.cn</strong> for UCloud in China (<em>Default</em>)</li></ul>"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -276,6 +276,7 @@
|
||||
"ucloud_uewaf": "优刻得 - 企业 Web 应用防火墙 UEWAF",
|
||||
"ucloud_upathx": "优刻得 - 全球动态加速 UPathX",
|
||||
"ucloud_us3": "优刻得 - 对象存储 US3",
|
||||
"ucloud_ussl": "优刻得 - 证书管理 USSL",
|
||||
"unicloud": "uniCloud (DCloud)",
|
||||
"unicloud_webhost": "uniCloud - 前端网页托管",
|
||||
"upyun": "又拍云",
|
||||
|
||||
@@ -3089,6 +3089,11 @@
|
||||
"label": "腾讯云 TSE 云原生网关 ID",
|
||||
"placeholder": "请输入腾讯云 TSE 云原生网关 ID",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com.cn/rcc/governance\" target=\"_blank\">https://console.cloud.tencent.com.cn/rcc/governance</a>"
|
||||
},
|
||||
"ucloud_ussl_endpoint": {
|
||||
"label": "优刻得接口端点(可选)",
|
||||
"placeholder": "请输入优刻得 USSL 接口端点(例如:api.ucloud.cn)",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://docs.ucloud.cn/api/summary/gateway\" target=\"_blank\">https://docs.ucloud.cn/api/summary/gateway</a><br>国际站用户请填写 <em>api.ucloud-global.com</em>。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user