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,10 +1,11 @@
import "@xterm/xterm/css/xterm.css";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { LNVpsApi, VmInstance, VmPayment } from "../api";
import VpsInstanceRow from "../components/vps-instance";
import useLogin from "../hooks/login";
import { ApiUrl } from "../const";
import { EventPublisher } from "@snort/system";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import VpsPayment from "../components/vps-payment";
import CostLabel from "../components/cost";
import { AsyncButton } from "../components/button";
@ -12,8 +13,10 @@ import { AttachAddon } from "@xterm/addon-attach";
import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { WebglAddon } from "@xterm/addon-webgl";
import "@xterm/xterm/css/xterm.css";
import { toEui64 } from "../utils";
import { Icon } from "../components/icon";
import Modal from "../components/modal";
import SSHKeySelector from "../components/ssh-keys";
const fit = new FitAddon();
@ -23,28 +26,28 @@ export default function VmPage() {
const login = useLogin();
const navigate = useNavigate();
const [payment, setPayment] = useState<VmPayment>();
const [term, setTerm] = useState<Terminal>()
const [term, setTerm] = useState<Terminal>();
const termRef = useRef<HTMLDivElement | null>(null);
const [editKey, setEditKey] = useState(false);
const [key, setKey] = useState(state?.ssh_key_id ?? -1);
const api = useMemo(() => {
if (!login?.builder) return;
return new LNVpsApi(ApiUrl, login.builder);
}, [login]);
const renew = useCallback(
async function () {
if (!login?.signer || !state) return;
const api = new LNVpsApi(
ApiUrl,
new EventPublisher(login.signer, login.pubkey),
);
if (!api || !state) return;
const p = await api.renewVm(state.id);
setPayment(p);
},
[login, state],
[api, state],
);
async function openTerminal() {
if (!login?.signer || !state) return;
const api = new LNVpsApi(
ApiUrl,
new EventPublisher(login.signer, login.pubkey),
);
if (!login?.builder || !state) return;
const api = new LNVpsApi(ApiUrl, login.builder);
const ws = await api.connect_terminal(state.id);
const te = new Terminal();
const webgl = new WebglAddon();
@ -55,7 +58,7 @@ export default function VmPage() {
te.loadAddon(fit);
te.onResize(({ cols, rows }) => {
ws.send(`${cols}:${rows}`);
})
});
const attach = new AttachAddon(ws);
te.loadAddon(attach);
setTerm(te);
@ -81,21 +84,9 @@ export default function VmPage() {
}
return (
<div className="flex flex-col gap-4">
<VpsInstanceRow vm={state} actions={false} />
<VpsInstanceRow vm={state} actions={true} />
{action === undefined && (
<>
<div className="text-xl">Renewal</div>
<div className="flex justify-between items-center">
<div>{new Date(state.expires).toDateString()}</div>
{state.template?.cost_plan && (
<div>
<CostLabel cost={state.template?.cost_plan} />
</div>
)}
</div>
<AsyncButton onClick={() => navigate("/vm/renew", { state })}>
Extend Now
</AsyncButton>
<div className="flex gap-4 items-center">
<div className="text-xl">Network:</div>
{(state.ip_assignments?.length ?? 0) === 0 && (
@ -118,7 +109,21 @@ export default function VmPage() {
<div className="text-sm bg-neutral-900 px-3 py-1 rounded-lg">
{state.ssh_key?.name}
</div>
<Icon name="pencil" onClick={() => setEditKey(true)} />
</div>
<hr />
<div className="text-xl">Renewal</div>
<div className="flex justify-between items-center">
<div>{new Date(state.expires).toDateString()}</div>
{state.template?.cost_plan && (
<div>
<CostLabel cost={state.template?.cost_plan} />
</div>
)}
</div>
<AsyncButton onClick={() => navigate("/vm/renew", { state })}>
Extend Now
</AsyncButton>
{/*
{!term && <AsyncButton onClick={openTerminal}>Connect Terminal</AsyncButton>}
{term && <div className="border p-2" ref={termRef}></div>}*/}
@ -131,11 +136,8 @@ export default function VmPage() {
<VpsPayment
payment={payment}
onPaid={async () => {
if (!login?.signer || !state) return;
const api = new LNVpsApi(
ApiUrl,
new EventPublisher(login.signer, login.pubkey),
);
if (!login?.builder || !state) return;
const api = new LNVpsApi(ApiUrl, login.builder);
const newState = await api.getVm(state.id);
navigate("/vm", {
state: newState,
@ -146,6 +148,30 @@ export default function VmPage() {
)}
</>
)}
{editKey && (
<Modal id="edit-ssh-key" onClose={() => setEditKey(false)}>
<SSHKeySelector selectedKey={key} setSelectedKey={setKey} />
<div className="flex flex-col gap-4 mt-8">
<small>After selecting a new key, please restart the VM.</small>
<AsyncButton
onClick={async () => {
if (!state) return;
await api?.patchVm(state.id, {
ssh_key_id: key,
});
const ns = await api?.getVm(state?.id);
navigate(".", {
state: ns,
replace: true,
});
setEditKey(false);
}}
>
Save
</AsyncButton>
</div>
</Modal>
)}
</div>
);
}