diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx
index 64082c6..a547e1c 100644
--- a/frontend/src/pages/Settings.tsx
+++ b/frontend/src/pages/Settings.tsx
@@ -597,7 +597,15 @@ function MachineEditor({
);
}
-function SSHKeyManager({ sshKeys }: { sshKeys: SSHKey[] }) {
+function SSHKeyManager({
+ sshKeys,
+ selectedKeyId,
+ onSelectKeyId,
+}: {
+ sshKeys: SSHKey[];
+ selectedKeyId: string;
+ onSelectKeyId: (id: string) => void;
+}) {
const saveKey = useSaveSSHKey();
const generateKey = useGenerateSSHKey();
const deleteKey = useDeleteSSHKey();
@@ -610,8 +618,12 @@ function SSHKeyManager({ sshKeys }: { sshKeys: SSHKey[] }) {
passphrase: "",
notes: "",
});
+ const selectedKey = useMemo(
+ () => sshKeys.find((key) => key.id === selectedKeyId) ?? null,
+ [sshKeys, selectedKeyId],
+ );
const editing = Boolean(draft.id);
- const clear = () =>
+ const clear = () => {
setDraft({
id: null,
name: "",
@@ -621,33 +633,124 @@ function SSHKeyManager({ sshKeys }: { sshKeys: SSHKey[] }) {
fingerprint: "",
notes: "",
});
+ onSelectKeyId("");
+ };
return (
-
-
-
-
+ {
+ clear();
}}
>
-
-
- SSH keys
-
+ New key
+
+ }
+ >
+
+ {sshKeys.length === 0 && (
+
- Import or generate reusable keys for SSH machines.
+ No SSH keys saved yet.
+ )}
+ {sshKeys.map((key) => {
+ const active = key.id === selectedKeyId;
+ return (
+ {
+ onSelectKeyId(key.id);
+ setDraft({
+ id: key.id,
+ name: key.name,
+ private_key: "",
+ passphrase: "",
+ public_key: key.public_key,
+ fingerprint: key.fingerprint,
+ notes: key.notes,
+ });
+ }}
+ sx={{
+ display: "grid",
+ gridTemplateColumns: "minmax(0, 1fr) auto",
+ gap: 1,
+ px: 1.25,
+ py: 1.1,
+ borderTop: 1,
+ borderColor: "divider",
+ cursor: "pointer",
+ width: "100%",
+ bgcolor: active
+ ? "action.selected"
+ : "background.paper",
+ "&:hover .rail-edit": { opacity: 1 },
+ }}
+ >
+
+
+ {key.name}
+
+
+ {key.private_key_set ? "key saved" : "no key"} ยท{" "}
+ {key.usage_count} machine
+ {key.usage_count === 1 ? "" : "s"}
+
+
+ {
+ onSelectKeyId(key.id);
+ setDraft({
+ id: key.id,
+ name: key.name,
+ private_key: "",
+ passphrase: "",
+ public_key: key.public_key,
+ fingerprint: key.fingerprint,
+ notes: key.notes,
+ });
+ }}
+ />
+
+ );
+ })}
+
+
+
-
+ ) : undefined
+ }
+ >
+
- setDraft((current) => ({ ...current, name: e.target.value }))
+ setDraft((current) => ({
+ ...current,
+ name: e.target.value,
+ }))
}
/>
@@ -683,7 +789,10 @@ function SSHKeyManager({ sshKeys }: { sshKeys: SSHKey[] }) {
label="Notes"
value={draft.notes}
onChange={(e) =>
- setDraft((current) => ({ ...current, notes: e.target.value }))
+ setDraft((current) => ({
+ ...current,
+ notes: e.target.value,
+ }))
}
/>
@@ -752,113 +861,70 @@ function SSHKeyManager({ sshKeys }: { sshKeys: SSHKey[] }) {
+ {selectedKey && (
+
+ )}
{saveKey.error && (
{String(saveKey.error)}
)}
- {sshKeys.length > 0 ? (
-
- {sshKeys.map((key) => (
-
-
-
-
-
-
- {key.name}
-
-
- {key.passphrase_set && (
-
- )}
-
-
- {key.notes && (
-
- {key.notes}
-
- )}
-
-
- Fingerprint
-
-
- {key.fingerprint || "Unavailable"}
-
-
-
-
- Public key
-
-
- {key.public_key || "Unavailable"}
-
-
-
-
-
-
-
-
-
- ))}
-
- ) : (
- No SSH keys saved yet.
+ {selectedKey && (
+
+
+
+ Fingerprint
+
+
+ {selectedKey.fingerprint || "Unavailable"}
+
+
+
+
+ Public key
+
+
+ {selectedKey.public_key || "Unavailable"}
+
+
+
)}
-
-
+
+
);
}
function ResetLocalDatabaseCard() {
@@ -989,6 +1055,7 @@ export function Settings() {
const testMachineSSH = useTestMonitoringMachineSSH();
const [tab, setTab] = useState("machines");
const [deleteMachineId, setDeleteMachineId] = useState(null);
+ const [selectedSSHKeyId, setSelectedSSHKeyId] = useState("");
const [sshValidationMessage, setSSHValidationMessage] = useState("");
const [sshValidationError, setSSHValidationError] = useState("");
const [sshValidationStatus, setSSHValidationStatus] = useState("");
@@ -1395,7 +1462,13 @@ export function Settings() {
) : null}
)}
- {tab === "ssh-keys" && }
+ {tab === "ssh-keys" && (
+
+ )}
{tab === "danger" && }