mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-19 01:41:55 +08:00
gts support
This commit is contained in:
parent
bff18a7be7
commit
8bec234fe8
@ -39,16 +39,19 @@ const defaultSSLProvider = "letsencrypt"
|
||||
const (
|
||||
sslProviderLetsencrypt = "letsencrypt"
|
||||
sslProviderZeroSSL = "zerossl"
|
||||
sslProviderGts = "gts"
|
||||
)
|
||||
|
||||
const (
|
||||
zerosslUrl = "https://acme.zerossl.com/v2/DV90"
|
||||
letsencryptUrl = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
gtsUrl = "https://dv.acme-v02.api.pki.goog/directory"
|
||||
)
|
||||
|
||||
var sslProviderUrls = map[string]string{
|
||||
sslProviderLetsencrypt: letsencryptUrl,
|
||||
sslProviderZeroSSL: zerosslUrl,
|
||||
sslProviderGts: gtsUrl,
|
||||
}
|
||||
|
||||
const defaultEmail = "536464346@qq.com"
|
||||
@ -157,10 +160,13 @@ type SSLProviderConfig struct {
|
||||
}
|
||||
|
||||
type SSLProviderConfigContent struct {
|
||||
Zerossl struct {
|
||||
EabHmacKey string `json:"eabHmacKey"`
|
||||
EabKid string `json:"eabKid"`
|
||||
}
|
||||
Zerossl SSLProviderEab `json:"zerossl"`
|
||||
Gts SSLProviderEab `json:"gts"`
|
||||
}
|
||||
|
||||
type SSLProviderEab struct {
|
||||
EabHmacKey string `json:"eabHmacKey"`
|
||||
EabKid string `json:"eabKid"`
|
||||
}
|
||||
|
||||
func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, error) {
|
||||
@ -247,6 +253,12 @@ func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.
|
||||
Kid: sslProvider.Config.Zerossl.EabKid,
|
||||
HmacEncoded: sslProvider.Config.Zerossl.EabHmacKey,
|
||||
})
|
||||
case sslProviderGts:
|
||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: sslProvider.Config.Gts.EabKid,
|
||||
HmacEncoded: sslProvider.Config.Gts.EabHmacKey,
|
||||
})
|
||||
|
||||
case sslProviderLetsencrypt:
|
||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
|
28
ui/public/imgs/providers/google.svg
Normal file
28
ui/public/imgs/providers/google.svg
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-0.5 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Google-color</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
|
||||
</defs>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Color-" transform="translate(-401.000000, -860.000000)">
|
||||
<g id="Google" transform="translate(401.000000, 860.000000)">
|
||||
<path d="M9.82727273,24 C9.82727273,22.4757333 10.0804318,21.0144 10.5322727,19.6437333 L2.62345455,13.6042667 C1.08206818,16.7338667 0.213636364,20.2602667 0.213636364,24 C0.213636364,27.7365333 1.081,31.2608 2.62025,34.3882667 L10.5247955,28.3370667 C10.0772273,26.9728 9.82727273,25.5168 9.82727273,24" id="Fill-1" fill="#FBBC05">
|
||||
|
||||
</path>
|
||||
<path d="M23.7136364,10.1333333 C27.025,10.1333333 30.0159091,11.3066667 32.3659091,13.2266667 L39.2022727,6.4 C35.0363636,2.77333333 29.6954545,0.533333333 23.7136364,0.533333333 C14.4268636,0.533333333 6.44540909,5.84426667 2.62345455,13.6042667 L10.5322727,19.6437333 C12.3545909,14.112 17.5491591,10.1333333 23.7136364,10.1333333" id="Fill-2" fill="#EB4335">
|
||||
|
||||
</path>
|
||||
<path d="M23.7136364,37.8666667 C17.5491591,37.8666667 12.3545909,33.888 10.5322727,28.3562667 L2.62345455,34.3946667 C6.44540909,42.1557333 14.4268636,47.4666667 23.7136364,47.4666667 C29.4455,47.4666667 34.9177955,45.4314667 39.0249545,41.6181333 L31.5177727,35.8144 C29.3995682,37.1488 26.7323182,37.8666667 23.7136364,37.8666667" id="Fill-3" fill="#34A853">
|
||||
|
||||
</path>
|
||||
<path d="M46.1454545,24 C46.1454545,22.6133333 45.9318182,21.12 45.6113636,19.7333333 L23.7136364,19.7333333 L23.7136364,28.8 L36.3181818,28.8 C35.6879545,31.8912 33.9724545,34.2677333 31.5177727,35.8144 L39.0249545,41.6181333 C43.3393409,37.6138667 46.1454545,31.6490667 46.1454545,24" id="Fill-4" fill="#4285F4">
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -1,7 +1,7 @@
|
||||
export type Setting = {
|
||||
export type Setting<T> = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
content?: EmailsSetting | NotifyTemplates | NotifyChannels | SSLProviderSetting;
|
||||
content?: T;
|
||||
};
|
||||
|
||||
export type EmailsSetting = {
|
||||
@ -53,7 +53,7 @@ export const defaultNotifyTemplate: NotifyTemplate = {
|
||||
content: "有 {COUNT} 张证书即将过期,域名分别为 {DOMAINS},请保持关注!",
|
||||
};
|
||||
|
||||
export type SSLProvider = "letsencrypt" | "zerossl";
|
||||
export type SSLProvider = "letsencrypt" | "zerossl" | "gts";
|
||||
|
||||
export type SSLProviderSetting = {
|
||||
provider: SSLProvider;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useContext, useEffect, useState, createContext } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
@ -14,93 +14,71 @@ import { getErrMessage } from "@/lib/error";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SSLProvider as SSLProviderType, SSLProviderSetting, Setting } from "@/domain/settings";
|
||||
import { getSetting, update } from "@/repository/settings";
|
||||
import { produce } from "immer";
|
||||
|
||||
type SSLProviderContext = {
|
||||
setting: Setting<SSLProviderSetting>;
|
||||
onSubmit: (data: Setting<SSLProviderSetting>) => void;
|
||||
setConfig: (config: Setting<SSLProviderSetting>) => void;
|
||||
};
|
||||
|
||||
const Context = createContext({} as SSLProviderContext);
|
||||
|
||||
export const useSSLProviderContext = () => {
|
||||
return useContext(Context);
|
||||
};
|
||||
|
||||
const SSLProvider = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.enum(["letsencrypt", "zerossl"], {
|
||||
message: t("settings.ca.provider.errmsg.empty"),
|
||||
}),
|
||||
eabKid: z.string().optional(),
|
||||
eabHmacKey: z.string().optional(),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
const [config, setConfig] = useState<Setting<SSLProviderSetting>>({
|
||||
id: "",
|
||||
content: {
|
||||
provider: "letsencrypt",
|
||||
config: {},
|
||||
},
|
||||
});
|
||||
|
||||
const [provider, setProvider] = useState("letsencrypt");
|
||||
|
||||
const [config, setConfig] = useState<Setting>();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const setting = await getSetting("ssl-provider");
|
||||
const setting = await getSetting<SSLProviderSetting>("ssl-provider");
|
||||
|
||||
if (setting) {
|
||||
setConfig(setting);
|
||||
const content = setting.content as SSLProviderSetting;
|
||||
|
||||
form.setValue("provider", content.provider);
|
||||
form.setValue("eabKid", content.config[content.provider].eabKid);
|
||||
form.setValue("eabHmacKey", content.config[content.provider].eabHmacKey);
|
||||
setProvider(content.provider);
|
||||
} else {
|
||||
form.setValue("provider", "letsencrypt");
|
||||
setProvider("letsencrypt");
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const setProvider = (val: SSLProviderType) => {
|
||||
const newData = produce(config, (draft) => {
|
||||
if (draft.content) {
|
||||
draft.content.provider = val;
|
||||
} else {
|
||||
draft.content = {
|
||||
provider: val,
|
||||
config: {},
|
||||
};
|
||||
}
|
||||
});
|
||||
setConfig(newData);
|
||||
};
|
||||
|
||||
const getOptionCls = (val: string) => {
|
||||
if (provider === val) {
|
||||
if (config.content?.provider === val) {
|
||||
return "border-primary";
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
if (values.provider === "zerossl") {
|
||||
if (!values.eabKid) {
|
||||
form.setError("eabKid", {
|
||||
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
|
||||
});
|
||||
}
|
||||
if (!values.eabHmacKey) {
|
||||
form.setError("eabHmacKey", {
|
||||
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
|
||||
});
|
||||
}
|
||||
if (!values.eabKid || !values.eabHmacKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const setting: Setting = {
|
||||
id: config?.id,
|
||||
name: "ssl-provider",
|
||||
content: {
|
||||
provider: values.provider,
|
||||
config: {
|
||||
letsencrypt: {},
|
||||
zerossl: {
|
||||
eabKid: values.eabKid ?? "",
|
||||
eabHmacKey: values.eabHmacKey ?? "",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const onSubmit = async (data: Setting<SSLProviderSetting>) => {
|
||||
try {
|
||||
await update(setting);
|
||||
console.log(data);
|
||||
const resp = await update({ ...data });
|
||||
setConfig(resp);
|
||||
toast({
|
||||
title: t("common.update.succeeded.message"),
|
||||
description: t("common.update.succeeded.message"),
|
||||
@ -117,89 +95,351 @@ const SSLProvider = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full md:max-w-[35em]">
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 dark:text-stone-200">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="provider"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("common.text.ca")}</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
className="flex"
|
||||
onValueChange={(val) => {
|
||||
setProvider(val);
|
||||
form.setValue("provider", val as SSLProviderType);
|
||||
}}
|
||||
value={provider}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="letsencrypt" id="letsencrypt" />
|
||||
<Label htmlFor="letsencrypt">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer", getOptionCls("letsencrypt"))}>
|
||||
<img src={"/imgs/providers/letsencrypt.svg"} className="h-6" />
|
||||
<div>{"Let's Encrypt"}</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="zerossl" id="zerossl" />
|
||||
<Label htmlFor="zerossl">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer", getOptionCls("zerossl"))}>
|
||||
<img src={"/imgs/providers/zerossl.svg"} className="h-6" />
|
||||
<div>{"ZeroSSL"}</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabKid"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={provider !== "zerossl"}>
|
||||
<FormLabel>EAB_KID</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_kid.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabHmacKey"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={provider !== "zerossl"}>
|
||||
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_hmac_key.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
<Context.Provider value={{ onSubmit, setConfig, setting: config }}>
|
||||
<div className="w-full md:max-w-[35em]">
|
||||
<Label>{t("common.text.ca")}</Label>
|
||||
<RadioGroup
|
||||
className="flex mt-3"
|
||||
onValueChange={(val) => {
|
||||
setProvider(val as SSLProviderType);
|
||||
}}
|
||||
value={config.content?.provider}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="letsencrypt" id="letsencrypt" />
|
||||
<Label htmlFor="letsencrypt">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer", getOptionCls("letsencrypt"))}>
|
||||
<img src={"/imgs/providers/letsencrypt.svg"} className="h-6" />
|
||||
<div>{"Let's Encrypt"}</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 ">
|
||||
<RadioGroupItem value="zerossl" id="zerossl" />
|
||||
<Label htmlFor="zerossl">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer", getOptionCls("zerossl"))}>
|
||||
<img src={"/imgs/providers/zerossl.svg"} className="h-6" />
|
||||
<div>{"ZeroSSL"}</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="gts" id="gts" />
|
||||
<Label htmlFor="gts">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer", getOptionCls("gts"))}>
|
||||
<img src={"/imgs/providers/google.svg"} className="h-6" />
|
||||
<div>{"Google Trust Services"}</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
<SSLProviderForm kind={config.content?.provider ?? ""} />
|
||||
</div>
|
||||
</Context.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SSLProviderForm = ({ kind }: { kind: string }) => {
|
||||
const getForm = () => {
|
||||
switch (kind) {
|
||||
case "zerossl":
|
||||
return <SSLProviderZeroSSLForm />;
|
||||
case "gts":
|
||||
return <SSLProviderGtsForm />;
|
||||
default:
|
||||
return <SSLProviderLetsEncryptForm />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-5">{getForm()}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getConfigStr = (content: SSLProviderSetting, kind: string, key: string) => {
|
||||
if (!content.config) {
|
||||
return "";
|
||||
}
|
||||
if (!content.config[kind]) {
|
||||
return "";
|
||||
}
|
||||
return content.config[kind][key] ?? "";
|
||||
};
|
||||
|
||||
const SSLProviderLetsEncryptForm = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { setting, onSubmit } = useSSLProviderContext();
|
||||
|
||||
const formSchema = z.object({
|
||||
kind: z.literal("letsencrypt"),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
kind: "letsencrypt",
|
||||
},
|
||||
});
|
||||
|
||||
const onLocalSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const newData = produce(setting, (draft) => {
|
||||
if (!draft.content) {
|
||||
draft.content = {
|
||||
provider: data.kind,
|
||||
config: {
|
||||
letsencrypt: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
onSubmit(newData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onLocalSubmit)} className="space-y-8 dark:text-stone-200">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="kind"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden>
|
||||
<FormLabel>kind</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
const SSLProviderZeroSSLForm = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { setting, onSubmit } = useSSLProviderContext();
|
||||
|
||||
const formSchema = z.object({
|
||||
kind: z.literal("zerossl"),
|
||||
eabKid: z.string().min(1, { message: t("settings.ca.eab_kid_hmac_key.errmsg.empty") }),
|
||||
eabHmacKey: z.string().min(1, { message: t("settings.ca.eab_kid_hmac_key.errmsg.empty") }),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
kind: "zerossl",
|
||||
eabKid: "",
|
||||
eabHmacKey: "",
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (setting.content) {
|
||||
const content = setting.content;
|
||||
|
||||
form.reset({
|
||||
eabKid: getConfigStr(content, "zerossl", "eabKid"),
|
||||
eabHmacKey: getConfigStr(content, "zerossl", "eabHmacKey"),
|
||||
});
|
||||
}
|
||||
}, [setting]);
|
||||
|
||||
const onLocalSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const newData = produce(setting, (draft) => {
|
||||
if (!draft.content) {
|
||||
draft.content = {
|
||||
provider: "zerossl",
|
||||
config: {
|
||||
zerossl: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
draft.content.config.zerossl = {
|
||||
eabKid: data.eabKid,
|
||||
eabHmacKey: data.eabHmacKey,
|
||||
};
|
||||
});
|
||||
onSubmit(newData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onLocalSubmit)} className="space-y-8 dark:text-stone-200">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="kind"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden>
|
||||
<FormLabel>kind</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabKid"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>EAB_KID</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_kid.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabHmacKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_hmac_key.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
const SSLProviderGtsForm = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { setting, onSubmit } = useSSLProviderContext();
|
||||
|
||||
const formSchema = z.object({
|
||||
kind: z.literal("gts"),
|
||||
eabKid: z.string().min(1, { message: t("settings.ca.eab_kid_hmac_key.errmsg.empty") }),
|
||||
eabHmacKey: z.string().min(1, { message: t("settings.ca.eab_kid_hmac_key.errmsg.empty") }),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
kind: "gts",
|
||||
eabKid: "",
|
||||
eabHmacKey: "",
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (setting.content) {
|
||||
const content = setting.content;
|
||||
|
||||
form.reset({
|
||||
eabKid: getConfigStr(content, "gts", "eabKid"),
|
||||
eabHmacKey: getConfigStr(content, "gts", "eabHmacKey"),
|
||||
});
|
||||
}
|
||||
}, [setting]);
|
||||
|
||||
const onLocalSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const newData = produce(setting, (draft) => {
|
||||
if (!draft.content) {
|
||||
draft.content = {
|
||||
provider: "gts",
|
||||
config: {
|
||||
zerossl: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
draft.content.config.gts = {
|
||||
eabKid: data.eabKid,
|
||||
eabHmacKey: data.eabHmacKey,
|
||||
};
|
||||
});
|
||||
onSubmit(newData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onLocalSubmit)} className="space-y-8 dark:text-stone-200">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="kind"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden>
|
||||
<FormLabel>kind</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabKid"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>EAB_KID</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_kid.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="eabHmacKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("settings.ca.eab_hmac_key.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default SSLProvider;
|
||||
|
@ -2,7 +2,7 @@ import { createContext, ReactNode, useCallback, useContext, useEffect, useReduce
|
||||
|
||||
import { Access } from "@/domain/access";
|
||||
import { AccessGroup } from "@/domain/access_groups";
|
||||
import { Setting } from "@/domain/settings";
|
||||
import { EmailsSetting, Setting } from "@/domain/settings";
|
||||
import { list } from "@/repository/access";
|
||||
import { list as getAccessGroups } from "@/repository/access_group";
|
||||
import { getEmails } from "@/repository/settings";
|
||||
@ -10,13 +10,13 @@ import { configReducer } from "./reducer";
|
||||
|
||||
export type ConfigData = {
|
||||
accesses: Access[];
|
||||
emails: Setting;
|
||||
emails: Setting<EmailsSetting>;
|
||||
accessGroups: AccessGroup[];
|
||||
};
|
||||
|
||||
export type ConfigContext = {
|
||||
config: ConfigData;
|
||||
setEmails: (email: Setting) => void;
|
||||
setEmails: (email: Setting<EmailsSetting>) => void;
|
||||
addAccess: (access: Access) => void;
|
||||
updateAccess: (access: Access) => void;
|
||||
deleteAccess: (id: string) => void;
|
||||
@ -68,7 +68,7 @@ export const ConfigProvider = ({ children }: ConfigProviderProps) => {
|
||||
dispatchConfig({ type: "SET_ACCESS_GROUPS", payload: accessGroups });
|
||||
}, []);
|
||||
|
||||
const setEmails = useCallback((emails: Setting) => {
|
||||
const setEmails = useCallback((emails: Setting<EmailsSetting>) => {
|
||||
dispatchConfig({ type: "SET_EMAILS", payload: emails });
|
||||
}, []);
|
||||
|
||||
|
@ -8,7 +8,7 @@ type Action =
|
||||
| { type: "DELETE_ACCESS"; payload: string }
|
||||
| { type: "UPDATE_ACCESS"; payload: Access }
|
||||
| { type: "SET_ACCESSES"; payload: Access[] }
|
||||
| { type: "SET_EMAILS"; payload: Setting }
|
||||
| { type: "SET_EMAILS"; payload: Setting<EmailsSetting> }
|
||||
| { type: "ADD_EMAIL"; payload: string }
|
||||
| { type: "SET_ACCESS_GROUPS"; payload: AccessGroup[] };
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ReactNode, useContext, createContext, useEffect, useReducer, useCallback } from "react";
|
||||
|
||||
import { NotifyChannel, Setting } from "@/domain/settings";
|
||||
import { NotifyChannel, NotifyChannels, Setting } from "@/domain/settings";
|
||||
import { getSetting } from "@/repository/settings";
|
||||
import { notifyReducer } from "./reducer";
|
||||
|
||||
export type NotifyContext = {
|
||||
config: Setting;
|
||||
config: Setting<NotifyChannels>;
|
||||
setChannel: (data: { channel: string; data: NotifyChannel }) => void;
|
||||
setChannels: (data: Setting) => void;
|
||||
setChannels: (data: Setting<NotifyChannels>) => void;
|
||||
};
|
||||
|
||||
const Context = createContext({} as NotifyContext);
|
||||
@ -23,7 +23,7 @@ export const NotifyProvider = ({ children }: NotifyProviderProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const featchData = async () => {
|
||||
const chanels = await getSetting("notifyChannels");
|
||||
const chanels = await getSetting<NotifyChannels>("notifyChannels");
|
||||
dispatchNotify({
|
||||
type: "SET_CHANNELS",
|
||||
payload: chanels,
|
||||
@ -39,7 +39,7 @@ export const NotifyProvider = ({ children }: NotifyProviderProps) => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const setChannels = useCallback((setting: Setting) => {
|
||||
const setChannels = useCallback((setting: Setting<NotifyChannels>) => {
|
||||
dispatchNotify({
|
||||
type: "SET_CHANNELS",
|
||||
payload: setting,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NotifyChannel, Setting } from "@/domain/settings";
|
||||
import { NotifyChannel, NotifyChannels, Setting } from "@/domain/settings";
|
||||
|
||||
type Action =
|
||||
| {
|
||||
@ -10,10 +10,10 @@ type Action =
|
||||
}
|
||||
| {
|
||||
type: "SET_CHANNELS";
|
||||
payload: Setting;
|
||||
payload: Setting<NotifyChannels>;
|
||||
};
|
||||
|
||||
export const notifyReducer = (state: Setting, action: Action) => {
|
||||
export const notifyReducer = (state: Setting<NotifyChannels>, action: Action) => {
|
||||
switch (action.type) {
|
||||
case "SET_CHANNEL": {
|
||||
const channel = action.payload.channel;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Setting } from "@/domain/settings";
|
||||
import { EmailsSetting, Setting } from "@/domain/settings";
|
||||
import { getPb } from "./api";
|
||||
|
||||
export const getEmails = async () => {
|
||||
try {
|
||||
const resp = await getPb().collection("settings").getFirstListItem<Setting>("name='emails'");
|
||||
const resp = await getPb().collection("settings").getFirstListItem<Setting<EmailsSetting>>("name='emails'");
|
||||
return resp;
|
||||
} catch (e) {
|
||||
return {
|
||||
@ -12,21 +12,21 @@ export const getEmails = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getSetting = async (name: string) => {
|
||||
export const getSetting = async <T>(name: string) => {
|
||||
try {
|
||||
const resp = await getPb().collection("settings").getFirstListItem<Setting>(`name='${name}'`);
|
||||
const resp = await getPb().collection("settings").getFirstListItem<Setting<T>>(`name='${name}'`);
|
||||
return resp;
|
||||
} catch (e) {
|
||||
const rs: Setting = {
|
||||
const rs: Setting<T> = {
|
||||
name: name,
|
||||
};
|
||||
return rs;
|
||||
}
|
||||
};
|
||||
|
||||
export const update = async (setting: Setting) => {
|
||||
export const update = async <T>(setting: Setting<T>) => {
|
||||
const pb = getPb();
|
||||
let resp: Setting;
|
||||
let resp: Setting<T>;
|
||||
if (setting.id) {
|
||||
resp = await pb.collection("settings").update(setting.id, setting);
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user