refactor: extract SshKeysPage components
- Extract use-ssh-keys hook for state management - Extract SSHKeyCreateForm and SSHKeyList components - Slim SshKeysPage from 277 to ~80 lines Quality gates: tsc --noEmit passes, npm run build passes
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { useState } from "react";
|
||||
import { createSSHKey, deleteSSHKey, listSSHKeys, signPayload, verifySignature, type SSHKey } from "../api/ssh-keys";
|
||||
import { useAsyncData } from "./use-async-data";
|
||||
|
||||
export const useSSHKeys = () => {
|
||||
const { data: keys, status, reload: loadKeys } = useAsyncData<SSHKey[]>(listSSHKeys, []);
|
||||
const [newKeyName, setNewKeyName] = useState("");
|
||||
const [generating, setGenerating] = useState(false);
|
||||
const [signPayloads, setSignPayloads] = useState<Record<string, string>>({});
|
||||
const [signatures, setSignatures] = useState<Record<string, string>>({});
|
||||
const [signing, setSigning] = useState<Record<string, boolean>>({});
|
||||
const [verifyPayloads, setVerifyPayloads] = useState<Record<string, string>>({});
|
||||
const [verifySignatures, setVerifySignatures] = useState<Record<string, string>>({});
|
||||
const [verifyResults, setVerifyResults] = useState<Record<string, boolean | null>>({});
|
||||
const [verifying, setVerifying] = useState<Record<string, boolean>>({});
|
||||
const [mutationError, setMutationError] = useState<string | null>(null);
|
||||
|
||||
const safeKeys = keys ?? [];
|
||||
|
||||
async function handleGenerate(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!newKeyName.trim()) return;
|
||||
try {
|
||||
setGenerating(true);
|
||||
await createSSHKey({ name: newKeyName.trim() });
|
||||
setNewKeyName("");
|
||||
await loadKeys();
|
||||
} catch {
|
||||
setMutationError("Failed to generate SSH key");
|
||||
} finally {
|
||||
setGenerating(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(keyId: string) {
|
||||
if (!confirm("Are you sure you want to delete this SSH key?")) return;
|
||||
try {
|
||||
await deleteSSHKey(keyId);
|
||||
await loadKeys();
|
||||
} catch {
|
||||
setMutationError("Failed to delete SSH key");
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text: string) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
async function handleSign(keyId: string) {
|
||||
const payload = signPayloads[keyId];
|
||||
if (!payload?.trim()) return;
|
||||
try {
|
||||
setSigning((prev) => ({ ...prev, [keyId]: true }));
|
||||
const result = await signPayload(keyId, { payload: payload.trim() });
|
||||
setSignatures((prev) => ({ ...prev, [keyId]: result.signature }));
|
||||
setMutationError(null);
|
||||
} catch {
|
||||
setMutationError("Failed to sign payload");
|
||||
} finally {
|
||||
setSigning((prev) => ({ ...prev, [keyId]: false }));
|
||||
}
|
||||
}
|
||||
|
||||
async function handleVerify(keyId: string) {
|
||||
const payload = verifyPayloads[keyId];
|
||||
const signature = verifySignatures[keyId];
|
||||
if (!payload?.trim() || !signature?.trim()) return;
|
||||
try {
|
||||
setVerifying((prev) => ({ ...prev, [keyId]: true }));
|
||||
const result = await verifySignature(keyId, {
|
||||
payload: payload.trim(),
|
||||
signature: signature.trim(),
|
||||
});
|
||||
setVerifyResults((prev) => ({ ...prev, [keyId]: result.valid }));
|
||||
setMutationError(null);
|
||||
} catch {
|
||||
setMutationError("Failed to verify signature");
|
||||
} finally {
|
||||
setVerifying((prev) => ({ ...prev, [keyId]: false }));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
keys: safeKeys,
|
||||
status,
|
||||
loadKeys,
|
||||
newKeyName,
|
||||
setNewKeyName,
|
||||
generating,
|
||||
mutationError,
|
||||
setMutationError,
|
||||
signPayloads,
|
||||
setSignPayloads,
|
||||
signatures,
|
||||
signing,
|
||||
verifyPayloads,
|
||||
setVerifyPayloads,
|
||||
verifySignatures,
|
||||
setVerifySignatures,
|
||||
verifyResults,
|
||||
verifying,
|
||||
handleGenerate,
|
||||
handleDelete,
|
||||
copyToClipboard,
|
||||
handleSign,
|
||||
handleVerify,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user