diff --git a/src/app/_components/SSHKeyInput.tsx b/src/app/_components/SSHKeyInput.tsx new file mode 100644 index 0000000..fb70f22 --- /dev/null +++ b/src/app/_components/SSHKeyInput.tsx @@ -0,0 +1,191 @@ +'use client'; + +import { useState, useRef } from 'react'; +import { Button } from './ui/button'; + +interface SSHKeyInputProps { + value: string; + onChange: (value: string) => void; + onError?: (error: string) => void; + disabled?: boolean; +} + +export function SSHKeyInput({ value, onChange, onError, disabled = false }: SSHKeyInputProps) { + const [inputMode, setInputMode] = useState<'upload' | 'paste'>('upload'); + const [isDragOver, setIsDragOver] = useState(false); + const fileInputRef = useRef(null); + + const validateSSHKey = (keyContent: string): boolean => { + const trimmed = keyContent.trim(); + return ( + trimmed.includes('BEGIN') && + trimmed.includes('PRIVATE KEY') && + trimmed.includes('END') && + trimmed.includes('PRIVATE KEY') + ); + }; + + const handleFileUpload = (file: File) => { + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target?.result as string; + if (validateSSHKey(content)) { + onChange(content); + onError?.(''); + } else { + onError?.('Invalid SSH key format. Please ensure the file contains a valid private key.'); + } + }; + reader.onerror = () => { + onError?.('Failed to read the file. Please try again.'); + }; + reader.readAsText(file); + }; + + const handleFileSelect = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + handleFileUpload(file); + } + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragOver(true); + }; + + const handleDragLeave = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragOver(false); + }; + + const handleDrop = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragOver(false); + + const file = event.dataTransfer.files[0]; + if (file) { + handleFileUpload(file); + } + }; + + const handlePasteChange = (event: React.ChangeEvent) => { + const content = event.target.value; + onChange(content); + + if (content.trim() && !validateSSHKey(content)) { + onError?.('Invalid SSH key format. Please ensure the content is a valid private key.'); + } else { + onError?.(''); + } + }; + + const getKeyFingerprint = (keyContent: string): string => { + // This is a simplified fingerprint - in a real implementation, + // you might want to use a library to generate proper SSH key fingerprints + if (!keyContent.trim()) return ''; + + const lines = keyContent.trim().split('\n'); + const keyLine = lines.find(line => + line.includes('BEGIN') && line.includes('PRIVATE KEY') + ); + + if (keyLine) { + const keyType = keyLine.includes('RSA') ? 'RSA' : + keyLine.includes('ED25519') ? 'ED25519' : + keyLine.includes('ECDSA') ? 'ECDSA' : 'Unknown'; + return `${keyType} key (${keyContent.length} characters)`; + } + + return 'Unknown key type'; + }; + + return ( +
+ {/* Mode Toggle */} +
+ + +
+ + {/* File Upload Mode */} + {inputMode === 'upload' && ( +
!disabled && fileInputRef.current?.click()} + > + +
+
📁
+

+ Drag and drop your SSH private key here, or click to browse +

+

+ Supported formats: RSA, ED25519, ECDSA (.pem, .key, .id_rsa, etc.) +

+
+
+ )} + + {/* Paste Mode */} + {inputMode === 'paste' && ( +
+ +