diff options
| author | 2023-04-19 17:30:39 +0800 | |
|---|---|---|
| committer | 2023-04-19 17:30:39 +0800 | |
| commit | 3adc965dd09490b7efa1cce9f09b0a3b30970277 (patch) | |
| tree | f813abb07d7b003984aa74e3154752b6ffc3ccd5 /envshare/app/share | |
| parent | c7c9ca6f0c8eddf6d34cd40779f3b2d9463f3a46 (diff) | |
| download | HydroRoll-3adc965dd09490b7efa1cce9f09b0a3b30970277.tar.gz HydroRoll-3adc965dd09490b7efa1cce9f09b0a3b30970277.zip | |
✨优化文档
Diffstat (limited to 'envshare/app/share')
| -rw-r--r-- | envshare/app/share/page.tsx | 227 |
1 files changed, 0 insertions, 227 deletions
diff --git a/envshare/app/share/page.tsx b/envshare/app/share/page.tsx deleted file mode 100644 index b6089f0..0000000 --- a/envshare/app/share/page.tsx +++ /dev/null @@ -1,227 +0,0 @@ -"use client"; -import { toBase58 } from "util/base58"; -import { useState, Fragment } from "react"; -import { Cog6ToothIcon, ClipboardDocumentIcon, ClipboardDocumentCheckIcon } from "@heroicons/react/24/outline"; -import { Title } from "@components/title"; -import { encrypt } from "pkg/encryption"; -import { ErrorMessage } from "@components/error"; -import { encodeCompositeKey } from "pkg/encoding"; -import { LATEST_KEY_VERSION } from "pkg/constants"; - -export default function Home() { - const [text, setText] = useState(""); - const [reads, setReads] = useState(999); - - const [ttl, setTtl] = useState(7); - const [ttlMultiplier, setTtlMultiplier] = useState(60 * 60 * 24); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [copied, setCopied] = useState(false); - - const [link, setLink] = useState(""); - - const onSubmit = async () => { - try { - setError(""); - setLink(""); - setLoading(true); - - const { encrypted, iv, key } = await encrypt(text); - - const { id } = (await fetch("/api/v1/store", { - method: "POST", - body: JSON.stringify({ - ttl: ttl * ttlMultiplier, - reads, - encrypted: toBase58(encrypted), - iv: toBase58(iv), - }), - }).then((r) => r.json())) as { id: string }; - - const compositeKey = encodeCompositeKey(LATEST_KEY_VERSION, id, key); - - const url = new URL(window.location.href); - url.pathname = "/unseal"; - url.hash = compositeKey; - setCopied(false); - setLink(url.toString()); - } catch (e) { - console.error(e); - setError((e as Error).message); - } finally { - setLoading(false); - } - }; - - return ( - <div className="container px-8 mx-auto mt-16 lg:mt-32 "> - {error ? <ErrorMessage message={error} /> : null} - - {link ? ( - <div className="flex flex-col items-center justify-center w-full h-full mt-8 md:mt-16 xl:mt-32"> - <Title>Share this link with others</Title> - <div className="relative flex items-stretch flex-grow mt-16 focus-within:z-10"> - <pre className="px-4 py-3 font-mono text-center bg-transparent border rounded border-zinc-600 focus:border-zinc-100/80 focus:ring-0 sm:text-sm text-zinc-100"> - {link} - </pre> - <button - type="button" - className="relative inline-flex items-center px-4 py-2 -ml-px space-x-2 text-sm font-medium duration-150 border text-zinc-700 border-zinc-300 rounded-r-md bg-zinc-50 hover focus:border-zinc-500 focus:outline-none focus:ring-1 focus:ring-zinc-500 hover:text-zinc-900 hover:bg-white" - onClick={() => { - navigator.clipboard.writeText(link); - setCopied(true); - }} - > - {copied ? ( - <ClipboardDocumentCheckIcon className="w-5 h-5" aria-hidden="true" /> - ) : ( - <ClipboardDocumentIcon className="w-5 h-5" aria-hidden="true" /> - )}{" "} - <span>{copied ? "Copied" : "Copy"}</span> - </button> - </div> - </div> - ) : ( - <form - className="max-w-3xl mx-auto" - onSubmit={(e) => { - e.preventDefault(); - if (text.length <= 0) return; - onSubmit(); - }} - > - <Title>Encrypt and Share</Title> - - <pre className="px-4 py-3 mt-8 font-mono text-left bg-transparent border rounded border-zinc-600 focus:border-zinc-100/80 focus:ring-0 sm:text-sm text-zinc-100"> - <div className="flex items-start px-1 text-sm"> - <div aria-hidden="true" className="pr-4 font-mono border-r select-none border-zinc-300/5 text-zinc-700"> - {Array.from({ - length: text.split("\n").length, - }).map((_, index) => ( - <Fragment key={index}> - {(index + 1).toString().padStart(2, "0")} - <br /> - </Fragment> - ))} - </div> - - <textarea - id="text" - name="text" - value={text} - minLength={1} - onChange={(e) => setText(e.target.value)} - rows={Math.max(5, text.split("\n").length)} - placeholder="DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres" - className="w-full p-0 text-base bg-transparent border-0 appearance-none resize-none hover:resize text-zinc-100 placeholder-zinc-500 focus:ring-0 sm:text-sm" - /> - </div> - </pre> - - <div className="flex flex-col items-center justify-center w-full gap-4 mt-4 sm:flex-row"> - <div className="w-full sm:w-1/5"> - <label - className="flex items-center justify-center h-16 px-3 py-2 text-sm whitespace-no-wrap duration-150 border rounded hover:border-zinc-100/80 border-zinc-600 focus:border-zinc-100/80 focus:ring-0 text-zinc-100 hover:text-white hover:cursor-pointer " - htmlFor="file_input" - > - Upload a file - </label> - <input - className="hidden" - id="file_input" - type="file" - onChange={(e) => { - const file = e.target.files![0]; - if (file.size > 1024 * 16) { - setError("File size must be less than 16kb"); - return; - } - - const reader = new FileReader(); - reader.onload = (e) => { - const t = e.target!.result as string; - setText(t); - }; - reader.readAsText(file); - }} - /> - </div> - - <div className="w-full h-16 px-3 py-2 duration-150 border rounded sm:w-2/5 hover:border-zinc-100/80 border-zinc-600 focus-within:border-zinc-100/80 focus-within:ring-0 "> - <label htmlFor="reads" className="block text-xs font-medium text-zinc-100"> - READS - </label> - <input - type="number" - name="reads" - id="reads" - className="w-full p-0 text-base bg-transparent border-0 appearance-none text-zinc-100 placeholder-zinc-500 focus:ring-0 sm:text-sm" - value={reads} - onChange={(e) => setReads(e.target.valueAsNumber)} - /> - </div> - <div className="relative w-full h-16 px-3 py-2 duration-150 border rounded sm:w-2/5 hover:border-zinc-100/80 border-zinc-600 focus-within:border-zinc-100/80 focus-within:ring-0 "> - <label htmlFor="reads" className="block text-xs font-medium text-zinc-100"> - TTL - </label> - <input - type="number" - name="reads" - id="reads" - className="w-full p-0 text-base bg-transparent border-0 appearance-none text-zinc-100 placeholder-zinc-500 focus:ring-0 sm:text-sm" - value={ttl} - onChange={(e) => setTtl(e.target.valueAsNumber)} - /> - <div className="absolute inset-y-0 right-0 flex items-center"> - <label htmlFor="ttlMultiplier" className="sr-only" /> - <select - id="ttlMultiplier" - name="ttlMultiplier" - className="h-full py-0 pl-2 bg-transparent border-0 border-transparent rounded pr-7 text-zinc-500 focus:ring-0 sm:text-sm" - onChange={(e) => setTtlMultiplier(parseInt(e.target.value))} - defaultValue={60 * 60 * 24} - > - <option value={60}>{ttl === 1 ? "Minute" : "Minutes"}</option> - <option value={60 * 60}>{ttl === 1 ? "Hour" : "Hours"}</option> - <option value={60 * 60 * 24}>{ttl === 1 ? "Day" : "Days"}</option> - </select> - </div> - </div> - </div> - <button - type="submit" - disabled={loading || text.length <= 0} - className={`mt-6 w-full h-12 inline-flex justify-center items-center transition-all rounded px-4 py-1.5 md:py-2 text-base font-semibold leading-7 bg-zinc-200 ring-1 ring-transparent duration-150 ${ - text.length <= 0 - ? "text-zinc-400 cursor-not-allowed" - : "text-zinc-900 hover:text-zinc-100 hover:ring-zinc-600/80 hover:bg-zinc-900/20" - } ${loading ? "animate-pulse" : ""}`} - > - <span>{loading ? <Cog6ToothIcon className="w-5 h-5 animate-spin" /> : "Share"}</span> - </button> - - <div className="mt-8"> - <ul className="space-y-2 text-xs text-zinc-500"> - <li> - <p> - <span className="font-semibold text-zinc-400">Reads:</span> The number of reads determines how often - the data can be shared, before it deletes itself. 0 means unlimited. - </p> - </li> - <li> - <p> - <span className="font-semibold text-zinc-400">TTL:</span> You can add a TTL (time to live) to the - data, to automatically delete it after a certain amount of time. 0 means no TTL. - </p> - </li> - <p> - Clicking Share will generate a new symmetrical key and encrypt your data before sending only the - encrypted data to the server. - </p> - </ul> - </div> - </form> - )} - </div> - ); -} |
