import { Proxy } from '../types/index.js'; import { ProxyModel } from '../models/Proxy.js'; import { CertificateModel } from '../models/Certificate.js'; import { NginxService } from './NginxService.js'; import { SSLService } from './SSLService.js'; import logger from '../utils/logger.js'; export class ProxyService { /** * Create a new proxy entry */ static async createProxy(proxyData: Omit): Promise { try { logger.info(`Creating proxy for ${proxyData.domain}`); // Check if proxy already exists const existingProxy = await ProxyModel.findByDomain(proxyData.domain); if (existingProxy) { throw new Error(`Proxy for domain ${proxyData.domain} already exists`); } // Create proxy in database const proxy = await ProxyModel.create(proxyData); // Handle SSL certificate if needed if (proxy.ssl_type === 'letsencrypt') { try { const certificate = await SSLService.requestLetsEncryptCert(proxy.domain); proxy.cert_path = certificate.path; proxy.key_path = certificate.key_path; // Update proxy with certificate paths await ProxyModel.update(proxy.id!, { cert_path: certificate.path, key_path: certificate.key_path }); } catch (sslError) { logger.error(`SSL certificate creation failed for ${proxy.domain}:`, sslError); // Continue with proxy creation but without SSL proxy.ssl_type = 'none'; await ProxyModel.update(proxy.id!, { ssl_type: 'none' }); } } // Generate and write NGINX configuration await this.updateNginxConfig(proxy); // Reload NGINX const reloadResult = await NginxService.reload(); if (!reloadResult.success) { // Rollback: remove proxy and config await ProxyModel.delete(proxy.id!); await NginxService.removeConfig(proxy.domain); throw new Error(`NGINX reload failed: ${reloadResult.output}`); } logger.info(`Proxy successfully created for ${proxy.domain}`); return proxy; } catch (error) { logger.error(`Failed to create proxy for ${proxyData.domain}:`, error); throw error; } } /** * Update an existing proxy */ static async updateProxy(id: number, updateData: Partial): Promise { try { const existingProxy = await ProxyModel.findById(id); if (!existingProxy) { throw new Error('Proxy not found'); } logger.info(`Updating proxy for ${existingProxy.domain}`); // Handle SSL type changes if (updateData.ssl_type && updateData.ssl_type !== existingProxy.ssl_type) { if (updateData.ssl_type === 'letsencrypt') { try { const certificate = await SSLService.requestLetsEncryptCert(existingProxy.domain); updateData.cert_path = certificate.path; updateData.key_path = certificate.key_path; } catch (sslError) { logger.error(`SSL certificate creation failed:`, sslError); throw sslError; } } else if (updateData.ssl_type === 'none') { updateData.cert_path = null; updateData.key_path = null; } } // Update proxy in database const updatedProxy = await ProxyModel.update(id, updateData); if (!updatedProxy) { throw new Error('Failed to update proxy'); } // Update NGINX configuration await this.updateNginxConfig(updatedProxy); // Reload NGINX const reloadResult = await NginxService.reload(); if (!reloadResult.success) { throw new Error(`NGINX reload failed: ${reloadResult.output}`); } logger.info(`Proxy successfully updated for ${updatedProxy.domain}`); return updatedProxy; } catch (error) { logger.error(`Failed to update proxy ${id}:`, error); throw error; } } /** * Delete a proxy */ static async deleteProxy(id: number): Promise { try { const proxy = await ProxyModel.findById(id); if (!proxy) { throw new Error('Proxy not found'); } logger.info(`Deleting proxy for ${proxy.domain}`); // Remove NGINX configuration await NginxService.removeConfig(proxy.domain); // Remove certificate if it's a Let's Encrypt cert if (proxy.ssl_type === 'letsencrypt') { const certificate = await CertificateModel.findByDomain(proxy.domain); if (certificate) { await SSLService.removeCertificate(certificate); await CertificateModel.delete(certificate.id!); } } // Delete proxy from database await ProxyModel.delete(id); // Reload NGINX const reloadResult = await NginxService.reload(); if (!reloadResult.success) { logger.warn(`NGINX reload failed after deleting proxy: ${reloadResult.output}`); } logger.info(`Proxy successfully deleted for ${proxy.domain}`); } catch (error) { logger.error(`Failed to delete proxy ${id}:`, error); throw error; } } /** * Get all proxies */ static async getAllProxies(): Promise { try { return await ProxyModel.findAll(); } catch (error) { logger.error('Failed to get all proxies:', error); throw error; } } /** * Get a single proxy by ID */ static async getProxyById(id: number): Promise { try { return await ProxyModel.findById(id); } catch (error) { logger.error(`Failed to get proxy ${id}:`, error); throw error; } } /** * Update NGINX configuration for a proxy */ private static async updateNginxConfig(proxy: Proxy): Promise { const sslEnabled = proxy.ssl_type !== 'none' && proxy.cert_path && proxy.key_path; const configOptions = { domain: proxy.domain, target: proxy.target, sslEnabled, certPath: proxy.cert_path, keyPath: proxy.key_path, redirectHttpToHttps: proxy.options.redirect_http_to_https, customHeaders: proxy.options.custom_headers, pathForwarding: proxy.options.path_forwarding, enableWebsockets: proxy.options.enable_websockets, clientMaxBodySize: proxy.options.client_max_body_size }; const configContent = NginxService.generateConfig(configOptions); await NginxService.writeConfig(proxy.domain, configContent); } /** * Test NGINX configuration */ static async testNginxConfig(): Promise<{ success: boolean; output: string }> { try { return await NginxService.testConfig(); } catch (error) { logger.error('Failed to test NGINX config:', error); throw error; } } /** * Reload NGINX */ static async reloadNginx(): Promise<{ success: boolean; output: string }> { try { return await NginxService.reload(); } catch (error) { logger.error('Failed to reload NGINX:', error); throw error; } } /** * Get NGINX status */ static async getNginxStatus(): Promise<{ success: boolean; output: string }> { try { return await NginxService.getStatus(); } catch (error) { logger.error('Failed to get NGINX status:', error); throw error; } } }