n8n-nodes-netdevices
Version:
n8n node to interact with network devices
356 lines • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinuxConnection = void 0;
const base_connection_1 = require("../base-connection");
class LinuxConnection extends base_connection_1.BaseConnection {
constructor(credentials) {
super(credentials);
this.shellPrompt = '';
this.rootUser = false;
this.initialPromptDetected = false;
}
async sessionPreparation() {
await this.createLinuxShellChannel();
await this.waitForInitialPrompt();
if (this.fastMode) {
await this.setBasePrompt();
}
else {
await this.setBasePrompt();
await this.checkRootUser();
await this.setShellOptions();
}
}
async createLinuxShellChannel() {
return new Promise((resolve, reject) => {
this.client.shell((err, channel) => {
if (err) {
reject(err);
return;
}
this.currentChannel = channel;
this.currentChannel.setEncoding(this.encoding);
let initialBuffer = '';
const initialPromptTimeout = this.fastMode ? 5000 : 10000;
const initialPromptTimer = setTimeout(() => {
if (!this.initialPromptDetected) {
this.initialPromptDetected = true;
resolve();
}
}, initialPromptTimeout);
const onInitialData = (data) => {
initialBuffer += data;
if (this.detectLinuxPrompt(initialBuffer)) {
this.initialPromptDetected = true;
clearTimeout(initialPromptTimer);
this.currentChannel.removeListener('data', onInitialData);
resolve();
}
};
this.currentChannel.on('data', onInitialData);
const minWaitTime = this.fastMode ? 150 : 400;
setTimeout(() => {
if (!this.initialPromptDetected) {
if (initialBuffer.length > 0) {
this.initialPromptDetected = true;
clearTimeout(initialPromptTimer);
this.currentChannel.removeListener('data', onInitialData);
resolve();
}
}
}, minWaitTime);
});
});
}
async waitForInitialPrompt() {
if (this.initialPromptDetected) {
return;
}
return new Promise((resolve) => {
const timeout = this.fastMode ? 2000 : 4000;
setTimeout(() => {
this.initialPromptDetected = true;
resolve();
}, timeout);
});
}
detectLinuxPrompt(buffer) {
const promptPatterns = [
/\$\s*$/m,
/#\s*$/m,
/>\s*$/m,
/\]\s*\$\s*$/m,
/\]\s*#\s*$/m,
/~\s*\$\s*$/m,
/~\s*#\s*$/m,
/@.*:\s*\$\s*$/m,
/@.*:\s*#\s*$/m,
/@.*:\s*~\s*\$\s*$/m,
/@.*:\s*~\s*#\s*$/m,
];
return promptPatterns.some(pattern => pattern.test(buffer));
}
async setBasePrompt() {
try {
await this.writeChannel(this.returnChar);
const output = await this.readChannel(4000);
if (!output.trim()) {
await this.writeChannel(this.newline);
const newOutput = await this.readChannel(3000);
if (newOutput.trim()) {
this.extractPromptFromOutput(newOutput);
}
else {
this.setFallbackPrompt();
}
}
else {
this.extractPromptFromOutput(output);
}
}
catch (error) {
this.setFallbackPrompt();
}
}
extractPromptFromOutput(output) {
const lines = output.trim().split('\n');
const lastLine = lines[lines.length - 1];
this.basePrompt = lastLine.replace(/[>#$%]\s*$/, '').trim();
this.shellPrompt = lastLine.trim();
this.enabledPrompt = this.basePrompt + '#';
this.configPrompt = this.basePrompt + '#';
}
setFallbackPrompt() {
this.basePrompt = 'linux';
this.shellPrompt = 'linux$';
this.enabledPrompt = 'linux#';
this.configPrompt = 'linux#';
}
async checkRootUser() {
try {
const result = await this.sendCommand('whoami');
if (result.success && result.output.includes('root')) {
this.rootUser = true;
}
}
catch (error) {
this.rootUser = false;
}
}
async setShellOptions() {
try {
await this.writeChannel('set +e' + this.newline);
await this.readChannel(1000);
await this.writeChannel('export PS1="\\u@\\h:\\w\\$ "' + this.newline);
await this.readChannel(1000);
}
catch (error) {
}
}
async sendCommand(command) {
try {
if (!this.isConnected || !this.currentChannel) {
throw new Error('Not connected to device');
}
await this.writeChannel(command + this.newline);
const timeout = this.fastMode ? 4000 : 8000;
const output = await this.readUntilPromptEnhanced(timeout);
const cleanOutput = this.sanitizeOutput(output, command);
return {
command,
output: cleanOutput,
success: true
};
}
catch (error) {
return {
command,
output: '',
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
async readUntilPromptEnhanced(timeout = 8000) {
return new Promise((resolve, reject) => {
let buffer = '';
let timeoutId;
const linuxPromptPatterns = [
this.basePrompt,
this.shellPrompt,
/\$\s*$/,
/#\s*$/,
/>\s*$/,
/\]\s*\$\s*$/,
/\]\s*#\s*$/,
/@.*:\s*\$\s*$/,
/@.*:\s*#\s*$/,
/@.*:\s*~\s*\$\s*$/,
/@.*:\s*~\s*#\s*$/,
];
const onData = (data) => {
buffer += data;
const hasPrompt = linuxPromptPatterns.some(pattern => {
if (typeof pattern === 'string') {
return buffer.includes(pattern);
}
else {
return pattern.test(buffer);
}
});
if (hasPrompt) {
cleanup();
resolve(buffer);
return;
}
const lines = buffer.split('\n');
const lastLine = lines[lines.length - 1];
if (lastLine.length > 0 && this.detectLinuxPrompt(lastLine)) {
cleanup();
resolve(buffer);
return;
}
};
const onError = (error) => {
cleanup();
reject(error);
};
const cleanup = () => {
if (this.currentChannel) {
this.currentChannel.removeListener('data', onData);
this.currentChannel.removeListener('error', onError);
}
if (timeoutId) {
clearTimeout(timeoutId);
}
};
timeoutId = setTimeout(() => {
cleanup();
reject(new Error(`Timeout waiting for prompt after ${timeout}ms. Buffer: ${buffer.slice(-200)}`));
}, timeout);
if (this.currentChannel) {
this.currentChannel.on('data', onData);
this.currentChannel.on('error', onError);
}
else {
cleanup();
reject(new Error('No active channel available'));
}
});
}
async sendConfig(configCommands) {
try {
if (!this.isConnected || !this.currentChannel) {
throw new Error('Not connected to device');
}
let allOutput = '';
for (const command of configCommands) {
await this.writeChannel(command + this.newline);
const output = await this.readChannel(3000);
allOutput += output;
if (output.includes('Permission denied') ||
output.includes('command not found') ||
output.includes('No such file or directory')) {
throw new Error(`Configuration error on command "${command}": ${output}`);
}
}
const cleanOutput = this.sanitizeOutput(allOutput, configCommands.join('; '));
return {
command: configCommands.join('; '),
output: cleanOutput,
success: true
};
}
catch (error) {
return {
command: configCommands.join('; '),
output: '',
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
async getCurrentConfig() {
return await this.sendCommand('cat /etc/os-release && echo "---" && uname -a');
}
async saveConfig() {
return await this.sendCommand('sync && echo "Configuration synchronized"');
}
async rebootDevice() {
try {
let command = 'reboot';
if (!this.rootUser) {
command = 'sudo reboot';
}
await this.writeChannel(command + this.newline);
const output = await this.readChannel(5000);
return {
command: command,
output: output,
success: true
};
}
catch (error) {
return {
command: 'reboot',
output: '',
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
sanitizeOutput(output, command) {
const escapedCommand = command.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
let cleanOutput = output.replace(new RegExp(escapedCommand, 'g'), '');
const escapedShellPrompt = this.shellPrompt.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
cleanOutput = cleanOutput.replace(new RegExp(escapedShellPrompt, 'g'), '');
cleanOutput = cleanOutput.replace(/\[.*?\]/g, '');
cleanOutput = cleanOutput.replace(/\$\s*$/gm, '');
cleanOutput = cleanOutput.replace(/#\s*$/gm, '');
cleanOutput = cleanOutput.replace(/^\s+|\s+$/g, '');
cleanOutput = cleanOutput.replace(/\r\n/g, '\n');
cleanOutput = cleanOutput.replace(/\r/g, '\n');
cleanOutput = cleanOutput.replace(/\n\s*\n/g, '\n');
return cleanOutput;
}
isRootUser() {
return this.rootUser;
}
async executeAsRoot(command) {
if (this.rootUser) {
return await this.sendCommand(command);
}
else {
return await this.sendCommand('sudo ' + command);
}
}
async getSystemInfo() {
return await this.sendCommand('uname -a && cat /etc/os-release');
}
async getProcessList() {
return await this.sendCommand('ps aux');
}
async getDiskUsage() {
return await this.sendCommand('df -h');
}
async getMemoryInfo() {
return await this.sendCommand('free -h');
}
async getNetworkInterfaces() {
return await this.sendCommand('ip addr show');
}
async getServiceStatus(serviceName) {
return await this.sendCommand(`systemctl status ${serviceName}`);
}
async startService(serviceName) {
return await this.executeAsRoot(`systemctl start ${serviceName}`);
}
async stopService(serviceName) {
return await this.executeAsRoot(`systemctl stop ${serviceName}`);
}
async restartService(serviceName) {
return await this.executeAsRoot(`systemctl restart ${serviceName}`);
}
}
exports.LinuxConnection = LinuxConnection;
//# sourceMappingURL=linux-connection.js.map