diff options
Diffstat (limited to 'front/src/routes/keys.jsx')
-rw-r--r-- | front/src/routes/keys.jsx | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/front/src/routes/keys.jsx b/front/src/routes/keys.jsx new file mode 100644 index 0000000..4dee1ce --- /dev/null +++ b/front/src/routes/keys.jsx @@ -0,0 +1,204 @@ +import Modal from "react-modal"; +import { useEffect, useState, useCallback } from "react"; +import { useAuthContext } from "../context/auth_context"; + +Modal.setAppElement("#root"); + +const MINIMIZE_KEY_LEN = 40; +const minimizeKey = (key) => { + const n = key.length; + if (n >= MINIMIZE_KEY_LEN) { + const half = Math.floor(MINIMIZE_KEY_LEN / 2); + return key.substring(0, half) + "..." + key.substring(n - half, n); + } + return key; +}; + +const KeyCard = ({ onDelete, props }) => { + const { id, name, key } = props; + + const deleteThisKey = () => { + if ( + window.confirm( + "Are you sure? This will close all your current ssh sessions." + ) + ) { + fetch(`/api/keys/${id}`, { + credentials: "same-origin", + method: "DELETE", + }) + .then((r) => r.json()) + .then((d) => d.success && onDelete && onDelete()); + } + }; + + return ( + <div className="key-card"> + <h4 style={{ flex: 1 }}>{name}</h4> + <p style={{ flex: 4 }}>{minimizeKey(key)}</p> + + <button + style={{ flex: 0 }} + className="button red" + onClick={deleteThisKey} + > + Delete + </button> + </div> + ); +}; + +const AddKeyButton = ({ onSave }) => { + const [open, setOpen] = useState(false); + const [name, setName] = useState(""); + const [key, setKey] = useState(""); + const [errors, setErrors] = useState(null); + + const setDefaults = () => { + setName(""); + setKey(""); + setErrors(null); + }; + + const close = () => { + setDefaults(); + setOpen(false); + }; + + const createKey = () => { + fetch(`/api/player/keys`, { + credentials: "same-origin", + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + key: key.trim(), + name: name.trim(), + }), + }) + .then((r) => r.json()) + .then((d) => { + if (d.success) { + if (onSave) { + onSave(); + } + close(); + } else if (d.errors) { + if (typeof d.errors === "object") { + setErrors( + Object.keys(d.errors).map( + (field) => `${field}: ${d.errors[field].join(",")}` + ) + ); + } else { + setErrors([d.errors]); + } + } + }); + }; + + return ( + <div> + <button className="button" onClick={() => setOpen(true)}> + + Add Key + </button> + <Modal + isOpen={open} + onRequestClose={close} + className="modal" + contentLabel="Add Key" + > + <div> + <h3>Add SSH Key</h3> + <p> + Not sure about this? Check{" "} + <a + href="https://www.ssh.com/academy/ssh/keygen" + target="_blank" + rel="noreferrer" + > + here + </a>{" "} + for help! + </p> + <hr /> + <p>Key Name *</p> + <input + value={name} + onChange={(e) => setName(e.target.value)} + required + /> + </div> + <div> + <p>SSH Key *</p> + <textarea + cols={40} + rows={5} + value={key} + onChange={(e) => setKey(e.target.value)} + required + /> + </div> + <div> + {errors && ( + <div style={{ color: "red" }}> + {errors.map((error, i) => ( + <p key={i}>{error}</p> + ))} + </div> + )} + </div> + <div className="flex-end-row"> + <button className="button" onClick={createKey}> + Add + </button> + <button className="button red" onClick={close}> + Cancel + </button> + </div> + </Modal> + </div> + ); +}; + +export const Keys = () => { + const { + player: { id: userId }, + } = useAuthContext(); + const [keys, setKeys] = useState(null); + + const refreshKeys = useCallback( + () => + fetch(`/api/player/${userId}/keys`) + .then((r) => r.json()) + .then((keys) => setKeys(keys)), + [userId] + ); + + useEffect(() => { + if (userId) { + refreshKeys(); + } + }, [userId, refreshKeys]); + + if (!keys) return <p>Loading...</p>; + + if (Array.isArray(keys)) { + return ( + <> + <h2>My Keys</h2> + <AddKeyButton onSave={refreshKeys} /> + <div className="key-card-collection"> + {keys.length ? ( + keys.map((key) => ( + <KeyCard key={key.id} onDelete={refreshKeys} props={key} /> + )) + ) : ( + <p>Looks like you've got no keys, try adding some!</p> + )} + </div> + </> + ); + } +}; |