UNPKG

@aerocorp/cli

Version:

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

325 lines (276 loc) • 10.5 kB
/** * AeroCorp CLI 5.0.0 - WSL Integration Service * Handles WSL-based SSH connections and Docker operations */ import { spawn, ChildProcess } from 'child_process'; import chalk from 'chalk'; import ora from 'ora'; import { ConfigService } from './config'; export interface SSHConfig { host: string; user: string; port?: number; keyPath?: string; } export interface DockerLogOptions { container?: string; applicationId?: string; lines?: number; follow?: boolean; } export class WSLService { private configService: ConfigService; private defaultSSHConfig: SSHConfig; constructor() { this.configService = new ConfigService(); this.defaultSSHConfig = { host: this.configService.get('server_ip') || '128.140.35.238', user: 'root', port: 22, keyPath: '~/.ssh/id_ed25519' }; } /** * Check if WSL is available and properly configured */ async checkWSLAvailability(): Promise<boolean> { const spinner = ora('Checking WSL availability...').start(); try { const result = await this.executeWSLCommand('echo "WSL is available"', { timeout: 5000 }); if (result.success) { spinner.succeed('WSL is available and configured'); return true; } else { spinner.fail('WSL is not properly configured'); console.log(chalk.yellow('šŸ’” Install WSL: https://docs.microsoft.com/en-us/windows/wsl/install')); return false; } } catch (error) { spinner.fail('WSL check failed'); console.error(chalk.red('āŒ Error:'), error.message); return false; } } /** * Setup SSH keys for Coolify server access */ async setupSSHKeys(): Promise<boolean> { const spinner = ora('Setting up SSH keys...').start(); try { console.log(chalk.blue('\nšŸ” SSH Key Setup for Coolify Server')); console.log(chalk.gray('This will create SSH keys and copy them to the server')); // Check if SSH key already exists const keyCheckResult = await this.executeWSLCommand('test -f ~/.ssh/id_ed25519'); if (!keyCheckResult.success) { spinner.text = 'Generating SSH key...'; // Generate SSH key const keyGenResult = await this.executeWSLCommand( 'ssh-keygen -t ed25519 -C "aerocorp-cli@aerocorp" -f ~/.ssh/id_ed25519 -N ""' ); if (!keyGenResult.success) { throw new Error('Failed to generate SSH key'); } console.log(chalk.green('āœ… SSH key generated')); } else { console.log(chalk.blue('šŸ”‘ SSH key already exists')); } spinner.text = 'Copying SSH key to server...'; // Copy SSH key to server const copyKeyResult = await this.executeWSLCommand( `ssh-copy-id -i ~/.ssh/id_ed25519.pub ${this.defaultSSHConfig.user}@${this.defaultSSHConfig.host}` ); if (copyKeyResult.success) { spinner.succeed('SSH keys configured successfully'); console.log(chalk.green('āœ… SSH access to Coolify server is ready')); return true; } else { spinner.fail('Failed to copy SSH key to server'); console.log(chalk.yellow('šŸ’” You may need to manually copy the key or check server access')); return false; } } catch (error) { spinner.fail('SSH setup failed'); console.error(chalk.red('āŒ Error:'), error.message); return false; } } /** * Test SSH connection to Coolify server */ async testSSHConnection(): Promise<boolean> { const spinner = ora('Testing SSH connection...').start(); try { const result = await this.executeWSLCommand( `ssh -o ConnectTimeout=10 -o BatchMode=yes ${this.defaultSSHConfig.user}@${this.defaultSSHConfig.host} "echo 'SSH connection successful'"` ); if (result.success) { spinner.succeed('SSH connection test passed'); console.log(chalk.green(`āœ… Connected to ${this.defaultSSHConfig.host}`)); return true; } else { spinner.fail('SSH connection test failed'); console.log(chalk.red('āŒ Cannot connect to Coolify server via SSH')); console.log(chalk.yellow('šŸ’” Run: aerocorp wsl setup-ssh')); return false; } } catch (error) { spinner.fail('SSH test failed'); console.error(chalk.red('āŒ Error:'), error.message); return false; } } /** * Get Docker logs via SSH */ async getDockerLogs(options: DockerLogOptions): Promise<void> { const spinner = ora('Fetching Docker logs via SSH...').start(); try { let dockerCommand: string; if (options.applicationId) { // Find container by Coolify application ID dockerCommand = `docker logs ${options.follow ? '-f' : ''} --tail ${options.lines || 100} $(docker ps --filter "label=coolify.applicationId=${options.applicationId}" --format "{{.ID}}" | head -1)`; } else if (options.container) { // Use specific container name/ID dockerCommand = `docker logs ${options.follow ? '-f' : ''} --tail ${options.lines || 100} ${options.container}`; } else { throw new Error('Either applicationId or container must be specified'); } spinner.stop(); console.log(chalk.cyan(`\nšŸ“‹ Docker Logs ${options.follow ? '(following)' : ''}:`)); console.log(chalk.gray('─'.repeat(80))); // Execute SSH command with live output const sshProcess = spawn('wsl', [ 'ssh', `${this.defaultSSHConfig.user}@${this.defaultSSHConfig.host}`, dockerCommand ], { stdio: 'inherit' }); if (options.follow) { console.log(chalk.gray('Press Ctrl+C to stop following logs')); } return new Promise((resolve, reject) => { sshProcess.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`SSH command failed with code ${code}`)); } }); sshProcess.on('error', (error) => { reject(new Error(`SSH process error: ${error.message}`)); }); }); } catch (error) { spinner.fail('Failed to fetch Docker logs'); throw error; } } /** * Execute a command in WSL */ private async executeWSLCommand(command: string, options: { timeout?: number } = {}): Promise<{ success: boolean; output: string; error?: string }> { return new Promise((resolve) => { const process = spawn('wsl', ['bash', '-c', command], { stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let error = ''; process.stdout?.on('data', (data) => { output += data.toString(); }); process.stderr?.on('data', (data) => { error += data.toString(); }); const timeout = options.timeout || 30000; const timer = setTimeout(() => { process.kill(); resolve({ success: false, output, error: 'Command timed out' }); }, timeout); process.on('close', (code) => { clearTimeout(timer); resolve({ success: code === 0, output: output.trim(), error: error.trim() }); }); process.on('error', (err) => { clearTimeout(timer); resolve({ success: false, output, error: err.message }); }); }); } /** * Get WSL distribution info */ async getWSLInfo(): Promise<void> { console.log(chalk.cyan('\n🐧 WSL Environment Information')); console.log(chalk.gray('─'.repeat(50))); try { const distroResult = await this.executeWSLCommand('cat /etc/os-release | grep PRETTY_NAME'); if (distroResult.success) { const distroName = distroResult.output.split('=')[1]?.replace(/"/g, '') || 'Unknown'; console.log(chalk.white(`šŸ“‹ Distribution: ${distroName}`)); } const kernelResult = await this.executeWSLCommand('uname -r'); if (kernelResult.success) { console.log(chalk.white(`šŸ”§ Kernel: ${kernelResult.output}`)); } const sshResult = await this.executeWSLCommand('which ssh'); if (sshResult.success) { console.log(chalk.green('āœ… SSH client available')); } else { console.log(chalk.red('āŒ SSH client not found')); console.log(chalk.yellow('šŸ’” Install: sudo apt update && sudo apt install openssh-client')); } const dockerResult = await this.executeWSLCommand('which docker'); if (dockerResult.success) { console.log(chalk.green('āœ… Docker client available')); } else { console.log(chalk.yellow('āš ļø Docker client not found (optional)')); } } catch (error) { console.error(chalk.red('āŒ Failed to get WSL info:'), error.message); } } /** * Interactive SSH key setup with user guidance */ async interactiveSSHSetup(): Promise<void> { console.log(chalk.cyan.bold('\nšŸ” Interactive SSH Setup for Coolify')); console.log(chalk.white('This will configure SSH access to your Coolify server')); console.log(chalk.gray('─'.repeat(60))); // Check WSL first const wslAvailable = await this.checkWSLAvailability(); if (!wslAvailable) { console.log(chalk.red('āŒ WSL is required for SSH functionality')); console.log(chalk.yellow('šŸ’” Install WSL and try again')); return; } // Setup SSH keys console.log(chalk.blue('\nšŸ“ Step 1: SSH Key Generation')); const sshSetup = await this.setupSSHKeys(); if (sshSetup) { console.log(chalk.blue('\nšŸ“ Step 2: Testing Connection')); const connectionTest = await this.testSSHConnection(); if (connectionTest) { console.log(chalk.green.bold('\nšŸŽ‰ SSH setup completed successfully!')); console.log(chalk.white('You can now use SSH-based commands like:')); console.log(chalk.gray(' • aerocorp logs --app <uuid> --ssh')); console.log(chalk.gray(' • aerocorp wsl info')); console.log(chalk.gray(' • aerocorp wsl test')); } else { console.log(chalk.yellow('\nāš ļø SSH setup completed but connection test failed')); console.log(chalk.white('Manual steps may be required:')); console.log(chalk.gray(' 1. Check server firewall settings')); console.log(chalk.gray(' 2. Verify SSH service is running')); console.log(chalk.gray(' 3. Check SSH key permissions')); } } } }