diff --git a/internal/certacme/certifiers/sp_simplycom.go b/internal/certacme/certifiers/sp_simplycom.go new file mode 100644 index 000000000..b693c41bc --- /dev/null +++ b/internal/certacme/certifiers/sp_simplycom.go @@ -0,0 +1,27 @@ +package certifiers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + chlgimpl "github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/simplycom" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + ACMEDns01Registries.MustRegister(domain.ACMEDns01ProviderTypeSimplyCom, func(options *ProviderFactoryOptions) (core.ACMEChallenger, error) { + credentials := domain.AccessConfigForSimplyCom{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := chlgimpl.NewChallenger(&chlgimpl.ChallengerConfig{ + AccountNumber: credentials.AccountNumber, + ApiKey: credentials.ApiKey, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return provider, err + }) +} diff --git a/internal/domain/access.go b/internal/domain/access.go index d21c820ae..0b4011bfb 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -546,6 +546,11 @@ type AccessConfigForSamWAF struct { AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } +type AccessConfigForSimplyCom struct { + AccountNumber string `json:"accountNumber"` + ApiKey string `json:"apiKey"` +} + type AccessConfigForSlackBot struct { BotToken string `json:"botToken"` ChannelId string `json:"channelId,omitempty"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 603d2f8ec..df14a7db5 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -114,6 +114,7 @@ const ( AccessProviderTypeSafeLine = AccessProviderType("safeline") AccessProviderTypeSamWAF = AccessProviderType("samwaf") AccessProviderTypeSectigo = AccessProviderType("sectigo") + AccessProviderTypeSimplyCom = AccessProviderType("simplycom") AccessProviderTypeSlackBot = AccessProviderType("slackbot") AccessProviderTypeSpaceship = AccessProviderType("spaceship") AccessProviderTypeSSH = AccessProviderType("ssh") @@ -256,6 +257,7 @@ const ( ACMEDns01ProviderTypeRegru = ACMEDns01ProviderType(AccessProviderTypeRegru) ACMEDns01ProviderTypeRFC2136 = ACMEDns01ProviderType(AccessProviderTypeRFC2136) ACMEDns01ProviderTypeRuCenter = ACMEDns01ProviderType(AccessProviderTypeRuCenter) + ACMEDns01ProviderTypeSimplyCom = ACMEDns01ProviderType(AccessProviderTypeSimplyCom) ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship) ACMEDns01ProviderTypeTechnitiumDNS = ACMEDns01ProviderType(AccessProviderTypeTechnitiumDNS) ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS] diff --git a/pkg/core/certifier/challengers/dns01/simplycom/simplycom.go b/pkg/core/certifier/challengers/dns01/simplycom/simplycom.go new file mode 100644 index 000000000..92ac9bfaf --- /dev/null +++ b/pkg/core/certifier/challengers/dns01/simplycom/simplycom.go @@ -0,0 +1,40 @@ +package gandinet + +import ( + "fmt" + "time" + + "github.com/go-acme/lego/v5/providers/dns/simply" + + "github.com/certimate-go/certimate/pkg/core" +) + +type ChallengerConfig struct { + AccountNumber string `json:"accountNumber"` + ApiKey string `json:"apiKey"` + DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int `json:"dnsTTL,omitempty"` +} + +func NewChallenger(config *ChallengerConfig) (core.ACMEChallenger, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the acme challenge provider is nil") + } + + providerConfig := simply.NewDefaultConfig() + providerConfig.AccountName = config.AccountNumber + providerConfig.APIKey = config.ApiKey + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = config.DnsTTL + } + + provider, err := simply.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/simplycom.png b/ui/public/imgs/providers/simplycom.png new file mode 100644 index 000000000..fca2b21e6 Binary files /dev/null and b/ui/public/imgs/providers/simplycom.png differ diff --git a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx index a6cc62e9c..38caff5fe 100644 --- a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx +++ b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx @@ -98,6 +98,7 @@ import AccessConfigFieldsProviderS3 from "./AccessConfigFieldsProviderS3"; import AccessConfigFieldsProviderSafeLine from "./AccessConfigFieldsProviderSafeLine"; import AccessConfigFieldsProviderSamWAF from "./AccessConfigFieldsProviderSamWAF"; import AccessConfigFieldsProviderSectigo from "./AccessConfigFieldsProviderSectigo"; +import AccessConfigFieldsProviderSimplyCom from "./AccessConfigFieldsProviderSimplyCom"; import AccessConfigFieldsProviderSlackBot from "./AccessConfigFieldsProviderSlackBot"; import AccessConfigFieldsProviderSpaceship from "./AccessConfigFieldsProviderSpaceship"; import AccessConfigFieldsProviderSSH from "./AccessConfigFieldsProviderSSH"; @@ -222,6 +223,7 @@ const providerComponentMap: Partial { + 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 { + accountNumber: "", + apiKey: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => { + const { t: _ } = i18n; + + return z.object({ + accountNumber: z.string().nonempty(), + apiKey: z.string().nonempty(), + }); +}; + +const _default = Object.assign(AccessConfigFormFieldsProviderSimplyCom, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 155b66a31..0e75c8ea3 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -114,6 +114,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ SAFELINE: "safeline", SAMWAF: "samwaf", SECTIGO: "sectigo", + SIMPLYCOM: "simplycom", SLACKBOT: "slackbot", SPACESHIP: "spaceship", SSH: "ssh", @@ -251,6 +252,7 @@ export const accessProvidersMap: Maphttps://www.sectigo.com/enterprise-solutions/certificate-manager/integrations-acme" }, + "simplycom_account_number": { + "label": "Simply.com account number", + "placeholder": "Please enter Simply.com account number", + "tooltip": "For more information, see https://www.simply.com/en/docs/api/" + }, + "simplycom_api_key": { + "label": "Simply.com API key", + "placeholder": "Please enter Simply.com API key", + "tooltip": "For more information, see https://www.simply.com/en/docs/api/" + }, "slackbot_token": { "label": "Slack bot token", "placeholder": "Please enter Slack bot token", diff --git a/ui/src/i18n/resources/en/nls.provider.json b/ui/src/i18n/resources/en/nls.provider.json index dde11668f..445499f3c 100644 --- a/ui/src/i18n/resources/en/nls.provider.json +++ b/ui/src/i18n/resources/en/nls.provider.json @@ -230,6 +230,7 @@ "safeline": "SafeLine", "samwaf": "SamWAF", "sectigo": "Sectigo", + "simplycom": "Simply.com", "slackbot": "Slack Bot", "spaceship": "Spaceship", "ssh": "Remote host (SSH)", diff --git a/ui/src/i18n/resources/zh/nls.access.json b/ui/src/i18n/resources/zh/nls.access.json index b19723b0c..f3cd1718d 100644 --- a/ui/src/i18n/resources/zh/nls.access.json +++ b/ui/src/i18n/resources/zh/nls.access.json @@ -1237,6 +1237,16 @@ "sectigo_eab": { "guide": "点击下方链接了解如何获取 Sectigo EAB:
https://www.sectigo.com/enterprise-solutions/certificate-manager/integrations-acme" }, + "simplycom_account_number": { + "label": "Simply.com 账号", + "placeholder": "请输入 Simply.com 账号", + "tooltip": "这是什么?请参阅 https://www.simply.com/en/docs/api/" + }, + "simplycom_api_key": { + "label": "Simply.com API Key", + "placeholder": "请输入 Simply.com API Key", + "tooltip": "这是什么?请参阅 https://www.simply.com/en/docs/api/" + }, "slackbot_token": { "label": "Slack 机器人 Token", "placeholder": "请输入 Slack 机器人 Token", diff --git a/ui/src/i18n/resources/zh/nls.provider.json b/ui/src/i18n/resources/zh/nls.provider.json index 593626227..96abf88b2 100644 --- a/ui/src/i18n/resources/zh/nls.provider.json +++ b/ui/src/i18n/resources/zh/nls.provider.json @@ -230,6 +230,7 @@ "safeline": "雷池", "samwaf": "SamWAF", "sectigo": "Sectigo", + "simplycom": "Simply.com", "slackbot": "Slack 机器人", "spaceship": "Spaceship", "ssh": "远程主机(SSH)",