@aerocorp/cli
Version:
AeroCorp CLI 5.1.0 - Future-Proofed Enterprise Infrastructure with Live Preview, Tunneling & Advanced DevOps
314 lines ⢠12.7 kB
JavaScript
;
/**
* 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