refactor(ui): refactor accesses state using zustand store

This commit is contained in:
Fu Diwei 2024-12-11 19:55:50 +08:00
parent b744363736
commit bb3009a124
48 changed files with 359 additions and 404 deletions

View File

@ -1,3 +1,4 @@
import { Statistics } from "@/domain/statistics";
import { getPocketBase } from "@/repository/pocketbase";
export const get = async () => {
@ -7,9 +8,9 @@ export const get = async () => {
method: "GET",
});
if (resp.code != 0) {
if (resp.code !== 0) {
throw new Error(resp.msg);
}
return resp.data;
return resp.data as Statistics;
};

View File

@ -3,11 +3,11 @@ import { Button, Dropdown, Form, Input, message, Space, Tooltip } from "antd";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { ChevronDown as ChevronDownIcon, Clipboard as ClipboardIcon, ThumbsUp as ThumbsUpIcon } from "lucide-react";
import { type Certificate } from "@/domain/certificate";
import { type CertificateModel } from "@/domain/certificate";
import { saveFiles2Zip } from "@/utils/file";
type CertificateDetailProps = {
data: Certificate;
data: CertificateModel;
};
const CertificateDetail = ({ data }: CertificateDetailProps) => {

View File

@ -1,11 +1,11 @@
import { useEffect, useState } from "react";
import { Drawer } from "antd";
import { type Certificate } from "@/domain/certificate";
import { type CertificateModel } from "@/domain/certificate";
import CertificateDetail from "./CertificateDetail";
type CertificateDetailDrawerProps = {
data?: Certificate;
data?: CertificateModel;
open?: boolean;
onClose?: () => void;
};

View File

@ -8,19 +8,21 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type AliyunConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type AliyunConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessAliyunFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { t } = useTranslation();
const { createAccess, updateAccess } = useAccessStore();
const formSchema = z.object({
id: z.string().optional(),
name: z
@ -56,7 +58,7 @@ const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -81,7 +83,7 @@ const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,19 +8,21 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { Access, accessProvidersMap, accessTypeFormSchema, type AwsConfig } from "@/domain/access";
import { AccessModel, accessProvidersMap, accessTypeFormSchema, type AwsConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessAwsFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { t } = useTranslation();
const { createAccess, updateAccess } = useAccessStore();
const formSchema = z.object({
id: z.string().optional(),
name: z
@ -68,7 +70,7 @@ const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -94,7 +96,8 @@ const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,19 +8,21 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type BaiduCloudConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type BaiduCloudConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessBaiduCloudFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { t } = useTranslation();
const { createAccess, updateAccess } = useAccessStore();
const formSchema = z.object({
id: z.string().optional(),
name: z
@ -56,7 +58,7 @@ const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProp
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -80,7 +82,8 @@ const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProp
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type ByteplusConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type ByteplusConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessByteplusFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -56,7 +56,7 @@ const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) =
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -80,7 +80,7 @@ const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) =
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type CloudflareConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type CloudflareConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessCloudflareFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -50,7 +50,7 @@ const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProp
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -73,7 +73,7 @@ const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProp
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type DogeCloudConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type DogeCloudConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessDogeCloudFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -50,7 +50,7 @@ const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps)
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -74,7 +74,7 @@ const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps)
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -23,14 +23,14 @@ import AccessWebhookForm from "./AccessWebhookForm";
import AccessKubernetesForm from "./AccessKubernetesForm";
import AccessVolcengineForm from "./AccessVolcengineForm";
import AccessByteplusForm from "./AccessByteplusForm";
import { Access } from "@/domain/access";
import { AccessModel } from "@/domain/access";
import { AccessTypeSelect } from "./AccessTypeSelect";
type AccessEditProps = {
op: "add" | "edit" | "copy";
className?: string;
trigger: React.ReactNode;
data?: Access;
data?: AccessModel;
outConfigType?: string;
};

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type GodaddyConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type GodaddyConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessGodaddyFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -56,7 +56,7 @@ const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) =>
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -80,7 +80,7 @@ const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) =>
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type HttpreqConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type HttpreqConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessHttpreqFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -62,7 +62,7 @@ const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) =>
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -89,7 +89,7 @@ const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) =>
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type HuaweiCloudConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type HuaweiCloudConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessHuaweiCloudFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -62,7 +62,7 @@ const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormPr
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -87,7 +87,7 @@ const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormPr
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -10,18 +10,18 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
import { Input } from "@/components/ui/input";
import { readFileContent } from "@/utils/file";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type KubernetesConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type KubernetesConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessKubernetesFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [fileName, setFileName] = useState("");
@ -60,7 +60,7 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -82,7 +82,7 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
if (data.id && op == "edit") {
updateAccess(req);
} else {
addAccess(req);
createAccess(req);
}
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessLocalFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
@ -41,7 +41,7 @@ const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -62,7 +62,7 @@ const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
if (data.id && op == "edit") {
updateAccess(req);
} else {
addAccess(req);
createAccess(req);
}
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type NamesiloConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type NamesiloConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessNamesiloFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -50,7 +50,7 @@ const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) =
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -73,7 +73,7 @@ const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) =
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type PdnsConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type PdnsConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessPdnsFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -53,7 +53,7 @@ const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -78,7 +78,7 @@ const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type QiniuConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type QiniuConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessQiniuFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -50,7 +50,7 @@ const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -74,7 +74,7 @@ const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -10,18 +10,18 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
import { Input } from "@/components/ui/input";
import { readFileContent } from "@/utils/file";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type SSHConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type SSHConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessSSHFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const fileInputRef = useRef<HTMLInputElement | null>(null);
@ -103,7 +103,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
let group = data.group;
if (group == "emptyId") group = "";
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -131,7 +131,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
if (data.id && op == "edit") {
updateAccess(req);
} else {
addAccess(req);
createAccess(req);
}
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type TencentConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type TencentConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessTencentFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -56,7 +56,7 @@ const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) =>
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -80,7 +80,7 @@ const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) =>
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base";
import { accessProvidersMap, accessTypeFormSchema, type Access, type VolcengineConfig } from "@/domain/access";
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type VolcengineConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessVolcengineFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -56,7 +56,7 @@ const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProp
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -80,7 +80,7 @@ const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProp
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base";
import { Access, accessProvidersMap, accessTypeFormSchema, WebhookConfig } from "@/domain/access";
import { AccessModel, accessProvidersMap, accessTypeFormSchema, WebhookConfig } from "@/domain/access";
import { save } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
type AccessWebhookFormProps = {
op: "add" | "edit" | "copy";
data?: Access;
data?: AccessModel;
onAfterReq: () => void;
};
const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) => {
const { addAccess, updateAccess } = useConfigContext();
const { createAccess, updateAccess } = useAccessStore();
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
@ -47,7 +47,7 @@ const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) =>
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
const req: Access = {
const req: AccessModel = {
id: data.id as string,
name: data.name,
configType: data.configType,
@ -70,7 +70,7 @@ const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) =>
updateAccess(req);
return;
}
addAccess(req);
createAccess(req);
} catch (e) {
const err = e as ClientResponseError;

View File

@ -2,7 +2,7 @@ import React, { useEffect } from "react";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "../ui/select";
import { accessProvidersMap } from "@/domain/access";
import { useTranslation } from "react-i18next";
import { useConfigContext } from "@/providers/config";
import { useAccessStore } from "@/stores/access";
import { deployTargetsMap } from "@/domain/domain";
type AccessSelectProps = {
@ -13,9 +13,7 @@ type AccessSelectProps = {
const AccessSelect = ({ value, onValueChange, providerType }: AccessSelectProps) => {
const [localValue, setLocalValue] = React.useState<string>("");
const { t } = useTranslation();
const {
config: { accesses },
} = useConfigContext();
const { accesses } = useAccessStore();
useEffect(() => {
setLocalValue(value);

View File

@ -15,8 +15,8 @@ import EmailsEdit from "@/components/certimate/EmailsEdit";
import StringList from "@/components/certimate/StringList";
import { accessProvidersMap } from "@/domain/access";
import { useAccessStore } from "@/stores/access";
import { useContactStore } from "@/stores/contact";
import { useConfigContext } from "@/providers/config";
import { Switch } from "@/components/ui/switch";
import { TooltipFast } from "@/components/ui/tooltip";
import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow";
@ -33,9 +33,7 @@ const selectState = (state: WorkflowState) => ({
const ApplyForm = ({ data }: ApplyFormProps) => {
const { updateNode } = useWorkflowStore(useShallow(selectState));
const {
config: { accesses },
} = useConfigContext();
const { accesses } = useAccessStore();
const { emails, fetchEmails } = useContactStore();
useEffect(() => {

View File

@ -1,15 +1,13 @@
import { ConfigProvider } from "@/providers/config";
import React from "react";
import { PanelProvider } from "./PanelProvider";
import { NotifyProvider } from "@/providers/notify";
import { PanelProvider } from "./PanelProvider";
const WorkflowProvider = ({ children }: { children: React.ReactNode }) => {
return (
<ConfigProvider>
<NotifyProvider>
<PanelProvider>{children}</PanelProvider>
</NotifyProvider>
</ConfigProvider>
<NotifyProvider>
<PanelProvider>{children}</PanelProvider>
</NotifyProvider>
);
};

View File

@ -1,4 +1,5 @@
import { z } from "zod";
import { type BaseModel } from "pocketbase";
type AccessUsages = "apply" | "deploy" | "all";
@ -59,8 +60,7 @@ export const accessTypeFormSchema = z.union(
{ message: "access.authorization.form.type.placeholder" }
);
export type Access = {
id: string;
export interface AccessModel extends BaseModel {
name: string;
configType: string;
usage: AccessUsages;
@ -83,10 +83,7 @@ export type Access = {
| KubernetesConfig
| VolcengineConfig
| ByteplusConfig;
deleted?: string;
created?: string;
updated?: string;
};
}
export type AliyunConfig = {
accessKeyId: string;

View File

@ -1,7 +1,8 @@
import { Workflow } from "./workflow";
import { type BaseModel } from "pocketbase";
export type Certificate = {
id: string;
import { WorkflowModel } from "./workflow";
export interface CertificateModel extends BaseModel {
san: string;
certificate: string;
privateKey: string;
@ -12,10 +13,7 @@ export type Certificate = {
expireAt: string;
workflow: string;
nodeId: string;
created: string;
updated: string;
expand: {
workflow?: Workflow;
workflow?: WorkflowModel;
};
};
}

View File

@ -56,16 +56,6 @@ export type ApplyConfig = {
disableFollowCNAME?: boolean;
};
export type Statistic = {
certificateTotal: number;
certificateExpired: number;
certificateExpireSoon: number;
workflowTotal: number;
workflowEnabled: number;
workflowDisabled: number;
};
export type DeployTarget = {
type: string;
provider: string;

View File

@ -1,6 +1,6 @@
import { type BaseModel } from "pocketbase";
export interface Settings<T> extends BaseModel {
export interface SettingsModel<T> extends BaseModel {
name: string;
content: T;
}

View File

@ -0,0 +1,8 @@
export type Statistics = {
certificateTotal: number;
certificateExpired: number;
certificateExpireSoon: number;
workflowTotal: number;
workflowEnabled: number;
workflowDisabled: number;
};

View File

@ -1,5 +1,6 @@
import { produce } from "immer";
import { nanoid } from "nanoid";
import { type BaseModel } from "pocketbase";
import i18n from "@/i18n";
import { deployTargets, KVType } from "./domain";
@ -27,8 +28,7 @@ export type WorkflowOutput = {
error: string;
};
export type Workflow = {
id: string;
export interface WorkflowModel extends BaseModel {
name: string;
description?: string;
type: string;
@ -37,9 +37,7 @@ export type Workflow = {
draft?: WorkflowNode;
enabled?: boolean;
hasDraft?: boolean;
created?: string;
updated?: string;
};
}
export enum WorkflowNodeType {
Start = "start",
@ -131,7 +129,7 @@ type NewWorkflowNodeOptions = {
providerType?: string;
};
export const initWorkflow = (): Workflow => {
export const initWorkflow = (): WorkflowModel => {
// 开始节点
const rs = newWorkflowNode(WorkflowNodeType.Start, {});
let root = rs;

View File

@ -18,7 +18,6 @@ import {
import Version from "@/components/certimate/Version";
import { useTheme } from "@/hooks";
import { getPocketBase } from "@/repository/pocketbase";
import { ConfigProvider } from "@/providers/config";
const ConsoleLayout = () => {
const navigate = useNavigate();
@ -52,65 +51,61 @@ const ConsoleLayout = () => {
}
return (
<>
<ConfigProvider>
<Layout className="w-full min-h-screen">
<Layout.Sider className="max-md:hidden" theme="light" width={256}>
<div className="flex flex-col items-center justify-between w-full h-full overflow-hidden">
<div className="w-full">
<SiderMenu />
</div>
<div className="w-full py-2 text-center">
<Version />
</div>
<Layout className="w-full min-h-screen">
<Layout.Sider className="max-md:hidden" theme="light" width={256}>
<div className="flex flex-col items-center justify-between w-full h-full overflow-hidden">
<div className="w-full">
<SiderMenu />
</div>
<div className="w-full py-2 text-center">
<Version />
</div>
</div>
</Layout.Sider>
<Layout>
<Layout.Header style={{ padding: 0, background: themeToken.colorBgContainer }}>
<div className="flex items-center justify-between size-full px-4 overflow-hidden">
<div className="flex items-center gap-4 size-full">
<Button className="md:hidden" icon={<MenuIcon />} size="large" onClick={handleSiderOpen} />
<Drawer
closable={false}
destroyOnClose
open={siderOpen}
placement="left"
styles={{
content: { paddingTop: themeToken.paddingSM, paddingBottom: themeToken.paddingSM },
body: { padding: 0 },
}}
onClose={handleSiderClose}
>
<SiderMenu onSelect={() => handleSiderClose()} />
</Drawer>
</div>
</Layout.Sider>
<div className="flex-grow flex items-center justify-end gap-4 size-full overflow-hidden">
<Tooltip title={t("common.menu.theme")} mouseEnterDelay={2}>
<ThemeToggleButton size="large" />
</Tooltip>
<Tooltip title={t("common.menu.locale")} mouseEnterDelay={2}>
<LocaleToggleButton size="large" />
</Tooltip>
<Tooltip title={t("common.menu.settings")} mouseEnterDelay={2}>
<Button icon={<SettingsIcon size={18} />} size="large" onClick={handleSettingsClick} />
</Tooltip>
<Tooltip title={t("common.menu.logout")} mouseEnterDelay={2}>
<Button danger icon={<LogOutIcon size={18} />} size="large" onClick={handleLogoutClick} />
</Tooltip>
</div>
</div>
</Layout.Header>
<Layout>
<Layout.Header style={{ padding: 0, background: themeToken.colorBgContainer }}>
<div className="flex items-center justify-between size-full px-4 overflow-hidden">
<div className="flex items-center gap-4 size-full">
<Button className="md:hidden" icon={<MenuIcon />} size="large" onClick={handleSiderOpen} />
<Drawer
closable={false}
destroyOnClose
open={siderOpen}
placement="left"
styles={{
content: { paddingTop: themeToken.paddingSM, paddingBottom: themeToken.paddingSM },
body: { padding: 0 },
}}
onClose={handleSiderClose}
>
<SiderMenu onSelect={() => handleSiderClose()} />
</Drawer>
</div>
<div className="flex-grow flex items-center justify-end gap-4 size-full overflow-hidden">
<Tooltip title={t("common.menu.theme")} mouseEnterDelay={2}>
<ThemeToggleButton size="large" />
</Tooltip>
<Tooltip title={t("common.menu.locale")} mouseEnterDelay={2}>
<LocaleToggleButton size="large" />
</Tooltip>
<Tooltip title={t("common.menu.settings")} mouseEnterDelay={2}>
<Button icon={<SettingsIcon size={18} />} size="large" onClick={handleSettingsClick} />
</Tooltip>
<Tooltip title={t("common.menu.logout")} mouseEnterDelay={2}>
<Button danger icon={<LogOutIcon size={18} />} size="large" onClick={handleLogoutClick} />
</Tooltip>
</div>
</div>
</Layout.Header>
<Layout.Content>
<div className="p-4">
<Outlet />
</div>
</Layout.Content>
</Layout>
</Layout>
</ConfigProvider>
</>
<Layout.Content>
<div className="p-4">
<Outlet />
</div>
</Layout.Content>
</Layout>
</Layout>
);
};

View File

@ -7,9 +7,8 @@ import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";
import AccessEditDialog from "@/components/certimate/AccessEditDialog";
import { accessProvidersMap, type Access as AccessType } from "@/domain/access";
import { remove as removeAccess } from "@/repository/access";
import { useConfigContext } from "@/providers/config";
import { accessProvidersMap, type AccessModel } from "@/domain/access";
import { useAccessStore } from "@/stores/access";
const AccessList = () => {
const { t } = useTranslation();
@ -17,9 +16,11 @@ const AccessList = () => {
const [modalApi, ModelContextHolder] = Modal.useModal();
const [notificationApi, NotificationContextHolder] = notification.useNotification();
const { accesses, fetchAccesses, deleteAccess } = useAccessStore();
const [loading, setLoading] = useState<boolean>(false);
const tableColumns: TableProps<AccessType>["columns"] = [
const tableColumns: TableProps<AccessModel>["columns"] = [
{
key: "$index",
align: "center",
@ -105,13 +106,15 @@ const AccessList = () => {
),
},
];
const [tableData, setTableData] = useState<AccessType[]>([]);
const [tableData, setTableData] = useState<AccessModel[]>([]);
const [tableTotal, setTableTotal] = useState<number>(0);
const [page, setPage] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10);
const configContext = useConfigContext();
useEffect(() => {
fetchAccesses();
}, []);
const fetchTableData = useCallback(async () => {
if (loading) return;
@ -120,10 +123,10 @@ const AccessList = () => {
try {
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const items = configContext.config?.accesses?.slice(startIndex, endIndex) ?? [];
const items = accesses.slice(startIndex, endIndex);
setTableData(items);
setTableTotal(configContext.config?.accesses?.length ?? 0);
setTableTotal(accesses.length);
} catch (err) {
if (err instanceof ClientResponseError && err.isAbort) {
return;
@ -134,21 +137,20 @@ const AccessList = () => {
} finally {
setLoading(false);
}
}, [page, pageSize, configContext.config.accesses]);
}, [page, pageSize, accesses]);
useEffect(() => {
fetchTableData();
}, [fetchTableData]);
const handleDeleteClick = async (data: AccessType) => {
const handleDeleteClick = async (data: AccessModel) => {
modalApi.confirm({
title: t("access.action.delete"),
content: t("access.action.delete.confirm"),
onOk: async () => {
// TODO: 有关联数据的不允许被删除
try {
const res = await removeAccess(data);
configContext.deleteAccess(res.id);
await deleteAccess(data);
} catch (err) {
console.error(err);
notificationApi.error({ message: t("common.text.request_error"), description: <>{String(err)}</> });
@ -177,7 +179,7 @@ const AccessList = () => {
]}
/>
<Table<AccessType>
<Table<AccessModel>
columns={tableColumns}
dataSource={tableData}
loading={loading}

View File

@ -8,7 +8,7 @@ import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";
import CertificateDetailDrawer from "@/components/certificate/CertificateDetailDrawer";
import { Certificate as CertificateType } from "@/domain/certificate";
import { CertificateModel } from "@/domain/certificate";
import { list as listCertificate, type CertificateListReq } from "@/repository/certificate";
const CertificateList = () => {
@ -23,7 +23,7 @@ const CertificateList = () => {
const [loading, setLoading] = useState<boolean>(false);
const tableColumns: TableProps<CertificateType>["columns"] = [
const tableColumns: TableProps<CertificateModel>["columns"] = [
{
key: "$index",
align: "center",
@ -165,7 +165,7 @@ const CertificateList = () => {
),
},
];
const [tableData, setTableData] = useState<CertificateType[]>([]);
const [tableData, setTableData] = useState<CertificateModel[]>([]);
const [tableTotal, setTableTotal] = useState<number>(0);
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
@ -177,7 +177,7 @@ const CertificateList = () => {
const [page, setPage] = useState<number>(() => parseInt(+searchParams.get("page")! + "") || 1);
const [pageSize, setPageSize] = useState<number>(() => parseInt(+searchParams.get("perPage")! + "") || 10);
const [currentRecord, setCurrentRecord] = useState<CertificateType>();
const [currentRecord, setCurrentRecord] = useState<CertificateModel>();
const [drawerOpen, setDrawerOpen] = useState(false);
@ -210,7 +210,7 @@ const CertificateList = () => {
fetchTableData();
}, [fetchTableData]);
const handleViewClick = (certificate: CertificateType) => {
const handleViewClick = (certificate: CertificateModel) => {
setDrawerOpen(true);
setCurrentRecord(certificate);
};
@ -221,7 +221,7 @@ const CertificateList = () => {
<PageHeader title={t("certificate.page.title")} />
<Table<CertificateType>
<Table<CertificateModel>
columns={tableColumns}
dataSource={tableData}
loading={loading}

View File

@ -12,7 +12,7 @@ import {
} from "lucide-react";
import { ClientResponseError } from "pocketbase";
import { type Statistic as StatisticType } from "@/domain/domain";
import { type Statistics } from "@/domain/statistics";
import { get as getStatistics } from "@/api/statistics";
const Dashboard = () => {
@ -26,7 +26,7 @@ const Dashboard = () => {
const [loading, setLoading] = useState<boolean>(false);
const statisticGridSpans = {
const statisticsGridSpans = {
xs: { flex: "50%" },
md: { flex: "50%" },
lg: { flex: "33.3333%" },
@ -34,15 +34,15 @@ const Dashboard = () => {
xxl: { flex: "20%" },
};
const [statistic, setStatistic] = useState<StatisticType>();
const [statistics, setStatistics] = useState<Statistics>();
const fetchStatistic = useCallback(async () => {
const fetchStatistics = useCallback(async () => {
if (loading) return;
setLoading(true);
try {
const data = await getStatistics();
setStatistic(data);
setStatistics(data);
} catch (err) {
if (err instanceof ClientResponseError && err.isAbort) {
return;
@ -56,7 +56,7 @@ const Dashboard = () => {
}, []);
useEffect(() => {
fetchStatistic();
fetchStatistics();
}, []);
return (
@ -66,47 +66,47 @@ const Dashboard = () => {
<PageHeader title={t("dashboard.page.title")} />
<Row className="justify-stretch" gutter={[16, 16]}>
<Col {...statisticGridSpans}>
<Col {...statisticsGridSpans}>
<StatisticCard
icon={<SquareSigmaIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />}
label={t("dashboard.statistics.all_certificates")}
value={statistic?.certificateTotal ?? "-"}
value={statistics?.certificateTotal ?? "-"}
suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates")}
/>
</Col>
<Col {...statisticGridSpans}>
<Col {...statisticsGridSpans}>
<StatisticCard
icon={<CalendarClockIcon size={48} strokeWidth={1} color={themeToken.colorWarning} />}
label={t("dashboard.statistics.expire_soon_certificates")}
value={statistic?.certificateExpireSoon ?? "-"}
value={statistics?.certificateExpireSoon ?? "-"}
suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates?state=expireSoon")}
/>
</Col>
<Col {...statisticGridSpans}>
<Col {...statisticsGridSpans}>
<StatisticCard
icon={<CalendarX2Icon size={48} strokeWidth={1} color={themeToken.colorError} />}
label={t("dashboard.statistics.expired_certificates")}
value={statistic?.certificateExpired ?? "-"}
value={statistics?.certificateExpired ?? "-"}
suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates?state=expired")}
/>
</Col>
<Col {...statisticGridSpans}>
<Col {...statisticsGridSpans}>
<StatisticCard
icon={<WorkflowIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />}
label={t("dashboard.statistics.all_workflows")}
value={statistic?.workflowTotal ?? "-"}
value={statistics?.workflowTotal ?? "-"}
suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/workflows")}
/>
</Col>
<Col {...statisticGridSpans}>
<Col {...statisticsGridSpans}>
<StatisticCard
icon={<FolderCheckIcon size={48} strokeWidth={1} color={themeToken.colorSuccess} />}
label={t("dashboard.statistics.enabled_workflows")}
value={statistic?.workflowEnabled ?? "-"}
value={statistics?.workflowEnabled ?? "-"}
suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/workflows?state=enabled")}
/>

View File

@ -12,14 +12,14 @@ import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error";
import { SSLProvider as SSLProviderType, SSLProviderSetting, Settings } from "@/domain/settings";
import { SSLProvider as SSLProviderType, SSLProviderSetting, SettingsModel } from "@/domain/settings";
import { get, save } from "@/repository/settings";
import { produce } from "immer";
type SSLProviderContext = {
setting: Settings<SSLProviderSetting>;
onSubmit: (data: Settings<SSLProviderSetting>) => void;
setConfig: (config: Settings<SSLProviderSetting>) => void;
setting: SettingsModel<SSLProviderSetting>;
onSubmit: (data: SettingsModel<SSLProviderSetting>) => void;
setConfig: (config: SettingsModel<SSLProviderSetting>) => void;
};
const Context = createContext({} as SSLProviderContext);
@ -31,12 +31,12 @@ export const useSSLProviderContext = () => {
const SSLProvider = () => {
const { t } = useTranslation();
const [config, setConfig] = useState<Settings<SSLProviderSetting>>({
const [config, setConfig] = useState<SettingsModel<SSLProviderSetting>>({
content: {
provider: "letsencrypt",
config: {},
},
} as Settings<SSLProviderSetting>);
} as SettingsModel<SSLProviderSetting>);
const { toast } = useToast();
@ -73,7 +73,7 @@ const SSLProvider = () => {
return "";
};
const onSubmit = async (data: Settings<SSLProviderSetting>) => {
const onSubmit = async (data: SettingsModel<SSLProviderSetting>) => {
try {
console.log(data);
const resp = await save({ ...data });

View File

@ -23,7 +23,7 @@ import { Filter as FilterIcon, Pencil as PencilIcon, Plus as PlusIcon, Trash2 as
import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";
import { Workflow as WorkflowType } from "@/domain/workflow";
import { WorkflowModel } from "@/domain/workflow";
import { list as listWorkflow, remove as removeWorkflow, save as saveWorkflow } from "@/repository/workflow";
const WorkflowList = () => {
@ -39,7 +39,7 @@ const WorkflowList = () => {
const [loading, setLoading] = useState<boolean>(false);
const tableColumns: TableProps<WorkflowType>["columns"] = [
const tableColumns: TableProps<WorkflowModel>["columns"] = [
{
key: "$index",
align: "center",
@ -196,7 +196,7 @@ const WorkflowList = () => {
),
},
];
const [tableData, setTableData] = useState<WorkflowType[]>([]);
const [tableData, setTableData] = useState<WorkflowModel[]>([]);
const [tableTotal, setTableTotal] = useState<number>(0);
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
@ -237,7 +237,7 @@ const WorkflowList = () => {
fetchTableData();
}, [fetchTableData]);
const handleEnabledChange = async (workflow: WorkflowType) => {
const handleEnabledChange = async (workflow: WorkflowModel) => {
try {
const resp = await saveWorkflow({
id: workflow.id,
@ -259,7 +259,7 @@ const WorkflowList = () => {
}
};
const handleDeleteClick = (workflow: WorkflowType) => {
const handleDeleteClick = (workflow: WorkflowModel) => {
modalApi.confirm({
title: t("workflow.action.delete"),
content: t("workflow.action.delete.confirm"),
@ -302,7 +302,7 @@ const WorkflowList = () => {
]}
/>
<Table<WorkflowType>
<Table<WorkflowModel>
columns={tableColumns}
dataSource={tableData}
loading={loading}

View File

@ -1,61 +0,0 @@
import { createContext, useCallback, useContext, useEffect, useReducer, type ReactNode } from "react";
import { type Access as AccessType } from "@/domain/access";
import { list as listAccess } from "@/repository/access";
import { configReducer } from "./reducer";
export type ConfigData = {
accesses: AccessType[];
};
export type ConfigContext = {
config: ConfigData;
addAccess: (access: AccessType) => void;
updateAccess: (access: AccessType) => void;
deleteAccess: (id: string) => void;
};
const Context = createContext({} as ConfigContext);
export const useConfigContext = () => useContext(Context);
export const ConfigProvider = ({ children }: { children: ReactNode }) => {
const [config, dispatchConfig] = useReducer(configReducer, {
accesses: [],
});
useEffect(() => {
const featchData = async () => {
const data = await listAccess();
dispatchConfig({ type: "SET_ACCESSES", payload: data });
};
featchData();
}, []);
const deleteAccess = useCallback((id: string) => {
dispatchConfig({ type: "DELETE_ACCESS", payload: id });
}, []);
const addAccess = useCallback((access: AccessType) => {
dispatchConfig({ type: "ADD_ACCESS", payload: access });
}, []);
const updateAccess = useCallback((access: AccessType) => {
dispatchConfig({ type: "UPDATE_ACCESS", payload: access });
}, []);
return (
<Context.Provider
value={{
config: {
accesses: config.accesses,
},
addAccess,
updateAccess,
deleteAccess,
}}
>
{children}
</Context.Provider>
);
};

View File

@ -1,39 +0,0 @@
import { Access } from "@/domain/access";
import { ConfigData } from "./";
type Action =
| { type: "ADD_ACCESS"; payload: Access }
| { type: "DELETE_ACCESS"; payload: string }
| { type: "UPDATE_ACCESS"; payload: Access }
| { type: "SET_ACCESSES"; payload: Access[] };
export const configReducer = (state: ConfigData, action: Action): ConfigData => {
switch (action.type) {
case "SET_ACCESSES": {
return {
...state,
accesses: action.payload,
};
}
case "ADD_ACCESS": {
return {
...state,
accesses: [action.payload, ...state.accesses],
};
}
case "DELETE_ACCESS": {
return {
...state,
accesses: state.accesses.filter((access) => access.id !== action.payload),
};
}
case "UPDATE_ACCESS": {
return {
...state,
accesses: state.accesses.map((access) => (access.id === action.payload.id ? action.payload : access)),
};
}
default:
return state;
}
};

View File

@ -1,13 +1,13 @@
import { ReactNode, useContext, createContext, useEffect, useReducer, useCallback } from "react";
import { NotifyChannel, NotifyChannels, Settings } from "@/domain/settings";
import { NotifyChannel, NotifyChannels, SettingsModel } from "@/domain/settings";
import { get } from "@/repository/settings";
import { notifyReducer } from "./reducer";
export type NotifyContext = {
config: Settings<NotifyChannels>;
config: SettingsModel<NotifyChannels>;
setChannel: (data: { channel: string; data: NotifyChannel }) => void;
setChannels: (data: Settings<NotifyChannels>) => void;
setChannels: (data: SettingsModel<NotifyChannels>) => void;
initChannels: () => void;
};
@ -45,7 +45,7 @@ export const NotifyProvider = ({ children }: NotifyProviderProps) => {
});
}, []);
const setChannels = useCallback((setting: Settings<NotifyChannels>) => {
const setChannels = useCallback((setting: SettingsModel<NotifyChannels>) => {
dispatchNotify({
type: "SET_CHANNELS",
payload: setting,

View File

@ -1,4 +1,4 @@
import { NotifyChannel, NotifyChannels, Settings } from "@/domain/settings";
import { NotifyChannel, NotifyChannels, SettingsModel } from "@/domain/settings";
type Action =
| {
@ -10,10 +10,10 @@ type Action =
}
| {
type: "SET_CHANNELS";
payload: Settings<NotifyChannels>;
payload: SettingsModel<NotifyChannels>;
};
export const notifyReducer = (state: Settings<NotifyChannels>, action: Action) => {
export const notifyReducer = (state: SettingsModel<NotifyChannels>, action: Action) => {
switch (action.type) {
case "SET_CHANNEL": {
const channel = action.payload.channel;

View File

@ -1,24 +1,26 @@
import dayjs from "dayjs";
import { type Access } from "@/domain/access";
import { type AccessModel } from "@/domain/access";
import { getPocketBase } from "./pocketbase";
const COLLECTION_NAME = "access";
export const list = async () => {
return await getPocketBase().collection("access").getFullList<Access>({
return await getPocketBase().collection(COLLECTION_NAME).getFullList<AccessModel>({
sort: "-created",
filter: "deleted = null",
filter: "deleted=null",
});
};
export const save = async (record: Access) => {
export const save = async (record: AccessModel) => {
if (record.id) {
return await getPocketBase().collection("access").update(record.id, record);
return await getPocketBase().collection(COLLECTION_NAME).update<AccessModel>(record.id, record);
}
return await getPocketBase().collection("access").create(record);
return await getPocketBase().collection(COLLECTION_NAME).create<AccessModel>(record);
};
export const remove = async (record: Access) => {
record.deleted = dayjs.utc().format("YYYY-MM-DD HH:mm:ss");
return await getPocketBase().collection("access").update(record.id, record);
export const remove = async (record: AccessModel) => {
record = { ...record, deleted: dayjs.utc().format("YYYY-MM-DD HH:mm:ss") };
await getPocketBase().collection(COLLECTION_NAME).update<AccessModel>(record.id, record);
};

View File

@ -1,9 +1,11 @@
import dayjs from "dayjs";
import { type RecordListOptions } from "pocketbase";
import { type Certificate } from "@/domain/certificate";
import { type CertificateModel } from "@/domain/certificate";
import { getPocketBase } from "./pocketbase";
const COLLECTION_NAME = "certificate";
export type CertificateListReq = {
page?: number;
perPage?: number;
@ -31,5 +33,5 @@ export const list = async (req: CertificateListReq) => {
});
}
return pb.collection("certificate").getList<Certificate>(page, perPage, options);
return pb.collection(COLLECTION_NAME).getList<CertificateModel>(page, perPage, options);
};

View File

@ -1,22 +1,22 @@
import { Settings } from "@/domain/settings";
import { SettingsModel } from "@/domain/settings";
import { getPocketBase } from "./pocketbase";
export const get = async <T>(name: string) => {
try {
const resp = await getPocketBase().collection("settings").getFirstListItem<Settings<T>>(`name='${name}'`);
const resp = await getPocketBase().collection("settings").getFirstListItem<SettingsModel<T>>(`name='${name}'`);
return resp;
} catch {
return {
name: name,
content: {} as T,
} as Settings<T>;
} as SettingsModel<T>;
}
};
export const save = async <T>(record: Settings<T>) => {
export const save = async <T>(record: SettingsModel<T>) => {
if (record.id) {
return await getPocketBase().collection("settings").update<Settings<T>>(record.id, record);
return await getPocketBase().collection("settings").update<SettingsModel<T>>(record.id, record);
}
return await getPocketBase().collection("settings").create<Settings<T>>(record);
return await getPocketBase().collection("settings").create<SettingsModel<T>>(record);
};

View File

@ -1,8 +1,10 @@
import { type RecordListOptions } from "pocketbase";
import { type Workflow, type WorkflowNode, type WorkflowRunLog } from "@/domain/workflow";
import { type WorkflowModel, type WorkflowNode, type WorkflowRunLog } from "@/domain/workflow";
import { getPocketBase } from "./pocketbase";
const COLLECTION_NAME = "workflow";
export type WorkflowListReq = {
page?: number;
perPage?: number;
@ -20,25 +22,25 @@ export const list = async (req: WorkflowListReq) => {
options.filter = pb.filter("enabled={:enabled}", { enabled: req.enabled });
}
return await pb.collection("workflow").getList<Workflow>(page, perPage, options);
return await pb.collection(COLLECTION_NAME).getList<WorkflowModel>(page, perPage, options);
};
export const get = async (id: string) => {
return await getPocketBase().collection("workflow").getOne<Workflow>(id);
return await getPocketBase().collection(COLLECTION_NAME).getOne<WorkflowModel>(id);
};
export const save = async (record: Record<string, string | boolean | WorkflowNode>) => {
if (record.id) {
return await getPocketBase()
.collection("workflow")
.update<Workflow>(record.id as string, record);
.collection(COLLECTION_NAME)
.update<WorkflowModel>(record.id as string, record);
}
return await getPocketBase().collection("workflow").create<Workflow>(record);
return await getPocketBase().collection(COLLECTION_NAME).create<WorkflowModel>(record);
};
export const remove = async (record: Workflow) => {
return await getPocketBase().collection("workflow").delete(record.id);
export const remove = async (record: WorkflowModel) => {
await getPocketBase().collection(COLLECTION_NAME).delete(record.id);
};
type WorkflowLogsReq = {

View File

@ -0,0 +1,58 @@
import { create } from "zustand";
import { produce } from "immer";
import { type AccessModel } from "@/domain/access";
import { list as listAccess, save as saveAccess, remove as removeAccess } from "@/repository/access";
export interface AccessState {
accesses: AccessModel[];
createAccess: (access: AccessModel) => void;
updateAccess: (access: AccessModel) => void;
deleteAccess: (access: AccessModel) => void;
fetchAccesses: () => Promise<void>;
}
export const useAccessStore = create<AccessState>((set) => {
return {
accesses: [],
createAccess: async (access) => {
access = await saveAccess(access);
set(
produce((state: AccessState) => {
state.accesses.unshift(access);
})
);
},
updateAccess: async (access) => {
access = await saveAccess(access);
set(
produce((state: AccessState) => {
const index = state.accesses.findIndex((e) => e.id === access.id);
state.accesses[index] = access;
})
);
},
deleteAccess: async (access) => {
await removeAccess(access);
set(
produce((state: AccessState) => {
state.accesses = state.accesses.filter((a) => a.id !== access.id);
})
);
},
fetchAccesses: async () => {
const accesses = await listAccess();
set({
accesses: accesses ?? [],
});
},
};
});

View File

@ -1,7 +1,7 @@
import { create } from "zustand";
import { produce } from "immer";
import { type EmailsSettingsContent, type Settings } from "@/domain/settings";
import { type EmailsSettingsContent, type SettingsModel } from "@/domain/settings";
import { get as getSettings, save as saveSettings } from "@/repository/settings";
export interface ContactState {
@ -11,7 +11,7 @@ export interface ContactState {
}
export const useContactStore = create<ContactState>((set) => {
let settings: Settings<EmailsSettingsContent>;
let settings: SettingsModel<EmailsSettingsContent>;
return {
emails: [],

View File

@ -9,7 +9,7 @@ import {
removeBranch,
removeNode,
updateNode,
Workflow,
WorkflowModel,
WorkflowBranchNode,
WorkflowNode,
WorkflowNodeType,
@ -17,7 +17,7 @@ import {
import { get as getWorkflow, save as saveWorkflow } from "@/repository/workflow";
export type WorkflowState = {
workflow: Workflow;
workflow: WorkflowModel;
initialized: boolean;
updateNode: (node: WorkflowNode) => void;
addNode: (node: WorkflowNode, preId: string) => void;