mirror of https://github.com/usual2970/certimate
447 lines
12 KiB
TypeScript
447 lines
12 KiB
TypeScript
import { useContext, useEffect, useState, createContext } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { useTranslation } from "react-i18next";
|
|
import { z } from "zod";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
import { useToast } from "@/components/ui/use-toast";
|
|
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 [config, setConfig] = useState<Setting<SSLProviderSetting>>({
|
|
id: "",
|
|
content: {
|
|
provider: "letsencrypt",
|
|
config: {},
|
|
},
|
|
});
|
|
|
|
const { toast } = useToast();
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
const setting = await getSetting<SSLProviderSetting>("ssl-provider");
|
|
|
|
if (setting) {
|
|
setConfig(setting);
|
|
}
|
|
};
|
|
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 (config.content?.provider === val) {
|
|
return "border-primary dark:border-primary";
|
|
}
|
|
|
|
return "";
|
|
};
|
|
|
|
const onSubmit = async (data: Setting<SSLProviderSetting>) => {
|
|
try {
|
|
console.log(data);
|
|
const resp = await update({ ...data });
|
|
setConfig(resp);
|
|
toast({
|
|
title: t("common.update.succeeded.message"),
|
|
description: t("common.update.succeeded.message"),
|
|
});
|
|
} catch (e) {
|
|
const message = getErrMessage(e);
|
|
toast({
|
|
title: t("common.update.failed.message"),
|
|
description: message,
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Context.Provider value={{ onSubmit, setConfig, setting: config }}>
|
|
<div className="w-full md:max-w-[35em]">
|
|
<Label className="dark:text-stone-200">{t("common.text.ca")}</Label>
|
|
<RadioGroup
|
|
className="flex mt-3 dark:text-stone-200"
|
|
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 dark:border-stone-700", 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 dark:border-stone-700", 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 dark:border-stone-700", 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;
|
|
|