diff --git a/README.md b/README.md index 97e851ea..b7cfc56e 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ make local.run | [Azure](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | | [ClouDNS](https://www.cloudns.net//) | | +| [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | | [NameSilo](https://www.namesilo.com/) | | diff --git a/README_EN.md b/README_EN.md index b5c71b3d..c2174fad 100644 --- a/README_EN.md +++ b/README_EN.md @@ -95,6 +95,7 @@ The following DNS providers are supported: | [Azure DNS](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | | [ClouDNS](https://www.cloudns.net//) | | +| [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | | [NameSilo](https://www.namesilo.com/) | | diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 8329ff54..f53c7287 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -12,6 +12,7 @@ import ( providerAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns" providerCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare" providerClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" + providerGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" providerGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy" providerHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud" providerNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom" @@ -131,6 +132,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeGname: + { + access := domain.AccessConfigForGname{} + if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to decode provider access config: %w", err) + } + + applicant, err := providerGname.NewChallengeProvider(&providerGname.GnameApplicantConfig{ + AppId: access.AppId, + AppKey: access.AppKey, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypeGoDaddy: { access := domain.AccessConfigForGoDaddy{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 70128622..c8448c21 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -78,6 +78,11 @@ type AccessConfigForEdgio struct { ClientSecret string `json:"clientSecret"` } +type AccessConfigForGname struct { + AppId string `json:"appId"` + AppKey string `json:"appKey"` +} + type AccessConfigForGoDaddy struct { ApiKey string `json:"apiKey"` ApiSecret string `json:"apiSecret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 374fab2c..894f8007 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -19,6 +19,7 @@ const ( AccessProviderTypeClouDNS = AccessProviderType("cloudns") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") AccessProviderTypeEdgio = AccessProviderType("edgio") + AccessProviderTypeGname = AccessProviderType("gname") AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeKubernetes = AccessProviderType("k8s") @@ -55,6 +56,7 @@ const ( ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns") ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare") ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns") + ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname") ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy") ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS] ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns") diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go new file mode 100644 index 00000000..90cec017 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go @@ -0,0 +1,40 @@ +package gname + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/challenge" + + internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal" +) + +type GnameApplicantConfig struct { + AppId string `json:"appId"` + AppKey string `json:"appKey"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *GnameApplicantConfig) (challenge.Provider, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.AppID = config.AppId + providerConfig.AppKey = config.AppKey + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go new file mode 100644 index 00000000..979a803b --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go @@ -0,0 +1,196 @@ +package lego_gname + +import ( + "errors" + "fmt" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + + gnamesdk "github.com/usual2970/certimate/internal/pkg/vendors/gname-sdk" +) + +const ( + envNamespace = "GNAME_" + + EnvAppID = envNamespace + "APP_ID" + EnvAppKey = envNamespace + "APP_KEY" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + AppID string + AppKey string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPTimeout time.Duration +} + +type DNSProvider struct { + client *gnamesdk.GnameClient + config *Config +} + +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAppID, EnvAppKey) + if err != nil { + return nil, fmt.Errorf("gname: %w", err) + } + + config := NewDefaultConfig() + config.AppID = values[EnvAppID] + config.AppKey = values[EnvAppKey] + + return NewDNSProviderConfig(config) +} + +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("gname: the configuration of the DNS provider is nil") + } + + client := gnamesdk.NewGnameClient(config.AppID, config.AppKey). + WithTimeout(config.HTTPTimeout) + + return &DNSProvider{ + client: client, + config: config, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("gname: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName) + if err != nil { + return fmt.Errorf("gname: %w", err) + } + + if err := d.addOrUpdateDNSRecord(domain, subDomain, info.Value); err != nil { + return fmt.Errorf("gname: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, value := dns01.GetRecord(domain, keyAuth) + subDomain := dns01.UnFqdn(fqdn) + + if err := d.removeDNSRecord(domain, subDomain, value); err != nil { + return fmt.Errorf("gname: %w", err) + } + + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getDNSRecord(domain, subDomain string) (*gnamesdk.ResolutionRecord, error) { + page := 1 + pageSize := 20 + for { + request := &gnamesdk.ListDomainResolutionRequest{} + request.ZoneName = domain + request.Page = &page + request.PageSize = &pageSize + + response, err := d.client.ListDomainResolution(request) + if err != nil { + return nil, err + } + + for _, record := range response.Data { + if record.RecordType == "TXT" && record.RecordName == subDomain { + return record, nil + } + } + + if len(response.Data) == 0 { + break + } + if response.Page*response.PageSize >= response.Count { + break + } + + page++ + } + + return nil, nil +} + +func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) error { + record, err := d.getDNSRecord(domain, subDomain) + if err != nil { + return err + } + + if record == nil { + request := &gnamesdk.AddDomainResolutionRequest{ + ZoneName: domain, + RecordType: "TXT", + RecordName: subDomain, + RecordValue: value, + TTL: d.config.TTL, + } + _, err := d.client.AddDomainResolution(request) + return err + } else { + request := &gnamesdk.ModifyDomainResolutionRequest{ + ID: record.ID, + ZoneName: domain, + RecordType: "TXT", + RecordName: subDomain, + RecordValue: value, + TTL: d.config.TTL, + } + _, err := d.client.ModifyDomainResolution(request) + return err + } + + return nil +} + +func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error { + record, err := d.getDNSRecord(domain, subDomain) + if err != nil { + return err + } + + if record == nil { + return nil + } + + request := &gnamesdk.DeleteDomainResolutionRequest{ + ZoneName: domain, + RecordID: record.ID, + } + _, err = d.client.DeleteDomainResolution(request) + return err +} diff --git a/internal/pkg/vendors/gname-sdk/api.go b/internal/pkg/vendors/gname-sdk/api.go index bb35f2e5..33972adc 100644 --- a/internal/pkg/vendors/gname-sdk/api.go +++ b/internal/pkg/vendors/gname-sdk/api.go @@ -5,7 +5,7 @@ type BaseResponse interface { GetMsg() string } -type AddDNSRecordRequest struct { +type AddDomainResolutionRequest struct { ZoneName string `json:"ym"` RecordType string `json:"lx"` RecordName string `json:"zj"` @@ -14,21 +14,21 @@ type AddDNSRecordRequest struct { TTL int `json:"ttl"` } -type AddDNSRecordResponse struct { +type AddDomainResolutionResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data int `json:"data"` } -func (r *AddDNSRecordResponse) GetCode() int { +func (r *AddDomainResolutionResponse) GetCode() int { return r.Code } -func (r *AddDNSRecordResponse) GetMsg() string { +func (r *AddDomainResolutionResponse) GetMsg() string { return r.Msg } -type EditDNSRecordRequest struct { +type ModifyDomainResolutionRequest struct { ID string `json:"jxid"` ZoneName string `json:"ym"` RecordType string `json:"lx"` @@ -38,53 +38,53 @@ type EditDNSRecordRequest struct { TTL int `json:"ttl"` } -type EditDNSRecordResponse struct { +type ModifyDomainResolutionResponse struct { Code int `json:"code"` Msg string `json:"msg"` } -func (r *EditDNSRecordResponse) GetCode() int { +func (r *ModifyDomainResolutionResponse) GetCode() int { return r.Code } -func (r *EditDNSRecordResponse) GetMsg() string { +func (r *ModifyDomainResolutionResponse) GetMsg() string { return r.Msg } -type DeleteDNSRecordRequest struct { +type DeleteDomainResolutionRequest struct { ZoneName string `json:"ym"` - RecordId int `json:"jxid"` + RecordID string `json:"jxid"` } -type DeleteDNSRecordResponse struct { +type DeleteDomainResolutionResponse struct { Code int `json:"code"` Msg string `json:"msg"` } -func (r *DeleteDNSRecordResponse) GetCode() int { +func (r *DeleteDomainResolutionResponse) GetCode() int { return r.Code } -func (r *DeleteDNSRecordResponse) GetMsg() string { +func (r *DeleteDomainResolutionResponse) GetMsg() string { return r.Msg } -type ListDNSRecordRequest struct { +type ListDomainResolutionRequest struct { ZoneName string `json:"ym"` Page *int `json:"page,omitempty"` PageSize *int `json:"limit,omitempty"` } -type ListDNSRecordResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` - Count int `json:"count"` - Data []*DNSRecord `json:"data"` - Page int `json:"page"` - PageSize int `json:"pagesize"` +type ListDomainResolutionResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Count int `json:"count"` + Data []*ResolutionRecord `json:"data"` + Page int `json:"page"` + PageSize int `json:"pagesize"` } -type DNSRecord struct { +type ResolutionRecord struct { ID string `json:"id"` ZoneName string `json:"ym"` RecordType string `json:"lx"` @@ -93,10 +93,10 @@ type DNSRecord struct { MX int `json:"mx"` } -func (r *ListDNSRecordResponse) GetCode() int { +func (r *ListDomainResolutionResponse) GetCode() int { return r.Code } -func (r *ListDNSRecordResponse) GetMsg() string { +func (r *ListDomainResolutionResponse) GetMsg() string { return r.Msg } diff --git a/internal/pkg/vendors/gname-sdk/client.go b/internal/pkg/vendors/gname-sdk/client.go index 81812682..d034cfeb 100644 --- a/internal/pkg/vendors/gname-sdk/client.go +++ b/internal/pkg/vendors/gname-sdk/client.go @@ -35,12 +35,12 @@ func (c *GnameClient) WithTimeout(timeout time.Duration) *GnameClient { return c } -func (c *GnameClient) AddDNSRecord(req *AddDNSRecordRequest) (*AddDNSRecordResponse, error) { +func (c *GnameClient) AddDomainResolution(req *AddDomainResolutionRequest) (*AddDomainResolutionResponse, error) { params := make(map[string]any) jsonData, _ := json.Marshal(req) json.Unmarshal(jsonData, ¶ms) - result := AddDNSRecordResponse{} + result := AddDomainResolutionResponse{} err := c.sendRequestWithResult("/api/resolution/add", params, &result) if err != nil { return nil, err @@ -48,12 +48,12 @@ func (c *GnameClient) AddDNSRecord(req *AddDNSRecordRequest) (*AddDNSRecordRespo return &result, nil } -func (c *GnameClient) EditDNSRecord(req *EditDNSRecordRequest) (*EditDNSRecordResponse, error) { +func (c *GnameClient) ModifyDomainResolution(req *ModifyDomainResolutionRequest) (*ModifyDomainResolutionResponse, error) { params := make(map[string]any) jsonData, _ := json.Marshal(req) json.Unmarshal(jsonData, ¶ms) - result := EditDNSRecordResponse{} + result := ModifyDomainResolutionResponse{} err := c.sendRequestWithResult("/api/resolution/edit", params, &result) if err != nil { return nil, err @@ -61,12 +61,12 @@ func (c *GnameClient) EditDNSRecord(req *EditDNSRecordRequest) (*EditDNSRecordRe return &result, nil } -func (c *GnameClient) DeleteDNSRecord(req *DeleteDNSRecordRequest) (*DeleteDNSRecordResponse, error) { +func (c *GnameClient) DeleteDomainResolution(req *DeleteDomainResolutionRequest) (*DeleteDomainResolutionResponse, error) { params := make(map[string]any) jsonData, _ := json.Marshal(req) json.Unmarshal(jsonData, ¶ms) - result := DeleteDNSRecordResponse{} + result := DeleteDomainResolutionResponse{} err := c.sendRequestWithResult("/api/resolution/delete", params, &result) if err != nil { return nil, err @@ -74,12 +74,12 @@ func (c *GnameClient) DeleteDNSRecord(req *DeleteDNSRecordRequest) (*DeleteDNSRe return &result, nil } -func (c *GnameClient) ListDNSRecord(req *ListDNSRecordRequest) (*ListDNSRecordResponse, error) { +func (c *GnameClient) ListDomainResolution(req *ListDomainResolutionRequest) (*ListDomainResolutionResponse, error) { params := make(map[string]any) jsonData, _ := json.Marshal(req) json.Unmarshal(jsonData, ¶ms) - result := ListDNSRecordResponse{} + result := ListDomainResolutionResponse{} err := c.sendRequestWithResult("/api/resolution/list", params, &result) if err != nil { return nil, err @@ -124,7 +124,7 @@ func (c *GnameClient) sendRequest(path string, params map[string]any) (*resty.Re data["gntime"] = fmt.Sprintf("%d", time.Now().Unix()) data["gntoken"] = c.generateSignature(data) - url := "https://api.gname.com" + path + url := "http://api.gname.com" + path req := c.client.R(). SetHeader("Content-Type", "application/x-www-form-urlencoded"). SetFormData(data) diff --git a/ui/public/imgs/providers/gname.svg b/ui/public/imgs/providers/gname.svg new file mode 100644 index 00000000..ec409ca5 --- /dev/null +++ b/ui/public/imgs/providers/gname.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/rainyun.svg b/ui/public/imgs/providers/rainyun.svg index 18413e16..1076cb76 100644 --- a/ui/public/imgs/providers/rainyun.svg +++ b/ui/public/imgs/providers/rainyun.svg @@ -1,10 +1 @@ - - - - - - - - - - + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index e007eb89..c6c44d2d 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -19,6 +19,7 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; +import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; @@ -106,6 +107,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DOGECLOUD: return ; + case ACCESS_PROVIDERS.GNAME: + return ; case ACCESS_PROVIDERS.GODADDY: return ; case ACCESS_PROVIDERS.EDGIO: diff --git a/ui/src/components/access/AccessFormGnameConfig.tsx b/ui/src/components/access/AccessFormGnameConfig.tsx new file mode 100644 index 00000000..f0c8f072 --- /dev/null +++ b/ui/src/components/access/AccessFormGnameConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForGname } from "@/domain/access"; + +type AccessFormGnameConfigFieldValues = Nullish; + +export type AccessFormGnameConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormGnameConfigFieldValues; + onValuesChange?: (values: AccessFormGnameConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormGnameConfigFieldValues => { + return { + appId: "", + appKey: "", + }; +}; + +const AccessFormGnameConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormGnameConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + appId: z + .string() + .min(1, t("access.form.gname_app_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + appKey: z + .string() + .min(1, t("access.form.gname_app_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormGnameConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 14b31be6..1e229751 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -18,6 +18,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForClouDNS | AccessConfigForDogeCloud | AccessConfigForEdgio + | AccessConfigForGname | AccessConfigForGoDaddy | AccessConfigForHuaweiCloud | AccessConfigForKubernetes @@ -91,6 +92,11 @@ export type AccessConfigForEdgio = { clientSecret: string; }; +export type AccessConfigForGname = { + appId: string; + appKey: string; +}; + export type AccessConfigForGoDaddy = { apiKey: string; apiSecret: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 16fe5da6..652f4332 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -13,6 +13,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ CLOUDFLARE: "cloudflare", CLOUDNS: "cloudns", DOGECLOUD: "dogecloud", + GNAME: "gname", GODADDY: "godaddy", EDGIO: "edgio", HUAWEICLOUD: "huaweicloud", @@ -73,6 +74,7 @@ export const accessProvidersMap: Maphttps://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", + "access.form.gname_app_id.label": "GNAME AppId", + "access.form.gname_app_id.placeholder": "Please enter GNAME AppId", + "access.form.gname_app_id.tooltip": "For more information, see https://www.gname.com/user#/dealer_api", + "access.form.gname_app_key.label": "GNAME AppKey", + "access.form.gname_app_key.placeholder": "Please enter GNAME AppKey", + "access.form.gname_app_key.tooltip": "For more information, see https://www.gname.com/user#/dealer_api", "access.form.godaddy_api_key.label": "GoDaddy API key", "access.form.godaddy_api_key.placeholder": "Please enter GoDaddy API key", "access.form.godaddy_api_key.tooltip": "For more information, see https://developer.godaddy.com/", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 4fb7e387..cb5c2d41 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -61,6 +61,7 @@ "common.provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", "common.provider.edgio": "Edgio", "common.provider.edgio.applications": "Edgio - Applications", + "common.provider.gname": "GNAME", "common.provider.godaddy": "GoDaddy", "common.provider.huaweicloud": "Huawei Cloud", "common.provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 6fce9673..d0c9037d 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -90,6 +90,12 @@ "access.form.edgio_client_secret.label": "Edgio 客户端密码", "access.form.edgio_client_secret.placeholder": "请输入 Edgio 客户端密码", "access.form.edgio_client_secret.tooltip": "这是什么?请参阅 https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", + "access.form.gname_app_id.label": "GNAME AppId", + "access.form.gname_app_id.placeholder": "请输入 GNAME AppId", + "access.form.gname_app_id.tooltip": "这是什么?请参阅 https://www.gname.com/user#/dealer_api", + "access.form.gname_app_key.label": "GNAME AppKey", + "access.form.gname_app_key.placeholder": "请输入 GNAME AppKey", + "access.form.gname_app_key.tooltip": "这是什么?请参阅 https://www.gname.com/user#/dealer_api", "access.form.godaddy_api_key.label": "GoDaddy API Key", "access.form.godaddy_api_key.placeholder": "请输入 GoDaddy API Key", "access.form.godaddy_api_key.tooltip": "这是什么?请参阅 https://developer.godaddy.com/", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 3035e533..b8b97842 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -61,6 +61,7 @@ "common.provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", "common.provider.edgio": "Edgio", "common.provider.edgio.applications": "Edgio - Applications", + "common.provider.gname": "GNAME", "common.provider.godaddy": "GoDaddy", "common.provider.huaweicloud": "华为云", "common.provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN",