@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
JavaScript
;
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();
}