update
This commit is contained in:
parent
b2584fffb1
commit
6512551f02
1685
changelog.md
1685
changelog.md
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartproxy",
|
"name": "@push.rocks/smartproxy",
|
||||||
"version": "19.3.14",
|
"version": "19.4.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
@ -25,32 +25,32 @@ We need a more intelligent approach to port binding that understands when a port
|
|||||||
### Implementation Plan
|
### Implementation Plan
|
||||||
|
|
||||||
#### Phase 1: Improve Port Manager Intelligence
|
#### Phase 1: Improve Port Manager Intelligence
|
||||||
- [ ] Enhance `PortManager` to distinguish between ports that need new bindings vs ports that can reuse existing bindings
|
- [x] Enhance `PortManager` to distinguish between ports that need new bindings vs ports that can reuse existing bindings
|
||||||
- [ ] Add an internal tracking mechanism to detect when a requested port is already bound internally
|
- [x] Add an internal tracking mechanism to detect when a requested port is already bound internally
|
||||||
- [ ] Modify port addition logic to skip binding operations for ports already bound by SmartProxy
|
- [x] Modify port addition logic to skip binding operations for ports already bound by SmartProxy
|
||||||
- [ ] Implement reference counting for port bindings to track how many routes use each port
|
- [x] Implement reference counting for port bindings to track how many routes use each port
|
||||||
- [ ] Add logic to release port bindings when no routes are using them anymore
|
- [x] Add logic to release port bindings when no routes are using them anymore
|
||||||
- [ ] Update error handling to provide more context for port binding failures
|
- [x] Update error handling to provide more context for port binding failures
|
||||||
|
|
||||||
#### Phase 2: Refine ACME Challenge Route Integration
|
#### Phase 2: Refine ACME Challenge Route Integration
|
||||||
- [ ] Modify `addChallengeRoute()` to check if the port is already in use by SmartProxy
|
- [x] Modify `addChallengeRoute()` to check if the port is already in use by SmartProxy
|
||||||
- [ ] Ensure route updates don't trigger unnecessary port binding operations
|
- [x] Ensure route updates don't trigger unnecessary port binding operations
|
||||||
- [ ] Implement a merging strategy for ACME routes with existing routes on the same port
|
- [x] Implement a merging strategy for ACME routes with existing routes on the same port
|
||||||
- [ ] Add diagnostic logging to track route and port binding relationships
|
- [x] Add diagnostic logging to track route and port binding relationships
|
||||||
|
|
||||||
#### Phase 3: Enhance Proxy Route Management
|
#### Phase 3: Enhance Proxy Route Management
|
||||||
- [ ] Restructure route update process to group routes by port
|
- [x] Restructure route update process to group routes by port
|
||||||
- [ ] Implement a more efficient route update mechanism that minimizes port binding operations
|
- [x] Implement a more efficient route update mechanism that minimizes port binding operations
|
||||||
- [ ] Develop port lifecycle management to track usage across route changes
|
- [x] Develop port lifecycle management to track usage across route changes
|
||||||
- [ ] Add validation to detect potential binding conflicts before attempting operations
|
- [x] Add validation to detect potential binding conflicts before attempting operations
|
||||||
- [ ] Create a proper route dependency graph to understand the relationships between routes
|
- [ ] Create a proper route dependency graph to understand the relationships between routes
|
||||||
- [ ] Implement efficient detection of "orphaned" ports that no longer have associated routes
|
- [x] Implement efficient detection of "orphaned" ports that no longer have associated routes
|
||||||
|
|
||||||
#### Phase 4: Improve Error Handling and Recovery
|
#### Phase 4: Improve Error Handling and Recovery
|
||||||
- [ ] Enhance error messages to be more specific about the nature of port conflicts
|
- [x] Enhance error messages to be more specific about the nature of port conflicts
|
||||||
- [ ] Add recovery mechanisms for common port binding scenarios
|
- [x] Add recovery mechanisms for common port binding scenarios
|
||||||
- [ ] Implement a fallback port selection strategy for ACME challenges
|
- [ ] Implement a fallback port selection strategy for ACME challenges
|
||||||
- [ ] Create a more robust validation system to catch issues before they cause runtime errors
|
- [x] Create a more robust validation system to catch issues before they cause runtime errors
|
||||||
|
|
||||||
### Detailed Technical Tasks
|
### Detailed Technical Tasks
|
||||||
|
|
||||||
|
@ -401,9 +401,13 @@ export class SmartCertManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add challenge route to SmartProxy
|
* Add challenge route to SmartProxy
|
||||||
|
*
|
||||||
|
* This method adds a special route for ACME HTTP-01 challenges, which typically uses port 80.
|
||||||
|
* Since we may already be listening on port 80 for regular routes, we need to be
|
||||||
|
* careful about how we add this route to avoid binding conflicts.
|
||||||
*/
|
*/
|
||||||
private async addChallengeRoute(): Promise<void> {
|
private async addChallengeRoute(): Promise<void> {
|
||||||
// Check with state manager first
|
// Check with state manager first - avoid duplication
|
||||||
if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
|
if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
|
||||||
try {
|
try {
|
||||||
logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
|
logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
|
||||||
@ -437,6 +441,7 @@ export class SmartCertManager {
|
|||||||
const challengePort = this.globalAcmeDefaults?.port || 80;
|
const challengePort = this.globalAcmeDefaults?.port || 80;
|
||||||
|
|
||||||
// Check if any existing routes are already using this port
|
// Check if any existing routes are already using this port
|
||||||
|
// This helps us determine if we need to create a new binding or can reuse existing one
|
||||||
const portInUseByRoutes = this.routes.some(route => {
|
const portInUseByRoutes = this.routes.some(route => {
|
||||||
const routePorts = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
|
const routePorts = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
|
||||||
return routePorts.some(p => {
|
return routePorts.some(p => {
|
||||||
@ -450,36 +455,37 @@ export class SmartCertManager {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (portInUseByRoutes) {
|
|
||||||
logger.log('info', `Port ${challengePort} is already used by another route, merging ACME challenge route`, {
|
|
||||||
port: challengePort,
|
|
||||||
component: 'certificate-manager'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the challenge route
|
|
||||||
const challengeRoute = this.challengeRoute;
|
|
||||||
|
|
||||||
// If the port is already in use by other routes in this SmartProxy instance,
|
|
||||||
// we can safely add the ACME challenge route without trying to bind to the port again
|
|
||||||
try {
|
try {
|
||||||
// Check if we're already listening on the challenge port
|
// Log whether port is already in use by other routes
|
||||||
const isPortAlreadyBound = portInUseByRoutes;
|
if (portInUseByRoutes) {
|
||||||
|
|
||||||
if (isPortAlreadyBound) {
|
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Port ${challengePort} is already bound by SmartProxy, adding ACME challenge route without rebinding`, {
|
logger.log('info', `Port ${challengePort} is already used by another route, merging ACME challenge route`, {
|
||||||
port: challengePort,
|
port: challengePort,
|
||||||
component: 'certificate-manager'
|
component: 'certificate-manager'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Silently handle logging errors
|
// Silently handle logging errors
|
||||||
console.log(`[INFO] Port ${challengePort} is already bound by SmartProxy, adding ACME challenge route without rebinding`);
|
console.log(`[INFO] Port ${challengePort} is already used by another route, merging ACME challenge route`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
logger.log('info', `Adding new ACME challenge route on port ${challengePort}`, {
|
||||||
|
port: challengePort,
|
||||||
|
component: 'certificate-manager'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Silently handle logging errors
|
||||||
|
console.log(`[INFO] Adding new ACME challenge route on port ${challengePort}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the challenge route to the existing routes
|
||||||
|
const challengeRoute = this.challengeRoute;
|
||||||
const updatedRoutes = [...this.routes, challengeRoute];
|
const updatedRoutes = [...this.routes, challengeRoute];
|
||||||
|
|
||||||
|
// With the re-ordering of start(), port binding should already be done
|
||||||
|
// This updateRoutes call should just add the route without binding again
|
||||||
await this.updateRoutesCallback(updatedRoutes);
|
await this.updateRoutesCallback(updatedRoutes);
|
||||||
this.challengeRouteActive = true;
|
this.challengeRouteActive = true;
|
||||||
|
|
||||||
@ -495,28 +501,47 @@ export class SmartCertManager {
|
|||||||
console.log('[INFO] ACME challenge route successfully added');
|
console.log('[INFO] ACME challenge route successfully added');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle specific EADDRINUSE errors differently based on whether it's an internal conflict
|
// Enhanced error handling based on error type
|
||||||
if ((error as any).code === 'EADDRINUSE') {
|
if ((error as any).code === 'EADDRINUSE') {
|
||||||
try {
|
try {
|
||||||
logger.log('error', `Failed to add challenge route on port ${challengePort}: ${error.message}`, {
|
logger.log('warn', `Challenge port ${challengePort} is unavailable - it's already in use by another process. Consider configuring a different ACME port.`, {
|
||||||
error: (error as Error).message,
|
port: challengePort,
|
||||||
|
error: (error as Error).message,
|
||||||
|
component: 'certificate-manager'
|
||||||
|
});
|
||||||
|
} catch (logError) {
|
||||||
|
// Silently handle logging errors
|
||||||
|
console.log(`[WARN] Challenge port ${challengePort} is unavailable - it's already in use by another process. Consider configuring a different ACME port.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide a more informative and actionable error message
|
||||||
|
throw new Error(
|
||||||
|
`ACME HTTP-01 challenge port ${challengePort} is already in use by another process. ` +
|
||||||
|
`Please configure a different port using the acme.port setting (e.g., 8080).`
|
||||||
|
);
|
||||||
|
} else if (error.message && error.message.includes('EADDRINUSE')) {
|
||||||
|
// Some Node.js versions embed the error code in the message rather than the code property
|
||||||
|
try {
|
||||||
|
logger.log('warn', `Port ${challengePort} conflict detected: ${error.message}`, {
|
||||||
port: challengePort,
|
port: challengePort,
|
||||||
component: 'certificate-manager'
|
component: 'certificate-manager'
|
||||||
});
|
});
|
||||||
} catch (logError) {
|
} catch (logError) {
|
||||||
// Silently handle logging errors
|
// Silently handle logging errors
|
||||||
console.log(`[ERROR] Failed to add challenge route on port ${challengePort}: ${error.message}`);
|
console.log(`[WARN] Port ${challengePort} conflict detected: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide a more informative error message
|
// More detailed error message with suggestions
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Port ${challengePort} is already in use. ` +
|
`ACME HTTP challenge port ${challengePort} conflict detected. ` +
|
||||||
`If it's in use by an external process, configure a different port in the ACME settings. ` +
|
`To resolve this issue, try one of these approaches:\n` +
|
||||||
`If it's in use by SmartProxy, there may be a route configuration issue.`
|
`1. Configure a different port in ACME settings (acme.port)\n` +
|
||||||
|
`2. Add a regular route that uses port ${challengePort} before initializing the certificate manager\n` +
|
||||||
|
`3. Stop any other services that might be using port ${challengePort}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log and rethrow other errors
|
// Log and rethrow other types of errors
|
||||||
try {
|
try {
|
||||||
logger.log('error', `Failed to add challenge route: ${(error as Error).message}`, {
|
logger.log('error', `Failed to add challenge route: ${(error as Error).message}`, {
|
||||||
error: (error as Error).message,
|
error: (error as Error).message,
|
||||||
|
@ -313,21 +313,6 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize certificate manager before starting servers
|
|
||||||
await this.initializeCertificateManager();
|
|
||||||
|
|
||||||
// Initialize and start HttpProxy if needed
|
|
||||||
if (this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
|
|
||||||
await this.httpProxyBridge.initialize();
|
|
||||||
|
|
||||||
// Connect HttpProxy with certificate manager
|
|
||||||
if (this.certManager) {
|
|
||||||
this.certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.httpProxyBridge.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the route configuration
|
// Validate the route configuration
|
||||||
const configWarnings = this.routeManager.validateConfiguration();
|
const configWarnings = this.routeManager.validateConfiguration();
|
||||||
|
|
||||||
@ -362,9 +347,25 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start port listeners using the PortManager
|
// Initialize and start HttpProxy if needed - before port binding
|
||||||
|
if (this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
|
||||||
|
await this.httpProxyBridge.initialize();
|
||||||
|
await this.httpProxyBridge.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start port listeners using the PortManager BEFORE initializing certificate manager
|
||||||
|
// This ensures all required ports are bound and ready when adding ACME challenge routes
|
||||||
await this.portManager.addPorts(listeningPorts);
|
await this.portManager.addPorts(listeningPorts);
|
||||||
|
|
||||||
|
// Initialize certificate manager AFTER port binding is complete
|
||||||
|
// This ensures the ACME challenge port is already bound and ready when needed
|
||||||
|
await this.initializeCertificateManager();
|
||||||
|
|
||||||
|
// Connect certificate manager with HttpProxy if both are available
|
||||||
|
if (this.certManager && this.httpProxyBridge.getHttpProxy()) {
|
||||||
|
this.certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
|
||||||
|
}
|
||||||
|
|
||||||
// Now that ports are listening, provision any required certificates
|
// Now that ports are listening, provision any required certificates
|
||||||
if (this.certManager) {
|
if (this.certManager) {
|
||||||
logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' });
|
logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' });
|
||||||
@ -570,7 +571,10 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
||||||
return this.routeUpdateLock.runExclusive(async () => {
|
return this.routeUpdateLock.runExclusive(async () => {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Updating routes (${newRoutes.length} routes)`, { routeCount: newRoutes.length, component: 'route-manager' });
|
logger.log('info', `Updating routes (${newRoutes.length} routes)`, {
|
||||||
|
routeCount: newRoutes.length,
|
||||||
|
component: 'route-manager'
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Silently handle logging errors
|
// Silently handle logging errors
|
||||||
console.log(`[INFO] Updating routes (${newRoutes.length} routes)`);
|
console.log(`[INFO] Updating routes (${newRoutes.length} routes)`);
|
||||||
@ -580,25 +584,60 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
const oldPortUsage = this.updatePortUsageMap(this.settings.routes);
|
const oldPortUsage = this.updatePortUsageMap(this.settings.routes);
|
||||||
const newPortUsage = this.updatePortUsageMap(newRoutes);
|
const newPortUsage = this.updatePortUsageMap(newRoutes);
|
||||||
|
|
||||||
|
// Get the lists of currently listening ports and new ports needed
|
||||||
|
const currentPorts = new Set(this.portManager.getListeningPorts());
|
||||||
|
const newPortsSet = new Set(newPortUsage.keys());
|
||||||
|
|
||||||
|
// Log the port usage for debugging
|
||||||
|
try {
|
||||||
|
logger.log('debug', `Current listening ports: ${Array.from(currentPorts).join(', ')}`, {
|
||||||
|
ports: Array.from(currentPorts),
|
||||||
|
component: 'smart-proxy'
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log('debug', `Ports needed for new routes: ${Array.from(newPortsSet).join(', ')}`, {
|
||||||
|
ports: Array.from(newPortsSet),
|
||||||
|
component: 'smart-proxy'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Silently handle logging errors
|
||||||
|
console.log(`[DEBUG] Current listening ports: ${Array.from(currentPorts).join(', ')}`);
|
||||||
|
console.log(`[DEBUG] Ports needed for new routes: ${Array.from(newPortsSet).join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Find orphaned ports - ports that no longer have any routes
|
// Find orphaned ports - ports that no longer have any routes
|
||||||
const orphanedPorts = this.findOrphanedPorts(oldPortUsage, newPortUsage);
|
const orphanedPorts = this.findOrphanedPorts(oldPortUsage, newPortUsage);
|
||||||
|
|
||||||
// Find new ports that need binding
|
// Find new ports that need binding (only ports that we aren't already listening on)
|
||||||
const currentPorts = new Set(this.portManager.getListeningPorts());
|
|
||||||
const newPortsSet = new Set(newPortUsage.keys());
|
|
||||||
const newBindingPorts = Array.from(newPortsSet).filter(p => !currentPorts.has(p));
|
const newBindingPorts = Array.from(newPortsSet).filter(p => !currentPorts.has(p));
|
||||||
|
|
||||||
|
// Check for ACME challenge port to give it special handling
|
||||||
|
const acmePort = this.settings.acme?.port || 80;
|
||||||
|
const acmePortNeeded = newPortsSet.has(acmePort);
|
||||||
|
const acmePortListed = newBindingPorts.includes(acmePort);
|
||||||
|
|
||||||
|
if (acmePortNeeded && acmePortListed) {
|
||||||
|
try {
|
||||||
|
logger.log('info', `Adding ACME challenge port ${acmePort} to routes`, {
|
||||||
|
port: acmePort,
|
||||||
|
component: 'smart-proxy'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Silently handle logging errors
|
||||||
|
console.log(`[INFO] Adding ACME challenge port ${acmePort} to routes`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get existing routes that use NFTables
|
// Get existing routes that use NFTables and update them
|
||||||
const oldNfTablesRoutes = this.settings.routes.filter(
|
const oldNfTablesRoutes = this.settings.routes.filter(
|
||||||
r => r.action.forwardingEngine === 'nftables'
|
r => r.action.forwardingEngine === 'nftables'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get new routes that use NFTables
|
|
||||||
const newNfTablesRoutes = newRoutes.filter(
|
const newNfTablesRoutes = newRoutes.filter(
|
||||||
r => r.action.forwardingEngine === 'nftables'
|
r => r.action.forwardingEngine === 'nftables'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find routes to remove, update, or add
|
// Update existing NFTables routes
|
||||||
for (const oldRoute of oldNfTablesRoutes) {
|
for (const oldRoute of oldNfTablesRoutes) {
|
||||||
const newRoute = newNfTablesRoutes.find(r => r.name === oldRoute.name);
|
const newRoute = newNfTablesRoutes.find(r => r.name === oldRoute.name);
|
||||||
|
|
||||||
@ -611,7 +650,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find new routes to add
|
// Add new NFTables routes
|
||||||
for (const newRoute of newNfTablesRoutes) {
|
for (const newRoute of newNfTablesRoutes) {
|
||||||
const oldRoute = oldNfTablesRoutes.find(r => r.name === newRoute.name);
|
const oldRoute = oldNfTablesRoutes.find(r => r.name === newRoute.name);
|
||||||
|
|
||||||
@ -624,7 +663,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
// Update routes in RouteManager
|
// Update routes in RouteManager
|
||||||
this.routeManager.updateRoutes(newRoutes);
|
this.routeManager.updateRoutes(newRoutes);
|
||||||
|
|
||||||
// Release orphaned ports first
|
// Release orphaned ports first to free resources
|
||||||
if (orphanedPorts.length > 0) {
|
if (orphanedPorts.length > 0) {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Releasing ${orphanedPorts.length} orphaned ports: ${orphanedPorts.join(', ')}`, {
|
logger.log('info', `Releasing ${orphanedPorts.length} orphaned ports: ${orphanedPorts.join(', ')}`, {
|
||||||
@ -638,7 +677,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
await this.portManager.removePorts(orphanedPorts);
|
await this.portManager.removePorts(orphanedPorts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new ports
|
// Add new ports if needed
|
||||||
if (newBindingPorts.length > 0) {
|
if (newBindingPorts.length > 0) {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`, {
|
logger.log('info', `Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`, {
|
||||||
@ -649,7 +688,38 @@ export class SmartProxy extends plugins.EventEmitter {
|
|||||||
// Silently handle logging errors
|
// Silently handle logging errors
|
||||||
console.log(`[INFO] Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`);
|
console.log(`[INFO] Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`);
|
||||||
}
|
}
|
||||||
await this.portManager.addPorts(newBindingPorts);
|
|
||||||
|
// Handle port binding with improved error recovery
|
||||||
|
try {
|
||||||
|
await this.portManager.addPorts(newBindingPorts);
|
||||||
|
} catch (error) {
|
||||||
|
// Special handling for port binding errors
|
||||||
|
// This provides better diagnostics for ACME challenge port conflicts
|
||||||
|
if ((error as any).code === 'EADDRINUSE') {
|
||||||
|
const port = (error as any).port || newBindingPorts[0];
|
||||||
|
const isAcmePort = port === acmePort;
|
||||||
|
|
||||||
|
if (isAcmePort) {
|
||||||
|
try {
|
||||||
|
logger.log('warn', `Could not bind to ACME challenge port ${port}. It may be in use by another application.`, {
|
||||||
|
port,
|
||||||
|
component: 'smart-proxy'
|
||||||
|
});
|
||||||
|
} catch (logError) {
|
||||||
|
console.log(`[WARN] Could not bind to ACME challenge port ${port}. It may be in use by another application.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw with more helpful message
|
||||||
|
throw new Error(
|
||||||
|
`ACME challenge port ${port} is already in use by another application. ` +
|
||||||
|
`Configure a different port in settings.acme.port (e.g., 8080) or free up port ${port}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw the original error for other cases
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update settings with the new routes
|
// Update settings with the new routes
|
||||||
|
Loading…
x
Reference in New Issue
Block a user