From 928a0443cc3b8fcffa0798c85c0a0a040f79a4ed Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 23 Jun 2025 21:43:01 +0800 Subject: [PATCH] feat: new acme dns-01 provider: spaceship --- internal/applicant/providers.go | 17 +++++ internal/domain/access.go | 5 ++ internal/domain/provider.go | 2 + .../providers/spaceship/spaceship.go | 40 +++++++++++ ui/public/imgs/providers/spaceship.png | Bin 0 -> 6623 bytes ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormSpaceshipConfig.tsx | 68 ++++++++++++++++++ ui/src/domain/access.ts | 6 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 6 ++ ui/src/i18n/locales/en/nls.provider.json | 1 + ui/src/i18n/locales/zh/nls.access.json | 6 ++ ui/src/i18n/locales/zh/nls.provider.json | 1 + 13 files changed, 159 insertions(+) create mode 100644 pkg/core/ssl-applicator/acme-dns01/providers/spaceship/spaceship.go create mode 100644 ui/public/imgs/providers/spaceship.png create mode 100644 ui/src/components/access/AccessFormSpaceshipConfig.tsx diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 1d5177bf..c5de24ef 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -38,6 +38,7 @@ import ( pPorkbun "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/porkbun" pPowerDNS "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/powerdns" pRainYun "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/rainyun" + pSpaceship "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/spaceship" pTencentCloud "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud" pTencentCloudEO "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud-eo" pUCloudUDNR "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ucloud-udnr" @@ -582,6 +583,22 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeSpaceship: + { + access := domain.AccessConfigForSpaceship{} + if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pSpaceship.NewChallengeProvider(&pSpaceship.ChallengeProviderConfig{ + ApiKey: access.ApiKey, + ApiSecret: access.ApiSecret, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS, domain.ACMEDns01ProviderTypeTencentCloudEO: { access := domain.AccessConfigForTencentCloud{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 29d07513..178a9d57 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -324,6 +324,11 @@ type AccessConfigForSlackBot struct { DefaultChannelId string `json:"defaultChannelId,omitempty"` } +type AccessConfigForSpaceship struct { + ApiKey string `json:"apiKey"` + ApiSecret string `json:"apiSecret"` +} + type AccessConfigForSSH struct { Host string `json:"host"` Port int32 `json:"port"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 3688d5e3..6deedee8 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -74,6 +74,7 @@ const ( AccessProviderTypeRatPanel = AccessProviderType("ratpanel") AccessProviderTypeSafeLine = AccessProviderType("safeline") AccessProviderTypeSlackBot = AccessProviderType("slackbot") + AccessProviderTypeSpaceship = AccessProviderType("spaceship") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeSSLCOM = AccessProviderType("sslcom") AccessProviderTypeTelegramBot = AccessProviderType("telegrambot") @@ -159,6 +160,7 @@ const ( ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun) ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS) ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun) + ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship) ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS] ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns") ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo") diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/spaceship/spaceship.go b/pkg/core/ssl-applicator/acme-dns01/providers/spaceship/spaceship.go new file mode 100644 index 00000000..c739c11c --- /dev/null +++ b/pkg/core/ssl-applicator/acme-dns01/providers/spaceship/spaceship.go @@ -0,0 +1,40 @@ +package spaceship + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/providers/dns/spaceship" + + "github.com/certimate-go/certimate/pkg/core" +) + +type ChallengeProviderConfig struct { + ApiKey string `json:"apiKey"` + ApiSecret string `json:"apiSecret"` + 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 := spaceship.NewDefaultConfig() + providerConfig.APIKey = config.ApiKey + providerConfig.APISecret = config.ApiSecret + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := spaceship.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/spaceship.png b/ui/public/imgs/providers/spaceship.png new file mode 100644 index 0000000000000000000000000000000000000000..79d6fe2ae65d07e1a94e6b155ce5d1b1878b516b GIT binary patch literal 6623 zcmV<586f6~P)uHL9$%*W>&% zM0pYwC2MVWEi{WrN>?^YgxK5ZDKBF-I%zC0St~F^E-*eSE>kctIV><$^!Ds7FGKkM z|1v#={r>m1#q8_y{Kd%FF+H>@Hj?xD{x2{xFFTkpJfr*l@Gde*^Yrv8FJC7vcPcMU z|Ns9gGjn-?o;5spL{@_(E^RVItnu{zFFA@tWS57FmoYV5CoNquOPnk&I~gBJ0|XT( zFmt%O#wsf_Hd2Q!ID{=UYcMlHF-oH@JFqS}p)_HfFg>X=TAVOKoHJph`1$yBb$~KV zsuC7L{Qv(kL!>Y?QP@_$DuDD=$eaM29alVx_6XEHs5KIh7nBF)KHk zGgFi^Q>xY2-!fdJSX*Z@O_MJ?rdC>fE;)TeM`$oJO)Wd6VP%9aIFKnWYV`Q@DKdJT zqs1mKW~aI0Dl&c^C~Y%jrywCJKtfWryxCM~r{Lr4pP{TWLXb04rC(uf?eXg)B{4Kv zsx(@b`~Cm^{r~Ol?J-B0mYlgXXsQ1G`zkG1CM!KHHi0}oNrQ={G&xHxIczF2g+oYo z{QUfagN!*kMJz8__xb-gajGjdeRFn`GHa#4!pki-ekC$*CNO0#REkPccq=r4E{|<{~9aFfctYXQ3e^L87YJ zKU18Jk)1C;u+GukEiXtbFI+EPl;r5=Cnz*7H+4BpoJL!pFFlVYJ%lVXf;()XA~$*` zIczL2cpW2IU}upmH=rgjQZ6q|BQ<~>K5-~2S|uxJD?45(J$)rKQ79`WbNgpSQ^@&H@Z0^9|fhT@G_kWN$__^Syg|}GlJaUK555q0AujIF-`4$YO}}H@3l1VK6KDWG%Wh==(lg53}m&xtl6m@M4D_DR}VM!EuAl z@H$FPU7L!A6Je+<;2?|3B5x34`-g2NUPClGbrQk|In@Xw#~SY8J{N2=@dNHHE(D%5 z+H}G&%0ZUR?FCcrizj}-1(%%E2u|&pxL^0h6R%mr!yk~AkK=4|$cT+*EI6fhkMqB&TBfKBjvrl*+iP7OF zn9FSlT`ML^AUIB=L1vrqRm;3Dk@yhWCb;3@6@oj7M;E>IZg+|JJ}roVdmM54aC^f8 zNxbJ4;ilrd-nGv%bokHP86}9Vx?{SV`hqQ|)e9$HcPkj&I~B*@&C1LSJL%7KUllK? ziZ$KG1A+%Mt8YzeXy~dCe|SPt6d33$VKR^16L?ky1(_0O6PI}(>UkTP;eQc{jqXT* z9Z=%Mix+=N!EqG)fV`Qw4YV$Set|)9EzQ?=>ounT=@QSY73c;g#yGntY<>Tl;D7xK zSQ9+*&m+&AIRi3+SFfga;|p=$e6w!d1ESU^O&YyRAfB6<+2M=oY#P%&S)7eF`s@kB zmuy65Q!ZK^eRf-b7Vu>#Q{a&lym$nHNf*YLg1OuW#lZ~?9^z#8%uHXhwsNxOHj5um zxI;umEKw%dDrw&^1Sa4S4kL)fs|T;{0YBhAV6A6`iksifG$sc4lzpPO>uNSNxGx0v zm3Xx!jC$>yDn4y5L9l|5(+pXQN(I?>CS09h4*ar-Eynl(DF+vG#U&e=!!t9pIy%4} zqDXs^74{*Pednrigo!e4f4$%kvieO-$nVENVk$DLQM}4uas=QkpnX46bX+qSChyM9 zz7qtQv#nvD=Yo4DHo|d6nA;#eIFHGU=*U7<<{J^ANQ;n!T?#?4*&QHkV^&)xzWY?X zlgV7}OTlG|?Fz+b27`T*7fk7^{lg*F@C+zo0da6Z@Q4v+nOM4lS?hz7)`T`Z!oOoO z)6V)T4B#3hWiZeR-?FT%jm-jD*PY|Xv%Rt{U)FP~H*9ByN!J8lKqsGXf~N)-7q5T7 z6&(MWDJfH&rC|69urDmz8G`1nuH&BBJ!M<&b0jA6ev|W{_(2jEVElD|W>}dbLXl-4 z;ZrDbN2XzsOt6~Q;e4l!d@D>0Q9;0A6|iB0fW$}Q$5aTkV*-I!r#&vMeXbGBz8SYl4BfXK(n>uS9E zgQLmI4qP~a*v&AmA0y8I4G+iU<}`Nj*r`*Kij#_qbJjl!#Wf2ghO#n)Wn~dl%wXW; zr*RYocxkg|^KOk#O*I0&ZX>t{9c?VT%P8>-yBS7z*r_=H?>Q7a z^--gEXV|GML*JFp$^w_~u zA#u*090+_@oOt3?pbZy>ofF9M{TOR{997fo=~ZHey+ZngQQ{ewiNqt#iOEhQ7bVWg zIaD0X5;y*Q%8+Ig&gZ1%bB?&Gn|NN$JYI9Zi6QW7m#G;|xM++WjPO)QoRfoK)^Flf zVUq0%TQKD1Z0B&C)as_FW}j*T4zZkDMv+b@UI>Q72&Mv`Azcbam6H>fQyeM?|2Z@B znnGa{&gX1TGZZD1*F`nA_>gdj88*3;Y)82>j9X$4crYo)#T6IdZQL0qc@p7kGjc~D z#}LTL@=MmIG?n|bSiwExni5;OW_T=>cq#;L5YXmjKH2KKRbg{-Z8yMz7MK;OkBVwZ zvA%h&^3v&cV@#1jb^sQgKZu)&f`cE84Q7Sj!hg>6&nz2dO3X1tz+~b?81g&7>&Q2L zfK&Dfc)dp7gfMHD5UGOOX$)d#%pe1gfEPM`5bRi((^LPs`I~jDr-H-<2LCeOA*M0r zP!&NGd=9EICF$k5(&px-Xfxa+tq}&7kf}~Pj5tHi>BUsw9M;re)>yG%o z2{0eo2K2iT4$L|ksUBKd-c)atExK$`(CRT%U@9?n(7Qg?2pg%hiRwwN# z&Us((U8cl$)TmKqlAD ztm5KZw;I1Y1@Mp|z8o`ra&Li5D(rSzZs24V<)>vypvuZ%`lU&h%i`q)Y#-(G#=V5L=0$(;|=#{4F)DH~uoS7&xaz|V@ zjWEou2G-rsP=V$XdkI=4iu@M6o=~Y4V=X1kK0F@JCz`E{$1tTVa{S~?gw5PEUXHQZ zB+TeP@f_6ZnIllAk$c?;vl>E2b0PA<*ZsnZPNaWW?NcMD@zEqVd-0+?;fY9H9#tyK zRqC;LRmP>g_RLMkTo@R=w46%Jcs4wTNPK4Ixic1>MmJMV@ekZk?!wSen)ps~Vxp#5 zz}xMGp2&PWdDTA6&CNcg?7F&;+!T5)T^+9$xh5nO#>cZ|QSmlg%^=Gn@tK)3N%tI? zm>A+>?mm|Dq=np2TI_@hLB*Xe8jThkyZd;Jr;kqv?0uC{DJ|u7q2TdSOF2Efg*q?M}tW zxM((NzLKtHE7e+&Qu_=T=xNB^aen3w)M_NNSO|v5WZ=B;JSs52sMY4fqY-8+bLsKy z+}svAJB6-np)0kyd^%kudKNr}km0oh1vPbsiNsJiIEFf)Q(+Rz_H)%+=|~%S3Xz2yP?HB_Uqf z-xw3@G?aJ;nV4i*EOLZtd9mSD7Kwd)nnIe4_bHv)W!se^Is?mPcIWEawea@wCr^mt zRedD(CNBb>M#wu*&HU=WU;T44>j&rk;gf>D{Oz~U(C|Bg*j>9SJa$!xE3#{nvo%ec zD6CwaQXlWC)M9!imYXYb<;&ys@hXNAlN4ZjT*y~RYhC#&m7Xue^q5@KT8Pzplb5kf z@I(^cF%dGqy-%?3kItdL1+%_u#KRL4X=1^uSX{j8pr#3Xyi1Q`Q3qxCP?-$JwB4>E zImTDw@ven@9ahUPl1g0?R5+Zxcm8-W^nOePL;aJo_Q)P(>+BEA7W$vsjSJT)i{6d$tU(F3iMKb;bci> zk)KS*&!0TGDD32TX)ESb;4O988<`cDb*-Qi)7gX}GXZ}Y^N%0)eLrm2y@UVxB3wL= z`NiwWr-yx#D2V@XrzXC@-7gtW{KW5cX?NCKJvP_xW0lLXD+w4sGE=9Gbn3?Rk|zb- z8Iw23F#QoHIsS3h-i+1^rz-{e*52?kxQJFyB;>C)Z~i4FX4qfr#Nxcz37;?zdSc>4 z`$gruFkBgryBJ#VMNd{?n6@+p<1e2p#c`Q?x?X-M6TXv6GiPh>)HrC;ZZ)Gwk zUy-ou?EP?^Os&`IYYmeloi3#tuE_NT?wkBvRJdLxagnylTZ=OE_>puWR&Y`-cNOAJ z`KnGU7vN>fW^b9jW!jdBMD6%$$Mt`Hy*_W;ut9@{X~gpumFzkwz!%J`sk5KAD#OL; zWD|B`5iaZ2Gemk({*m-6e(96P3o)sy(-rsS37AllA#K%54KA(t$+=RjAWSapZpG8H z0_1(d%cg-lA4OidWAlx#Z?N|5zgKeqsQp1~_({QSo;AP9UY}qnx zKFYgt$CoQ(+}6MS*FVOW?;Mnf4`;rm!9xZuNW@mz&&Bc9W1M<@hWlPQj$OGV$I`DA z;JC{b7rk)&cno)SPscl<)sC3V;3CDh9}B=EIqvY8oQ@O-9j(ibav?F~g~Ty#Km6kS z_5%MwgBA?aVD6JQ4rdnG?}Ue-jqX?6{g#)N#;07`l+}@Me@TdSJ6(%N+dkP|7z=`fvZS;683E6q50NLv%!ifhhMV&gx5I9aZXR6}RB@H&S*798(9tqExl~;@ zRHS9pY8kK<3ALTxBG_s4e66=kt1T?76{&=sI+;)|>@1WERno}4ot*)~0LOp;v-mq% zJ>dBeI3{Mx+h4By)y>WMW|G@)2c46vVrexs@T-F6Ra057ho=UXXXqfKJYLRM@jHd8PF-iKx0J8Kgb7%iqfjX9?UL}8cPM!N#HF)hZU7$R=IreD z=xgVL_j$LyG#X7*jmGD3ow~BLw7eY>YYVk-(%|!TIxTz#6<;MP6y>V4g({Jh?=AH9 z7Q!x6+u5p<2FMZH+WNb^Pxu{E;`u9MmR?-B(hcClzZ`ZB-*t}%XZPli>gwteO=)$C zdT8#@cCA*ZBgUl`snYnD;o2K2TAP*bK%A!c+ek(UQ<6q|$@3^VKaJY~DpsMoVrOb7PO zZD!Z)Fzp`9EP$({j1wBM42JhW*k>jtGEZOe=0kwDZTmX`yB)q+6ELnJi7jyxBkA$aBClenya1q?R{Z+*H zMnxGLP2}Y>98E0x*KKXR;kKUjOn3uwrxTGc67FG=aBz|@9Pe1*%;q%Ww|91MrZR5s`gZt=S6_1!$#)U&9B7cJ7NG7&4ez$nr z0IRYE8vowzy`O&i2;k{|e>iU3*U;yCvDHYeCf%qM%JpVAI<`4N;{M+7`C&qy{`cwQ z#$B9p7{-@aox!E1GzG(L1abL}uMqx-sOjI1`xU0u&AciquyJM?6T`X@0GVGM3~@jD z=!0*+9fziKUS3rdkLom)7>zNb7Fz8czz6!(!5{oYxDO9Fe}D6^GcAb9YYc2o46`}F z@m0bfeel5tA00UG{r7o!=!f9EYO`#DA+b0A*_*(?VN~COd-E{t`!r1~1xHo(Ow4e6 dwebH7FaTFL&=(M$y*2;<002ovPDHLkV1gFws=NRI literal 0 HcmV?d00001 diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 09553347..d49fe661 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -68,6 +68,7 @@ import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig"; +import AccessFormSpaceshipConfig from "./AccessFormSpaceshipConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig"; @@ -301,6 +302,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.SLACKBOT: return ; + case ACCESS_PROVIDERS.SPACESHIP: + return ; case ACCESS_PROVIDERS.SSH: return ; case ACCESS_PROVIDERS.TELEGRAMBOT: diff --git a/ui/src/components/access/AccessFormSpaceshipConfig.tsx b/ui/src/components/access/AccessFormSpaceshipConfig.tsx new file mode 100644 index 00000000..7437a2f6 --- /dev/null +++ b/ui/src/components/access/AccessFormSpaceshipConfig.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod/v4"; + +import { type AccessConfigForSpaceship } from "@/domain/access"; + +type AccessFormSpaceshipConfigFieldValues = Nullish; + +export type AccessFormSpaceshipConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormSpaceshipConfigFieldValues; + onValuesChange?: (values: AccessFormSpaceshipConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormSpaceshipConfigFieldValues => { + return { + apiKey: "", + apiSecret: "", + }; +}; + +const AccessFormSpaceshipConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSpaceshipConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiKey: z.string().nonempty(t("access.form.spaceship_api_key.placeholder")), + apiSecret: z.string().nonempty(t("access.form.spaceship_api_secret.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormSpaceshipConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index a2dbde73..df0e8a06 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -62,6 +62,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForRatPanel | AccessConfigForSafeLine | AccessConfigForSlackBot + | AccessConfigForSpaceship | AccessConfigForSSH | AccessConfigForSSLCom | AccessConfigForTelegramBot @@ -389,6 +390,11 @@ export type AccessConfigForSlackBot = { defaultChannelId?: string; }; +export type AccessConfigForSpaceship = { + apiKey: string; + apiSecret: string; +}; + export type AccessConfigForSSH = { host: string; port: number; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index fb1e2e12..12511f81 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -65,6 +65,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ RATPANEL: "ratpanel", SAFELINE: "safeline", SLACKBOT: "slackbot", + SPACESHIP: "spaceship", SSH: "ssh", SSLCOM: "sslcom", TELEGRAMBOT: "telegrambot", @@ -164,6 +165,7 @@ export const accessProvidersMap: Maphttps://www.youtube.com/watch?v=Uz5Yi5C2pwQ", + "access.form.spaceship_api_key.label": "Spaceship API key", + "access.form.spaceship_api_key.placeholder": "Please enter Spaceship API key", + "access.form.spaceship_api_key.tooltip": "For more information, see https://www.spaceship.com/application/api-manager/", + "access.form.spaceship_api_secret.label": "Spaceship API secret", + "access.form.spaceship_api_secret.placeholder": "Please enter Spaceship API secret", + "access.form.spaceship_api_secret.tooltip": "For more information, see https://www.spaceship.com/application/api-manager/", "access.form.ssh_host.label": "Server host", "access.form.ssh_host.placeholder": "Please enter server host", "access.form.ssh_port.label": "Server port", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index f2a07351..fe76a727 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -130,6 +130,7 @@ "provider.ratpanel.site": "RatPanel - Website", "provider.safeline": "SafeLine", "provider.slackbot": "Slack Bot", + "provider.spaceship": "Spaceship", "provider.ssh": "Remote host (SSH)", "provider.sslcom": "SSL.com", "provider.telegrambot": "Telegram Bot", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index a3852926..837ccc17 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -387,6 +387,12 @@ "access.form.slackbot_default_channel_id.label": "默认的 Slack 频道 ID(可选)", "access.form.slackbot_default_channel_id.placeholder": "请输入默认的 Slack 频道 ID", "access.form.slackbot_default_channel_id.tooltip": "如何获取此参数?请参阅 https://www.youtube.com/watch?v=Uz5Yi5C2pwQ", + "access.form.spaceship_api_key.label": "Spaceship API Key", + "access.form.spaceship_api_key.placeholder": "请输入 Spaceship API Key", + "access.form.spaceship_api_key.tooltip": "这是什么?请参阅 https://www.spaceship.com/application/api-manager/", + "access.form.spaceship_api_secret.label": "Spaceship API Secret", + "access.form.spaceship_api_secret.placeholder": "请输入 Spaceship API Secret", + "access.form.spaceship_api_secret.tooltip": "这是什么?请参阅 https://www.spaceship.com/application/api-manager/", "access.form.ssh_host.label": "服务器地址", "access.form.ssh_host.placeholder": "请输入服务器地址", "access.form.ssh_port.label": "服务器端口", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 36e01d37..fa16398a 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -130,6 +130,7 @@ "provider.ratpanel.site": "耗子面板 - 网站", "provider.safeline": "雷池", "provider.slackbot": "Slack 机器人", + "provider.spaceship": "Spaceship", "provider.ssh": "远程主机(SSH)", "provider.sslcom": "SSL.com", "provider.telegrambot": "Telegram 机器人",