diff --git a/ui/src/components/certimate/AccessEditDialog.tsx b/ui/src/components/certimate/AccessEditDialog.tsx index d93230f7..326a97f2 100644 --- a/ui/src/components/certimate/AccessEditDialog.tsx +++ b/ui/src/components/certimate/AccessEditDialog.tsx @@ -36,10 +36,10 @@ const AccessEditDialog = ({ trigger, op, data, className }: AccessEditProps) => const [configType, setConfigType] = useState(data?.configType || ""); - let form = <> ; + let childComponent = <> ; switch (configType) { case "aliyun": - form = ( + childComponent = ( ); break; case "tencent": - form = ( + childComponent = ( ); break; case "huaweicloud": - form = ( + childComponent = ( ); break; case "qiniu": - form = ( + childComponent = ( ); break; case "aws": - form = ( + childComponent = ( ); break; case "cloudflare": - form = ( + childComponent = ( ); break; case "namesilo": - form = ( + childComponent = ( ); break; case "godaddy": - form = ( + childComponent = ( ); break; case "pdns": - form = ( + childComponent = ( ); break; case "httpreq": - form = ( + childComponent = ( ); break; case "local": - form = ( + childComponent = ( ); break; case "ssh": - form = ( + childComponent = ( ); break; case "webhook": - form = ( + childComponent = ( ); break; case "k8s": - form = ( + childComponent = ( -
{form}
+
{childComponent}
diff --git a/ui/src/components/certimate/DeployEdit.tsx b/ui/src/components/certimate/DeployEdit.tsx new file mode 100644 index 00000000..0b22fcd2 --- /dev/null +++ b/ui/src/components/certimate/DeployEdit.tsx @@ -0,0 +1,16 @@ +import { createContext, useContext } from "react"; + +import { DeployConfig } from "@/domain/domain"; + +type DeployEditContext = { + deploy: DeployConfig; + error: Record; + setDeploy: (deploy: DeployConfig) => void; + setError: (error: Record) => void; +}; + +export const Context = createContext({} as DeployEditContext); + +export const useDeployEditContext = () => { + return useContext(Context); +}; diff --git a/ui/src/components/certimate/DeployEditDialog.tsx b/ui/src/components/certimate/DeployEditDialog.tsx new file mode 100644 index 00000000..1e5b900e --- /dev/null +++ b/ui/src/components/certimate/DeployEditDialog.tsx @@ -0,0 +1,252 @@ +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 } from "./DeployEdit"; +import DeployToAliyunOSS from "./DeployToAliyunOSS"; +import DeployToAliyunCDN from "./DeployToAliyunCDN"; +import DeployToTencentCDN from "./DeployToTencentCDN"; +import DeployToHuaweiCloudCDN from "./DeployToHuaweiCloudCDN"; +import DeployToQiniuCDN from "./DeployToQiniuCDN"; +import DeployToSSH from "./DeployToSSH"; +import DeployToWebhook from "./DeployToWebhook"; +import DeployToKubernetesSecret from "./DeployToKubernetesSecret"; +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({ + access: "", + type: "", + }); + + const [error, setError] = useState>({}); + + const [open, setOpen] = useState(false); + + useEffect(() => { + if (deployConfig) { + setLocDeployConfig({ ...deployConfig }); + } else { + setLocDeployConfig({ + access: "", + type: "", + }); + } + }, [deployConfig]); + + useEffect(() => { + setDeployType(locDeployConfig.type); + setError({}); + }, [locDeployConfig.type]); + + const setDeploy = 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 === locDeployConfig.type.split("-")[0]; + }); + + const handleSaveClick = () => { + // 验证数据 + const newError = { ...error }; + newError.type = locDeployConfig.type === "" ? t("domain.deployment.form.access.placeholder") : ""; + newError.access = locDeployConfig.access === "" ? t("domain.deployment.form.access.placeholder") : ""; + setError(newError); + if (Object.values(newError).some((e) => !!e)) return; + + // 保存数据 + onSave(locDeployConfig); + + // 清理数据 + setLocDeployConfig({ + access: "", + type: "", + }); + setError({}); + + // 关闭弹框 + setOpen(false); + }; + + let childComponent = <>; + switch (deployType) { + case "aliyun-oss": + childComponent = ; + break; + case "aliyun-cdn": + case "aliyun-dcdn": + childComponent = ; + break; + case "tencent-cdn": + childComponent = ; + break; + case "huaweicloud-cdn": + childComponent = ; + break; + case "qiniu-cdn": + childComponent = ; + break; + case "ssh": + case "local": + childComponent = ; + break; + case "webhook": + childComponent = ; + break; + case "k8s-secret": + childComponent = ; + break; + } + + return ( + + + {trigger} + + + {t("domain.deployment.tab")} + + + + +
+ {/* 部署方式 */} +
+ + + + +
{error.type}
+
+ + {/* 授权配置 */} +
+
+ } + op="add" + /> + + + + +
{error.access}
+
+ + {/* 其他参数 */} +
{childComponent}
+ +
+ + + + +
+
+
+ ); +}; + +export default DeployEditDialog; diff --git a/ui/src/components/certimate/DeployList.tsx b/ui/src/components/certimate/DeployList.tsx index dc459a32..1e9866e0 100644 --- a/ui/src/components/certimate/DeployList.tsx +++ b/ui/src/components/certimate/DeployList.tsx @@ -1,37 +1,16 @@ -import { createContext, useCallback, useContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { z } from "zod"; -import { produce } from "immer"; import { nanoid } from "nanoid"; -import { EditIcon, Plus, Trash2 } from "lucide-react"; +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 { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Textarea } from "@/components/ui/textarea"; -import AccessEditDialog from "./AccessEditDialog"; -import KVList from "./KVList"; -import { DeployConfig, KVType, targetTypeKeys, targetTypeMap } from "@/domain/domain"; +import DeployEditDialog from "./DeployEditDialog"; +import { DeployConfig } from "@/domain/domain"; import { accessProvidersMap } from "@/domain/access"; import { useConfigContext } from "@/providers/config"; -type DeployEditContextProps = { - deploy: DeployConfig; - error: Record; - setDeploy: (deploy: DeployConfig) => void; - setError: (error: Record) => void; -}; - -const DeployEditContext = createContext({} as DeployEditContextProps); - -export const useDeployEditContext = () => { - return useContext(DeployEditContext); -}; - type DeployItemProps = { item: DeployConfig; onDelete: () => void; @@ -190,693 +169,3 @@ const DeployList = ({ deploys, onChange }: DeployListProps) => { }; export default DeployList; - -type DeployEditDialogProps = { - trigger: React.ReactNode; - deployConfig?: DeployConfig; - onSave: (deploy: DeployConfig) => void; -}; - -const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogProps) => { - const { - config: { accesses }, - } = useConfigContext(); - - const [deployType, setDeployType] = useState(); - - const [locDeployConfig, setLocDeployConfig] = useState({ - access: "", - type: "", - }); - - const [error, setError] = useState>({}); - - const [open, setOpen] = useState(false); - - useEffect(() => { - if (deployConfig) { - setLocDeployConfig({ ...deployConfig }); - } else { - setLocDeployConfig({ - access: "", - type: "", - }); - } - }, [deployConfig]); - - useEffect(() => { - const temp = locDeployConfig.type.split("-"); - - let t; - if (temp && temp.length > 1) { - // TODO: code smell, maybe a dictionary is better - t = temp[0] === "k8s" ? temp[0] : temp[1]; - } else { - t = locDeployConfig.type; - } - - setDeployType(t as TargetType); - setError({}); - }, [locDeployConfig.type]); - - const setDeploy = useCallback( - (deploy: DeployConfig) => { - if (deploy.type !== locDeployConfig.type) { - setLocDeployConfig({ ...deploy, access: "", config: {} }); - } else { - setLocDeployConfig({ ...deploy }); - } - }, - [locDeployConfig.type] - ); - - const { t } = useTranslation(); - - const targetAccesses = accesses.filter((item) => { - if (item.usage == "apply") { - return false; - } - - if (locDeployConfig.type == "") { - return true; - } - const types = locDeployConfig.type.split("-"); - return item.configType === types[0]; - }); - - const handleSaveClick = () => { - // 验证数据 - // 保存数据 - // 清理数据 - // 关闭弹框 - const newError = { ...error }; - if (locDeployConfig.type === "") { - newError.type = t("domain.deployment.form.access.placeholder"); - } else { - newError.type = ""; - } - - if (locDeployConfig.access === "") { - newError.access = t("domain.deployment.form.access.placeholder"); - } else { - newError.access = ""; - } - setError(newError); - - for (const key in newError) { - if (newError[key] !== "") { - return; - } - } - - onSave(locDeployConfig); - - setLocDeployConfig({ - access: "", - type: "", - }); - - setError({}); - - setOpen(false); - }; - - return ( - - - {trigger} - - - {t("domain.deployment.tab")} - - - - {/* 部署方式 */} -
- - - - -
{error.type}
-
- - {/* 授权配置 */} -
-
- } - op="add" - /> - - - - -
{error.access}
- - - {/* 其他参数 */} - - - - - -
-
-
- ); -}; - -type TargetType = "oss" | "cdn" | "dcdn" | "local" | "ssh" | "webhook" | "k8s"; - -type DeployEditProps = { - type: TargetType; -}; - -const DeployEdit = ({ type }: DeployEditProps) => { - const getDeploy = () => { - switch (type) { - case "cdn": - return ; - case "dcdn": - return ; - case "oss": - return ; - case "ssh": - return ; - case "local": - return ; - case "webhook": - return ; - case "k8s": - return ; - default: - return ; - } - }; - return getDeploy(); -}; - -const DeployToSSH = () => { - const { t } = useTranslation(); - const { setError } = useDeployEditContext(); - - useEffect(() => { - setError({}); - }, []); - - const { deploy: data, setDeploy } = useDeployEditContext(); - - useEffect(() => { - if (!data.id) { - setDeploy({ - ...data, - config: { - certPath: "/etc/nginx/ssl/nginx.crt", - keyPath: "/etc/nginx/ssl/nginx.key", - preCommand: "", - command: "sudo service nginx reload", - }, - }); - } - }, []); - return ( - <> -
-
- - { - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.certPath = e.target.value; - }); - setDeploy(newData); - }} - /> -
-
- - { - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.keyPath = e.target.value; - }); - setDeploy(newData); - }} - /> -
- -
- - -
- -
- - -
-
- - ); -}; - -const DeployToWebhook = () => { - const { deploy: data, setDeploy } = useDeployEditContext(); - - const { setError } = useDeployEditContext(); - - useEffect(() => { - setError({}); - }, []); - - return ( - <> - { - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.variables = variables; - }); - setDeploy(newData); - }} - /> - - ); -}; - -const DeployToOSS = () => { - const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); - - const { t } = useTranslation(); - - useEffect(() => { - setError({}); - }, []); - - useEffect(() => { - const resp = domainSchema.safeParse(data.config?.domain); - if (!resp.success) { - setError({ - ...error, - domain: JSON.parse(resp.error.message)[0].message, - }); - } else { - setError({ - ...error, - domain: "", - }); - } - }, [data]); - - useEffect(() => { - const bucketResp = bucketSchema.safeParse(data.config?.domain); - if (!bucketResp.success) { - setError({ - ...error, - bucket: JSON.parse(bucketResp.error.message)[0].message, - }); - } else { - setError({ - ...error, - bucket: "", - }); - } - }, []); - - useEffect(() => { - if (!data.id) { - setDeploy({ - ...data, - config: { - endpoint: "oss-cn-hangzhou.aliyuncs.com", - bucket: "", - domain: "", - }, - }); - } - }, []); - - const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { - message: t("common.errmsg.domain_invalid"), - }); - - const bucketSchema = z.string().min(1, { - message: t("domain.deployment.form.oss_bucket.placeholder"), - }); - - return ( -
-
- - - { - const temp = e.target.value; - - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.endpoint = temp; - }); - setDeploy(newData); - }} - /> -
{error?.endpoint}
- - - { - const temp = e.target.value; - - const resp = bucketSchema.safeParse(temp); - if (!resp.success) { - setError({ - ...error, - bucket: JSON.parse(resp.error.message)[0].message, - }); - } else { - setError({ - ...error, - bucket: "", - }); - } - - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.bucket = temp; - }); - setDeploy(newData); - }} - /> -
{error?.bucket}
- - - { - const temp = e.target.value; - - const resp = domainSchema.safeParse(temp); - if (!resp.success) { - setError({ - ...error, - domain: JSON.parse(resp.error.message)[0].message, - }); - } else { - setError({ - ...error, - domain: "", - }); - } - - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.domain = temp; - }); - setDeploy(newData); - }} - /> -
{error?.domain}
-
-
- ); -}; - -const DeployToCDN = () => { - const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); - - const { t } = useTranslation(); - - useEffect(() => { - setError({}); - }, []); - - useEffect(() => { - const resp = domainSchema.safeParse(data.config?.domain); - if (!resp.success) { - setError({ - ...error, - domain: JSON.parse(resp.error.message)[0].message, - }); - } else { - setError({ - ...error, - domain: "", - }); - } - }, [data]); - - const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { - message: t("common.errmsg.domain_invalid"), - }); - - return ( -
-
- - { - const temp = e.target.value; - - const resp = domainSchema.safeParse(temp); - if (!resp.success) { - setError({ - ...error, - domain: JSON.parse(resp.error.message)[0].message, - }); - } else { - setError({ - ...error, - domain: "", - }); - } - - const newData = produce(data, (draft) => { - if (!draft.config) { - draft.config = {}; - } - draft.config.domain = temp; - }); - setDeploy(newData); - }} - /> -
{error?.domain}
-
-
- ); -}; - -const DeployToKubernetes = () => { - const { t } = useTranslation(); - const { setError } = useDeployEditContext(); - - useEffect(() => { - setError({}); - }, []); - - const { deploy: data, setDeploy } = useDeployEditContext(); - - useEffect(() => { - if (!data.id) { - setDeploy({ - ...data, - config: { - namespace: "default", - secretName: "", - secretDataKeyForCrt: "tls.crt", - secretDataKeyForKey: "tls.key", - }, - }); - } - }, []); - - return ( - <> -
-
- - { - const newData = produce(data, (draft) => { - draft.config ??= {}; - draft.config.namespace = e.target.value; - }); - setDeploy(newData); - }} - /> -
- -
- - { - const newData = produce(data, (draft) => { - draft.config ??= {}; - draft.config.secretName = e.target.value; - }); - setDeploy(newData); - }} - /> -
- -
- - { - const newData = produce(data, (draft) => { - draft.config ??= {}; - draft.config.secretDataKeyForCrt = e.target.value; - }); - setDeploy(newData); - }} - /> -
- -
- - { - const newData = produce(data, (draft) => { - draft.config ??= {}; - draft.config.secretDataKeyForKey = e.target.value; - }); - setDeploy(newData); - }} - /> -
-
- - ); -}; diff --git a/ui/src/components/certimate/DeployToAliyunCDN.tsx b/ui/src/components/certimate/DeployToAliyunCDN.tsx new file mode 100644 index 00000000..d6735473 --- /dev/null +++ b/ui/src/components/certimate/DeployToAliyunCDN.tsx @@ -0,0 +1,77 @@ +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"; + +const DeployToAliyunCDN = () => { + const { t } = useTranslation(); + + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const resp = domainSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.domain = temp; + }); + setDeploy(newData); + }} + /> +
{error?.domain}
+
+
+ ); +}; + +export default DeployToAliyunCDN; diff --git a/ui/src/components/certimate/DeployToAliyunOSS.tsx b/ui/src/components/certimate/DeployToAliyunOSS.tsx new file mode 100644 index 00000000..7a79da2c --- /dev/null +++ b/ui/src/components/certimate/DeployToAliyunOSS.tsx @@ -0,0 +1,164 @@ +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"; + +const DeployToAliyunOSS = () => { + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + const { t } = useTranslation(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + useEffect(() => { + const bucketResp = bucketSchema.safeParse(data.config?.domain); + if (!bucketResp.success) { + setError({ + ...error, + bucket: JSON.parse(bucketResp.error.message)[0].message, + }); + } else { + setError({ + ...error, + bucket: "", + }); + } + }, []); + + useEffect(() => { + if (!data.id) { + setDeploy({ + ...data, + config: { + endpoint: "oss-cn-hangzhou.aliyuncs.com", + bucket: "", + domain: "", + }, + }); + } + }, []); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + const bucketSchema = z.string().min(1, { + message: t("domain.deployment.form.oss_bucket.placeholder"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.endpoint = temp; + }); + setDeploy(newData); + }} + /> +
{error?.endpoint}
+
+ +
+ + { + const temp = e.target.value; + + const resp = bucketSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + bucket: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + bucket: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.bucket = temp; + }); + setDeploy(newData); + }} + /> +
{error?.bucket}
+
+ +
+ + { + const temp = e.target.value; + + const resp = domainSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.domain = temp; + }); + setDeploy(newData); + }} + /> +
{error?.domain}
+
+
+ ); +}; + +export default DeployToAliyunOSS; diff --git a/ui/src/components/certimate/DeployToHuaweiCloudCDN.tsx b/ui/src/components/certimate/DeployToHuaweiCloudCDN.tsx new file mode 100644 index 00000000..4e61c652 --- /dev/null +++ b/ui/src/components/certimate/DeployToHuaweiCloudCDN.tsx @@ -0,0 +1,77 @@ +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"; + +const DeployToHuaweiCloudCDN = () => { + const { t } = useTranslation(); + + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const resp = domainSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.domain = temp; + }); + setDeploy(newData); + }} + /> +
{error?.domain}
+
+
+ ); +}; + +export default DeployToHuaweiCloudCDN; diff --git a/ui/src/components/certimate/DeployToKubernetesSecret.tsx b/ui/src/components/certimate/DeployToKubernetesSecret.tsx new file mode 100644 index 00000000..c5129324 --- /dev/null +++ b/ui/src/components/certimate/DeployToKubernetesSecret.tsx @@ -0,0 +1,104 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { produce } from "immer"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { useDeployEditContext } from "./DeployEdit"; + +const DeployToKubernetesSecret = () => { + const { t } = useTranslation(); + const { setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + const { deploy: data, setDeploy } = useDeployEditContext(); + + useEffect(() => { + if (!data.id) { + setDeploy({ + ...data, + config: { + namespace: "default", + secretName: "", + secretDataKeyForCrt: "tls.crt", + secretDataKeyForKey: "tls.key", + }, + }); + } + }, []); + + return ( + <> +
+
+ + { + const newData = produce(data, (draft) => { + draft.config ??= {}; + draft.config.namespace = e.target.value; + }); + setDeploy(newData); + }} + /> +
+ +
+ + { + const newData = produce(data, (draft) => { + draft.config ??= {}; + draft.config.secretName = e.target.value; + }); + setDeploy(newData); + }} + /> +
+ +
+ + { + const newData = produce(data, (draft) => { + draft.config ??= {}; + draft.config.secretDataKeyForCrt = e.target.value; + }); + setDeploy(newData); + }} + /> +
+ +
+ + { + const newData = produce(data, (draft) => { + draft.config ??= {}; + draft.config.secretDataKeyForKey = e.target.value; + }); + setDeploy(newData); + }} + /> +
+
+ + ); +}; + +export default DeployToKubernetesSecret; diff --git a/ui/src/components/certimate/DeployToQiniuCDN.tsx b/ui/src/components/certimate/DeployToQiniuCDN.tsx new file mode 100644 index 00000000..327edce8 --- /dev/null +++ b/ui/src/components/certimate/DeployToQiniuCDN.tsx @@ -0,0 +1,77 @@ +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"; + +const DeployToQiniuCDN = () => { + const { t } = useTranslation(); + + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const resp = domainSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.domain = temp; + }); + setDeploy(newData); + }} + /> +
{error?.domain}
+
+
+ ); +}; + +export default DeployToQiniuCDN; diff --git a/ui/src/components/certimate/DeployToSSH.tsx b/ui/src/components/certimate/DeployToSSH.tsx new file mode 100644 index 00000000..80eb15fb --- /dev/null +++ b/ui/src/components/certimate/DeployToSSH.tsx @@ -0,0 +1,113 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +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"; + +const DeployToSSH = () => { + const { t } = useTranslation(); + const { setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + const { deploy: data, setDeploy } = useDeployEditContext(); + + useEffect(() => { + if (!data.id) { + setDeploy({ + ...data, + config: { + certPath: "/etc/nginx/ssl/nginx.crt", + keyPath: "/etc/nginx/ssl/nginx.key", + preCommand: "", + command: "sudo service nginx reload", + }, + }); + } + }, []); + + return ( + <> +
+
+ + { + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.certPath = e.target.value; + }); + setDeploy(newData); + }} + /> +
+ +
+ + { + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.keyPath = e.target.value; + }); + setDeploy(newData); + }} + /> +
+ +
+ + +
+ +
+ + +
+
+ + ); +}; + +export default DeployToSSH; diff --git a/ui/src/components/certimate/DeployToTencentCDN.tsx b/ui/src/components/certimate/DeployToTencentCDN.tsx new file mode 100644 index 00000000..dd13b3b2 --- /dev/null +++ b/ui/src/components/certimate/DeployToTencentCDN.tsx @@ -0,0 +1,77 @@ +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"; + +const DeployToTencentCDN = () => { + const { t } = useTranslation(); + + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const resp = domainSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.domain = temp; + }); + setDeploy(newData); + }} + /> +
{error?.domain}
+
+
+ ); +}; + +export default DeployToTencentCDN; diff --git a/ui/src/components/certimate/DeployToWebhook.tsx b/ui/src/components/certimate/DeployToWebhook.tsx new file mode 100644 index 00000000..3134983a --- /dev/null +++ b/ui/src/components/certimate/DeployToWebhook.tsx @@ -0,0 +1,35 @@ +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 { deploy: data, setDeploy } = useDeployEditContext(); + + const { setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + return ( + <> + { + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.variables = variables; + }); + setDeploy(newData); + }} + /> + + ); +}; + +export default DeployToWebhook; diff --git a/ui/src/domain/domain.ts b/ui/src/domain/domain.ts index 6d31ee63..234abe2c 100644 --- a/ui/src/domain/domain.ts +++ b/ui/src/domain/domain.ts @@ -63,21 +63,23 @@ export type Statistic = { disabled: number; }; -export const getLastDeployment = (domain: Domain): Deployment | undefined => { - return domain.expand?.lastDeployment; +type DeployTarget = { + type: string; + name: string; + icon: string; }; -export const targetTypeMap: Map = new Map([ - ["aliyun-oss", ["common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"]], - ["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]], - ["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]], - ["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]], - ["huaweicloud-cdn", ["common.provider.huaweicloud.cdn", "/imgs/providers/huaweicloud.svg"]], - ["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]], - ["local", ["common.provider.local", "/imgs/providers/local.svg"]], - ["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]], - ["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]], - ["k8s-secret", ["common.provider.kubernetes.secret", "/imgs/providers/k8s.svg"]], -]); - -export const targetTypeKeys = Array.from(targetTypeMap.keys()); +export const deployTargetsMap: Map = new Map( + [ + ["aliyun-oss", "common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"], + ["aliyun-cdn", "common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"], + ["aliyun-dcdn", "common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"], + ["tencent-cdn", "common.provider.tencent.cdn", "/imgs/providers/tencent.svg"], + ["huaweicloud-cdn", "common.provider.huaweicloud.cdn", "/imgs/providers/huaweicloud.svg"], + ["qiniu-cdn", "common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"], + ["local", "common.provider.local", "/imgs/providers/local.svg"], + ["ssh", "common.provider.ssh", "/imgs/providers/ssh.svg"], + ["webhook", "common.provider.webhook", "/imgs/providers/webhook.svg"], + ["k8s-secret", "common.provider.kubernetes.secret", "/imgs/providers/k8s.svg"], + ].map(([type, name, icon]) => [type, { type, name, icon }]) +);