679 lines
23 KiB
TypeScript
679 lines
23 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import type { IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';
|
|
import { ConnectionManager } from './classes.pp.connectionmanager.js';
|
|
import { SecurityManager } from './classes.pp.securitymanager.js';
|
|
import { DomainConfigManager } from './classes.pp.domainconfigmanager.js';
|
|
import { TlsManager } from './classes.pp.tlsmanager.js';
|
|
import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
|
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
|
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
|
import { ConnectionHandler } from './classes.pp.connectionhandler.js';
|
|
import { Port80Handler, Port80HandlerEvents } from '../port80handler/classes.port80handler.js';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
|
|
/**
|
|
* SmartProxy - Main class that coordinates all components
|
|
*/
|
|
export class SmartProxy {
|
|
private netServers: plugins.net.Server[] = [];
|
|
private connectionLogger: NodeJS.Timeout | null = null;
|
|
private isShuttingDown: boolean = false;
|
|
|
|
// Component managers
|
|
private connectionManager: ConnectionManager;
|
|
private securityManager: SecurityManager;
|
|
public domainConfigManager: DomainConfigManager;
|
|
private tlsManager: TlsManager;
|
|
private networkProxyBridge: NetworkProxyBridge;
|
|
private timeoutManager: TimeoutManager;
|
|
private portRangeManager: PortRangeManager;
|
|
private connectionHandler: ConnectionHandler;
|
|
|
|
// Port80Handler for ACME certificate management
|
|
private port80Handler: Port80Handler | null = null;
|
|
|
|
constructor(settingsArg: IPortProxySettings) {
|
|
// Set reasonable defaults for all settings
|
|
this.settings = {
|
|
...settingsArg,
|
|
targetIP: settingsArg.targetIP || 'localhost',
|
|
initialDataTimeout: settingsArg.initialDataTimeout || 120000,
|
|
socketTimeout: settingsArg.socketTimeout || 3600000,
|
|
inactivityCheckInterval: settingsArg.inactivityCheckInterval || 60000,
|
|
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 86400000,
|
|
inactivityTimeout: settingsArg.inactivityTimeout || 14400000,
|
|
gracefulShutdownTimeout: settingsArg.gracefulShutdownTimeout || 30000,
|
|
noDelay: settingsArg.noDelay !== undefined ? settingsArg.noDelay : true,
|
|
keepAlive: settingsArg.keepAlive !== undefined ? settingsArg.keepAlive : true,
|
|
keepAliveInitialDelay: settingsArg.keepAliveInitialDelay || 10000,
|
|
maxPendingDataSize: settingsArg.maxPendingDataSize || 10 * 1024 * 1024,
|
|
disableInactivityCheck: settingsArg.disableInactivityCheck || false,
|
|
enableKeepAliveProbes:
|
|
settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
|
|
enableDetailedLogging: settingsArg.enableDetailedLogging || false,
|
|
enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
|
|
enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
|
|
allowSessionTicket:
|
|
settingsArg.allowSessionTicket !== undefined ? settingsArg.allowSessionTicket : true,
|
|
maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
|
|
connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300,
|
|
keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
|
|
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
|
|
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
|
|
networkProxyPort: settingsArg.networkProxyPort || 8443,
|
|
port80HandlerConfig: settingsArg.port80HandlerConfig || {},
|
|
globalPortRanges: settingsArg.globalPortRanges || [],
|
|
};
|
|
|
|
// Set port80HandlerConfig defaults, using legacy acme config if available
|
|
if (!this.settings.port80HandlerConfig || Object.keys(this.settings.port80HandlerConfig).length === 0) {
|
|
if (this.settings.acme) {
|
|
// Migrate from legacy acme config
|
|
this.settings.port80HandlerConfig = {
|
|
enabled: this.settings.acme.enabled,
|
|
port: this.settings.acme.port || 80,
|
|
contactEmail: this.settings.acme.contactEmail || 'admin@example.com',
|
|
useProduction: this.settings.acme.useProduction || false,
|
|
renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
|
|
autoRenew: this.settings.acme.autoRenew !== false, // Default to true
|
|
certificateStore: this.settings.acme.certificateStore || './certs',
|
|
skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
|
|
httpsRedirectPort: this.settings.fromPort,
|
|
renewCheckIntervalHours: 24
|
|
};
|
|
} else {
|
|
// Set defaults if no config provided
|
|
this.settings.port80HandlerConfig = {
|
|
enabled: false,
|
|
port: 80,
|
|
contactEmail: 'admin@example.com',
|
|
useProduction: false,
|
|
renewThresholdDays: 30,
|
|
autoRenew: true,
|
|
certificateStore: './certs',
|
|
skipConfiguredCerts: false,
|
|
httpsRedirectPort: this.settings.fromPort,
|
|
renewCheckIntervalHours: 24
|
|
};
|
|
}
|
|
}
|
|
|
|
// Initialize component managers
|
|
this.timeoutManager = new TimeoutManager(this.settings);
|
|
this.securityManager = new SecurityManager(this.settings);
|
|
this.connectionManager = new ConnectionManager(
|
|
this.settings,
|
|
this.securityManager,
|
|
this.timeoutManager
|
|
);
|
|
this.domainConfigManager = new DomainConfigManager(this.settings);
|
|
this.tlsManager = new TlsManager(this.settings);
|
|
this.networkProxyBridge = new NetworkProxyBridge(this.settings);
|
|
this.portRangeManager = new PortRangeManager(this.settings);
|
|
|
|
// Initialize connection handler
|
|
this.connectionHandler = new ConnectionHandler(
|
|
this.settings,
|
|
this.connectionManager,
|
|
this.securityManager,
|
|
this.domainConfigManager,
|
|
this.tlsManager,
|
|
this.networkProxyBridge,
|
|
this.timeoutManager,
|
|
this.portRangeManager
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The settings for the port proxy
|
|
*/
|
|
public settings: IPortProxySettings;
|
|
|
|
/**
|
|
* Initialize the Port80Handler for ACME certificate management
|
|
*/
|
|
private async initializePort80Handler(): Promise<void> {
|
|
const config = this.settings.port80HandlerConfig;
|
|
|
|
if (!config || !config.enabled) {
|
|
console.log('Port80Handler is disabled in configuration');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Ensure the certificate store directory exists
|
|
if (config.certificateStore) {
|
|
const certStorePath = path.resolve(config.certificateStore);
|
|
if (!fs.existsSync(certStorePath)) {
|
|
fs.mkdirSync(certStorePath, { recursive: true });
|
|
console.log(`Created certificate store directory: ${certStorePath}`);
|
|
}
|
|
}
|
|
|
|
// Create Port80Handler with options from config
|
|
this.port80Handler = new Port80Handler({
|
|
port: config.port,
|
|
contactEmail: config.contactEmail,
|
|
useProduction: config.useProduction,
|
|
renewThresholdDays: config.renewThresholdDays,
|
|
httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort,
|
|
renewCheckIntervalHours: config.renewCheckIntervalHours,
|
|
enabled: config.enabled,
|
|
autoRenew: config.autoRenew,
|
|
certificateStore: config.certificateStore,
|
|
skipConfiguredCerts: config.skipConfiguredCerts
|
|
});
|
|
|
|
// Register domain forwarding configurations
|
|
if (config.domainForwards) {
|
|
for (const forward of config.domainForwards) {
|
|
this.port80Handler.addDomain({
|
|
domainName: forward.domain,
|
|
sslRedirect: true,
|
|
acmeMaintenance: true,
|
|
forward: forward.forwardConfig,
|
|
acmeForward: forward.acmeForwardConfig
|
|
});
|
|
|
|
console.log(`Registered domain forwarding for ${forward.domain}`);
|
|
}
|
|
}
|
|
|
|
// Register all non-wildcard domains from domain configs
|
|
for (const domainConfig of this.settings.domainConfigs) {
|
|
for (const domain of domainConfig.domains) {
|
|
// Skip wildcards
|
|
if (domain.includes('*')) continue;
|
|
|
|
this.port80Handler.addDomain({
|
|
domainName: domain,
|
|
sslRedirect: true,
|
|
acmeMaintenance: true
|
|
});
|
|
|
|
console.log(`Registered domain ${domain} with Port80Handler`);
|
|
}
|
|
}
|
|
|
|
// Set up event listeners
|
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (certData) => {
|
|
console.log(`Certificate issued for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
|
|
});
|
|
|
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (certData) => {
|
|
console.log(`Certificate renewed for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
|
|
});
|
|
|
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, (failureData) => {
|
|
console.log(`Certificate ${failureData.isRenewal ? 'renewal' : 'issuance'} failed for ${failureData.domain}: ${failureData.error}`);
|
|
});
|
|
|
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, (expiryData) => {
|
|
console.log(`Certificate for ${expiryData.domain} is expiring in ${expiryData.daysRemaining} days`);
|
|
});
|
|
|
|
// Share Port80Handler with NetworkProxyBridge
|
|
this.networkProxyBridge.setPort80Handler(this.port80Handler);
|
|
|
|
// Start Port80Handler
|
|
await this.port80Handler.start();
|
|
console.log(`Port80Handler started on port ${config.port}`);
|
|
} catch (err) {
|
|
console.log(`Error initializing Port80Handler: ${err}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start the proxy server
|
|
*/
|
|
public async start() {
|
|
// Don't start if already shutting down
|
|
if (this.isShuttingDown) {
|
|
console.log("Cannot start PortProxy while it's shutting down");
|
|
return;
|
|
}
|
|
|
|
// Initialize Port80Handler if enabled
|
|
await this.initializePort80Handler();
|
|
|
|
// Initialize and start NetworkProxy if needed
|
|
if (
|
|
this.settings.useNetworkProxy &&
|
|
this.settings.useNetworkProxy.length > 0
|
|
) {
|
|
await this.networkProxyBridge.initialize();
|
|
await this.networkProxyBridge.start();
|
|
}
|
|
|
|
// Validate port configuration
|
|
const configWarnings = this.portRangeManager.validateConfiguration();
|
|
if (configWarnings.length > 0) {
|
|
console.log("Port configuration warnings:");
|
|
for (const warning of configWarnings) {
|
|
console.log(` - ${warning}`);
|
|
}
|
|
}
|
|
|
|
// Get listening ports from PortRangeManager
|
|
const listeningPorts = this.portRangeManager.getListeningPorts();
|
|
|
|
// Create servers for each port
|
|
for (const port of listeningPorts) {
|
|
const server = plugins.net.createServer((socket) => {
|
|
// Check if shutting down
|
|
if (this.isShuttingDown) {
|
|
socket.end();
|
|
socket.destroy();
|
|
return;
|
|
}
|
|
|
|
// Delegate to connection handler
|
|
this.connectionHandler.handleConnection(socket);
|
|
}).on('error', (err: Error) => {
|
|
console.log(`Server Error on port ${port}: ${err.message}`);
|
|
});
|
|
|
|
server.listen(port, () => {
|
|
const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
|
|
console.log(
|
|
`PortProxy -> OK: Now listening on port ${port}${
|
|
this.settings.sniEnabled && !isNetworkProxyPort ? ' (SNI passthrough enabled)' : ''
|
|
}${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}`
|
|
);
|
|
});
|
|
|
|
this.netServers.push(server);
|
|
}
|
|
|
|
// Set up periodic connection logging and inactivity checks
|
|
this.connectionLogger = setInterval(() => {
|
|
// Immediately return if shutting down
|
|
if (this.isShuttingDown) return;
|
|
|
|
// Perform inactivity check
|
|
this.connectionManager.performInactivityCheck();
|
|
|
|
// Log connection statistics
|
|
const now = Date.now();
|
|
let maxIncoming = 0;
|
|
let maxOutgoing = 0;
|
|
let tlsConnections = 0;
|
|
let nonTlsConnections = 0;
|
|
let completedTlsHandshakes = 0;
|
|
let pendingTlsHandshakes = 0;
|
|
let keepAliveConnections = 0;
|
|
let networkProxyConnections = 0;
|
|
|
|
// Get connection records for analysis
|
|
const connectionRecords = this.connectionManager.getConnections();
|
|
|
|
// Analyze active connections
|
|
for (const record of connectionRecords.values()) {
|
|
// Track connection stats
|
|
if (record.isTLS) {
|
|
tlsConnections++;
|
|
if (record.tlsHandshakeComplete) {
|
|
completedTlsHandshakes++;
|
|
} else {
|
|
pendingTlsHandshakes++;
|
|
}
|
|
} else {
|
|
nonTlsConnections++;
|
|
}
|
|
|
|
if (record.hasKeepAlive) {
|
|
keepAliveConnections++;
|
|
}
|
|
|
|
if (record.usingNetworkProxy) {
|
|
networkProxyConnections++;
|
|
}
|
|
|
|
maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
|
|
if (record.outgoingStartTime) {
|
|
maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime);
|
|
}
|
|
}
|
|
|
|
// Get termination stats
|
|
const terminationStats = this.connectionManager.getTerminationStats();
|
|
|
|
// Log detailed stats
|
|
console.log(
|
|
`Active connections: ${connectionRecords.size}. ` +
|
|
`Types: TLS=${tlsConnections} (Completed=${completedTlsHandshakes}, Pending=${pendingTlsHandshakes}), ` +
|
|
`Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, NetworkProxy=${networkProxyConnections}. ` +
|
|
`Longest running: IN=${plugins.prettyMs(maxIncoming)}, OUT=${plugins.prettyMs(maxOutgoing)}. ` +
|
|
`Termination stats: ${JSON.stringify({
|
|
IN: terminationStats.incoming,
|
|
OUT: terminationStats.outgoing,
|
|
})}`
|
|
);
|
|
}, this.settings.inactivityCheckInterval || 60000);
|
|
|
|
// Make sure the interval doesn't keep the process alive
|
|
if (this.connectionLogger.unref) {
|
|
this.connectionLogger.unref();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop the proxy server
|
|
*/
|
|
public async stop() {
|
|
console.log('PortProxy shutting down...');
|
|
this.isShuttingDown = true;
|
|
|
|
// Stop the Port80Handler if running
|
|
if (this.port80Handler) {
|
|
try {
|
|
await this.port80Handler.stop();
|
|
console.log('Port80Handler stopped');
|
|
this.port80Handler = null;
|
|
} catch (err) {
|
|
console.log(`Error stopping Port80Handler: ${err}`);
|
|
}
|
|
}
|
|
|
|
// Stop accepting new connections
|
|
const closeServerPromises: Promise<void>[] = this.netServers.map(
|
|
(server) =>
|
|
new Promise<void>((resolve) => {
|
|
if (!server.listening) {
|
|
resolve();
|
|
return;
|
|
}
|
|
server.close((err) => {
|
|
if (err) {
|
|
console.log(`Error closing server: ${err.message}`);
|
|
}
|
|
resolve();
|
|
});
|
|
})
|
|
);
|
|
|
|
// Stop the connection logger
|
|
if (this.connectionLogger) {
|
|
clearInterval(this.connectionLogger);
|
|
this.connectionLogger = null;
|
|
}
|
|
|
|
// Wait for servers to close
|
|
await Promise.all(closeServerPromises);
|
|
console.log('All servers closed. Cleaning up active connections...');
|
|
|
|
// Clean up all active connections
|
|
this.connectionManager.clearConnections();
|
|
|
|
// Stop NetworkProxy
|
|
await this.networkProxyBridge.stop();
|
|
|
|
// Clear all servers
|
|
this.netServers = [];
|
|
|
|
console.log('PortProxy shutdown complete.');
|
|
}
|
|
|
|
/**
|
|
* Updates the domain configurations for the proxy
|
|
*/
|
|
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
|
|
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
|
|
|
|
// Update domain configs in DomainConfigManager
|
|
this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
|
|
|
|
// If NetworkProxy is initialized, resync the configurations
|
|
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
}
|
|
|
|
// If Port80Handler is running, register non-wildcard domains
|
|
if (this.port80Handler && this.settings.port80HandlerConfig?.enabled) {
|
|
for (const domainConfig of newDomainConfigs) {
|
|
for (const domain of domainConfig.domains) {
|
|
// Skip wildcards
|
|
if (domain.includes('*')) continue;
|
|
|
|
this.port80Handler.addDomain({
|
|
domainName: domain,
|
|
sslRedirect: true,
|
|
acmeMaintenance: true
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log('Registered non-wildcard domains with Port80Handler');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the Port80Handler configuration
|
|
*/
|
|
public async updatePort80HandlerConfig(config: IPortProxySettings['port80HandlerConfig']): Promise<void> {
|
|
if (!config) return;
|
|
|
|
console.log('Updating Port80Handler configuration');
|
|
|
|
// Update the settings
|
|
this.settings.port80HandlerConfig = {
|
|
...this.settings.port80HandlerConfig,
|
|
...config
|
|
};
|
|
|
|
// Check if we need to restart Port80Handler
|
|
let needsRestart = false;
|
|
|
|
// Restart if enabled state changed
|
|
if (this.port80Handler && config.enabled === false) {
|
|
needsRestart = true;
|
|
} else if (!this.port80Handler && config.enabled === true) {
|
|
needsRestart = true;
|
|
} else if (this.port80Handler && (
|
|
config.port !== undefined ||
|
|
config.contactEmail !== undefined ||
|
|
config.useProduction !== undefined ||
|
|
config.renewThresholdDays !== undefined ||
|
|
config.renewCheckIntervalHours !== undefined
|
|
)) {
|
|
// Restart if critical settings changed
|
|
needsRestart = true;
|
|
}
|
|
|
|
if (needsRestart) {
|
|
// Stop if running
|
|
if (this.port80Handler) {
|
|
try {
|
|
await this.port80Handler.stop();
|
|
this.port80Handler = null;
|
|
console.log('Stopped Port80Handler for configuration update');
|
|
} catch (err) {
|
|
console.log(`Error stopping Port80Handler: ${err}`);
|
|
}
|
|
}
|
|
|
|
// Start with new config if enabled
|
|
if (this.settings.port80HandlerConfig.enabled) {
|
|
await this.initializePort80Handler();
|
|
console.log('Restarted Port80Handler with new configuration');
|
|
}
|
|
} else if (this.port80Handler) {
|
|
// Just update domain forwards if they changed
|
|
if (config.domainForwards) {
|
|
for (const forward of config.domainForwards) {
|
|
this.port80Handler.addDomain({
|
|
domainName: forward.domain,
|
|
sslRedirect: true,
|
|
acmeMaintenance: true,
|
|
forward: forward.forwardConfig,
|
|
acmeForward: forward.acmeForwardConfig
|
|
});
|
|
}
|
|
console.log('Updated domain forwards in Port80Handler');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request a certificate for a specific domain
|
|
*/
|
|
public async requestCertificate(domain: string): Promise<boolean> {
|
|
// Validate domain format
|
|
if (!this.isValidDomain(domain)) {
|
|
console.log(`Invalid domain format: ${domain}`);
|
|
return false;
|
|
}
|
|
|
|
// Use Port80Handler if available
|
|
if (this.port80Handler) {
|
|
try {
|
|
// Check if we already have a certificate
|
|
const cert = this.port80Handler.getCertificate(domain);
|
|
if (cert) {
|
|
console.log(`Certificate already exists for ${domain}, valid until ${cert.expiryDate.toISOString()}`);
|
|
return true;
|
|
}
|
|
|
|
// Register domain for certificate issuance
|
|
this.port80Handler.addDomain({
|
|
domainName: domain,
|
|
sslRedirect: true,
|
|
acmeMaintenance: true
|
|
});
|
|
|
|
console.log(`Domain ${domain} registered for certificate issuance`);
|
|
return true;
|
|
} catch (err) {
|
|
console.log(`Error registering domain with Port80Handler: ${err}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Fall back to NetworkProxyBridge
|
|
return this.networkProxyBridge.requestCertificate(domain);
|
|
}
|
|
|
|
/**
|
|
* Validates if a domain name is valid for certificate issuance
|
|
*/
|
|
private isValidDomain(domain: string): boolean {
|
|
// Very basic domain validation
|
|
if (!domain || domain.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
// Check for wildcard domains (they can't get ACME certs)
|
|
if (domain.includes('*')) {
|
|
console.log(`Wildcard domains like "${domain}" are not supported for ACME certificates`);
|
|
return false;
|
|
}
|
|
|
|
// Check if domain has at least one dot and no invalid characters
|
|
const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
if (!validDomainRegex.test(domain)) {
|
|
console.log(`Domain "${domain}" has invalid format`);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get statistics about current connections
|
|
*/
|
|
public getStatistics(): any {
|
|
const connectionRecords = this.connectionManager.getConnections();
|
|
const terminationStats = this.connectionManager.getTerminationStats();
|
|
|
|
let tlsConnections = 0;
|
|
let nonTlsConnections = 0;
|
|
let keepAliveConnections = 0;
|
|
let networkProxyConnections = 0;
|
|
|
|
// Analyze active connections
|
|
for (const record of connectionRecords.values()) {
|
|
if (record.isTLS) tlsConnections++;
|
|
else nonTlsConnections++;
|
|
if (record.hasKeepAlive) keepAliveConnections++;
|
|
if (record.usingNetworkProxy) networkProxyConnections++;
|
|
}
|
|
|
|
return {
|
|
activeConnections: connectionRecords.size,
|
|
tlsConnections,
|
|
nonTlsConnections,
|
|
keepAliveConnections,
|
|
networkProxyConnections,
|
|
terminationStats,
|
|
acmeEnabled: !!this.port80Handler,
|
|
port80HandlerPort: this.port80Handler ? this.settings.port80HandlerConfig?.port : null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get a list of eligible domains for ACME certificates
|
|
*/
|
|
public getEligibleDomainsForCertificates(): string[] {
|
|
// Collect all non-wildcard domains from domain configs
|
|
const domains: string[] = [];
|
|
|
|
for (const config of this.settings.domainConfigs) {
|
|
// Skip domains that can't be used with ACME
|
|
const eligibleDomains = config.domains.filter(domain =>
|
|
!domain.includes('*') && this.isValidDomain(domain)
|
|
);
|
|
|
|
domains.push(...eligibleDomains);
|
|
}
|
|
|
|
return domains;
|
|
}
|
|
|
|
/**
|
|
* Get status of certificates managed by Port80Handler
|
|
*/
|
|
public getCertificateStatus(): any {
|
|
if (!this.port80Handler) {
|
|
return {
|
|
enabled: false,
|
|
message: 'Port80Handler is not enabled'
|
|
};
|
|
}
|
|
|
|
// Get eligible domains
|
|
const eligibleDomains = this.getEligibleDomainsForCertificates();
|
|
const certificateStatus: Record<string, any> = {};
|
|
|
|
// Check each domain
|
|
for (const domain of eligibleDomains) {
|
|
const cert = this.port80Handler.getCertificate(domain);
|
|
|
|
if (cert) {
|
|
const now = new Date();
|
|
const expiryDate = cert.expiryDate;
|
|
const daysRemaining = Math.floor((expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000));
|
|
|
|
certificateStatus[domain] = {
|
|
status: 'valid',
|
|
expiryDate: expiryDate.toISOString(),
|
|
daysRemaining,
|
|
renewalNeeded: daysRemaining <= this.settings.port80HandlerConfig.renewThresholdDays
|
|
};
|
|
} else {
|
|
certificateStatus[domain] = {
|
|
status: 'missing',
|
|
message: 'No certificate found'
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
enabled: true,
|
|
port: this.settings.port80HandlerConfig.port,
|
|
useProduction: this.settings.port80HandlerConfig.useProduction,
|
|
autoRenew: this.settings.port80HandlerConfig.autoRenew,
|
|
certificates: certificateStatus
|
|
};
|
|
}
|
|
} |