From ab61cadd371f4a0a984eb4b7c1674a390ed8a1db Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 5 Nov 2025 23:04:53 +0800 Subject: [PATCH] feat(provider): new acme dns-01 provider: infomaniak --- .../certapply/applicators/sp_infomaniak.go | 29 ++++++++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 2 +- .../providers/infomaniak/infomaniak.go | 38 +++++++++++++ ui/public/imgs/providers/infomaniak.png | Bin 0 -> 3005 bytes .../forms/AccessConfigFieldsProvider.tsx | 2 + .../AccessConfigFieldsProviderInfomaniak.tsx | 52 ++++++++++++++++++ ui/src/domain/provider.ts | 2 + ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/zh/nls.access.json | 3 + 10 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 internal/certapply/applicators/sp_infomaniak.go create mode 100644 pkg/core/ssl-applicator/acme-dns01/providers/infomaniak/infomaniak.go create mode 100644 ui/public/imgs/providers/infomaniak.png create mode 100644 ui/src/components/access/forms/AccessConfigFieldsProviderInfomaniak.tsx diff --git a/internal/certapply/applicators/sp_infomaniak.go b/internal/certapply/applicators/sp_infomaniak.go new file mode 100644 index 000000000..fb59e1976 --- /dev/null +++ b/internal/certapply/applicators/sp_infomaniak.go @@ -0,0 +1,29 @@ +package applicators + +import ( + "fmt" + + "github.com/go-acme/lego/v4/challenge" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/infomaniak" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeInfomaniak, func(options *ProviderFactoryOptions) (challenge.Provider, error) { + credentials := domain.AccessConfigForInfomaniak{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := infomaniak.NewChallengeProvider(&infomaniak.ChallengeProviderConfig{ + AccessToken: credentials.AccessToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/domain/access.go b/internal/domain/access.go index 29f5b6272..20207c419 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -274,6 +274,10 @@ type AccessConfigForHuaweiCloud struct { EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"` } +type AccessConfigForInfomaniak struct { + AccessToken string `json:"accessToken"` +} + type AccessConfigForIONOS struct { ApiKeyPublicPrefix string `json:"apiKeyPublicPrefix"` ApiKeySecret string `json:"apiKeySecret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 8bab76c08..aa1c63556 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -60,7 +60,7 @@ const ( AccessProviderTypeHostingde = AccessProviderType("hostingde") AccessProviderTypeHostinger = AccessProviderType("hostinger") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") - AccessProviderTypeInfomaniak = AccessProviderType("infomaniak") // Infomaniak(预留) + AccessProviderTypeInfomaniak = AccessProviderType("infomaniak") AccessProviderTypeIONOS = AccessProviderType("ionos") AccessProviderTypeJDCloud = AccessProviderType("jdcloud") AccessProviderTypeKong = AccessProviderType("kong") diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/infomaniak/infomaniak.go b/pkg/core/ssl-applicator/acme-dns01/providers/infomaniak/infomaniak.go new file mode 100644 index 000000000..8ae9c5cb9 --- /dev/null +++ b/pkg/core/ssl-applicator/acme-dns01/providers/infomaniak/infomaniak.go @@ -0,0 +1,38 @@ +package infomaniak + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/providers/dns/infomaniak" + + "github.com/certimate-go/certimate/pkg/core" +) + +type ChallengeProviderConfig struct { + AccessToken string `json:"accessToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) { + if config == nil { + return nil, errors.New("the configuration of the acme challenge provider is nil") + } + + providerConfig := infomaniak.NewDefaultConfig() + providerConfig.AccessToken = config.AccessToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := infomaniak.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/infomaniak.png b/ui/public/imgs/providers/infomaniak.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9f8931d482a4907117a9658a722475621d0a82 GIT binary patch literal 3005 zcmd^>=|9zpAH}~^sAMT+Nycp^iXO)=IK5<^L1E-IBJLMEd~D(R+NJCWT*WGO=0 zEZHO3vs`y9*Lp42#rf6$FwcJH^*ZnKI!``NK2Od)6C=G1>m=3@1hK(DU)%IYR{b>J zsvnFBJV*Nx;_f<@?x&m`+&yjG>vz@)P!4+FadsBN`vbSrsy#_(7B212(=?t=< zl7VI>Zr4IC9k2RfT?WAzT>XG8&v5wzHpW9J4xN+uEd_3$P&@#;x5(;&SuqmZ!4rY# zHs}|iaSV6ra3~vFpF=kf(#Z&EhH)XSY82p8A~geF**mCRM?b*IgbGr3i_}*9P>+ql@_Ks*Fv7T>4QH3hDx$m~Wr9mcQl?;Pq!F}8r*?+C0%{|vaP z5Q;%$E7~U^^&AfxaUdJ)C1m#?qZ{H05KlyU7v9ow`5mU&$nV3sGMq1mWCHeN;(j9@ zG+_-D0?}AS#qb<1eSl95$mQS9|>g zm*25520K$QG6%Ph;G%-Hh!?%+o`zQ?WRp=ch>g#X-HWGR5LAnvDQM>-vJDn*FtLF5 z3`jf!ZwvzJal8;lMKCCYVIf3fVN!^Djo6k5t5Q5{f5#IAj)lvG~Vi$QdTln;a+xk=iO;O6^pF%5sO*vyAz6=9!WpUNZ(UrXx zW~Nyjm9>wH2FPn}FtrRVea!XRf9LC z(UgLkO0jz5{@|VGqy($`W(#aOj+HCve+ag0ia(rFHYS`Zrt~3rVzBAWE3re5IpPy+ z>mUimE}JG10UcN#uBmKfD#^~3v+~MB;%5uiCz!`1q@?baay#WG6IJ4J+Sym_;lO3l zW<#mkL${nC)(YkMK8h9?c#$7A7gWGrN@8teFD^O6>BZQ1R==?F`oy#oDs8YB*dUud zw$lB&{@`co-18%jCz=<2f4a>miMA;An=@;bx5m3DQ9d#_Yym5V!01|>_;r?)QbnofJ6dxk&#*|mzq zX_rlEJT^vkMF)y0N+Wd>jIKV9wObR5XxX8+Uq*+>1{LmEcaLn`pKL@G*J5opWB+QL z7)sHICZ(p+)G1o0&luLd4EfG!6uTo5a5ncVfucWh#zvaz0;#w(5e!JCg%@_m~ zof8bp3RR{wZ&)gnR4cZLt;@N(ez0XwXrj|>Uwk*ik1}SvL+iMc3?ssv8Q}F#zdcfh zM7>^rHvDpQk-cDMqNQnb!GU}2wK8_T9Epe5ZVOF4;!Is!GSk1xLTyp8jZ-T0HR9 zYT$|^dA;nFEMs2p4u&K>WNVuEjqQW#$2djPC9n6^iT0~mIVf}p zaruMRw$n=SzUld1LgwRVY4`U>nWqbeyTz8#Hwm;YOmF!1RV}$8rF>jQUvd1KRhUGY z$YdJJO3C(D&t7G@S5Y_W$G6iH_ILSS_w~EKzGXOkgxh^_cF&;(yY&L=Gg3LtPsAG>-^ft!dx(OB~xxOk~~A% zn>?pGE*5NF&-mM>&D+vr{QTm-5yoMcR5R7SFd0|AljPD&Gvs&o?NStP&MNMt=z0!o zxK<1Ow-`L)vn>CXAiEv9rO|Jt{bP?420BLC*;?ms{|~TF B+3f%T literal 0 HcmV?d00001 diff --git a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx index 459286478..2d59bf3a0 100644 --- a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx +++ b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx @@ -50,6 +50,7 @@ import AccessConfigFieldsProviderHetzner from "./AccessConfigFieldsProviderHetzn import AccessConfigFieldsProviderHostingde from "./AccessConfigFieldsProviderHostingde"; import AccessConfigFieldsProviderHostinger from "./AccessConfigFieldsProviderHostinger"; import AccessConfigFieldsProviderHuaweiCloud from "./AccessConfigFieldsProviderHuaweiCloud"; +import AccessConfigFieldsProviderInfomaniak from "./AccessConfigFieldsProviderInfomaniak"; import AccessConfigFieldsProviderIONOS from "./AccessConfigFieldsProviderIONOS"; import AccessConfigFieldsProviderJDCloud from "./AccessConfigFieldsProviderJDCloud"; import AccessConfigFieldsProviderKong from "./AccessConfigFieldsProviderKong"; @@ -151,6 +152,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 { + accessToken: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => { + const { t } = i18n; + + return z.object({ + accessToken: z.string().nonempty(t("access.form.infomaniak_access_token.placeholder")), + }); +}; + +const _default = Object.assign(AccessConfigFormFieldsProviderInfomaniak, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 979436964..ddc9904f2 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -193,6 +193,7 @@ export const accessProvidersMap: Maphttps://support.huaweicloud.com/intl/en-us/usermanual-em/em_03_0000.html", + "access.form.infomaniak_access_token.label": "Infomaniak access token", + "access.form.infomaniak_access_token.placeholder": "Please enter Infomaniak access token", + "access.form.infomaniak_access_token.tooltip": "For more information, see https://manager.infomaniak.com/v3/infomaniak-api", "access.form.ionos_api_key_public_prefix.label": "IONOS API key public prefix", "access.form.ionos_api_key_public_prefix.placeholder": "Please enter IONOS API key public prefix", "access.form.ionos_api_key_public_prefix.tooltip": "For more information, see https://developer.hosting.ionos.com/docs/getstarted", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index ffb82b0ba..37f8eb381 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -322,6 +322,9 @@ "access.form.huaweicloud_enterprise_project_id.label": "华为云企业项目 ID(可选)", "access.form.huaweicloud_enterprise_project_id.placeholder": "请输入华为云企业项目 ID", "access.form.huaweicloud_enterprise_project_id.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/usermanual-em/zh-cn_topic_0126101490.html", + "access.form.infomaniak_access_token.label": "Infomaniak AccessToken", + "access.form.infomaniak_access_token.placeholder": "请输入 Infomaniak AccessToken", + "access.form.infomaniak_access_token.tooltip": "这是什么?请参阅 https://manager.infomaniak.com/v3/infomaniak-api", "access.form.ionos_api_key_public_prefix.label": "IONOS API Key Public Prefix", "access.form.ionos_api_key_public_prefix.placeholder": "请输入 IONOS API Key Public Prefix", "access.form.ionos_api_key_public_prefix.tooltip": "这是什么?请参阅 https://developer.hosting.ionos.com/docs/getstarted",