details improvement and unnecessary files deletion
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { update } from "@/repository/access_group";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
|
||||
type AccessGroupEditProps = {
|
||||
className?: string;
|
||||
trigger: React.ReactNode;
|
||||
};
|
||||
|
||||
const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
||||
const { reloadAccessGroups } = useConfigContext();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.group.form.name.errmsg.empty")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
try {
|
||||
await update({
|
||||
name: data.name,
|
||||
});
|
||||
|
||||
// 更新本地状态
|
||||
reloadAccessGroups();
|
||||
|
||||
setOpen(false);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
Object.entries(err.response.data as PbErrorData).forEach(([key, value]) => {
|
||||
form.setError(key as keyof z.infer<typeof formSchema>, {
|
||||
type: "manual",
|
||||
message: value.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog onOpenChange={setOpen} open={open}>
|
||||
<DialogTrigger asChild className={cn(className)}>
|
||||
{trigger}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("access.group.add")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="container py-3">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
className="space-y-8"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("access.group.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.group.form.name.errmsg.empty")} {...field} type="text" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessGroupEdit;
|
||||
@@ -1,171 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Group } from "lucide-react";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import AccessGroupEdit from "./AccessGroupEdit";
|
||||
import { accessProvidersMap } from "@/domain/access";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { remove } from "@/repository/access_group";
|
||||
|
||||
const AccessGroupList = () => {
|
||||
const {
|
||||
config: { accessGroups },
|
||||
reloadAccessGroups,
|
||||
} = useConfigContext();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleRemoveClick = async (id: string) => {
|
||||
try {
|
||||
await remove(id);
|
||||
reloadAccessGroups();
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("common.delete.failed.message"),
|
||||
description: getErrMessage(e),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddAccess = () => {
|
||||
navigate("/access");
|
||||
};
|
||||
return (
|
||||
<div className="mt-10">
|
||||
<Show when={accessGroups.length == 0}>
|
||||
<>
|
||||
<div className="flex flex-col items-center mt-10">
|
||||
<span className="bg-orange-100 p-5 rounded-full">
|
||||
<Group size={40} className="text-primary" />
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">{t("access.group.domains.nodata")}</div>
|
||||
<AccessGroupEdit trigger={<Button>{t("access.group.add")}</Button>} className="mt-3" />
|
||||
</div>
|
||||
</>
|
||||
</Show>
|
||||
|
||||
<ScrollArea className="h-[75vh] overflow-hidden">
|
||||
<div className="flex gap-5 flex-wrap">
|
||||
{accessGroups.map((accessGroup) => (
|
||||
<Card className="w-full md:w-[350px]">
|
||||
<CardHeader>
|
||||
<CardTitle>{accessGroup.name}</CardTitle>
|
||||
<CardDescription>
|
||||
{t("access.group.total", {
|
||||
total: accessGroup.expand ? accessGroup.expand.access.length : 0,
|
||||
})}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-[180px]">
|
||||
{accessGroup.expand ? (
|
||||
<>
|
||||
{accessGroup.expand.access.slice(0, 3).map((access) => (
|
||||
<div key={access.id} className="flex flex-col mb-3">
|
||||
<div className="flex items-center">
|
||||
<div className="">
|
||||
<img src={accessProvidersMap.get(access.configType)!.icon} alt="provider" className="w-8 h-8"></img>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-semibold text-gray-700 dark:text-gray-200">{access.name}</div>
|
||||
<div className="text-xs text-muted-foreground">{accessProvidersMap.get(access.configType)!.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex text-gray-700 dark:text-gray-200 items-center">
|
||||
<div>
|
||||
<Group size={40} />
|
||||
</div>
|
||||
<div className="ml-2">{t("access.group.nodata")}</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-end w-full">
|
||||
<Show when={accessGroup.expand && accessGroup.expand.access.length > 0 ? true : false}>
|
||||
<div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={"link"}
|
||||
onClick={() => {
|
||||
navigate(`/access?accessGroupId=${accessGroup.id}&tab=access`, {
|
||||
replace: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("access.group.domains")}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={!accessGroup.expand || accessGroup.expand.access.length == 0 ? true : false}>
|
||||
<div>
|
||||
<Button size="sm" onClick={handleAddAccess}>
|
||||
{t("access.authorization.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div className="ml-3">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"destructive"} size={"sm"}>
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="dark:text-gray-200">{t("access.group.delete")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{t("access.group.delete.confirm")}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="dark:text-gray-200">{t("common.cancel")}</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
handleRemoveClick(accessGroup.id ? accessGroup.id : "");
|
||||
}}
|
||||
>
|
||||
{t("common.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessGroupList;
|
||||
@@ -3,16 +3,12 @@ import { useTranslation } from "react-i18next";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Plus } from "lucide-react";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import AccessGroupEdit from "./AccessGroupEdit";
|
||||
import { readFileContent } from "@/lib/file";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type SSHConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
@@ -26,12 +22,7 @@ type AccessSSHFormProps = {
|
||||
};
|
||||
|
||||
const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
const {
|
||||
addAccess,
|
||||
updateAccess,
|
||||
reloadAccessGroups,
|
||||
config: { accessGroups },
|
||||
} = useConfigContext();
|
||||
const { addAccess, updateAccess, reloadAccessGroups } = useConfigContext();
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@@ -216,52 +207,6 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="group"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="w-full flex justify-between">
|
||||
<div>{t("access.authorization.form.ssh_group.label")}</div>
|
||||
<AccessGroupEdit
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
value={field.value}
|
||||
defaultValue="emptyId"
|
||||
onValueChange={(value) => {
|
||||
form.setValue("group", value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("access.authorization.form.access_group.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="emptyId">
|
||||
<div className={cn("flex items-center space-x-2 rounded cursor-pointer")}>--</div>
|
||||
</SelectItem>
|
||||
{accessGroups.map((item) => (
|
||||
<SelectItem value={item.id ? item.id : ""} key={item.id}>
|
||||
<div className={cn("flex items-center space-x-2 rounded cursor-pointer")}>{item.name}</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="id"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { createContext, useContext, type Context as ReactContext } from "react";
|
||||
|
||||
import { type DeployConfig } from "@/domain/domain";
|
||||
|
||||
export type DeployEditContext<T extends DeployConfig["config"] = DeployConfig["config"]> = {
|
||||
config: Omit<DeployConfig, "config"> & { config: T };
|
||||
setConfig: (config: Omit<DeployConfig, "config"> & { config: T }) => void;
|
||||
|
||||
errors: { [K in keyof T]?: string };
|
||||
setErrors: (error: { [K in keyof T]?: string }) => void;
|
||||
};
|
||||
|
||||
export const Context = createContext<DeployEditContext>({} as DeployEditContext);
|
||||
|
||||
export function useDeployEditContext<T extends DeployConfig["config"] = DeployConfig["config"]>() {
|
||||
return useContext<DeployEditContext<T>>(Context as unknown as ReactContext<DeployEditContext<T>>);
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Plus } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import AccessEditDialog from "./AccessEditDialog";
|
||||
import { Context as DeployEditContext, type DeployEditContext as DeployEditContextType } from "./DeployEdit";
|
||||
import DeployToAliyunOSS from "./DeployToAliyunOSS";
|
||||
import DeployToAliyunCDN from "./DeployToAliyunCDN";
|
||||
import DeployToAliyunCLB from "./DeployToAliyunCLB";
|
||||
import DeployToAliyunALB from "./DeployToAliyunALB";
|
||||
import DeployToAliyunNLB from "./DeployToAliyunNLB";
|
||||
import DeployToTencentCDN from "./DeployToTencentCDN";
|
||||
import DeployToTencentCLB from "./DeployToTencentCLB";
|
||||
import DeployToTencentCOS from "./DeployToTencentCOS";
|
||||
import DeployToTencentTEO from "./DeployToTencentTEO";
|
||||
import DeployToHuaweiCloudCDN from "./DeployToHuaweiCloudCDN";
|
||||
import DeployToHuaweiCloudELB from "./DeployToHuaweiCloudELB";
|
||||
import DeployToBaiduCloudCDN from "./DeployToBaiduCloudCDN";
|
||||
import DeployToQiniuCDN from "./DeployToQiniuCDN";
|
||||
import DeployToDogeCloudCDN from "./DeployToDogeCloudCDN";
|
||||
import DeployToLocal from "./DeployToLocal";
|
||||
import DeployToSSH from "./DeployToSSH";
|
||||
import DeployToWebhook from "./DeployToWebhook";
|
||||
import DeployToKubernetesSecret from "./DeployToKubernetesSecret";
|
||||
import DeployToVolcengineLive from "./DeployToVolcengineLive";
|
||||
import DeployToVolcengineCDN from "./DeployToVolcengineCDN";
|
||||
import DeployToByteplusCDN from "./DeployToByteplusCDN";
|
||||
import { deployTargetsMap, type DeployConfig } from "@/domain/domain";
|
||||
import { accessProvidersMap } from "@/domain/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
|
||||
type DeployEditDialogProps = {
|
||||
trigger: React.ReactNode;
|
||||
deployConfig?: DeployConfig;
|
||||
onSave: (deploy: DeployConfig) => void;
|
||||
};
|
||||
|
||||
const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
config: { accesses },
|
||||
} = useConfigContext();
|
||||
|
||||
const [deployType, setDeployType] = useState("");
|
||||
|
||||
const [locDeployConfig, setLocDeployConfig] = useState<DeployConfig>({
|
||||
access: "",
|
||||
type: "",
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (deployConfig) {
|
||||
setLocDeployConfig({ ...deployConfig });
|
||||
} else {
|
||||
setLocDeployConfig({
|
||||
access: "",
|
||||
type: "",
|
||||
});
|
||||
}
|
||||
}, [deployConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
setDeployType(locDeployConfig.type);
|
||||
setErrors({});
|
||||
}, [locDeployConfig.type]);
|
||||
|
||||
const setConfig = useCallback(
|
||||
(deploy: DeployConfig) => {
|
||||
if (deploy.type !== locDeployConfig.type) {
|
||||
setLocDeployConfig({ ...deploy, access: "", config: {} });
|
||||
} else {
|
||||
setLocDeployConfig({ ...deploy });
|
||||
}
|
||||
},
|
||||
[locDeployConfig.type]
|
||||
);
|
||||
|
||||
const targetAccesses = accesses.filter((item) => {
|
||||
if (item.usage == "apply") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (locDeployConfig.type == "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return item.configType === deployTargetsMap.get(locDeployConfig.type)?.provider;
|
||||
});
|
||||
|
||||
const handleSaveClick = () => {
|
||||
// 验证数据
|
||||
const newError = { ...errors };
|
||||
newError.type = locDeployConfig.type === "" ? t("domain.deployment.form.access.placeholder") : "";
|
||||
newError.access = locDeployConfig.access === "" ? t("domain.deployment.form.access.placeholder") : "";
|
||||
setErrors(newError);
|
||||
if (Object.values(newError).some((e) => !!e)) return;
|
||||
|
||||
// 保存数据
|
||||
onSave(locDeployConfig);
|
||||
|
||||
// 清理数据
|
||||
setLocDeployConfig({
|
||||
access: "",
|
||||
type: "",
|
||||
});
|
||||
setErrors({});
|
||||
|
||||
// 关闭弹框
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
let childComponent = <></>;
|
||||
switch (deployType) {
|
||||
case "aliyun-oss":
|
||||
childComponent = <DeployToAliyunOSS />;
|
||||
break;
|
||||
case "aliyun-cdn":
|
||||
case "aliyun-dcdn":
|
||||
childComponent = <DeployToAliyunCDN />;
|
||||
break;
|
||||
case "aliyun-clb":
|
||||
childComponent = <DeployToAliyunCLB />;
|
||||
break;
|
||||
case "aliyun-alb":
|
||||
childComponent = <DeployToAliyunALB />;
|
||||
break;
|
||||
case "aliyun-nlb":
|
||||
childComponent = <DeployToAliyunNLB />;
|
||||
break;
|
||||
case "tencent-cdn":
|
||||
case "tencent-ecdn":
|
||||
childComponent = <DeployToTencentCDN />;
|
||||
break;
|
||||
case "tencent-clb":
|
||||
childComponent = <DeployToTencentCLB />;
|
||||
break;
|
||||
case "tencent-cos":
|
||||
childComponent = <DeployToTencentCOS />;
|
||||
break;
|
||||
case "tencent-teo":
|
||||
childComponent = <DeployToTencentTEO />;
|
||||
break;
|
||||
case "huaweicloud-cdn":
|
||||
childComponent = <DeployToHuaweiCloudCDN />;
|
||||
break;
|
||||
case "huaweicloud-elb":
|
||||
childComponent = <DeployToHuaweiCloudELB />;
|
||||
break;
|
||||
case "baiducloud-cdn":
|
||||
childComponent = <DeployToBaiduCloudCDN />;
|
||||
break;
|
||||
case "qiniu-cdn":
|
||||
childComponent = <DeployToQiniuCDN />;
|
||||
break;
|
||||
case "dogecloud-cdn":
|
||||
childComponent = <DeployToDogeCloudCDN />;
|
||||
break;
|
||||
case "local":
|
||||
childComponent = <DeployToLocal />;
|
||||
break;
|
||||
case "ssh":
|
||||
childComponent = <DeployToSSH />;
|
||||
break;
|
||||
case "webhook":
|
||||
childComponent = <DeployToWebhook />;
|
||||
break;
|
||||
case "k8s-secret":
|
||||
childComponent = <DeployToKubernetesSecret />;
|
||||
break;
|
||||
case "volcengine-live":
|
||||
childComponent = <DeployToVolcengineLive />;
|
||||
break;
|
||||
case "volcengine-cdn":
|
||||
childComponent = <DeployToVolcengineCDN />;
|
||||
break;
|
||||
case "byteplus-cdn":
|
||||
childComponent = <DeployToByteplusCDN />;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<DeployEditContext.Provider
|
||||
value={{
|
||||
config: locDeployConfig as DeployEditContextType["config"],
|
||||
setConfig: setConfig as DeployEditContextType["setConfig"],
|
||||
errors: errors as DeployEditContextType["errors"],
|
||||
setErrors: setErrors as DeployEditContextType["setErrors"],
|
||||
}}
|
||||
>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger>{trigger}</DialogTrigger>
|
||||
<DialogContent
|
||||
className="dark:text-stone-200"
|
||||
onInteractOutside={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("domain.deployment.tab")}</DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<ScrollArea className="max-h-[80vh]">
|
||||
<div className="container py-3">
|
||||
{/* 部署方式 */}
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.type.label")}</Label>
|
||||
|
||||
<Select
|
||||
value={locDeployConfig.type}
|
||||
onValueChange={(val: string) => {
|
||||
setConfig({ ...locDeployConfig, type: val });
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="mt-2">
|
||||
<SelectValue placeholder={t("domain.deployment.form.type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{t("domain.deployment.form.type.list")}</SelectLabel>
|
||||
{Array.from(deployTargetsMap.entries()).map(([key, target]) => (
|
||||
<SelectItem key={key} value={key}>
|
||||
<div className="flex items-center space-x-2">
|
||||
<img className="w-6" src={target.icon} />
|
||||
<div>{t(target.name)}</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<div className="text-red-500 text-sm mt-1">{errors.type}</div>
|
||||
</div>
|
||||
|
||||
{/* 授权配置 */}
|
||||
<div className="mt-8">
|
||||
<Label className="flex justify-between">
|
||||
<div>{t("domain.deployment.form.access.label")}</div>
|
||||
<AccessEditDialog
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Select
|
||||
value={locDeployConfig.access}
|
||||
onValueChange={(val: string) => {
|
||||
setConfig({ ...locDeployConfig, access: val });
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="mt-2">
|
||||
<SelectValue placeholder={t("domain.deployment.form.access.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{t("domain.deployment.form.access.list")}</SelectLabel>
|
||||
{targetAccesses.map((item) => (
|
||||
<SelectItem key={item.id} value={item.id}>
|
||||
<div className="flex items-center space-x-2">
|
||||
<img className="w-6" src={accessProvidersMap.get(item.configType)?.icon} />
|
||||
<div>{item.name}</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<div className="text-red-500 text-sm mt-1">{errors.access}</div>
|
||||
</div>
|
||||
|
||||
{/* 其他参数 */}
|
||||
<div className="mt-8">{childComponent}</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</DeployEditContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployEditDialog;
|
||||
@@ -1,169 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { nanoid } from "nanoid";
|
||||
import { EditIcon, Trash2 } from "lucide-react";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import DeployEditDialog from "./DeployEditDialog";
|
||||
import { DeployConfig } from "@/domain/domain";
|
||||
import { accessProvidersMap } from "@/domain/access";
|
||||
import { deployTargetsMap } from "@/domain/domain";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
|
||||
type DeployItemProps = {
|
||||
item: DeployConfig;
|
||||
onDelete: () => void;
|
||||
onSave: (deploy: DeployConfig) => void;
|
||||
};
|
||||
|
||||
const DeployItem = ({ item, onDelete, onSave }: DeployItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
config: { accesses },
|
||||
} = useConfigContext();
|
||||
|
||||
const access = accesses.find((access) => access.id === item.access);
|
||||
|
||||
const getTypeIcon = () => {
|
||||
if (!access) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return accessProvidersMap.get(access.configType)?.icon || "";
|
||||
};
|
||||
|
||||
const getTypeName = () => {
|
||||
return t(deployTargetsMap.get(item.type)?.name || "");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-between text-sm p-3 items-center text-stone-700 dark:text-stone-200">
|
||||
<div className="flex space-x-2 items-center">
|
||||
<div>
|
||||
<img src={getTypeIcon()} className="w-9"></img>
|
||||
</div>
|
||||
<div className="text-stone-600 flex-col flex space-y-0 dark:text-stone-200">
|
||||
<div>{getTypeName()}</div>
|
||||
<div>{access?.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<DeployEditDialog
|
||||
trigger={<EditIcon size={16} className="cursor-pointer" />}
|
||||
deployConfig={item}
|
||||
onSave={(deploy: DeployConfig) => {
|
||||
onSave(deploy);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Trash2
|
||||
size={16}
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
onDelete();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type DeployListProps = {
|
||||
deploys: DeployConfig[];
|
||||
onChange: (deploys: DeployConfig[]) => void;
|
||||
};
|
||||
|
||||
const DeployList = ({ deploys, onChange }: DeployListProps) => {
|
||||
const [list, setList] = useState<DeployConfig[]>([]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setList(deploys);
|
||||
}, [deploys]);
|
||||
|
||||
const handleAdd = (deploy: DeployConfig) => {
|
||||
deploy.id = nanoid();
|
||||
|
||||
const newList = [...list, deploy];
|
||||
|
||||
setList(newList);
|
||||
|
||||
onChange(newList);
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
const newList = list.filter((item) => item.id !== id);
|
||||
|
||||
setList(newList);
|
||||
|
||||
onChange(newList);
|
||||
};
|
||||
|
||||
const handleSave = (deploy: DeployConfig) => {
|
||||
const newList = list.map((item) => {
|
||||
if (item.id === deploy.id) {
|
||||
return { ...deploy };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
setList(newList);
|
||||
|
||||
onChange(newList);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show
|
||||
when={list.length > 0}
|
||||
fallback={
|
||||
<Alert className="w-full border dark:border-stone-400">
|
||||
<AlertDescription className="flex flex-col items-center">
|
||||
<div>{t("domain.deployment.nodata")}</div>
|
||||
<div className="flex justify-end mt-2">
|
||||
<DeployEditDialog
|
||||
onSave={(config: DeployConfig) => {
|
||||
handleAdd(config);
|
||||
}}
|
||||
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
|
||||
/>
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
}
|
||||
>
|
||||
<div className="flex justify-end py-2 border-b dark:border-stone-400">
|
||||
<DeployEditDialog
|
||||
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
|
||||
onSave={(config: DeployConfig) => {
|
||||
handleAdd(config);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full md:w-[35em] rounded mt-5 border dark:border-stone-400 dark:text-stone-200">
|
||||
<div className="">
|
||||
{list.map((item) => (
|
||||
<DeployItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
onDelete={() => {
|
||||
handleDelete(item.id ?? "");
|
||||
}}
|
||||
onSave={(deploy: DeployConfig) => {
|
||||
handleSave(deploy);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployList;
|
||||
@@ -1,55 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type DeployProgressProps = {
|
||||
phase?: "check" | "apply" | "deploy";
|
||||
phaseSuccess?: boolean;
|
||||
};
|
||||
|
||||
const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let step = 0;
|
||||
|
||||
if (phase === "check") {
|
||||
step = 1;
|
||||
} else if (phase === "apply") {
|
||||
step = 2;
|
||||
} else if (phase === "deploy") {
|
||||
step = 3;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div className={cn("text-xs text-nowrap", step === 1 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "", step > 1 ? "text-green-600" : "")}>
|
||||
{t("history.props.stage.progress.check")}
|
||||
</div>
|
||||
<Separator className={cn("h-1 grow max-w-[60px]", step > 1 ? "bg-green-600" : "")} />
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-nowrap",
|
||||
step < 2 ? "text-muted-foreground" : "",
|
||||
step === 2 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||
step > 2 ? "text-green-600" : ""
|
||||
)}
|
||||
>
|
||||
{t("history.props.stage.progress.apply")}
|
||||
</div>
|
||||
<Separator className={cn("h-1 grow max-w-[60px]", step > 2 ? "bg-green-600" : "")} />
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-nowrap",
|
||||
step < 3 ? "text-muted-foreground" : "",
|
||||
step === 3 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||
step > 3 ? "text-green-600" : ""
|
||||
)}
|
||||
>
|
||||
{t("history.props.stage.progress.deploy")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployProgress;
|
||||
@@ -1,43 +0,0 @@
|
||||
import { CircleCheck, CircleX } from "lucide-react";
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Deployment } from "@/domain/deployment";
|
||||
|
||||
type DeployStateProps = {
|
||||
deployment: Deployment;
|
||||
};
|
||||
|
||||
const DeployState = ({ deployment }: DeployStateProps) => {
|
||||
// 获取指定阶段的错误信息
|
||||
const error = (state: "check" | "apply" | "deploy") => {
|
||||
if (!deployment.log[state]) {
|
||||
return "";
|
||||
}
|
||||
return deployment.log[state][deployment.log[state].length - 1].error;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{(deployment.phase === "deploy" && deployment.phaseSuccess) || deployment.wholeSuccess ? (
|
||||
<CircleCheck size={16} className="text-green-700" />
|
||||
) : (
|
||||
<>
|
||||
{error(deployment.phase).length ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild className="cursor-pointer">
|
||||
<CircleX size={16} className="text-red-700" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-[35em]">{error(deployment.phase)}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<CircleX size={16} className="text-red-700" />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployState;
|
||||
@@ -1,156 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToAliyunALBConfigParams = {
|
||||
region?: string;
|
||||
resourceType?: string;
|
||||
loadbalancerId?: string;
|
||||
listenerId?: string;
|
||||
};
|
||||
|
||||
const DeployToAliyunALB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToAliyunALBConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_alb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_alb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerId: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? !!data.listenerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_alb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error?.errors?.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error?.errors?.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error?.errors?.find((e) => e.path[0] === "listenerId")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_alb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_alb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_alb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
{config?.config?.resourceType === "loadbalancer" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.loadbalancerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunALB;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToAliyunCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToAliyunCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToAliyunCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunCDN;
|
||||
@@ -1,153 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToAliyunCLBConfigParams = {
|
||||
region?: string;
|
||||
resourceType?: string;
|
||||
loadbalancerId?: string;
|
||||
listenerPort?: string;
|
||||
};
|
||||
|
||||
const DeployToAliyunCLB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToAliyunCLBConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
listenerPort: "443",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_clb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("certificate"), z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_clb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerPort: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" || data.resourceType === "listener" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? +data.listenerPort! > 0 && +data.listenerPort! < 65535 : true), {
|
||||
message: t("domain.deployment.form.aliyun_clb_listener_port.placeholder"),
|
||||
path: ["listenerPort"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error?.errors?.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error?.errors?.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerPort: res.error?.errors?.find((e) => e.path[0] === "listenerPort")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_clb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_clb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_clb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.loadbalancerId}</div>
|
||||
</div>
|
||||
|
||||
{config?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_listener_port.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_listener_port.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.listenerPort}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerPort = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.listenerPort}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunCLB;
|
||||
@@ -1,156 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToAliyunNLBConfigParams = {
|
||||
region?: string;
|
||||
resourceType?: string;
|
||||
loadbalancerId?: string;
|
||||
listenerId?: string;
|
||||
};
|
||||
|
||||
const DeployToAliyunNLB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToAliyunNLBConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_nlb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_nlb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerId: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? !!data.listenerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_nlb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error?.errors?.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error?.errors?.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error?.errors?.find((e) => e.path[0] === "listenerId")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_nlb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_nlb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_nlb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
{config?.config?.resourceType === "loadbalancer" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.loadbalancerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunNLB;
|
||||
@@ -1,114 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToAliyunOSSConfigParams = {
|
||||
endpoint?: string;
|
||||
bucket?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToAliyunOSS = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToAliyunOSSConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
endpoint: "oss.aliyuncs.com",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
endpoint: z.string().min(1, {
|
||||
message: t("domain.deployment.form.aliyun_oss_endpoint.placeholder"),
|
||||
}),
|
||||
bucket: z.string().min(1, {
|
||||
message: t("domain.deployment.form.aliyun_oss_bucket.placeholder"),
|
||||
}),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
endpoint: res.error?.errors?.find((e) => e.path[0] === "endpoint")?.message,
|
||||
bucket: res.error?.errors?.find((e) => e.path[0] === "bucket")?.message,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_oss_endpoint.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_oss_endpoint.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.endpoint}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.endpoint = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.endpoint}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_oss_bucket.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_oss_bucket.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.bucket}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.bucket = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.bucket}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunOSS;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToBaiduCloudCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToBaiduCloudCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToBaiduCloudCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToBaiduCloudCDN;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToByteplusCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToByteplusCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToByteplusCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label.wildsupported")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToByteplusCDN;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToDogeCloudCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToDogeCloudCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToDogeCloudCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToDogeCloudCDN;
|
||||
@@ -1,92 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToHuaweiCloudCDNConfigParams = {
|
||||
region?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToHuaweiCloudCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToHuaweiCloudCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "cn-north-1",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
region: z.string().min(1, {
|
||||
message: t("domain.deployment.form.huaweicloud_cdn_region.placeholder"),
|
||||
}),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_cdn_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.huaweicloud_cdn_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToHuaweiCloudCDN;
|
||||
@@ -1,185 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToHuaweiCloudELBConfigParams = {
|
||||
region?: string;
|
||||
resourceType?: string;
|
||||
certificateId?: string;
|
||||
loadbalancerId?: string;
|
||||
listenerId?: string;
|
||||
};
|
||||
|
||||
const DeployToHuaweiCloudELB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToHuaweiCloudELBConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "cn-north-1",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.huaweicloud_elb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("certificate"), z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_resource_type.placeholder"),
|
||||
}),
|
||||
certificateId: z.string().optional(),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerId: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "certificate" ? !!data.certificateId?.trim() : true), {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_certificate_id.placeholder"),
|
||||
path: ["certificateId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? !!data.listenerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error?.errors?.find((e) => e.path[0] === "resourceType")?.message,
|
||||
certificateId: res.error?.errors?.find((e) => e.path[0] === "certificateId")?.message,
|
||||
loadbalancerId: res.error?.errors?.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error?.errors?.find((e) => e.path[0] === "listenerId")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_elb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.huaweicloud_elb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_elb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.huaweicloud_elb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="certificate">{t("domain.deployment.form.huaweicloud_elb_resource_type.option.certificate.label")}</SelectItem>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.huaweicloud_elb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.huaweicloud_elb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
{config?.config?.resourceType === "certificate" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_elb_certificate_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.huaweicloud_elb_certificate_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.certificateId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certificateId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.certificateId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "loadbalancer" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_elb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.loadbalancerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.huaweicloud_elb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.huaweicloud_elb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToHuaweiCloudELB;
|
||||
@@ -1,136 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToKubernetesSecretConfigParams = {
|
||||
namespace?: string;
|
||||
secretName?: string;
|
||||
secretDataKeyForCrt?: string;
|
||||
secretDataKeyForKey?: string;
|
||||
};
|
||||
|
||||
const DeployToKubernetesSecret = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToKubernetesSecretConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
namespace: "default",
|
||||
secretDataKeyForCrt: "tls.crt",
|
||||
secretDataKeyForKey: "tls.key",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
namespace: z.string().min(1, {
|
||||
message: t("domain.deployment.form.k8s_namespace.placeholder"),
|
||||
}),
|
||||
secretName: z.string().min(1, {
|
||||
message: t("domain.deployment.form.k8s_secret_name.placeholder"),
|
||||
}),
|
||||
secretDataKeyForCrt: z.string().min(1, {
|
||||
message: t("domain.deployment.form.k8s_secret_data_key_for_crt.placeholder"),
|
||||
}),
|
||||
secretDataKeyForKey: z.string().min(1, {
|
||||
message: t("domain.deployment.form.k8s_secret_data_key_for_key.placeholder"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
namespace: res.error?.errors?.find((e) => e.path[0] === "namespace")?.message,
|
||||
secretName: res.error?.errors?.find((e) => e.path[0] === "secretName")?.message,
|
||||
secretDataKeyForCrt: res.error?.errors?.find((e) => e.path[0] === "secretDataKeyForCrt")?.message,
|
||||
secretDataKeyForKey: res.error?.errors?.find((e) => e.path[0] === "secretDataKeyForKey")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_namespace.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_namespace.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.namespace}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.namespace = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_name.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_name.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.secretName}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretName = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_data_key_for_crt.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_data_key_for_crt.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.secretDataKeyForCrt}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretDataKeyForCrt = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_data_key_for_key.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_data_key_for_key.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.secretDataKeyForKey}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretDataKeyForKey = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToKubernetesSecret;
|
||||
@@ -1,481 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type DeployToLocalConfigParams = {
|
||||
format?: string;
|
||||
certPath?: string;
|
||||
keyPath?: string;
|
||||
pfxPassword?: string;
|
||||
jksAlias?: string;
|
||||
jksKeypass?: string;
|
||||
jksStorepass?: string;
|
||||
shell?: string;
|
||||
preCommand?: string;
|
||||
command?: string;
|
||||
};
|
||||
|
||||
const DeployToLocal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToLocalConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
format: "pem",
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
shell: "sh",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx"), z.literal("jks")], {
|
||||
message: t("domain.deployment.form.file_format.placeholder"),
|
||||
}),
|
||||
certPath: z
|
||||
.string()
|
||||
.min(1, t("domain.deployment.form.file_cert_path.placeholder"))
|
||||
.max(255, t("common.errmsg.string_max", { max: 255 })),
|
||||
keyPath: z
|
||||
.string()
|
||||
.min(0, t("domain.deployment.form.file_key_path.placeholder"))
|
||||
.max(255, t("common.errmsg.string_max", { max: 255 })),
|
||||
pfxPassword: z.string().optional(),
|
||||
jksAlias: z.string().optional(),
|
||||
jksKeypass: z.string().optional(),
|
||||
jksStorepass: z.string().optional(),
|
||||
shell: z.union([z.literal("sh"), z.literal("cmd"), z.literal("powershell")], {
|
||||
message: t("domain.deployment.form.shell.placeholder"),
|
||||
}),
|
||||
preCommand: z.string().optional(),
|
||||
command: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.format === "pem" ? !!data.keyPath?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_key_path.placeholder"),
|
||||
path: ["keyPath"],
|
||||
})
|
||||
.refine((data) => (data.format === "pfx" ? !!data.pfxPassword?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_pfx_password.placeholder"),
|
||||
path: ["pfxPassword"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksAlias?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_alias.placeholder"),
|
||||
path: ["jksAlias"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksKeypass?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_keypass.placeholder"),
|
||||
path: ["jksKeypass"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksStorepass?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_storepass.placeholder"),
|
||||
path: ["jksStorepass"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
format: res.error?.errors?.find((e) => e.path[0] === "format")?.message,
|
||||
certPath: res.error?.errors?.find((e) => e.path[0] === "certPath")?.message,
|
||||
keyPath: res.error?.errors?.find((e) => e.path[0] === "keyPath")?.message,
|
||||
pfxPassword: res.error?.errors?.find((e) => e.path[0] === "pfxPassword")?.message,
|
||||
jksAlias: res.error?.errors?.find((e) => e.path[0] === "jksAlias")?.message,
|
||||
jksKeypass: res.error?.errors?.find((e) => e.path[0] === "jksKeypass")?.message,
|
||||
jksStorepass: res.error?.errors?.find((e) => e.path[0] === "jksStorepass")?.message,
|
||||
shell: res.error?.errors?.find((e) => e.path[0] === "shell")?.message,
|
||||
preCommand: res.error?.errors?.find((e) => e.path[0] === "preCommand")?.message,
|
||||
command: res.error?.errors?.find((e) => e.path[0] === "command")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
useEffect(() => {
|
||||
if (config.config?.format === "pem") {
|
||||
if (/(.pfx|.jks)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.pfx|.jks)$/, ".crt");
|
||||
})
|
||||
);
|
||||
}
|
||||
} else if (config.config?.format === "pfx") {
|
||||
if (/(.crt|.jks)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.crt|.jks)$/, ".pfx");
|
||||
})
|
||||
);
|
||||
}
|
||||
} else if (config.config?.format === "jks") {
|
||||
if (/(.crt|.pfx)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.crt|.pfx)$/, ".jks");
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [config.config?.format]);
|
||||
|
||||
const getOptionCls = (val: string) => {
|
||||
if (config.config?.shell === val) {
|
||||
return "border-primary dark:border-primary";
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const handleUsePresetScript = (key: string) => {
|
||||
switch (key) {
|
||||
case "reload_nginx":
|
||||
{
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.shell = "sh";
|
||||
draft.config.command = "sudo service nginx reload";
|
||||
})
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "binding_iis":
|
||||
{
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.shell = "powershell";
|
||||
draft.config.command = `
|
||||
# 请将以下变量替换为实际值
|
||||
$pfxPath = "<your-pfx-path>" # PFX 文件路径
|
||||
$pfxPassword = "<your-pfx-password>" # PFX 密码
|
||||
$siteName = "<your-site-name>" # IIS 网站名称
|
||||
$domain = "<your-domain-name>" # 域名
|
||||
$ipaddr = "<your-binding-ip>" # 绑定 IP,“*”表示所有 IP 绑定
|
||||
$port = "<your-binding-port>" # 绑定端口
|
||||
|
||||
|
||||
# 导入证书到本地计算机的个人存储区
|
||||
$cert = Import-PfxCertificate -FilePath "$pfxPath" -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String "$pfxPassword" -AsPlainText -Force) -Exportable
|
||||
# 获取 Thumbprint
|
||||
$thumbprint = $cert.Thumbprint
|
||||
# 导入 WebAdministration 模块
|
||||
Import-Module WebAdministration
|
||||
# 检查是否已存在 HTTPS 绑定
|
||||
$existingBinding = Get-WebBinding -Name "$siteName" -Protocol "https" -Port $port -HostHeader "$domain" -ErrorAction SilentlyContinue
|
||||
if (!$existingBinding) {
|
||||
# 添加新的 HTTPS 绑定
|
||||
New-WebBinding -Name "$siteName" -Protocol "https" -Port $port -IPAddress "$ipaddr" -HostHeader "$domain"
|
||||
}
|
||||
# 获取绑定对象
|
||||
$binding = Get-WebBinding -Name "$siteName" -Protocol "https" -Port $port -IPAddress "$ipaddr" -HostHeader "$domain"
|
||||
# 绑定 SSL 证书
|
||||
$binding.AddSslCertificate($thumbprint, "My")
|
||||
# 删除目录下的证书文件
|
||||
Remove-Item -Path "$pfxPath" -Force
|
||||
`.trim();
|
||||
})
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "binding_netsh":
|
||||
{
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.shell = "powershell";
|
||||
draft.config.command = `
|
||||
# 请将以下变量替换为实际值
|
||||
$pfxPath = "<your-pfx-path>" # PFX 文件路径
|
||||
$pfxPassword = "<your-pfx-password>" # PFX 密码
|
||||
$ipaddr = "<your-binding-ip>" # 绑定 IP,“0.0.0.0”表示所有 IP 绑定,可填入域名。
|
||||
$port = "<your-binding-port>" # 绑定端口
|
||||
|
||||
$addr = $ipaddr + ":" + $port
|
||||
|
||||
# 导入证书到本地计算机的个人存储区
|
||||
$cert = Import-PfxCertificate -FilePath "$pfxPath" -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String "$pfxPassword" -AsPlainText -Force) -Exportable
|
||||
# 获取 Thumbprint
|
||||
$thumbprint = $cert.Thumbprint
|
||||
# 检测端口是否绑定证书,如绑定则删除绑定
|
||||
$isExist = netsh http show sslcert ipport=$addr
|
||||
if ($isExist -like "*$addr*"){ netsh http delete sslcert ipport=$addr }
|
||||
# 绑定到端口
|
||||
netsh http add sslcert ipport=$addr certhash=$thumbprint
|
||||
# 删除目录下的证书文件
|
||||
Remove-Item -Path "$pfxPath" -Force
|
||||
`.trim();
|
||||
})
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_format.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.format}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.format = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.file_format.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
<SelectItem value="jks">JKS</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.format}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_cert_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_cert_path.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.certPath}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.certPath}</div>
|
||||
</div>
|
||||
|
||||
{config.config?.format === "pem" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.keyPath}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config.config?.format === "pfx" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_pfx_password.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_pfx_password.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.pfxPassword}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.pfxPassword = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.pfxPassword}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config.config?.format === "jks" ? (
|
||||
<>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_alias.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_alias.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksAlias}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksAlias = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksAlias}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_keypass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_keypass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksKeypass}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksKeypass = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksKeypass}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_storepass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_storepass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksStorepass}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksStorepass = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksStorepass}</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell.label")}</Label>
|
||||
<RadioGroup
|
||||
className="flex mt-1"
|
||||
value={config?.config?.shell}
|
||||
onValueChange={(val) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.shell = val;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="sh" id="shellOptionSh" />
|
||||
<Label htmlFor="shellOptionSh">
|
||||
<div className={cn("flex items-center space-x-2 border p-2 rounded cursor-pointer dark:border-stone-700", getOptionCls("sh"))}>
|
||||
<div>POSIX Bash (Linux)</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="cmd" id="shellOptionCmd" />
|
||||
<Label htmlFor="shellOptionCmd">
|
||||
<div className={cn("border p-2 rounded cursor-pointer dark:border-stone-700", getOptionCls("cmd"))}>
|
||||
<div>CMD (Windows)</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="powershell" id="shellOptionPowerShell" />
|
||||
<Label htmlFor="shellOptionPowerShell">
|
||||
<div className={cn("border p-2 rounded cursor-pointer dark:border-stone-700", getOptionCls("powershell"))}>
|
||||
<div>PowerShell (Windows)</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.shell}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell_pre_command.label")}</Label>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={config?.config?.preCommand}
|
||||
placeholder={t("domain.deployment.form.shell_pre_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.preCommand = e.target.value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
></Textarea>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.preCommand}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>{t("domain.deployment.form.shell_command.label")}</Label>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<a className="text-xs text-blue-500 cursor-pointer">{t("domain.deployment.form.shell_preset_scripts.trigger")}</a>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={() => handleUsePresetScript("reload_nginx")}>
|
||||
{t("domain.deployment.form.shell_preset_scripts.option.reload_nginx.label")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleUsePresetScript("binding_iis")}>
|
||||
{t("domain.deployment.form.shell_preset_scripts.option.binding_iis.label")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleUsePresetScript("binding_netsh")}>
|
||||
{t("domain.deployment.form.shell_preset_scripts.option.binding_netsh.label")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={config?.config?.command}
|
||||
placeholder={t("domain.deployment.form.shell_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.command = e.target.value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
></Textarea>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.command}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToLocal;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToQiniuCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToQiniuCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToQiniuCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label.wildsupported")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToQiniuCDN;
|
||||
@@ -1,319 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToSSHConfigParams = {
|
||||
format?: string;
|
||||
certPath?: string;
|
||||
keyPath?: string;
|
||||
pfxPassword?: string;
|
||||
jksAlias?: string;
|
||||
jksKeypass?: string;
|
||||
jksStorepass?: string;
|
||||
shell?: string;
|
||||
preCommand?: string;
|
||||
command?: string;
|
||||
};
|
||||
|
||||
const DeployToSSH = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToSSHConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
format: "pem",
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
command: "sudo service nginx reload",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx"), z.literal("jks")], {
|
||||
message: t("domain.deployment.form.file_format.placeholder"),
|
||||
}),
|
||||
certPath: z
|
||||
.string()
|
||||
.min(1, t("domain.deployment.form.file_cert_path.placeholder"))
|
||||
.max(255, t("common.errmsg.string_max", { max: 255 })),
|
||||
keyPath: z
|
||||
.string()
|
||||
.min(0, t("domain.deployment.form.file_key_path.placeholder"))
|
||||
.max(255, t("common.errmsg.string_max", { max: 255 })),
|
||||
pfxPassword: z.string().optional(),
|
||||
jksAlias: z.string().optional(),
|
||||
jksKeypass: z.string().optional(),
|
||||
jksStorepass: z.string().optional(),
|
||||
preCommand: z.string().optional(),
|
||||
command: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.format === "pem" ? !!data.keyPath?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_key_path.placeholder"),
|
||||
path: ["keyPath"],
|
||||
})
|
||||
.refine((data) => (data.format === "pfx" ? !!data.pfxPassword?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_pfx_password.placeholder"),
|
||||
path: ["pfxPassword"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksAlias?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_alias.placeholder"),
|
||||
path: ["jksAlias"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksKeypass?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_keypass.placeholder"),
|
||||
path: ["jksKeypass"],
|
||||
})
|
||||
.refine((data) => (data.format === "jks" ? !!data.jksStorepass?.trim() : true), {
|
||||
message: t("domain.deployment.form.file_jks_storepass.placeholder"),
|
||||
path: ["jksStorepass"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
format: res.error?.errors?.find((e) => e.path[0] === "format")?.message,
|
||||
certPath: res.error?.errors?.find((e) => e.path[0] === "certPath")?.message,
|
||||
keyPath: res.error?.errors?.find((e) => e.path[0] === "keyPath")?.message,
|
||||
pfxPassword: res.error?.errors?.find((e) => e.path[0] === "pfxPassword")?.message,
|
||||
jksAlias: res.error?.errors?.find((e) => e.path[0] === "jksAlias")?.message,
|
||||
jksKeypass: res.error?.errors?.find((e) => e.path[0] === "jksKeypass")?.message,
|
||||
jksStorepass: res.error?.errors?.find((e) => e.path[0] === "jksStorepass")?.message,
|
||||
preCommand: res.error?.errors?.find((e) => e.path[0] === "preCommand")?.message,
|
||||
command: res.error?.errors?.find((e) => e.path[0] === "command")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
useEffect(() => {
|
||||
if (config.config?.format === "pem") {
|
||||
if (/(.pfx|.jks)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.pfx|.jks)$/, ".crt");
|
||||
})
|
||||
);
|
||||
}
|
||||
} else if (config.config?.format === "pfx") {
|
||||
if (/(.crt|.jks)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.crt|.jks)$/, ".pfx");
|
||||
})
|
||||
);
|
||||
}
|
||||
} else if (config.config?.format === "jks") {
|
||||
if (/(.crt|.pfx)$/.test(config.config.certPath!)) {
|
||||
setConfig(
|
||||
produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = config.config!.certPath!.replace(/(.crt|.pfx)$/, ".jks");
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [config.config?.format]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_format.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.format}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.format = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.file_format.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
<SelectItem value="jks">JKS</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.format}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_cert_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_cert_path.label")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.certPath}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.certPath}</div>
|
||||
</div>
|
||||
|
||||
{config.config?.format === "pem" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.keyPath}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config.config?.format === "pfx" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_pfx_password.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_pfx_password.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.pfxPassword}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.pfxPassword = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.pfxPassword}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config.config?.format === "jks" ? (
|
||||
<>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_alias.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_alias.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksAlias}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksAlias = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksAlias}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_keypass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_keypass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksKeypass}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksKeypass = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksKeypass}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_storepass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_storepass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.jksStorepass}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksStorepass = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.jksStorepass}</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell_pre_command.label")}</Label>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={config?.config?.preCommand}
|
||||
placeholder={t("domain.deployment.form.shell_pre_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.preCommand = e.target.value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
></Textarea>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.preCommand}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell_command.label")}</Label>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={config?.config?.command}
|
||||
placeholder={t("domain.deployment.form.shell_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.command = e.target.value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
></Textarea>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.command}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToSSH;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToTencentCDNParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToTencentCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToTencentCDNParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label.wildsupported")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToTencentCDN;
|
||||
@@ -1,220 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToTencentCLBParams = {
|
||||
region?: string;
|
||||
resourceType?: string;
|
||||
loadbalancerId?: string;
|
||||
listenerId?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToTencentCLB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToTencentCLBParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "ap-guangzhou",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.tencent_clb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("ssl-deploy"), z.literal("loadbalancer"), z.literal("listener"), z.literal("ruledomain")], {
|
||||
message: t("domain.deployment.form.tencent_clb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().min(1, t("domain.deployment.form.tencent_clb_loadbalancer_id.placeholder")),
|
||||
listenerId: z.string().optional(),
|
||||
domain: z.string().optional(),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
switch (data.resourceType) {
|
||||
case "ssl-deploy":
|
||||
case "listener":
|
||||
case "ruledomain":
|
||||
return !!data.listenerId?.trim();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: t("domain.deployment.form.tencent_clb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
switch (data.resourceType) {
|
||||
case "ssl-deploy":
|
||||
case "ruledomain":
|
||||
return !!data.domain?.trim() && /^$|^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(data.domain);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: t("domain.deployment.form.tencent_clb_ruledomain.placeholder"),
|
||||
path: ["domain"],
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error?.errors?.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error?.errors?.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error?.errors?.find((e) => e.path[0] === "listenerId")?.message,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_clb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={config?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.tencent_clb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="ssl-deploy">{t("domain.deployment.form.tencent_clb_resource_type.option.ssl_deploy.label")}</SelectItem>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.tencent_clb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.tencent_clb_resource_type.option.listener.label")}</SelectItem>
|
||||
<SelectItem value="ruledomain">{t("domain.deployment.form.tencent_clb_resource_type.option.ruledomain.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_clb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.loadbalancerId}</div>
|
||||
</div>
|
||||
|
||||
{config?.config?.resourceType === "ssl-deploy" || config?.config?.resourceType === "listener" || config?.config?.resourceType === "ruledomain" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_clb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "ssl-deploy" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_clb_domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{config?.config?.resourceType === "ruledomain" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_clb_ruledomain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_clb_ruledomain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToTencentCLB;
|
||||
@@ -1,110 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToTencentCOSParams = {
|
||||
region?: string;
|
||||
bucket?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToTencentCOS = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToTencentCOSParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {
|
||||
region: "ap-guangzhou",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.tencent_cos_region.placeholder")),
|
||||
bucket: z.string().min(1, t("domain.deployment.form.tencent_cos_bucket.placeholder")),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
region: res.error?.errors?.find((e) => e.path[0] === "region")?.message,
|
||||
bucket: res.error?.errors?.find((e) => e.path[0] === "bucket")?.message,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_cos_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_cos_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.region}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_cos_bucket.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_cos_bucket.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.bucket}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.bucket = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.bucket}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToTencentCOS;
|
||||
@@ -1,89 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToTencentTEOParams = {
|
||||
zoneId?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToTencentTEO = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToTencentTEOParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
zoneId: z.string().min(1, t("domain.deployment.form.tencent_teo_zone_id.placeholder")),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
zoneId: res.error?.errors?.find((e) => e.path[0] === "zoneId")?.message,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_teo_zone_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.tencent_teo_zone_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.zoneId}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.zoneId = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.zoneId}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.tencent_teo_domain.label")}</Label>
|
||||
<Textarea
|
||||
placeholder={t("domain.deployment.form.tencent_teo_domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToTencentTEO;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToVolcengineCDNConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToVolcengineCDN = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToVolcengineCDNConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label.wildsupported")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToVolcengineCDN;
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
type DeployToVolcengineLiveConfigParams = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
const DeployToVolcengineLive = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { config, setConfig, errors, setErrors } = useDeployEditContext<DeployToVolcengineLiveConfigParams>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(config.config);
|
||||
setErrors({
|
||||
...errors,
|
||||
domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.domain.label.wildsupported")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.domain.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={config?.config?.domain}
|
||||
onChange={(e) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.domain = e.target.value?.trim();
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{errors?.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToVolcengineLive;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
import KVList from "./KVList";
|
||||
import { type KVType } from "@/domain/domain";
|
||||
|
||||
const DeployToWebhook = () => {
|
||||
const { config, setConfig, setErrors } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.id) {
|
||||
setConfig({
|
||||
...config,
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setErrors({});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<KVList
|
||||
variables={config?.config?.variables}
|
||||
onValueChange={(variables: KVType[]) => {
|
||||
const nv = produce(config, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.variables = variables;
|
||||
});
|
||||
setConfig(nv);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToWebhook;
|
||||
@@ -18,6 +18,9 @@ import DeployToTencentCOS from "./DeployToTencentCOS";
|
||||
import DeployToTencentTEO from "./DeployToTencentTEO";
|
||||
import DeployToSSH from "./DeployToSSH";
|
||||
import DeployToLocal from "./DeployToLocal";
|
||||
import DeployToByteplusCDN from "./DeployToByteplusCDN";
|
||||
import DeployToVolcengineCDN from "./DeployToVolcengineCDN";
|
||||
import DeployToVolcengineLive from "./DeployToVolcengineLive";
|
||||
|
||||
export type DeployFormProps = {
|
||||
data: WorkflowNode;
|
||||
@@ -70,6 +73,12 @@ const getForm = (data: WorkflowNode, defaultProivder?: string) => {
|
||||
return <DeployToSSH data={data} />;
|
||||
case "local":
|
||||
return <DeployToLocal data={data} />;
|
||||
case "byteplus-cdn":
|
||||
return <DeployToByteplusCDN data={data} />;
|
||||
case "volcengine-cdn":
|
||||
return <DeployToVolcengineCDN data={data} />;
|
||||
case "volcengine-live":
|
||||
return <DeployToVolcengineLive data={data} />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ const DeployToBaiduCloudCDN = ({ data }: DeployFormProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const rs = getWorkflowOuptutBeforeId(data.id, "certificate");
|
||||
console.log(rs);
|
||||
setBeforeOutput(rs);
|
||||
}, [data]);
|
||||
|
||||
|
||||
181
ui/src/components/workflow/DeployToByteplusCDN.tsx
Normal file
181
ui/src/components/workflow/DeployToByteplusCDN.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { DeployFormProps } from "./DeployForm";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/providers/workflow";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { usePanel } from "./PanelProvider";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { SelectLabel } from "@radix-ui/react-select";
|
||||
import AccessSelect from "./AccessSelect";
|
||||
import AccessEditDialog from "../certimate/AccessEditDialog";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
const selectState = (state: WorkflowState) => ({
|
||||
updateNode: state.updateNode,
|
||||
getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId,
|
||||
});
|
||||
const DeployToByteplusCDN = ({ data }: DeployFormProps) => {
|
||||
const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState));
|
||||
const { hidePanel } = usePanel();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [beforeOutput, setBeforeOutput] = useState<WorkflowNode[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const rs = getWorkflowOuptutBeforeId(data.id, "certificate");
|
||||
setBeforeOutput(rs);
|
||||
}, [data]);
|
||||
|
||||
const formSchema = z.object({
|
||||
providerType: z.string(),
|
||||
access: z.string().min(1, t("domain.deployment.form.access.placeholder")),
|
||||
certificate: z.string().min(1),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
let config: WorkflowNodeConfig = {
|
||||
certificate: "",
|
||||
providerType: "byteplus-cdn",
|
||||
access: "",
|
||||
|
||||
domain: "",
|
||||
};
|
||||
if (data) config = data.config ?? config;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerType: "byteplus-cdn",
|
||||
access: config.access as string,
|
||||
certificate: config.certificate as string,
|
||||
domain: config.domain as string,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (config: z.infer<typeof formSchema>) => {
|
||||
updateNode({ ...data, config: { ...config }, validated: true });
|
||||
hidePanel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
className="space-y-8"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="access"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex justify-between">
|
||||
<div>{t("domain.deployment.form.access.label")}</div>
|
||||
|
||||
<AccessEditDialog
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
outConfigType="byteplus"
|
||||
/>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<AccessSelect
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("access", value);
|
||||
}}
|
||||
providerType="byteplus-cdn"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="certificate"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("workflow.common.certificate.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("certificate", value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("workflow.common.certificate.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{beforeOutput.map((item) => (
|
||||
<>
|
||||
<SelectGroup key={item.id}>
|
||||
<SelectLabel>{item.name}</SelectLabel>
|
||||
{item.output?.map((output) => (
|
||||
<SelectItem key={output.name} value={`${item.id}#${output.name}`}>
|
||||
<div>
|
||||
{item.name}-{output.label}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="domain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("domain.deployment.form.domain.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("domain.deployment.form.domain.label")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToByteplusCDN;
|
||||
181
ui/src/components/workflow/DeployToVolcengineCDN.tsx
Normal file
181
ui/src/components/workflow/DeployToVolcengineCDN.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { DeployFormProps } from "./DeployForm";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/providers/workflow";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { usePanel } from "./PanelProvider";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { SelectLabel } from "@radix-ui/react-select";
|
||||
import AccessSelect from "./AccessSelect";
|
||||
import AccessEditDialog from "../certimate/AccessEditDialog";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
const selectState = (state: WorkflowState) => ({
|
||||
updateNode: state.updateNode,
|
||||
getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId,
|
||||
});
|
||||
const DeployToVolcengineCDN = ({ data }: DeployFormProps) => {
|
||||
const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState));
|
||||
const { hidePanel } = usePanel();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [beforeOutput, setBeforeOutput] = useState<WorkflowNode[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const rs = getWorkflowOuptutBeforeId(data.id, "certificate");
|
||||
setBeforeOutput(rs);
|
||||
}, [data]);
|
||||
|
||||
const formSchema = z.object({
|
||||
providerType: z.string(),
|
||||
access: z.string().min(1, t("domain.deployment.form.access.placeholder")),
|
||||
certificate: z.string().min(1),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
let config: WorkflowNodeConfig = {
|
||||
certificate: "",
|
||||
providerType: "volcengine-cdn",
|
||||
access: "",
|
||||
|
||||
domain: "",
|
||||
};
|
||||
if (data) config = data.config ?? config;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerType: "volcengine-cdn",
|
||||
access: config.access as string,
|
||||
certificate: config.certificate as string,
|
||||
domain: config.domain as string,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (config: z.infer<typeof formSchema>) => {
|
||||
updateNode({ ...data, config: { ...config }, validated: true });
|
||||
hidePanel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
className="space-y-8"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="access"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex justify-between">
|
||||
<div>{t("domain.deployment.form.access.label")}</div>
|
||||
|
||||
<AccessEditDialog
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
outConfigType="volcengine"
|
||||
/>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<AccessSelect
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("access", value);
|
||||
}}
|
||||
providerType="volcengine-cdn"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="certificate"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("workflow.common.certificate.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("certificate", value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("workflow.common.certificate.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{beforeOutput.map((item) => (
|
||||
<>
|
||||
<SelectGroup key={item.id}>
|
||||
<SelectLabel>{item.name}</SelectLabel>
|
||||
{item.output?.map((output) => (
|
||||
<SelectItem key={output.name} value={`${item.id}#${output.name}`}>
|
||||
<div>
|
||||
{item.name}-{output.label}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="domain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("domain.deployment.form.domain.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("domain.deployment.form.domain.label")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToVolcengineCDN;
|
||||
181
ui/src/components/workflow/DeployToVolcengineLive.tsx
Normal file
181
ui/src/components/workflow/DeployToVolcengineLive.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { DeployFormProps } from "./DeployForm";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/providers/workflow";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { usePanel } from "./PanelProvider";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { SelectLabel } from "@radix-ui/react-select";
|
||||
import AccessSelect from "./AccessSelect";
|
||||
import AccessEditDialog from "../certimate/AccessEditDialog";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
const selectState = (state: WorkflowState) => ({
|
||||
updateNode: state.updateNode,
|
||||
getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId,
|
||||
});
|
||||
const DeployToVolcengineLive = ({ data }: DeployFormProps) => {
|
||||
const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState));
|
||||
const { hidePanel } = usePanel();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [beforeOutput, setBeforeOutput] = useState<WorkflowNode[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const rs = getWorkflowOuptutBeforeId(data.id, "certificate");
|
||||
setBeforeOutput(rs);
|
||||
}, [data]);
|
||||
|
||||
const formSchema = z.object({
|
||||
providerType: z.string(),
|
||||
access: z.string().min(1, t("domain.deployment.form.access.placeholder")),
|
||||
certificate: z.string().min(1),
|
||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
}),
|
||||
});
|
||||
|
||||
let config: WorkflowNodeConfig = {
|
||||
certificate: "",
|
||||
providerType: "volcengine-live",
|
||||
access: "",
|
||||
|
||||
domain: "",
|
||||
};
|
||||
if (data) config = data.config ?? config;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerType: "volcengine-live",
|
||||
access: config.access as string,
|
||||
certificate: config.certificate as string,
|
||||
domain: config.domain as string,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (config: z.infer<typeof formSchema>) => {
|
||||
updateNode({ ...data, config: { ...config }, validated: true });
|
||||
hidePanel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
className="space-y-8"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="access"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex justify-between">
|
||||
<div>{t("domain.deployment.form.access.label")}</div>
|
||||
|
||||
<AccessEditDialog
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
outConfigType="volcengine"
|
||||
/>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<AccessSelect
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("access", value);
|
||||
}}
|
||||
providerType="volcengine-live"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="certificate"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("workflow.common.certificate.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue("certificate", value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("workflow.common.certificate.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{beforeOutput.map((item) => (
|
||||
<>
|
||||
<SelectGroup key={item.id}>
|
||||
<SelectLabel>{item.name}</SelectLabel>
|
||||
{item.output?.map((output) => (
|
||||
<SelectItem key={output.name} value={`${item.id}#${output.name}`}>
|
||||
<div>
|
||||
{item.name}-{output.label}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="domain"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("domain.deployment.form.domain.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("domain.deployment.form.domain.label")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToVolcengineLive;
|
||||
Reference in New Issue
Block a user