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.

448 lines (447 loc) 20.3 kB
"use strict"; 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 __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 }); exports.WebServer = void 0; const express_1 = __importDefault(require("express")); const open_1 = __importDefault(require("open")); const path = __importStar(require("path")); const proxy_service_1 = require("./proxy-service"); class WebServer { constructor(hostsManager, certManager, config) { this.server = null; this.app = (0, express_1.default)(); this.hostsManager = hostsManager; this.certManager = certManager; this.proxyService = new proxy_service_1.ProxyService(); this.config = { port: config.port || 10191, }; this.setupMiddleware(); this.setupRoutes(); } /** * Configure middleware for Express */ setupMiddleware() { this.app.use(express_1.default.json()); this.app.use(express_1.default.urlencoded({ extended: true })); // Serve static files const staticDir = path.join(__dirname, '..', '..', 'public'); this.app.use(express_1.default.static(staticDir)); } /** * Configure routes for the API */ setupRoutes() { // Original routes... this.app.get('/api/hosts', (_, res) => __awaiter(this, void 0, void 0, function* () { try { const hosts = yield this.hostsManager.readLocalHosts(); res.json({ success: true, hosts }); } catch (error) { console.error('Error fetching hosts:', error); res.status(500).json({ success: false, error: 'Failed to fetch hosts' }); } })); this.app.post('/api/hosts', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain, ip = '127.0.0.1' } = req.body; if (!domain) { res.status(400).json({ success: false, error: 'Domain is required' }); return; } const success = yield this.hostsManager.addHost(domain, ip); if (success) { res.json({ success: true, message: `Host ${domain} added successfully` }); } else { res.status(500).json({ success: false, error: 'Failed to add host' }); } } catch (error) { console.error('Error adding host:', error); res.status(500).json({ success: false, error: 'Failed to add host' }); } })); this.app.post('/api/hosts/:domain/adopt', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const { ip = '127.0.0.1' } = req.body; const success = yield this.hostsManager.adoptHost(domain, ip); if (success) { res.json({ success: true, message: `Host ${domain} adopted successfully` }); } else { res.status(404).json({ success: false, error: 'Host not found or already adopted' }); } } catch (error) { console.error('Error adopting host:', error); res.status(500).json({ success: false, error: 'Failed to adopt host' }); } })); this.app.post('/api/hosts/import-all', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const result = yield this.hostsManager.importAllLocalHosts(); if (result.success) { res.json({ success: true, message: `${result.count} hosts imported successfully` }); } else { res.status(404).json({ success: false, error: 'No hosts found to import' }); } } catch (error) { console.error('Error importing hosts:', error); res.status(500).json({ success: false, error: 'Failed to import hosts' }); } })); this.app.delete('/api/hosts/:domain', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const { ip = '127.0.0.1' } = req.query; const hostSuccess = yield this.hostsManager.removeHost(domain, ip); // Also remove the associated certificate let certSuccess = false; try { certSuccess = yield this.certManager.deleteCertificate(domain); } catch (certError) { console.error(`Error removing certificate for ${domain}:`, certError); // Don't fail the main operation if deleting the certificate fails } if (hostSuccess) { const message = certSuccess ? `Host ${domain} and its certificate removed successfully` : `Host ${domain} removed successfully`; res.json({ success: true, message, certificateRemoved: certSuccess }); } else { res.status(404).json({ success: false, error: 'Host not found or not created by this application' }); } } catch (error) { console.error('Error removing host:', error); res.status(500).json({ success: false, error: 'Failed to remove host' }); } })); this.app.patch('/api/hosts/:domain/toggle', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const { disabled, ip = '127.0.0.1' } = req.body; if (disabled === undefined) { res.status(400).json({ success: false, error: 'Disabled state is required' }); return; } const success = yield this.hostsManager.toggleHostState(domain, disabled, ip); if (success) { const state = disabled ? 'disabled' : 'enabled'; res.json({ success: true, message: `Host ${domain} ${state} successfully` }); } else { res.status(404).json({ success: false, error: 'Host not found or not created by this application' }); } } catch (error) { console.error('Error toggling host:', error); res.status(500).json({ success: false, error: 'Failed to toggle host state' }); } })); this.app.get('/api/certificates', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const certificates = yield this.certManager.listCertificates(); res.json({ success: true, certificates }); } catch (error) { console.error('Error fetching certificates:', error); res.status(500).json({ success: false, error: 'Failed to fetch certificates' }); } })); this.app.get('/api/certificates/:domain', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const certInfo = yield this.certManager.verifyCertificate(domain); if (certInfo) { res.json({ success: true, certificate: certInfo }); } else { res.status(404).json({ success: false, error: 'Certificate not found' }); } } catch (error) { console.error('Error verifying certificate:', error); res.status(500).json({ success: false, error: 'Failed to verify certificate' }); } })); this.app.post('/api/certificates', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.body; if (!domain) { res.status(400).json({ success: false, error: 'Domain is required' }); return; } const certInfo = yield this.certManager.createCertificate(domain); res.json({ success: true, certificate: certInfo }); } catch (error) { console.error('Error creating certificate:', error); res.status(500).json({ success: false, error: 'Failed to create certificate' }); } })); this.app.delete('/api/certificates/:domain', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const success = yield this.certManager.deleteCertificate(domain); if (success) { res.json({ success: true, message: `Certificate for ${domain} removed successfully` }); } else { res.status(404).json({ success: false, error: 'Certificate not found' }); } } catch (error) { console.error('Error removing certificate:', error); res.status(500).json({ success: false, error: 'Failed to remove certificate' }); } })); this.app.get('/api/status/:domain', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; const hosts = yield this.hostsManager.readLocalHosts(); const hostEntry = hosts.find(host => host.domain === domain); const hostExists = !!hostEntry; const certInfo = yield this.certManager.verifyCertificate(domain); const certValid = (certInfo === null || certInfo === void 0 ? void 0 : certInfo.isValid) || false; const status = { domain, hostConfigured: hostExists, certificateValid: certValid, isValid: hostExists && certValid, isDisabled: (hostEntry === null || hostEntry === void 0 ? void 0 : hostEntry.isDisabled) || false }; res.json({ success: true, status }); } catch (error) { console.error('Error checking domain status:', error); res.status(500).json({ success: false, error: 'Failed to check domain status' }); } })); // New proxy-related routes this.app.get('/api/proxies', (req, res) => { try { const proxies = this.proxyService.getProxies(); res.json({ success: true, proxies }); } catch (error) { console.error('Error fetching proxies:', error); res.status(500).json({ success: false, error: 'Failed to fetch proxies' }); } }); this.app.post('/api/proxies', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain, target, port } = req.body; if (!domain || !target) { res.status(400).json({ success: false, error: 'Domain and target are required' }); return; } // Verify domain exists and has a valid certificate const hosts = yield this.hostsManager.readLocalHosts(); const hostEntry = hosts.find(host => host.domain === domain); if (!hostEntry) { res.status(404).json({ success: false, error: 'Domain not found in hosts file' }); return; } const certInfo = yield this.certManager.verifyCertificate(domain); if (!certInfo || !certInfo.isValid) { res.status(400).json({ success: false, error: 'Domain does not have a valid certificate' }); return; } const proxyConfig = { domain, target, isRunning: false, port: port || 443 }; const config = this.proxyService.addProxy(proxyConfig); res.json({ success: true, message: `Proxy configuration for ${domain} added successfully`, proxy: config }); } catch (error) { console.error('Error adding proxy:', error); res.status(500).json({ success: false, error: 'Failed to add proxy' }); } })); this.app.delete('/api/proxies/:domain', (req, res) => { try { const { domain } = req.params; const success = this.proxyService.removeProxy(domain); if (success) { res.json({ success: true, message: `Proxy for ${domain} removed successfully` }); } else { res.status(404).json({ success: false, error: 'Proxy not found' }); } } catch (error) { console.error('Error removing proxy:', error); res.status(500).json({ success: false, error: 'Failed to remove proxy' }); } }); this.app.post('/api/proxies/:domain/start', (req, res) => __awaiter(this, void 0, void 0, function* () { try { const { domain } = req.params; // Get certificate paths const certInfo = yield this.certManager.verifyCertificate(domain); if (!certInfo || !certInfo.isValid || !certInfo.certFilePath || !certInfo.keyFilePath) { res.status(400).json({ success: false, error: 'Domain does not have a valid certificate' }); return; } const success = this.proxyService.startProxy(domain, certInfo.certFilePath, certInfo.keyFilePath); if (success) { res.json({ success: true, message: `Proxy for ${domain} started successfully` }); } else { res.status(500).json({ success: false, error: 'Failed to start proxy' }); } } catch (error) { console.error('Error starting proxy:', error); res.status(500).json({ success: false, error: 'Failed to start proxy' }); } })); this.app.post('/api/proxies/:domain/stop', (req, res) => { try { const { domain } = req.params; const success = this.proxyService.stopProxy(domain); if (success) { res.json({ success: true, message: `Proxy for ${domain} stopped successfully` }); } else { res.status(404).json({ success: false, error: 'Proxy not found or not running' }); } } catch (error) { console.error('Error stopping proxy:', error); res.status(500).json({ success: false, error: 'Failed to stop proxy' }); } }); this.app.patch('/api/proxies/:domain', (req, res) => { try { const { domain } = req.params; const { target, port } = req.body; if (!target && !port) { res.status(400).json({ success: false, error: 'No update parameters provided' }); return; } const success = this.proxyService.updateProxy(domain, { target, port }); if (success) { res.json({ success: true, message: `Proxy for ${domain} updated successfully` }); } else { res.status(404).json({ success: false, error: 'Proxy not found' }); } } catch (error) { console.error('Error updating proxy:', error); res.status(500).json({ success: false, error: 'Failed to update proxy' }); } }); // Route to serve the user interface this.app.get('*', (_, res) => { res.sendFile(path.join(__dirname, '..', '..', 'public', 'index.html')); }); } /** * Start the web server */ start() { return __awaiter(this, void 0, void 0, function* () { this.server = this.app.listen(this.config.port, () => { console.log(`Server listening on http://localhost:${this.config.port}`); }); try { yield (0, open_1.default)(`http://localhost:${this.config.port}`); } catch (error) { console.warn('Could not open browser automatically:', error); } }); } /** * Stop the web server */ stop() { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { if (this.server) { // Stop all proxies first this.proxyService.stopAllProxies(); this.server.close(err => { if (err) { console.error('Error stopping HTTP server:', err); reject(err); } else { console.log('HTTP server stopped'); resolve(); } }); } else { resolve(); } }); }); } } exports.WebServer = WebServer;