diff --git a/src/components/CreateConnectionModal.tsx b/src/components/CreateConnectionModal.tsx index 75bf5d7..e59905b 100644 --- a/src/components/CreateConnectionModal.tsx +++ b/src/components/CreateConnectionModal.tsx @@ -1,7 +1,8 @@ import { cloneDeep, head } from "lodash-es"; -import { useEffect, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { toast } from "react-hot-toast"; +import TextareaAutosize from "react-textarea-autosize"; import { useConnectionStore } from "@/store"; import { Connection, Engine, ResponseObject } from "@/types"; import Icon from "./Icon"; @@ -16,6 +17,8 @@ interface Props { type SSLType = "none" | "ca-only" | "full"; +type SSLFieldType = "ca" | "cert" | "key"; + const SSLTypeOptions = [ { label: "None", @@ -47,20 +50,49 @@ const CreateConnectionModal = (props: Props) => { const [connection, setConnection] = useState(defaultConnection); const [showDeleteConnectionModal, setShowDeleteConnectionModal] = useState(false); const [sslType, setSSLType] = useState("none"); + const [selectedSSLField, setSelectedSSLField] = useState("ca"); const [isRequesting, setIsRequesting] = useState(false); const showDatabaseField = connection.engineType === Engine.PostgreSQL; const isEditing = editConnection !== undefined; - const allowSave = - connection.host !== "" && connection.username !== "" && (connection.engineType === Engine.PostgreSQL ? connection.database : true); + const allowSave = connection.host !== "" && connection.username !== ""; useEffect(() => { if (show) { - setConnection(isEditing ? editConnection : defaultConnection); + const connection = isEditing ? editConnection : defaultConnection; + setConnection(connection); + if (connection.ssl) { + if (connection.ssl.ca && connection.ssl.cert && connection.ssl.key) { + setSSLType("full"); + } else { + setSSLType("ca-only"); + } + } else { + setSSLType("none"); + } setIsRequesting(false); setShowDeleteConnectionModal(false); } }, [show]); + useEffect(() => { + let ssl = undefined; + if (sslType === "ca-only") { + ssl = { + ca: "", + }; + } else if (sslType === "full") { + ssl = { + ca: "", + cert: "", + key: "", + }; + } + setConnection({ + ...connection, + ssl, + }); + }, [sslType]); + const setPartialConnection = (state: Partial) => { setConnection({ ...connection, @@ -68,6 +100,42 @@ const CreateConnectionModal = (props: Props) => { }); }; + const handleSSLFileInputChange = (event: ChangeEvent) => { + const files = event.currentTarget.files; + if (!files || files.length === 0) { + return; + } + + const file = files[0]; + if (file.type.startsWith("audio/") || file.type.startsWith("video/") || file.type.startsWith("image/")) { + toast.error(`Invalid file type:${file.type}`); + return; + } + + const fr = new FileReader(); + fr.addEventListener("load", () => { + setPartialConnection({ + ssl: { + ...connection.ssl, + [selectedSSLField]: fr.result as string, + }, + }); + }); + fr.addEventListener("error", () => { + toast.error("Failed to read file"); + }); + fr.readAsText(file); + }; + + const handleSSLValueChange = (event: ChangeEvent) => { + setPartialConnection({ + ssl: { + ...connection.ssl, + [selectedSSLField]: event.target.value, + }, + }); + }; + const handleCreateConnection = async () => { if (isRequesting) { return; @@ -208,12 +276,11 @@ const CreateConnectionModal = (props: Props) => { onChange={(e) => setPartialConnection({ password: e.target.value })} /> - {/* TODO: implement SSL textarea */} -
+
{SSLTypeOptions.map((option) => ( -
+ {sslType !== "none" && ( + <> +
+ setSelectedSSLField("ca")} + > + CA Certificate + + {sslType === "full" && ( + <> + setSelectedSSLField("key")} + > + Client Key + + setSelectedSSLField("cert")} + > + Client Certificate + + + )} +
+
+ +
+ Input or + +
+
+ + )}