#!/usr/bin/env node import fs from 'fs' import path from 'path' import { execSync } from 'child_process' import { fileURLToPath } from 'url' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const sslDir = path.join(__dirname, 'ssl') const keyPath = path.join(sslDir, 'server.key') const certPath = path.join(sslDir, 'server.crt') const colors = { reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m' } const log = (message, color = 'reset') => { console.log(`${colors[color]}${message}${colors.reset}`) } const checkOpenSSL = () => { try { execSync('openssl version', { stdio: 'pipe' }) return true } catch (error) { return false } } const installInstructions = () => { log('\n📋 Инструкции по установке OpenSSL:', 'cyan') log('Windows: https://slproweb.com/products/Win32OpenSSL.html', 'blue') log('macOS: brew install openssl', 'blue') log('Ubuntu/Debian: sudo apt-get install openssl', 'blue') log('CentOS/RHEL: sudo yum install openssl', 'blue') } const createCertificates = (domain = 'localhost', days = 365) => { if (!fs.existsSync(sslDir)) { fs.mkdirSync(sslDir, { recursive: true }) log(`📁 Создана папка: ${sslDir}`, 'green') } try { log('🔐 Создание SSL сертификатов...', 'yellow') // Генерируем приватный ключ log('📝 Генерация приватного ключа...', 'blue') execSync(`openssl genrsa -out "${keyPath}" 2048`, { stdio: 'pipe' }) // Конфигурация для сертификата с SAN const configContent = ` [req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = RU ST = Development L = Local O = Development Server OU = IT Department CN = ${domain} [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = ${domain} DNS.2 = *.${domain} DNS.3 = localhost DNS.4 = *.localhost IP.1 = 127.0.0.1 IP.2 = ::1 ` const configPath = path.join(sslDir, 'openssl.conf') fs.writeFileSync(configPath, configContent.trim()) // Генерируем сертификат log('📜 Генерация сертификата...', 'blue') const certCommand = `openssl req -new -x509 -key "${keyPath}" -out "${certPath}" -days ${days} -config "${configPath}" -extensions v3_req` execSync(certCommand, { stdio: 'pipe' }) // Удаляем временный конфиг fs.unlinkSync(configPath) log('\n✅ SSL сертификаты успешно созданы!', 'green') log(`🔑 Ключ: ${keyPath}`, 'cyan') log(`📄 Сертификат: ${certPath}`, 'cyan') log(`🌐 Домен: ${domain}`, 'cyan') log(`⏰ Действителен: ${days} дней`, 'cyan') // Показываем информацию о сертификате showCertInfo() log('\n⚠️ Важно:', 'yellow') log('• Это сертификаты только для разработки', 'yellow') log('• Добавьте сертификат в доверенные в браузере для устранения предупреждений', 'yellow') } catch (error) { log('\n❌ Ошибка создания SSL сертификатов:', 'red') log(error.message, 'red') installInstructions() } } const showCertInfo = () => { if (!fs.existsSync(certPath)) { log('❌ Сертификат не найден', 'red') return } try { log('\n📋 Информация о сертификате:', 'cyan') const certInfo = execSync(`openssl x509 -in "${certPath}" -text -noout`, { encoding: 'utf8' }) // Извлекаем нужную информацию const subjectMatch = certInfo.match(/Subject: (.+)/); const validFromMatch = certInfo.match(/Not Before: (.+)/); const validToMatch = certInfo.match(/Not After : (.+)/); const sanMatch = certInfo.match(/DNS:([^,\n]+)/g); if (subjectMatch) log(`📝 Subject: ${subjectMatch[1].trim()}`, 'blue') if (validFromMatch) log(`📅 Действителен с: ${validFromMatch[1].trim()}`, 'blue') if (validToMatch) log(`📅 Действителен до: ${validToMatch[1].trim()}`, 'blue') if (sanMatch) { log(`🌐 Домены: ${sanMatch.map(d => d.replace('DNS:', '')).join(', ')}`, 'blue') } } catch (error) { log('❌ Ошибка чтения информации о сертификате', 'red') } } const deleteCertificates = () => { let deleted = false if (fs.existsSync(keyPath)) { fs.unlinkSync(keyPath) log(`🗑️ Удален ключ: ${keyPath}`, 'yellow') deleted = true } if (fs.existsSync(certPath)) { fs.unlinkSync(certPath) log(`🗑️ Удален сертификат: ${certPath}`, 'yellow') deleted = true } if (deleted) { log('✅ SSL сертификаты удалены', 'green') } else { log('ℹ️ SSL сертификаты не найдены', 'blue') } } const checkCertificates = () => { const keyExists = fs.existsSync(keyPath) const certExists = fs.existsSync(certPath) log('\n🔍 Проверка SSL сертификатов:', 'cyan') log(`🔑 Ключ: ${keyExists ? '✅ Существует' : '❌ Не найден'}`, keyExists ? 'green' : 'red') log(`📄 Сертификат: ${certExists ? '✅ Существует' : '❌ Не найден'}`, certExists ? 'green' : 'red') if (keyExists && certExists) { showCertInfo() } return keyExists && certExists } const showHelp = () => { log('\n🔧 SSL Manager - Утилита для управления SSL сертификатами\n', 'cyan') log('Использование:', 'yellow') log(' node ssl-manager.js <команда> [опции]\n', 'blue') log('Команды:', 'yellow') log(' create [domain] [days] Создать новые сертификаты (по умолчанию: localhost, 365 дней)', 'blue') log(' check Проверить существующие сертификаты', 'blue') log(' info Показать информацию о сертификате', 'blue') log(' delete Удалить существующие сертификаты', 'blue') log(' help Показать эту справку\n', 'blue') log('Примеры:', 'yellow') log(' node ssl-manager.js create', 'green') log(' node ssl-manager.js create mysite.local 30', 'green') log(' node ssl-manager.js check', 'green') } // Основная логика const command = process.argv[2] const arg1 = process.argv[3] const arg2 = process.argv[4] // Проверяем наличие OpenSSL if (!checkOpenSSL() && command !== 'help' && command !== 'check') { log('❌ OpenSSL не найден в системе', 'red') installInstructions() process.exit(1) } switch (command) { case 'create': const domain = arg1 || 'localhost' const days = parseInt(arg2) || 365 createCertificates(domain, days) break case 'check': checkCertificates() break case 'info': showCertInfo() break case 'delete': deleteCertificates() break case 'help': case undefined: showHelp() break default: log(`❌ Неизвестная команда: ${command}`, 'red') showHelp() process.exit(1) }