@@ -17,6 +17,8 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
1717 ip : '' ,
1818 user : '' ,
1919 password : '' ,
20+ ssh_key : '' ,
21+ auth_method : 'password' ,
2022 }
2123 ) ;
2224
@@ -43,8 +45,21 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
4345 newErrors . user = 'Username is required' ;
4446 }
4547
46- if ( ! formData . password . trim ( ) ) {
47- newErrors . password = 'Password is required' ;
48+ // Validate authentication method
49+ if ( formData . auth_method === 'password' ) {
50+ if ( ! formData . password ?. trim ( ) ) {
51+ newErrors . password = 'Password is required for password authentication' ;
52+ }
53+ } else if ( formData . auth_method === 'ssh_key' ) {
54+ if ( ! formData . ssh_key ?. trim ( ) ) {
55+ newErrors . ssh_key = 'SSH private key is required for key authentication' ;
56+ } else {
57+ // Basic SSH key validation
58+ const sshKeyPattern = / ^ - - - - - B E G I N ( R S A | O P E N S S H | D S A | E C | E D 2 5 5 1 9 ) P R I V A T E K E Y - - - - - / ;
59+ if ( ! sshKeyPattern . test ( formData . ssh_key . trim ( ) ) ) {
60+ newErrors . ssh_key = 'Invalid SSH private key format' ;
61+ }
62+ }
4863 }
4964
5065 setErrors ( newErrors ) ;
@@ -56,7 +71,7 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
5671 if ( validateForm ( ) ) {
5772 onSubmit ( formData ) ;
5873 if ( ! isEditing ) {
59- setFormData ( { name : '' , ip : '' , user : '' , password : '' } ) ;
74+ setFormData ( { name : '' , ip : '' , user : '' , password : '' , ssh_key : '' , auth_method : 'password' } ) ;
6075 }
6176 }
6277 } ;
@@ -125,14 +140,45 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
125140 { errors . user && < p className = "mt-1 text-sm text-red-600" > { errors . user } </ p > }
126141 </ div >
127142
143+ < div >
144+ < label htmlFor = "auth_method" className = "block text-sm font-medium text-gray-700 mb-1" >
145+ Authentication Method *
146+ </ label >
147+ < select
148+ id = "auth_method"
149+ value = { formData . auth_method }
150+ onChange = { ( e ) => {
151+ const newAuthMethod = e . target . value as 'password' | 'ssh_key' ;
152+ setFormData ( prev => ( {
153+ ...prev ,
154+ auth_method : newAuthMethod ,
155+ // Clear the other auth field when switching methods
156+ ...( newAuthMethod === 'password' ? { ssh_key : '' } : { password : '' } )
157+ } ) ) ;
158+ // Clear related errors
159+ if ( newAuthMethod === 'password' ) {
160+ setErrors ( prev => ( { ...prev , ssh_key : undefined } ) ) ;
161+ } else {
162+ setErrors ( prev => ( { ...prev , password : undefined } ) ) ;
163+ }
164+ } }
165+ className = "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
166+ >
167+ < option value = "password" > Password</ option >
168+ < option value = "ssh_key" > SSH Key</ option >
169+ </ select >
170+ </ div >
171+ </ div >
172+
173+ { formData . auth_method === 'password' && (
128174 < div >
129175 < label htmlFor = "password" className = "block text-sm font-medium text-gray-700 mb-1" >
130176 Password *
131177 </ label >
132178 < input
133179 type = "password"
134180 id = "password"
135- value = { formData . password }
181+ value = { formData . password ?? '' }
136182 onChange = { handleChange ( 'password' ) }
137183 className = { `w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
138184 errors . password ? 'border-red-300' : 'border-gray-300'
@@ -141,6 +187,36 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
141187 />
142188 { errors . password && < p className = "mt-1 text-sm text-red-600" > { errors . password } </ p > }
143189 </ div >
190+ ) }
191+
192+ { formData . auth_method === 'ssh_key' && (
193+ < div >
194+ < label htmlFor = "ssh_key" className = "block text-sm font-medium text-gray-700 mb-1" >
195+ SSH Private Key *
196+ </ label >
197+ < textarea
198+ id = "ssh_key"
199+ value = { formData . ssh_key ?? '' }
200+ onChange = { ( e ) => {
201+ setFormData ( prev => ( { ...prev , ssh_key : e . target . value } ) ) ;
202+ if ( errors . ssh_key ) {
203+ setErrors ( prev => ( { ...prev , ssh_key : undefined } ) ) ;
204+ }
205+ } }
206+ rows = { 8 }
207+ className = { `w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm ${
208+ errors . ssh_key ? 'border-red-300' : 'border-gray-300'
209+ } `}
210+ placeholder = "-----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY-----"
211+ />
212+ { errors . ssh_key && < p className = "mt-1 text-sm text-red-600" > { errors . ssh_key } </ p > }
213+ < p className = "mt-1 text-xs text-gray-500" >
214+ Paste your SSH private key here. Make sure it's in OpenSSH format and matches a public key installed on the target server.
215+ </ p >
216+ </ div >
217+ ) }
218+
219+ < div className = "grid grid-cols-1" > { /* This div ensures proper layout continuation */ }
144220 </ div >
145221
146222 < div className = "flex justify-end space-x-3 pt-4" >
0 commit comments