fix(routing): unify route based architecture

This commit is contained in:
2025-05-13 12:48:41 +00:00
parent fe632bde67
commit fcc8cf9caa
46 changed files with 7943 additions and 1332 deletions

View File

@ -8,6 +8,7 @@ import { CertificateEvents } from '../../certificate/events/certificate-events.j
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
import type { IDomainOptions } from '../../certificate/models/certificate-types.js';
import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
/**
* Manages SSL certificates for NetworkProxy including ACME integration
@ -91,7 +92,7 @@ export class CertificateManager {
public setExternalPort80Handler(handler: Port80Handler): void {
if (this.port80Handler && !this.externalPort80Handler) {
this.logger.warn('Replacing existing internal Port80Handler with external handler');
// Clean up existing handler if needed
if (this.port80Handler !== handler) {
// Unregister event handlers to avoid memory leaks
@ -101,11 +102,11 @@ export class CertificateManager {
this.port80Handler.removeAllListeners(CertificateEvents.CERTIFICATE_EXPIRING);
}
}
// Set the external handler
this.port80Handler = handler;
this.externalPort80Handler = true;
// Subscribe to Port80Handler events
subscribeToPort80Handler(this.port80Handler, {
onCertificateIssued: this.handleCertificateIssued.bind(this),
@ -115,17 +116,40 @@ export class CertificateManager {
this.logger.info(`Certificate for ${data.domain} expires in ${data.daysRemaining} days`);
}
});
this.logger.info('External Port80Handler connected to CertificateManager');
// Register domains with Port80Handler if we have any certificates cached
if (this.certificateCache.size > 0) {
const domains = Array.from(this.certificateCache.keys())
.filter(domain => !domain.includes('*')); // Skip wildcard domains
this.registerDomainsWithPort80Handler(domains);
}
}
/**
* Update route configurations managed by this certificate manager
* This method is called when route configurations change
*
* @param routes Array of route configurations
*/
public updateRouteConfigs(routes: IRouteConfig[]): void {
if (!this.port80Handler) {
this.logger.warn('Cannot update routes - Port80Handler is not initialized');
return;
}
// Register domains from routes with Port80Handler
this.registerRoutesWithPort80Handler(routes);
// Process individual routes for certificate requirements
for (const route of routes) {
this.processRouteForCertificates(route);
}
this.logger.info(`Updated certificate management for ${routes.length} routes`);
}
/**
* Handle newly issued or renewed certificates from Port80Handler
@ -317,20 +341,21 @@ export class CertificateManager {
/**
* Registers domains with Port80Handler for ACME certificate management
* @param domains String array of domains to register
*/
public registerDomainsWithPort80Handler(domains: string[]): void {
if (!this.port80Handler) {
this.logger.warn('Port80Handler is not initialized');
return;
}
for (const domain of domains) {
// Skip wildcard domains - can't get certs for these with HTTP-01 validation
if (domain.includes('*')) {
this.logger.info(`Skipping wildcard domain for ACME: ${domain}`);
continue;
}
// Skip domains already with certificates if configured to do so
if (this.options.acme?.skipConfiguredCerts) {
const cachedCert = this.certificateCache.get(domain);
@ -339,18 +364,97 @@ export class CertificateManager {
continue;
}
}
// Register the domain for certificate issuance with new domain options format
const domainOptions: IDomainOptions = {
domainName: domain,
sslRedirect: true,
acmeMaintenance: true
};
this.port80Handler.addDomain(domainOptions);
this.logger.info(`Registered domain for ACME certificate issuance: ${domain}`);
}
}
/**
* Extract domains from route configurations and register with Port80Handler
* This method enables direct integration with route-based configuration
*
* @param routes Array of route configurations
*/
public registerRoutesWithPort80Handler(routes: IRouteConfig[]): void {
if (!this.port80Handler) {
this.logger.warn('Port80Handler is not initialized');
return;
}
// Extract domains from route configurations
const domains: Set<string> = new Set();
for (const route of routes) {
// Skip disabled routes
if (route.enabled === false) {
continue;
}
// Skip routes without HTTPS termination
if (route.action.type !== 'forward' || route.action.tls?.mode !== 'terminate') {
continue;
}
// Extract domains from match criteria
if (route.match.domains) {
if (typeof route.match.domains === 'string') {
domains.add(route.match.domains);
} else if (Array.isArray(route.match.domains)) {
for (const domain of route.match.domains) {
domains.add(domain);
}
}
}
}
// Register extracted domains
this.registerDomainsWithPort80Handler(Array.from(domains));
}
/**
* Process a route config to determine if it requires automatic certificate provisioning
* @param route Route configuration to process
*/
public processRouteForCertificates(route: IRouteConfig): void {
// Skip disabled routes
if (route.enabled === false) {
return;
}
// Skip routes without HTTPS termination or auto certificate
if (route.action.type !== 'forward' ||
route.action.tls?.mode !== 'terminate' ||
route.action.tls?.certificate !== 'auto') {
return;
}
// Extract domains from match criteria
const domains: string[] = [];
if (route.match.domains) {
if (typeof route.match.domains === 'string') {
domains.push(route.match.domains);
} else if (Array.isArray(route.match.domains)) {
domains.push(...route.match.domains);
}
}
// Request certificates for the domains
for (const domain of domains) {
if (!domain.includes('*')) { // Skip wildcard domains
this.requestCertificate(domain).catch(err => {
this.logger.error(`Error requesting certificate for domain ${domain}:`, err);
});
}
}
}
/**
* Initialize internal Port80Handler