feat: edit ssh key

This commit is contained in:
2024-12-29 19:15:04 +00:00
parent 1510b16ceb
commit f4227fa121
16 changed files with 385 additions and 226 deletions

View File

@ -1,3 +1,4 @@
import classNames from "classnames";
import { MouseEventHandler } from "react";
type Props = {
@ -15,7 +16,7 @@ export function Icon(props: Props) {
<svg
width={size}
height={size}
className={props.className}
className={classNames(props.className, "cursor-pointer")}
onClick={props.onClick}
>
<use href={href} />

View File

@ -1,32 +1,24 @@
import { SnortContext } from "@snort/system-react";
import { useContext } from "react";
import { AsyncButton } from "./button";
import { loginNip7 } from "../login";
import useLogin from "../hooks/login";
import Profile from "./profile";
import { NostrLink } from "@snort/system";
import { Link, useNavigate } from "react-router-dom";
export default function LoginButton() {
const system = useContext(SnortContext);
const login = useLogin();
const navigate = useNavigate();
return !login ? (
<AsyncButton
onClick={async () => {
if (window.nostr) {
await loginNip7(system);
} else {
navigate("/new-account");
}
navigate("/login");
}}
>
Sign In
</AsyncButton>
) : (
<Link to="/account">
<Profile link={NostrLink.publicKey(login.pubkey)} />
<Profile link={NostrLink.publicKey(login.publicKey)} />
</Link>
);
}

View File

@ -51,7 +51,7 @@ export default function Modal(props: ModalProps) {
className={
props.bodyClassName ??
classNames(
"relative bg-layer-1 p-8 transition max-xl:rounded-t-3xl xl:rounded-3xl max-xl:mt-auto xl:my-auto max-lg:w-full",
"relative bg-neutral-700 p-8 transition max-xl:rounded-t-3xl xl:rounded-3xl max-xl:mt-auto xl:my-auto max-lg:w-full",
{
"max-xl:-translate-y-[calc(100vh-100dvh)]": props.ready ?? true,
"max-xl:translate-y-[50vh]": !(props.ready ?? true),

104
src/components/ssh-keys.tsx Normal file
View File

@ -0,0 +1,104 @@
import { useEffect, useMemo, useState } from "react";
import { LNVpsApi, UserSshKey } from "../api";
import useLogin from "../hooks/login";
import { ApiUrl } from "../const";
import { AsyncButton } from "./button";
export default function SSHKeySelector({
selectedKey,
setSelectedKey,
}: {
selectedKey: UserSshKey["id"];
setSelectedKey: (k: UserSshKey["id"]) => void;
}) {
const login = useLogin();
const [newKey, setNewKey] = useState("");
const [newKeyError, setNewKeyError] = useState("");
const [newKeyName, setNewKeyName] = useState("");
const [showAddKey, setShowAddKey] = useState(false);
const [sshKeys, setSshKeys] = useState<Array<UserSshKey>>([]);
const api = useMemo(() => {
if (!login?.builder) return;
const api = new LNVpsApi(ApiUrl, login.builder);
return api;
}, [login]);
async function addNewKey() {
if (!api) return;
setNewKeyError("");
try {
const nk = await api.addSshKey(newKeyName, newKey);
setNewKey("");
setNewKeyName("");
setSelectedKey(nk.id);
setShowAddKey(false);
api.listSshKeys().then((a) => setSshKeys(a));
} catch (e) {
if (e instanceof Error) {
setNewKeyError(e.message);
}
}
}
useEffect(() => {
if (!api) return;
api.listSshKeys().then((a) => {
setSshKeys(a);
if (a.length > 0) {
setSelectedKey(a[0].id);
} else {
setShowAddKey(true);
}
});
}, []);
return (
<div className="flex flex-col gap-2">
{sshKeys.length > 0 && (
<>
<b>Select SSH Key:</b>
<select
className="bg-neutral-900 p-2 rounded-xl"
value={selectedKey}
onChange={(e) => setSelectedKey(Number(e.target.value))}
>
{sshKeys.map((a) => (
<option value={a.id}>{a.name}</option>
))}
</select>
</>
)}
{!showAddKey && sshKeys.length > 0 && (
<AsyncButton onClick={() => setShowAddKey(true)}>
Add new SSH key
</AsyncButton>
)}
{(showAddKey || sshKeys.length === 0) && (
<>
<b>Add SSH Key:</b>
<textarea
rows={5}
placeholder="ssh-[rsa|ed25519] AA== id"
value={newKey}
onChange={(e) => setNewKey(e.target.value)}
/>
<input
type="text"
placeholder="Key name"
value={newKeyName}
onChange={(e) => setNewKeyName(e.target.value)}
/>
<AsyncButton
disabled={newKey.length < 10 || newKeyName.length < 2}
onClick={addNewKey}
>
Add Key
</AsyncButton>
{newKeyError && <b className="text-red-500">{newKeyError}</b>}
</>
)}
</div>
);
}