UNPKG

@aerocorp/cli

Version:

AeroCorp CLI 5.1.0 - Future-Proofed Enterprise Infrastructure with Live Preview, Tunneling & Advanced DevOps

314 lines • 12.7 kB
"use strict"; /** * AeroCorp CLI 5.0.0 - Native SSH Service * Pure Node.js SSH implementation that works on Windows without WSL * Uses ssh2 library for cross-platform SSH functionality */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NativeSSHService = void 0; const ssh2_1 = require("ssh2"); const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const fs_1 = require("fs"); const path_1 = require("path"); const os_1 = require("os"); const config_1 = require("./config"); class NativeSSHService { constructor() { this.configService = new config_1.ConfigService(); this.defaultConfig = { host: this.configService.get('server_ip') || '128.140.35.238', port: 22, username: 'root', timeout: 10000 }; } /** * Connect to SSH server using Node.js ssh2 library (Windows native) */ async connect(config) { const spinner = (0, ora_1.default)('Connecting to server via SSH...').start(); try { const connectionConfig = { ...this.defaultConfig, ...config }; // Try to load SSH private key const privateKey = await this.loadSSHKey(); if (privateKey) { connectionConfig.privateKey = privateKey; } this.client = new ssh2_1.Client(); return new Promise((resolve, reject) => { this.client.on('ready', () => { spinner.succeed('SSH connection established'); console.log(chalk_1.default.green(`āœ… Connected to ${connectionConfig.host}`)); resolve(true); }); this.client.on('error', (error) => { spinner.fail('SSH connection failed'); console.error(chalk_1.default.red('āŒ SSH Error:'), error.message); if (error.message.includes('ECONNREFUSED')) { console.log(chalk_1.default.yellow('šŸ’” Check if SSH service is running on the server')); } else if (error.message.includes('Authentication')) { console.log(chalk_1.default.yellow('šŸ’” Check SSH key or try: aerocorp windows setup-ssh')); } resolve(false); }); this.client.connect(connectionConfig); }); } catch (error) { spinner.fail('SSH connection setup failed'); console.error(chalk_1.default.red('āŒ Error:'), error.message); return false; } } /** * Execute command via SSH */ async executeCommand(command, options = {}) { if (!this.client) { const connected = await this.connect(); if (!connected) { throw new Error('SSH connection failed'); } } return new Promise((resolve, reject) => { this.client.exec(command, (err, stream) => { if (err) { reject(new Error(`SSH exec error: ${err.message}`)); return; } let output = ''; let error = ''; stream.on('close', (code) => { resolve({ success: code === 0, output: output.trim(), error: error.trim() }); }); stream.on('data', (data) => { const text = data.toString(); output += text; if (options.follow) { process.stdout.write(text); } }); stream.stderr.on('data', (data) => { const text = data.toString(); error += text; if (options.follow) { process.stderr.write(chalk_1.default.red(text)); } }); // Handle timeout if (options.timeout) { setTimeout(() => { stream.destroy(); reject(new Error('SSH command timed out')); }, options.timeout); } }); }); } /** * Get Docker logs using pure Node.js SSH */ async getDockerLogs(applicationId, options = {}) { const spinner = (0, ora_1.default)(`Fetching Docker logs for ${applicationId}...`).start(); try { const dockerCommand = `docker logs ${options.follow ? '-f' : ''} --tail ${options.lines || 100} $(docker ps --filter "label=coolify.applicationId=${applicationId}" --format "{{.ID}}" | head -1)`; spinner.stop(); console.log(chalk_1.default.cyan(`\nšŸ“‹ Docker Logs (${options.follow ? 'following' : 'last ' + (options.lines || 100) + ' lines'}):`)); console.log(chalk_1.default.gray('─'.repeat(80))); if (options.follow) { console.log(chalk_1.default.gray('Press Ctrl+C to stop following logs\n')); } const result = await this.executeCommand(dockerCommand, { follow: options.follow, timeout: options.timeout }); if (!options.follow) { if (result.success && result.output) { console.log(result.output); } else if (result.error) { console.error(chalk_1.default.red('āŒ Error:'), result.error); } else { console.log(chalk_1.default.yellow('āš ļø No logs available')); } } } catch (error) { spinner.fail('Failed to get Docker logs'); throw error; } } /** * Load SSH private key from standard locations */ async loadSSHKey() { const possibleKeyPaths = [ (0, path_1.join)((0, os_1.homedir)(), '.ssh', 'id_ed25519'), (0, path_1.join)((0, os_1.homedir)(), '.ssh', 'id_rsa'), (0, path_1.join)((0, os_1.homedir)(), '.ssh', 'aerocorp_key'), // Windows-specific paths (0, path_1.join)(process.env.USERPROFILE || (0, os_1.homedir)(), '.ssh', 'id_ed25519'), (0, path_1.join)(process.env.USERPROFILE || (0, os_1.homedir)(), '.ssh', 'id_rsa') ]; for (const keyPath of possibleKeyPaths) { try { if ((0, fs_1.existsSync)(keyPath)) { const privateKey = (0, fs_1.readFileSync)(keyPath); console.log(chalk_1.default.blue(`šŸ”‘ Using SSH key: ${keyPath}`)); return privateKey; } } catch (error) { // Continue to next key path } } console.log(chalk_1.default.yellow('āš ļø No SSH private key found')); console.log(chalk_1.default.blue('šŸ’” Generate one with: aerocorp windows setup-ssh')); return null; } /** * Generate SSH key pair using Node.js crypto (Windows compatible) */ async generateSSHKeyPair() { const { generateKeyPairSync } = await Promise.resolve().then(() => __importStar(require('crypto'))); const { publicKey, privateKey } = generateKeyPairSync('ed25519', { publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); return { publicKey, privateKey }; } /** * Test SSH connection */ async testConnection() { const spinner = (0, ora_1.default)('Testing SSH connection...').start(); try { const connected = await this.connect(); if (connected) { // Test a simple command const result = await this.executeCommand('echo "SSH test successful"'); if (result.success) { spinner.succeed('SSH connection test passed'); console.log(chalk_1.default.green('āœ… SSH functionality is working')); return true; } else { spinner.fail('SSH command execution failed'); return false; } } else { spinner.fail('SSH connection failed'); return false; } } catch (error) { spinner.fail('SSH test failed'); console.error(chalk_1.default.red('āŒ Error:'), error.message); return false; } finally { await this.disconnect(); } } /** * Disconnect SSH client */ async disconnect() { if (this.client) { this.client.end(); this.client = undefined; } } /** * Check if SSH is available (either native Windows SSH or Node.js ssh2) */ async checkSSHAvailability() { const spinner = (0, ora_1.default)('Checking SSH availability...').start(); try { // Check for Windows native SSH let nativeSSH = false; try { const { spawn } = require('child_process'); const sshProcess = spawn('ssh', ['-V'], { stdio: 'pipe' }); await new Promise((resolve) => { sshProcess.on('close', (code) => { nativeSSH = code === 0; resolve(void 0); }); sshProcess.on('error', () => { nativeSSH = false; resolve(void 0); }); }); } catch (error) { nativeSSH = false; } // Node.js ssh2 is always available if installed const nodeSSH = true; // We just installed it spinner.succeed('SSH availability checked'); const recommended = nativeSSH ? 'native' : (nodeSSH ? 'node' : 'none'); console.log(chalk_1.default.cyan('\nšŸ”— SSH Availability:')); console.log(nativeSSH ? chalk_1.default.green(' āœ… Windows native SSH (ssh.exe)') : chalk_1.default.yellow(' āš ļø Windows native SSH not available')); console.log(nodeSSH ? chalk_1.default.green(' āœ… Node.js SSH (ssh2 library)') : chalk_1.default.red(' āŒ Node.js SSH not available')); console.log(chalk_1.default.blue(` šŸŽÆ Recommended: ${recommended === 'native' ? 'Windows native SSH' : recommended === 'node' ? 'Node.js SSH' : 'Install SSH client'}`)); return { nativeSSH, nodeSSH, recommended }; } catch (error) { spinner.fail('SSH availability check failed'); throw error; } } } exports.NativeSSHService = NativeSSHService; //# sourceMappingURL=ssh-native.js.map