diff --git a/go.mod b/go.mod
index 190cc75c..8623edb0 100644
--- a/go.mod
+++ b/go.mod
@@ -106,6 +106,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
+ github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/qiniu/dyn v1.3.0 // indirect
github.com/qiniu/x v1.10.5 // indirect
diff --git a/go.sum b/go.sum
index 8c79203b..41ce2a0e 100644
--- a/go.sum
+++ b/go.sum
@@ -650,6 +650,8 @@ github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CF
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
+github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
+github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index dcfc1dde..5119f86d 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -25,6 +25,7 @@ import (
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
+ pPorkbun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun"
pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns"
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
@@ -345,6 +346,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
+ case domain.ApplyDNSProviderTypePorkbun:
+ {
+ access := domain.AccessConfigForPorkbun{}
+ if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ applicant, err := pPorkbun.NewChallengeProvider(&pPorkbun.ChallengeProviderConfig{
+ ApiKey: access.ApiKey,
+ SecretApiKey: access.SecretApiKey,
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return applicant, err
+ }
+
case domain.ApplyDNSProviderTypePowerDNS:
{
access := domain.AccessConfigForPowerDNS{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index f19a0871..60726bd6 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -165,6 +165,11 @@ type AccessConfigForNS1 struct {
ApiKey string `json:"apiKey"`
}
+type AccessConfigForPorkbun struct {
+ ApiKey string `json:"apiKey"`
+ SecretApiKey string `json:"secretApiKey"`
+}
+
type AccessConfigForPowerDNS struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index d79d5f59..7b3a8770 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -26,6 +26,7 @@ const (
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
+ AccessProviderTypeDeSEC = AccessProviderType("desec") // deSEC(预留)
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
@@ -43,6 +44,7 @@ const (
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
AccessProviderTypeNS1 = AccessProviderType("ns1")
+ AccessProviderTypePorkbun = AccessProviderType("porkbun") // Porkbun(预留)
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
@@ -52,6 +54,7 @@ const (
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeUpyun = AccessProviderType("upyun")
+ AccessProviderTypeVercel = AccessProviderType("vercel") // Vercel(预留)
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeWebhook = AccessProviderType("webhook")
AccessProviderTypeWestcn = AccessProviderType("westcn")
@@ -79,6 +82,7 @@ const (
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud")
+ ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType("desec")
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla")
ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType("dynv6")
ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore")
@@ -92,10 +96,12 @@ const (
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
+ ApplyDNSProviderTypePorkbun = ApplyDNSProviderType("porkbun")
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns")
ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun")
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns")
+ ApplyDNSProviderTypeVercel = ApplyDNSProviderType("vercel")
ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS]
ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns")
ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn")
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun/porkbun.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun/porkbun.go
new file mode 100644
index 00000000..ba60a791
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun/porkbun.go
@@ -0,0 +1,38 @@
+package porkbun
+
+import (
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+ "github.com/go-acme/lego/v4/providers/dns/porkbun"
+)
+
+type ChallengeProviderConfig struct {
+ ApiKey string `json:"apiKey"`
+ SecretApiKey string `json:"secretApiKey"`
+ DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
+ DnsTTL int32 `json:"dnsTTL,omitempty"`
+}
+
+func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
+ if config == nil {
+ panic("config is nil")
+ }
+
+ providerConfig := porkbun.NewDefaultConfig()
+ providerConfig.APIKey = config.ApiKey
+ providerConfig.SecretAPIKey = config.SecretApiKey
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = int(config.DnsTTL)
+ }
+
+ provider, err := porkbun.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go
index 3dc86d66..e5275efe 100644
--- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go
@@ -1,4 +1,4 @@
-package namesilo
+package powerdns
import (
"net/url"
diff --git a/migrations/1742644800_upgrade.go b/migrations/1742644800_upgrade.go
index 97fb2bc4..b28634a1 100644
--- a/migrations/1742644800_upgrade.go
+++ b/migrations/1742644800_upgrade.go
@@ -7,7 +7,7 @@ import (
func init() {
m.Register(func(app core.App) error {
- // create collection `workflow_run`
+ // update collection `workflow_run`
{
collection, err := app.FindCollectionByNameOrId("qjp8lygssgwyqyz")
if err != nil {
@@ -37,7 +37,7 @@ func init() {
}
}
- // create collection `workflow_output`
+ // update collection `workflow_output`
{
collection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp")
if err != nil {
@@ -63,7 +63,7 @@ func init() {
}
}
- // create collection `workflow_logs`
+ // update collection `workflow_logs`
{
collection, err := app.FindCollectionByNameOrId("pbc_1682296116")
if err != nil {
@@ -107,6 +107,83 @@ func init() {
}
}
+ // update collection `access`
+ {
+ collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
+ if err != nil {
+ return err
+ }
+
+ // update field
+ if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{
+ "hidden": false,
+ "id": "hwy7m03o",
+ "maxSelect": 1,
+ "name": "provider",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "1panel",
+ "acmehttpreq",
+ "akamai",
+ "aliyun",
+ "aws",
+ "azure",
+ "baiducloud",
+ "baishan",
+ "baotapanel",
+ "byteplus",
+ "cachefly",
+ "cdnfly",
+ "cloudflare",
+ "cloudns",
+ "cmcccloud",
+ "ctcccloud",
+ "cucccloud",
+ "desec",
+ "dnsla",
+ "dogecloud",
+ "dynv6",
+ "edgio",
+ "fastly",
+ "gname",
+ "gcore",
+ "godaddy",
+ "goedge",
+ "huaweicloud",
+ "jdcloud",
+ "k8s",
+ "local",
+ "namecheap",
+ "namedotcom",
+ "namesilo",
+ "ns1",
+ "porkbun",
+ "powerdns",
+ "qiniu",
+ "qingcloud",
+ "rainyun",
+ "safeline",
+ "ssh",
+ "tencentcloud",
+ "ucloud",
+ "upyun",
+ "vercel",
+ "volcengine",
+ "webhook",
+ "westcn"
+ ]
+ }`)); err != nil {
+ return err
+ }
+
+ if err := app.Save(collection); err != nil {
+ return err
+ }
+ }
+
return nil
}, func(app core.App) error {
return nil
diff --git a/ui/public/imgs/providers/porkbun.svg b/ui/public/imgs/providers/porkbun.svg
new file mode 100644
index 00000000..096d03ac
--- /dev/null
+++ b/ui/public/imgs/providers/porkbun.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 63a66875..6a948b9a 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -38,6 +38,7 @@ import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig";
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig";
import AccessFormNS1Config from "./AccessFormNS1Config";
+import AccessFormPorkbunConfig from "./AccessFormPorkbunConfig";
import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig";
import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
@@ -160,6 +161,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.NS1:
return ;
+ case ACCESS_PROVIDERS.PORKBUN:
+ return ;
case ACCESS_PROVIDERS.POWERDNS:
return ;
case ACCESS_PROVIDERS.QINIU:
diff --git a/ui/src/components/access/AccessFormPorkbunConfig.tsx b/ui/src/components/access/AccessFormPorkbunConfig.tsx
new file mode 100644
index 00000000..20cbc38a
--- /dev/null
+++ b/ui/src/components/access/AccessFormPorkbunConfig.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 AccessConfigForPorkbun } from "@/domain/access";
+
+type AccessFormPorkbunConfigFieldValues = Nullish;
+
+export type AccessFormPorkbunConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormPorkbunConfigFieldValues;
+ onValuesChange?: (values: AccessFormPorkbunConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormPorkbunConfigFieldValues => {
+ return {
+ apiKey: "",
+ secretApiKey: "",
+ };
+};
+
+const AccessFormPorkbunConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormPorkbunConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiKey: z
+ .string()
+ .min(1, t("access.form.porkbun_api_key.placeholder"))
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim(),
+ secretApiKey: z
+ .string()
+ .min(1, t("access.form.porkbun_secret_api_key.placeholder"))
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ );
+};
+
+export default AccessFormPorkbunConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 8c011857..9c33ff4e 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -34,6 +34,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForNamecheap
| AccessConfigForNameDotCom
| AccessConfigForNameSilo
+ | AccessConfigForPorkbun
| AccessConfigForPowerDNS
| AccessConfigForQiniu
| AccessConfigForRainYun
@@ -190,6 +191,11 @@ export type AccessConfigForNS1 = {
apiKey: string;
};
+export type AccessConfigForPorkbun = {
+ apiKey: string;
+ secretApiKey: string;
+};
+
export type AccessConfigForPowerDNS = {
apiUrl: string;
apiKey: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index bbc1ce78..37a3f8a0 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -18,6 +18,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
CLOUDFLARE: "cloudflare",
CLOUDNS: "cloudns",
CMCCCLOUD: "cmcccloud",
+ DESEC: "desec",
DNSLA: "dnsla",
DOGECLOUD: "dogecloud",
DYNV6: "dynv6",
@@ -33,6 +34,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
NAMEDOTCOM: "namedotcom",
NAMESILO: "namesilo",
NS1: "ns1",
+ PORKBUN: "porkbun",
POWERDNS: "powerdns",
QINIU: "qiniu",
RAINYUN: "rainyun",
@@ -41,6 +43,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
TENCENTCLOUD: "tencentcloud",
UCLOUD: "ucloud",
UPYUN: "upyun",
+ VERCEL: "vercel",
VOLCENGINE: "volcengine",
WEBHOOK: "webhook",
WESTCN: "westcn",
@@ -105,6 +108,7 @@ export const accessProvidersMap: Maphttps://www.ibm.com/docs/en/ns1-connect?topic=introduction-using-api",
+ "access.form.porkbun_api_key.label": "Porkbun API key",
+ "access.form.porkbun_api_key.placeholder": "Please enter Porkbun API key",
+ "access.form.porkbun_api_key.tooltip": "For more information, see https://porkbun.com/api/json/v3/documentation",
+ "access.form.porkbun_secret_api_key.label": "Porkbun secret API key",
+ "access.form.porkbun_secret_api_key.placeholder": "Please enter Porkbun secret API key",
+ "access.form.porkbun_secret_api_key.tooltip": "For more information, see https://porkbun.com/api/json/v3/documentation",
"access.form.powerdns_api_url.label": "PowerDNS API URL",
"access.form.powerdns_api_url.placeholder": "Please enter PowerDNS API URL",
"access.form.powerdns_api_url.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index cb1f434c..0b3e158e 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -44,6 +44,7 @@
"provider.cmcccloud": "China Mobile Cloud (ECloud)",
"provider.ctcccloud": "China Telecom Cloud (State Cloud)",
"provider.cucccloud": "China Unicom Cloud",
+ "provider.desec": "deSEC",
"provider.dnsla": "DNS.LA",
"provider.dogecloud": "Doge Cloud",
"provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)",
@@ -75,6 +76,7 @@
"provider.namedotcom": "Name.com",
"provider.namesilo": "NameSilo",
"provider.ns1": "NS1 (IBM NS1 Connect)",
+ "provider.porkbun": "Porkbun",
"provider.powerdns": "PowerDNS",
"provider.qiniu": "Qiniu",
"provider.qiniu.cdn": "Qiniu - CDN (Content Delivery Network)",
@@ -102,6 +104,7 @@
"provider.upyun": "UPYUN",
"provider.upyun.cdn": "UPYUN - CDN (Content Delivery Network)",
"provider.upyun.file": "UPYUN - File Storage",
+ "provider.vercel": "Vercel",
"provider.volcengine": "Volcengine",
"provider.volcengine.alb": "Volcengine - ALB (Application Load Balancer)",
"provider.volcengine.cdn": "Volcengine - 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 12c10595..4a5ead78 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -190,6 +190,12 @@
"access.form.ns1_api_key.label": "NS1 API Key",
"access.form.ns1_api_key.placeholder": "请输入 NS1 API Key",
"access.form.ns1_api_key.tooltip": "这是什么?请参阅 https://www.ibm.com/docs/zh/ns1-connect?topic=introduction-using-api",
+ "access.form.porkbun_api_key.label": "Porkbun API Key",
+ "access.form.porkbun_api_key.placeholder": "请输入 Porkbun API Key",
+ "access.form.porkbun_api_key.tooltip": "这是什么?请参阅 https://porkbun.com/api/json/v3/documentation",
+ "access.form.porkbun_secret_api_key.label": "Porkbun Secret API Key",
+ "access.form.porkbun_secret_api_key.placeholder": "请输入 Porkbun Secret API Key",
+ "access.form.porkbun_secret_api_key.tooltip": "这是什么?请参阅 https://porkbun.com/api/json/v3/documentation",
"access.form.powerdns_api_url.label": "PowerDNS API URL",
"access.form.powerdns_api_url.placeholder": "请输入 PowerDNS API URL",
"access.form.powerdns_api_url.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 1b8f4e24..d3086452 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -44,6 +44,7 @@
"provider.cmcccloud": "移动云",
"provider.ctcccloud": "联通云",
"provider.cucccloud": "天翼云",
+ "provider.desec": "deSEC",
"provider.dnsla": "DNS.LA",
"provider.dogecloud": "多吉云",
"provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN",
@@ -75,6 +76,7 @@
"provider.namedotcom": "Name.com",
"provider.namesilo": "NameSilo",
"provider.ns1": "NS1(IBM NS1 Connect)",
+ "provider.porkbun": "Porkbun",
"provider.powerdns": "PowerDNS",
"provider.qiniu": "七牛云",
"provider.qiniu.cdn": "七牛云 - 内容分发网络 CDN",
@@ -102,6 +104,7 @@
"provider.upyun": "又拍云",
"provider.upyun.cdn": "又拍云 - 云分发 CDN",
"provider.upyun.file": "又拍云 - 云存储",
+ "provider.vercel": "Vercel",
"provider.volcengine": "火山引擎",
"provider.volcengine.alb": "火山引擎 - 应用型负载均衡 ALB",
"provider.volcengine.cdn": "火山引擎 - 内容分发网络 CDN",