feat: Add SSH key authentication and custom port support (#97)
* feat: Add SSH key authentication and custom port support - Add SSH key authentication support with three modes: password, key, or both - Add custom SSH port support (defaults to 22) - Create SSHKeyInput component with file upload and paste modes - Update database schema with auth_type, ssh_key, ssh_key_passphrase, and ssh_port columns - Update TypeScript interfaces to support new authentication fields - Update SSH services to handle key authentication and custom ports - Update ServerForm with authentication type selection and SSH port field - Update API routes with validation for new fields - Add proper cleanup for temporary SSH key files - Support for encrypted SSH keys with passphrase protection - Maintain backward compatibility with existing password-only servers * fix: Resolve TypeScript build errors and improve type safety - Replace || operators with ?? (nullish coalescing) for better type safety - Add proper null checks for password fields in SSH services - Fix JSDoc type annotations for better TypeScript inference - Update error object types to use Record<keyof CreateServerData, string> - Ensure all SSH authentication methods handle optional fields correctly
This commit is contained in:
committed by
GitHub
parent
e8be9e7214
commit
ff1ab35b46
@@ -52,16 +52,55 @@ export async function PUT(
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { name, ip, user, password }: CreateServerData = body;
|
||||
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port }: CreateServerData = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!name || !ip || !user || !password) {
|
||||
if (!name || !ip || !user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required fields' },
|
||||
{ error: 'Missing required fields: name, ip, and user are required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate SSH port
|
||||
if (ssh_port !== undefined && (ssh_port < 1 || ssh_port > 65535)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'SSH port must be between 1 and 65535' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate authentication based on auth_type
|
||||
const authType = auth_type ?? 'password';
|
||||
|
||||
if (authType === 'password' || authType === 'both') {
|
||||
if (!password?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Password is required for password authentication' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (authType === 'key' || authType === 'both') {
|
||||
if (!ssh_key?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'SSH key is required for key authentication' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if at least one authentication method is provided
|
||||
if (authType === 'both') {
|
||||
if (!password?.trim() && !ssh_key?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'At least one authentication method (password or SSH key) is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const db = getDatabase();
|
||||
|
||||
// Check if server exists
|
||||
@@ -73,7 +112,16 @@ export async function PUT(
|
||||
);
|
||||
}
|
||||
|
||||
const result = db.updateServer(id, { name, ip, user, password });
|
||||
const result = db.updateServer(id, {
|
||||
name,
|
||||
ip,
|
||||
user,
|
||||
password,
|
||||
auth_type: authType,
|
||||
ssh_key,
|
||||
ssh_key_passphrase,
|
||||
ssh_port: ssh_port ?? 22
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -20,18 +20,66 @@ export async function GET() {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { name, ip, user, password }: CreateServerData = body;
|
||||
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port }: CreateServerData = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!name || !ip || !user || !password) {
|
||||
if (!name || !ip || !user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required fields' },
|
||||
{ error: 'Missing required fields: name, ip, and user are required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate SSH port
|
||||
if (ssh_port !== undefined && (ssh_port < 1 || ssh_port > 65535)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'SSH port must be between 1 and 65535' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate authentication based on auth_type
|
||||
const authType = auth_type ?? 'password';
|
||||
|
||||
if (authType === 'password' || authType === 'both') {
|
||||
if (!password?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Password is required for password authentication' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (authType === 'key' || authType === 'both') {
|
||||
if (!ssh_key?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'SSH key is required for key authentication' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if at least one authentication method is provided
|
||||
if (authType === 'both') {
|
||||
if (!password?.trim() && !ssh_key?.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'At least one authentication method (password or SSH key) is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const db = getDatabase();
|
||||
const result = db.createServer({ name, ip, user, password });
|
||||
const result = db.createServer({
|
||||
name,
|
||||
ip,
|
||||
user,
|
||||
password,
|
||||
auth_type: authType,
|
||||
ssh_key,
|
||||
ssh_key_passphrase,
|
||||
ssh_port: ssh_port ?? 22
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user