This commit is contained in:
yoan 2024-10-11 07:52:16 +08:00
parent 9f1b00f04c
commit 3b06c7b0a6
4 changed files with 557 additions and 18 deletions

View File

@ -0,0 +1,338 @@
import { createContext, useContext, useEffect, useState } from "react";
import { Button } from "../ui/button";
import { EditIcon, Plus, Trash2 } from "lucide-react";
import { DeployConfig, targetTypeKeys, targetTypeMap } from "@/domain/domain";
import Show from "../Show";
import { Alert, AlertDescription } from "../ui/alert";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
import { Label } from "../ui/label";
import { useConfig } from "@/providers/config";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "../ui/select";
import { Access, accessTypeMap } from "@/domain/access";
import { useTranslation } from "react-i18next";
import { AccessEdit } from "./AccessEdit";
import { Input } from "../ui/input";
import { Textarea } from "../ui/textarea";
import KVList from "./KVList";
type DeployListContextProps = {
deploys: DeployConfig[];
};
const DeployListContext = createContext<DeployListContextProps>({
deploys: [],
});
export const useDeployListContext = () => {
return useContext(DeployListContext);
};
type DeployListProps = {
deploys: DeployConfig[];
};
const DeployList = ({ deploys }: DeployListProps) => {
const [list, setList] = useState<DeployConfig[]>([]);
useEffect(() => {
setList(deploys);
}, [deploys]);
return (
<>
<DeployListContext.Provider value={{ deploys: deploys }}>
<Show
when={list.length > 0}
fallback={
<Alert className="w-full">
<AlertDescription className="flex flex-col items-center">
<div></div>
<div className="flex justify-end mt-2">
<DeployEditDialog
trigger={<Button size={"sm"}></Button>}
/>
</div>
</AlertDescription>
</Alert>
}
>
<div className="flex justify-end py-2 border-b">
<DeployEditDialog trigger={<Button size={"sm"}></Button>} />
</div>
<div className="w-full md:w-[35em] rounded mt-5 border">
<div className="">
<div className="flex justify-between text-sm p-3 items-center text-stone-700">
<div className="flex space-x-2 items-center">
<div>
<img src="/imgs/providers/ssh.svg" className="w-9"></img>
</div>
<div className="text-stone-600 flex-col flex space-y-0">
<div>ssh部署</div>
<div></div>
</div>
</div>
<div className="flex space-x-2">
<EditIcon size={16} className="cursor-pointer" />
<Trash2 size={16} className="cursor-pointer" />
</div>
</div>
</div>
</div>
</Show>
</DeployListContext.Provider>
</>
);
};
type DeployEditDialogProps = {
trigger: React.ReactNode;
};
const DeployEditDialog = ({ trigger }: DeployEditDialogProps) => {
const {
config: { accesses },
} = useConfig();
const [access, setAccess] = useState<Access>();
const [deployType, setDeployType] = useState<TargetType>();
const [accessType, setAccessType] = useState("");
useEffect(() => {
const temp = accessType.split("-");
console.log(temp);
let t;
if (temp && temp.length > 1) {
t = temp[1];
} else {
t = accessType;
}
setDeployType(t as TargetType);
}, [accessType]);
const { t } = useTranslation();
const targetAccesses = accesses.filter((item) => {
if (item.usage == "apply") {
return false;
}
if (accessType == "") {
return true;
}
const types = accessType.split("-");
return item.configType === types[0];
});
return (
<Dialog>
<DialogTrigger>{trigger}</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{/* 授权类型 */}
<div>
<Label></Label>
<Select
value={accessType}
onValueChange={(val: string) => {
setAccessType(val);
}}
>
<SelectTrigger className="mt-2">
<SelectValue
placeholder={t(
"domain.management.edit.access.not.empty.message"
)}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
{t("domain.management.edit.access.label")}
</SelectLabel>
{targetTypeKeys.map((item) => (
<SelectItem key={item} value={item}>
<div className="flex items-center space-x-2">
<img className="w-6" src={targetTypeMap.get(item)?.[1]} />
<div>{t(targetTypeMap.get(item)?.[0] ?? "")}</div>
</div>
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
{/* 授权 */}
<div>
<Label className="flex justify-between">
<div></div>
<AccessEdit
trigger={
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
<Plus size={14} />
{t("add")}
</div>
}
op="add"
/>
</Label>
<Select
value={access?.id}
onValueChange={(val: string) => {
const temp = accesses.find((access) => {
return access.id == val;
});
setAccess(temp!);
}}
>
<SelectTrigger className="mt-2">
<SelectValue
placeholder={t(
"domain.management.edit.access.not.empty.message"
)}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
{t("domain.management.edit.access.label")}
</SelectLabel>
{targetAccesses.map((item) => (
<SelectItem key={item.id} value={item.id}>
<div className="flex items-center space-x-2">
<img
className="w-6"
src={accessTypeMap.get(item.configType)?.[1]}
/>
<div>{item.name}</div>
</div>
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
<DeployEdit type={deployType!} />
<DialogFooter>
<Button></Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
type TargetType = "ssh" | "cdn" | "webhook" | "local" | "oss" | "dcdn";
type DeployEditProps = {
type: TargetType;
data?: DeployConfig;
};
const DeployEdit = ({ type, data }: DeployEditProps) => {
const getDeploy = () => {
switch (type) {
case "ssh":
return <DeploySSH data={data} />;
case "local":
return <DeploySSH data={data} />;
case "cdn":
return <DeployCDN data={data} />;
case "dcdn":
return <DeployCDN data={data} />;
case "oss":
return <DeployCDN data={data} />;
case "webhook":
return <DeployWebhook data={data} />;
default:
return <DeployCDN data={data} />;
}
};
return getDeploy();
};
type DeployProps = {
data?: DeployConfig;
};
const DeploySSH = ({ data }: DeployProps) => {
const { t } = useTranslation();
return (
<>
<div className="flex flex-col space-y-2">
<div>
<Label>{t("access.form.ssh.cert.path")}</Label>
<Input
placeholder={t("access.form.ssh.cert.path")}
className="w-full mt-1"
value={data?.config?.certPath}
/>
</div>
<div>
<Label>{t("access.form.ssh.key.path")}</Label>
<Input
placeholder={t("access.form.ssh.key.path")}
className="w-full mt-1"
value={data?.config?.keyPath}
/>
</div>
<div>
<Label></Label>
<Textarea className="mt-1"></Textarea>
</div>
<div>
<Label></Label>
<Textarea className="mt-1"></Textarea>
</div>
</div>
</>
);
};
const DeployCDN = ({ data }: DeployProps) => {
return (
<div className="flex flex-col space-y-2">
<div>
<Label></Label>
<Input
placeholder="部署至域名"
className="w-full mt-1"
value={data?.config?.domain}
/>
</div>
</div>
);
};
const DeployWebhook = ({ data }: DeployProps) => {
return (
<>
<KVList variables={data?.config?.variables} />
</>
);
};
export default DeployList;

View File

@ -0,0 +1,215 @@
import { KVType } from "@/domain/domain";
import { useEffect, useState } from "react";
import { Label } from "../ui/label";
import { Edit, Plus, Trash2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import Show from "../Show";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
type KVListProps = {
variables?: KVType[];
};
const KVList = ({ variables }: KVListProps) => {
const [locVariables, setLocVariables] = useState<KVType[]>([]);
const { t } = useTranslation();
useEffect(() => {
if (variables) {
setLocVariables(variables);
}
}, [variables]);
const handleAddClick = (variable: KVType) => {
// 查看是否存在key存在则更新不存在则添加
const index = locVariables.findIndex((item) => {
return item.key === variable.key;
});
if (index === -1) {
setLocVariables([...locVariables, variable]);
} else {
const newList = [...locVariables];
newList[index] = variable;
setLocVariables(newList);
}
};
const handleDeleteClick = (index: number) => {
const newList = [...locVariables];
newList.splice(index, 1);
setLocVariables(newList);
};
const handleEditClick = (index: number, variable: KVType) => {
const newList = [...locVariables];
newList[index] = variable;
setLocVariables(newList);
};
return (
<>
<div className="flex justify-between">
<Label></Label>
<Show when={!!locVariables?.length}>
<KVEdit
variable={{
key: "",
value: "",
}}
trigger={
<div className="flex items-center text-primary">
<Plus size={16} className="cursor-pointer " />
<div className="text-sm ">{t("add")}</div>
</div>
}
onSave={(variable) => {
handleAddClick(variable);
}}
/>
</Show>
</div>
<Show
when={!!locVariables?.length}
fallback={
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
<div className="text-muted-foreground">
{t("not.added.yet.variable")}
</div>
<KVEdit
trigger={
<div className="flex items-center text-primary">
<Plus size={16} className="cursor-pointer " />
<div className="text-sm ">{t("add")}</div>
</div>
}
variable={{
key: "",
value: "",
}}
onSave={(variable) => {
handleAddClick(variable);
}}
/>
</div>
}
>
<div className="border p-3 rounded-md text-stone-700 text-sm">
{locVariables?.map((item, index) => (
<div key={index} className="flex justify-between items-center">
<div>
{item.key}={item.value}
</div>
<div className="flex space-x-2">
<KVEdit
trigger={<Edit size={16} className="cursor-pointer" />}
variable={item}
onSave={(variable) => {
handleEditClick(index, variable);
}}
/>
<Trash2
size={16}
className="cursor-pointer"
onClick={() => {
handleDeleteClick(index);
}}
/>
</div>
</div>
))}
</div>
</Show>
</>
);
};
type KVEditProps = {
variable?: KVType;
trigger: React.ReactNode;
onSave: (variable: KVType) => void;
};
const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
const [locVariable, setLocVariable] = useState<KVType>({
key: "",
value: "",
});
useEffect(() => {
if (variable) setLocVariable(variable!);
}, [variable]);
const { t } = useTranslation();
const [open, setOpen] = useState<boolean>(false);
return (
<Dialog
open={open}
onOpenChange={() => {
setOpen(!open);
}}
>
<DialogTrigger>{trigger}</DialogTrigger>
<DialogContent>
<DialogHeader className="flex flex-col">
<DialogTitle></DialogTitle>
<div className="pt-5 flex flex-col items-start">
<Label></Label>
<Input
placeholder="请输入变量名"
value={locVariable?.key}
onChange={(e) => {
setLocVariable({ ...locVariable, key: e.target.value });
}}
className="w-full mt-1"
/>
</div>
<div className="pt-2 flex flex-col items-start">
<Label></Label>
<Input
placeholder="请输入变量值"
value={locVariable?.value}
onChange={(e) => {
setLocVariable({ ...locVariable, value: e.target.value });
}}
className="w-full mt-1"
/>
</div>
</DialogHeader>
<DialogFooter>
<div className="flex justify-end">
<Button
onClick={() => {
onSave?.(locVariable);
setOpen(false);
}}
>
{t("save")}
</Button>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default KVList;

View File

@ -31,7 +31,7 @@ export type Domain = {
deployConfig?: DeployConfig[];
};
type KVType = {
export type KVType = {
key: string;
value: string;
};

View File

@ -40,6 +40,7 @@ import { EmailsSetting } from "@/domain/settings";
import { useTranslation } from "react-i18next";
import StringList from "@/components/certimate/StringList";
import { Input } from "@/components/ui/input";
import DeployList from "@/components/certimate/DeployList";
const Edit = () => {
const {
@ -420,26 +421,11 @@ const Edit = () => {
<div
className={cn(
"flex flex-col space-y-5",
"flex flex-col space-y-5 w-full md:w-[35em]",
tab == "apply" && "hidden"
)}
>
<div className="flex justify-end py-2 border-b">
<Button size={"sm"} variant={"secondary"}>
</Button>
</div>
<div className="w-full md:w-[35em] rounded mt-5 border">
<div className="">
<div className="flex justify-between text-sm p-3 items-center text-stone-700">
<div>ALIYUN-CDN</div>
<div className="flex space-x-2">
<EditIcon size={16} />
<Trash2 size={16} />
</div>
</div>
</div>
</div>
<DeployList deploys={domain?.deployConfig ?? []} />
</div>
</div>
</div>