240 lines
7.2 KiB
TypeScript
240 lines
7.2 KiB
TypeScript
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|