reverse-proxy/src/services/ProxyService.ts

240 lines
7.2 KiB
TypeScript
Raw Normal View History

2025-06-12 01:33:06 -04:00
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<Proxy, 'id' | 'created_at' | 'updated_at'>): Promise<Proxy> {
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<Proxy>): Promise<Proxy> {
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<void> {
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<Proxy[]> {
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<Proxy | null> {
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<void> {
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;
}
}
}