UNPKG

@axlotl-lab/navigrator

Version:

A powerful local domain manager for development environments. Navigrator helps you manage local domains and SSL certificates with a simple web interface.

380 lines (379 loc) 19.7 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chalk_1 = __importDefault(require("chalk")); const child_process_1 = require("child_process"); const commander_1 = require("commander"); const figlet_1 = __importDefault(require("figlet")); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const util_1 = require("util"); const package_json_1 = __importDefault(require("./../package.json")); const ca_generator_1 = require("./lib/ca-generator"); const ca_installer_1 = require("./lib/ca-installer"); const certificates_1 = require("./lib/certificates"); const hosts_1 = require("./lib/hosts"); const web_server_1 = require("./lib/web-server"); const execAsync = (0, util_1.promisify)(child_process_1.exec); const program = new commander_1.Command(); program .version(package_json_1.default.version) .description('A local domain manager for development environments'); function displayBanner() { console.log(chalk_1.default.blue(figlet_1.default.textSync('Navigrator', { horizontalLayout: 'full' }))); console.log(chalk_1.default.cyan(' Local domain manager by Axlotl Lab\n')); } /** * Check if OpenSSL is installed */ function checkOpenSSL() { return __awaiter(this, void 0, void 0, function* () { try { yield execAsync('openssl version'); return true; } catch (error) { return false; } }); } /** * Display error message about missing OpenSSL */ function displayOpenSSLError() { console.error(chalk_1.default.red('\n❌ Error: OpenSSL is not installed or not available in PATH')); console.error(chalk_1.default.yellow('\nNavigrator requires OpenSSL to create and manage SSL certificates.')); // OS-specific installation instructions if (process.platform === 'win32') { console.error(chalk_1.default.yellow('\nTo install OpenSSL on Windows:')); console.error(chalk_1.default.white(' 1. Download the installer from https://slproweb.com/products/Win32OpenSSL.html')); console.error(chalk_1.default.white(' 2. Run the installer and make sure to select "Copy OpenSSL DLLs to Windows system directory"')); console.error(chalk_1.default.white(' 3. Restart your terminal/command prompt')); } else if (process.platform === 'darwin') { console.error(chalk_1.default.yellow('\nTo install OpenSSL on macOS:')); console.error(chalk_1.default.white(' Using Homebrew: brew install openssl')); } else { console.error(chalk_1.default.yellow('\nTo install OpenSSL on Linux:')); console.error(chalk_1.default.white(' Debian/Ubuntu: sudo apt-get install openssl')); console.error(chalk_1.default.white(' Fedora/RHEL: sudo dnf install openssl')); } console.error(chalk_1.default.yellow('\nPlease install OpenSSL and try again.\n')); } // Main command program .command('start') .description('Start the web interface and manage certificates') .option('-p, --port <port>', 'HTTP port to use', '10191') .option('--no-ca-check', 'Skip checking for the CA certificate') .option('--no-ca-install', 'Skip installing the CA certificate') .action((options) => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); // Verify privileges const isRoot = process.getuid && process.getuid() === 0; const isAdmin = process.platform === 'win32' && new Buffer(process.env.PATH, 'utf-8').toString().toLowerCase().includes('system32'); if (!isRoot && !isAdmin) { console.log(chalk_1.default.yellow('⚠️ Warning: This tool may need elevated privileges to modify the hosts file and install certificates.')); console.log(chalk_1.default.yellow(' Try running as administrator or with sudo.\n')); } try { // Check for OpenSSL before proceeding console.log(chalk_1.default.cyan('Checking OpenSSL installation...')); const hasOpenSSL = yield checkOpenSSL(); if (!hasOpenSSL) { displayOpenSSLError(); process.exit(1); } console.log(chalk_1.default.green('✅ OpenSSL found')); // Check if CA certificate exists and install if needed if (options.caCheck !== false) { const certsDir = path_1.default.join(os_1.default.homedir(), '.navigrator', 'certs'); const caInstaller = new ca_installer_1.CAInstaller(certsDir); const caGenerator = new ca_generator_1.CAGenerator(certsDir); const caExists = yield caInstaller.checkCAExists(); if (!caExists) { console.log(chalk_1.default.cyan('Root CA certificate not found. Generating...')); // Initialize directories yield caGenerator.initialize(); // Generate the CA yield caGenerator.generateCA(); console.log(chalk_1.default.green('✅ CA certificate generated')); // Install the CA certificate if option is enabled if (options.caInstall !== false) { console.log(chalk_1.default.cyan('Installing the root CA certificate...')); const result = yield caInstaller.installCA(); if (result.success) { console.log(chalk_1.default.green(`✅ CA certificate installed successfully`)); // Show browser-specific instructions console.log(chalk_1.default.cyan('\nBrowser-specific notes:')); if (process.platform === 'win32') { console.log(chalk_1.default.white('• Chrome and Edge: Should recognize the certificate immediately.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else if (process.platform === 'darwin') { console.log(chalk_1.default.white('• Safari and Chrome: Should recognize the certificate after restart.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else { console.log(chalk_1.default.white('• Chrome: May require restarting the browser.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } console.log(chalk_1.default.cyan('\nIf you experience any issues:')); console.log(chalk_1.default.white('• Try restarting your browsers completely')); console.log(chalk_1.default.white('• For Chrome, you can visit chrome://restart\n')); } else { console.log(chalk_1.default.yellow(`\n⚠️ ${result.message}`)); // Continue anyway with a warning console.log(chalk_1.default.yellow('Continuing without a trusted certificate. HTTPS certificates will be generated but browsers will show warnings.\n')); } } else { // Just use the generated CA without installing console.log(chalk_1.default.yellow('\n⚠️ Skipping CA installation as requested (--no-ca-install flag).')); console.log(chalk_1.default.yellow('Browsers will show warnings for the generated certificates.\n')); } } else if (options.caInstall !== false) { // CA exists, check if it should be installed console.log(chalk_1.default.cyan('Root CA certificate found. Installing...')); // For simplicity, we'll try installing the certificate even if it's already installed // Most operating systems handle this gracefully const result = yield caInstaller.installCA(); if (result.success) { console.log(chalk_1.default.green(`✅ CA certificate installed successfully`)); } else { console.log(chalk_1.default.yellow(`\n⚠️ ${result.message}`)); } } } const hostsManager = new hosts_1.HostsManager(); const certsDir = path_1.default.join(os_1.default.homedir(), '.navigrator', 'certs'); const certManager = new certificates_1.CertificateManager(certsDir); console.log(chalk_1.default.cyan('\nInitializing certificate manager...')); yield certManager.initialize(); const config = { port: parseInt(options.port, 10) }; const webServer = new web_server_1.WebServer(hostsManager, certManager, config); console.log(chalk_1.default.cyan('Starting server...')); yield webServer.start(); const handleShutdown = () => __awaiter(void 0, void 0, void 0, function* () { console.log(chalk_1.default.cyan('\nShutting down server...')); yield webServer.stop(); process.exit(0); }); process.on('SIGINT', handleShutdown); process.on('SIGTERM', handleShutdown); } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); // Command to list all local domains program .command('list') .description('List all local domains') .action(() => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); try { const hostsManager = new hosts_1.HostsManager(); const hosts = yield hostsManager.readLocalHosts(); console.log(chalk_1.default.cyan('Local domains:')); if (hosts.length === 0) { console.log(chalk_1.default.yellow(' No local domains found')); } else { hosts.forEach(host => { const indicator = host.isCreatedByUs ? chalk_1.default.green('✓') : chalk_1.default.gray('·'); console.log(` ${indicator} ${host.domain}${host.ip}`); }); console.log(); console.log(chalk_1.default.green('✓') + ' Created by Navigrator'); } } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); // Command to add a new domain program .command('add <domain>') .description('Add a new local domain') .option('-i, --ip <ip>', 'IP address to use', '127.0.0.1') .action((domain, options) => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); try { // Check for OpenSSL before proceeding console.log(chalk_1.default.cyan('Checking OpenSSL installation...')); const hasOpenSSL = yield checkOpenSSL(); if (!hasOpenSSL) { displayOpenSSLError(); process.exit(1); } console.log(chalk_1.default.green('✅ OpenSSL found')); const hostsManager = new hosts_1.HostsManager(); const certsDir = path_1.default.join(os_1.default.homedir(), '.navigrator', 'certs'); const certManager = new certificates_1.CertificateManager(certsDir); yield certManager.initialize(); console.log(chalk_1.default.cyan(`Adding ${domain} to hosts file...`)); yield hostsManager.addHost(domain, options.ip); console.log(chalk_1.default.cyan(`Creating SSL certificate for ${domain}...`)); yield certManager.createCertificate(domain); console.log(chalk_1.default.green(`\n✅ Domain ${domain} successfully added`)); } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); // Command to remove a domain program .command('remove <domain>') .description('Remove a local domain') .action((domain) => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); try { const hostsManager = new hosts_1.HostsManager(); console.log(chalk_1.default.cyan(`Removing ${domain} from hosts file...`)); const removed = yield hostsManager.removeHost(domain); if (removed) { console.log(chalk_1.default.green(`\n✅ Domain ${domain} successfully removed`)); } else { console.log(chalk_1.default.yellow(`\n⚠️ Domain ${domain} not found or not created by Navigrator`)); } } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); // Command to initialize and install the CA certificate program .command('init-ca') .description('Generate and install the root CA certificate') .action(() => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); try { const certsDir = path_1.default.join(os_1.default.homedir(), '.navigrator', 'certs'); const caInstaller = new ca_installer_1.CAInstaller(certsDir); console.log(chalk_1.default.cyan('Generating and installing the root CA certificate...')); const result = yield caInstaller.generateAndInstallCA(); if (result.success) { console.log(chalk_1.default.green(`\n✅ ${result.message}`)); // Show browser-specific instructions console.log(chalk_1.default.cyan('\nBrowser-specific notes:')); if (process.platform === 'win32') { console.log(chalk_1.default.white('• Chrome and Edge: Should recognize the certificate immediately.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else if (process.platform === 'darwin') { console.log(chalk_1.default.white('• Safari and Chrome: Should recognize the certificate after restart.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else { console.log(chalk_1.default.white('• Chrome: May require restarting the browser.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } console.log(chalk_1.default.cyan('\nIf you experience any issues:')); console.log(chalk_1.default.white('• Try restarting your browsers completely')); console.log(chalk_1.default.white('• For Chrome, you can visit chrome://restart')); console.log(chalk_1.default.green('\n🚀 You are now ready to use Navigrator! Start the web interface with:')); console.log(chalk_1.default.white(' navigrator start')); } else { console.log(chalk_1.default.yellow(`\n⚠️ ${result.message}`)); } } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); // Command to install the CA certificate (if it exists) program .command('install-ca') .description('Install the root CA certificate in system/browser trust stores') .action(() => __awaiter(void 0, void 0, void 0, function* () { displayBanner(); try { // First check for OpenSSL console.log(chalk_1.default.cyan('Checking OpenSSL installation...')); const hasOpenSSL = yield checkOpenSSL(); if (!hasOpenSSL) { displayOpenSSLError(); process.exit(1); } console.log(chalk_1.default.green('✅ OpenSSL found')); const certsDir = path_1.default.join(os_1.default.homedir(), '.navigrator', 'certs'); const caInstaller = new ca_installer_1.CAInstaller(certsDir); // Check if CA exists, if not, generate it const caExists = yield caInstaller.checkCAExists(); if (!caExists) { console.log(chalk_1.default.cyan('Root CA certificate not found. Generating...')); const result = yield caInstaller.generateAndInstallCA(); if (result.success) { console.log(chalk_1.default.green(`\n✅ ${result.message}`)); } else { console.log(chalk_1.default.yellow(`\n⚠️ ${result.message}`)); process.exit(1); } } else { console.log(chalk_1.default.cyan('Installing the existing root CA certificate...')); const result = yield caInstaller.installCA(); if (result.success) { console.log(chalk_1.default.green(`\n✅ ${result.message}`)); } else { console.log(chalk_1.default.yellow(`\n⚠️ ${result.message}`)); process.exit(1); } } // Show browser-specific instructions console.log(chalk_1.default.cyan('\nBrowser-specific notes:')); if (process.platform === 'win32') { console.log(chalk_1.default.white('• Chrome and Edge: Should recognize the certificate immediately.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else if (process.platform === 'darwin') { console.log(chalk_1.default.white('• Safari and Chrome: Should recognize the certificate after restart.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } else { console.log(chalk_1.default.white('• Chrome: May require restarting the browser.')); console.log(chalk_1.default.white('• Firefox: May require manual import. Check the Firefox notification above.')); } console.log(chalk_1.default.cyan('\nIf you experience any issues:')); console.log(chalk_1.default.white('• Try restarting your browsers completely')); console.log(chalk_1.default.white('• For Chrome, you can visit chrome://restart')); } catch (error) { console.error(chalk_1.default.red(`\n❌ Error: ${error === null || error === void 0 ? void 0 : error.message}`)); process.exit(1); } })); program.parse(process.argv); // If no argument is provided, show help if (process.argv.length === 2) { displayBanner(); program.outputHelp(); }