Re add ssh.ts bulk add backend
This commit is contained in:
@@ -691,4 +691,116 @@ router.delete('/config_editor/shortcuts', authenticateJWT, async (req: Request,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route: Bulk import SSH hosts from JSON (requires JWT)
|
||||||
|
// POST /ssh/bulk-import
|
||||||
|
router.post('/bulk-import', authenticateJWT, async (req: Request, res: Response) => {
|
||||||
|
const userId = (req as any).userId;
|
||||||
|
const { hosts } = req.body;
|
||||||
|
|
||||||
|
if (!Array.isArray(hosts) || hosts.length === 0) {
|
||||||
|
logger.warn('Invalid bulk import data - hosts array is required and must not be empty');
|
||||||
|
return res.status(400).json({error: 'Hosts array is required and must not be empty'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hosts.length > 100) {
|
||||||
|
logger.warn(`Bulk import attempted with too many hosts: ${hosts.length}`);
|
||||||
|
return res.status(400).json({error: 'Maximum 100 hosts allowed per import'});
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
success: 0,
|
||||||
|
failed: 0,
|
||||||
|
errors: [] as string[]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < hosts.length; i++) {
|
||||||
|
const hostData = hosts[i];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isNonEmptyString(hostData.ip) || !isValidPort(hostData.port) || !isNonEmptyString(hostData.username)) {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}: Missing or invalid required fields (ip, port, username)`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostData.authType !== 'password' && hostData.authType !== 'key') {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}: Invalid authType. Must be 'password' or 'key'`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostData.authType === 'password' && !isNonEmptyString(hostData.password)) {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}: Password required for password authentication`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostData.authType === 'key' && !isNonEmptyString(hostData.key)) {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}: SSH key required for key authentication`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostData.enableTunnel && Array.isArray(hostData.tunnelConnections)) {
|
||||||
|
for (let j = 0; j < hostData.tunnelConnections.length; j++) {
|
||||||
|
const conn = hostData.tunnelConnections[j];
|
||||||
|
if (!isValidPort(conn.sourcePort) || !isValidPort(conn.endpointPort) || !isNonEmptyString(conn.endpointHost)) {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}, Tunnel ${j + 1}: Invalid tunnel connection data`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sshDataObj: any = {
|
||||||
|
userId: userId,
|
||||||
|
name: hostData.name || '',
|
||||||
|
folder: hostData.folder || '',
|
||||||
|
tags: Array.isArray(hostData.tags) ? hostData.tags.join(',') : (hostData.tags || ''),
|
||||||
|
ip: hostData.ip,
|
||||||
|
port: hostData.port,
|
||||||
|
username: hostData.username,
|
||||||
|
authType: hostData.authType,
|
||||||
|
pin: !!hostData.pin ? 1 : 0,
|
||||||
|
enableTerminal: !!hostData.enableTerminal ? 1 : 0,
|
||||||
|
enableTunnel: !!hostData.enableTunnel ? 1 : 0,
|
||||||
|
tunnelConnections: Array.isArray(hostData.tunnelConnections) ? JSON.stringify(hostData.tunnelConnections) : null,
|
||||||
|
enableConfigEditor: !!hostData.enableConfigEditor ? 1 : 0,
|
||||||
|
defaultPath: hostData.defaultPath || null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hostData.authType === 'password') {
|
||||||
|
sshDataObj.password = hostData.password;
|
||||||
|
sshDataObj.key = null;
|
||||||
|
sshDataObj.keyPassword = null;
|
||||||
|
sshDataObj.keyType = null;
|
||||||
|
} else if (hostData.authType === 'key') {
|
||||||
|
sshDataObj.key = hostData.key;
|
||||||
|
sshDataObj.keyPassword = hostData.keyPassword || null;
|
||||||
|
sshDataObj.keyType = hostData.keyType || null;
|
||||||
|
sshDataObj.password = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.insert(sshData).values(sshDataObj);
|
||||||
|
results.success++;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
results.failed++;
|
||||||
|
results.errors.push(`Host ${i + 1}: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
||||||
|
logger.error(`Failed to import host ${i + 1}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.success > 0) {
|
||||||
|
logger.success(`Bulk import completed: ${results.success} successful, ${results.failed} failed`);
|
||||||
|
} else {
|
||||||
|
logger.warn(`Bulk import failed: ${results.failed} failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `Import completed: ${results.success} successful, ${results.failed} failed`,
|
||||||
|
...results
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
Reference in New Issue
Block a user