Fix SSH key upload and credential editing issues

Fixed two major credential management issues:

1. Fix SSH key upload button not responding (Issue #232)
   - Error handling was silently swallowing exceptions
   - Added proper error propagation in axios functions
   - Improved error display to show specific error messages
   - Users now see actual error details instead of generic messages

2. Improve credential editing to show actual content
   - Both "Upload File" and "Paste Key" modes now display existing data
   - Upload mode: shows current key content in read-only preview area
   - Paste mode: shows editable key content in textarea
   - Smart input method switching preserves existing data
   - Enhanced button labels and status indicators

Key changes:
- Fixed handleApiError propagation in main-axios.ts credential functions
- Enhanced CredentialEditor.tsx with key content preview
- Improved error handling with console logging for debugging
- Better UX with clear status indicators and preserved data

These fixes resolve the "Add Credential button does nothing" issue
and provide full visibility of credential content during editing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-14 22:44:30 +08:00
parent 0f75cd4d16
commit ecb12d72fe
2 changed files with 53 additions and 22 deletions

View File

@@ -176,7 +176,7 @@ export function CredentialEditor({
if (defaultAuthType === "password") {
formData.password = fullCredentialDetails.password || "";
} else if (defaultAuthType === "key") {
formData.key = "existing_key";
formData.key = fullCredentialDetails.key || "";
formData.keyPassword = fullCredentialDetails.keyPassword || "";
formData.keyType =
(fullCredentialDetails.keyType as any) || ("auto" as const);
@@ -230,8 +230,6 @@ export function CredentialEditor({
if (data.key instanceof File) {
const keyContent = await data.key.text();
submitData.key = keyContent;
} else if (data.key === "existing_key") {
delete submitData.key;
} else {
submitData.key = data.key;
}
@@ -259,7 +257,12 @@ export function CredentialEditor({
form.reset();
} catch (error) {
toast.error(t("credentials.failedToSaveCredential"));
console.error("Credential save error:", error);
if (error instanceof Error) {
toast.error(error.message);
} else {
toast.error(t("credentials.failedToSaveCredential"));
}
}
};
@@ -593,10 +596,22 @@ export function CredentialEditor({
value={keyInputMethod}
onValueChange={(value) => {
setKeyInputMethod(value as "upload" | "paste");
if (value === "upload") {
form.setValue("key", null);
// Only reset key value if we're not editing an existing credential
if (!editingCredential) {
if (value === "upload") {
form.setValue("key", null);
} else {
form.setValue("key", "");
}
} else {
form.setValue("key", "");
// For existing credentials, preserve the key data when switching methods
const currentKey = fullCredentialDetails?.key || "";
if (value === "paste") {
form.setValue("key", currentKey);
} else {
// For upload mode, keep the current string value to show "existing key" status
form.setValue("key", currentKey);
}
}
}}
className="w-full"
@@ -642,13 +657,13 @@ export function CredentialEditor({
t("credentials.upload")
}
>
{field.value === "existing_key"
? t("hosts.existingKey")
: field.value
? editingCredential
{field.value instanceof File
? field.value.name
: typeof field.value === "string" && field.value && editingCredential
? t("hosts.existingKey") + " - " + t("credentials.updateKey")
: field.value
? t("credentials.updateKey")
: field.value.name
: t("credentials.upload")}
: t("credentials.upload")}
</span>
</Button>
</div>
@@ -656,6 +671,22 @@ export function CredentialEditor({
</FormItem>
)}
/>
{/* Show existing key content preview for upload mode */}
{editingCredential && fullCredentialDetails?.key && typeof form.watch("key") === "string" && (
<FormItem className="mb-4">
<FormLabel>{t("credentials.sshPrivateKey")} ({t("hosts.existingKey")})</FormLabel>
<FormControl>
<textarea
readOnly
className="flex min-h-[120px] w-full rounded-md border border-input bg-muted px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
value={fullCredentialDetails.key}
/>
</FormControl>
<div className="text-xs text-muted-foreground mt-1">
Current SSH key content - {t("credentials.uploadFile")} to replace
</div>
</FormItem>
)}
<div className="grid grid-cols-15 gap-4 mt-4">
<FormField
control={form.control}