@push.rocks/smartproxy 19.3.12 → 19.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.plan.md ADDED
@@ -0,0 +1,384 @@
1
+ # SmartProxy Development Plan
2
+
3
+ ## ACME Route Port Binding Intelligence Improvement
4
+
5
+ ### Problem Statement
6
+ Currently, SmartProxy has an issue with port binding conflicts between regular routes and ACME challenge routes. While SmartProxy is designed to support multiple routes sharing the same port (differentiated by host, path, etc.), there's a specific conflict when adding ACME challenge routes to a port that is already in use by other routes.
7
+
8
+ This results in the error: `Port 80 is already in use for ACME challenges` when SmartProxy tries to bind the ACME challenge route to a port that it's already using.
9
+
10
+ ### Root Cause Analysis
11
+ 1. **Double Binding Attempt**: SmartProxy tries to bind to port 80 twice - once for application routes and once for ACME challenge routes.
12
+ 2. **Overlapping Route Updates**: When adding a challenge route, it triggers a port binding operation without checking if the port is already bound.
13
+ 3. **Naive Error Handling**: The code detects EADDRINUSE but doesn't distinguish between external conflicts and internal conflicts.
14
+ 4. **Port Binding Semantics**: The port manager doesn't recognize that a port already bound by SmartProxy can be reused for additional routes.
15
+
16
+ ### Solution Architecture
17
+ We need a more intelligent approach to port binding that understands when a port can be shared between routes vs. when a new binding is needed:
18
+
19
+ 1. **Port Binding Awareness**: Track what ports are already bound by SmartProxy itself.
20
+ 2. **Smart Route Updates**: Only attempt to bind to ports that aren't already bound by SmartProxy.
21
+ 3. **Route Merging Logic**: When adding ACME challenge routes, merge them with existing routes on the same ports.
22
+ 4. **Dynamic Port Management**: Release port bindings when no routes are using them and rebind when needed.
23
+ 5. **Improved Error Recovery**: Handle port conflicts gracefully, with distinct handling for internal vs. external conflicts.
24
+
25
+ ### Implementation Plan
26
+
27
+ #### Phase 1: Improve Port Manager Intelligence
28
+ - [ ] Enhance `PortManager` to distinguish between ports that need new bindings vs ports that can reuse existing bindings
29
+ - [ ] Add an internal tracking mechanism to detect when a requested port is already bound internally
30
+ - [ ] Modify port addition logic to skip binding operations for ports already bound by SmartProxy
31
+ - [ ] Implement reference counting for port bindings to track how many routes use each port
32
+ - [ ] Add logic to release port bindings when no routes are using them anymore
33
+ - [ ] Update error handling to provide more context for port binding failures
34
+
35
+ #### Phase 2: Refine ACME Challenge Route Integration
36
+ - [ ] Modify `addChallengeRoute()` to check if the port is already in use by SmartProxy
37
+ - [ ] Ensure route updates don't trigger unnecessary port binding operations
38
+ - [ ] Implement a merging strategy for ACME routes with existing routes on the same port
39
+ - [ ] Add diagnostic logging to track route and port binding relationships
40
+
41
+ #### Phase 3: Enhance Proxy Route Management
42
+ - [ ] Restructure route update process to group routes by port
43
+ - [ ] Implement a more efficient route update mechanism that minimizes port binding operations
44
+ - [ ] Develop port lifecycle management to track usage across route changes
45
+ - [ ] Add validation to detect potential binding conflicts before attempting operations
46
+ - [ ] Create a proper route dependency graph to understand the relationships between routes
47
+ - [ ] Implement efficient detection of "orphaned" ports that no longer have associated routes
48
+
49
+ #### Phase 4: Improve Error Handling and Recovery
50
+ - [ ] Enhance error messages to be more specific about the nature of port conflicts
51
+ - [ ] Add recovery mechanisms for common port binding scenarios
52
+ - [ ] Implement a fallback port selection strategy for ACME challenges
53
+ - [ ] Create a more robust validation system to catch issues before they cause runtime errors
54
+
55
+ ### Detailed Technical Tasks
56
+
57
+ #### Phase 1: Improve Port Manager Intelligence
58
+ 1. Modify `/ts/proxies/smart-proxy/port-manager.ts`:
59
+ - Add a new method `isPortBoundBySmartProxy(port: number): boolean`
60
+ - Refactor `addPort()` to check if the port is already bound
61
+ - Update `updatePorts()` to be more intelligent about which ports need binding
62
+ - Add reference counting for port usage
63
+
64
+ 2. Implement Port Reference Counting:
65
+ ```typescript
66
+ // Add to PortManager class
67
+ private portRefCounts: Map<number, number> = new Map();
68
+
69
+ public incrementPortRefCount(port: number): void {
70
+ const currentCount = this.portRefCounts.get(port) || 0;
71
+ this.portRefCounts.set(port, currentCount + 1);
72
+ logger.log('debug', `Port ${port} reference count increased to ${currentCount + 1}`, { port, refCount: currentCount + 1 });
73
+ }
74
+
75
+ public decrementPortRefCount(port: number): number {
76
+ const currentCount = this.portRefCounts.get(port) || 0;
77
+ if (currentCount <= 0) {
78
+ logger.log('warn', `Attempted to decrement reference count for port ${port} below zero`, { port });
79
+ return 0;
80
+ }
81
+
82
+ const newCount = currentCount - 1;
83
+ this.portRefCounts.set(port, newCount);
84
+ logger.log('debug', `Port ${port} reference count decreased to ${newCount}`, { port, refCount: newCount });
85
+ return newCount;
86
+ }
87
+
88
+ public getPortRefCount(port: number): number {
89
+ return this.portRefCounts.get(port) || 0;
90
+ }
91
+ ```
92
+
93
+ 3. Port Binding Logic Enhancements:
94
+ ```typescript
95
+ public async addPort(port: number): Promise<void> {
96
+ // If already bound by this instance, just increment ref count and return
97
+ if (this.servers.has(port)) {
98
+ this.incrementPortRefCount(port);
99
+ logger.log('debug', `Port ${port} is already bound by SmartProxy, reusing binding`, { port });
100
+ return;
101
+ }
102
+
103
+ // Initialize ref count for new port
104
+ this.portRefCounts.set(port, 1);
105
+
106
+ // Continue with normal binding...
107
+ }
108
+
109
+ public async removePort(port: number): Promise<void> {
110
+ // Decrement reference count
111
+ const newCount = this.decrementPortRefCount(port);
112
+
113
+ // If port is still in use by other routes, keep it
114
+ if (newCount > 0) {
115
+ logger.log('debug', `Port ${port} still in use by ${newCount} routes, keeping binding open`, { port, refCount: newCount });
116
+ return;
117
+ }
118
+
119
+ // No more references, can actually close the port
120
+ const server = this.servers.get(port);
121
+ if (!server) {
122
+ logger.log('warn', `Port ${port} not found in servers map`, { port });
123
+ return;
124
+ }
125
+
126
+ // Continue with normal unbinding logic...
127
+ }
128
+ ```
129
+
130
+ 4. Add Smarter Port Conflict Detection:
131
+ ```typescript
132
+ private isPortConflict(error: any): { isConflict: boolean; isExternal: boolean } {
133
+ if (error.code !== 'EADDRINUSE') {
134
+ return { isConflict: false, isExternal: false };
135
+ }
136
+
137
+ // Check if we already have this port
138
+ const isBoundInternally = this.servers.has(Number(error.port));
139
+ return { isConflict: true, isExternal: !isBoundInternally };
140
+ }
141
+ ```
142
+
143
+ #### Phase 2: Refine ACME Challenge Route Integration
144
+ 1. Modify `/ts/proxies/smart-proxy/certificate-manager.ts`:
145
+ - Enhance `addChallengeRoute()` to be aware of existing port bindings
146
+ - Add port verification before attempting to add challenge routes
147
+
148
+ 2. Smart Route Merging Logic:
149
+ ```typescript
150
+ private async addChallengeRoute(): Promise<void> {
151
+ // Check if route is already active
152
+ if (this.challengeRouteActive) {
153
+ return;
154
+ }
155
+
156
+ // Create challenge route
157
+ const challengeRoute = this.challengeRoute;
158
+ const challengePort = this.globalAcmeDefaults?.port || 80;
159
+
160
+ // Check if port is already in use by another route
161
+ const portAlreadyUsed = this.routes.some(r =>
162
+ Array.isArray(r.match.ports)
163
+ ? r.match.ports.includes(challengePort)
164
+ : r.match.ports === challengePort
165
+ );
166
+
167
+ if (portAlreadyUsed) {
168
+ logger.log('info', `Port ${challengePort} is already used by an existing route, merging ACME challenge route`);
169
+ }
170
+
171
+ // Continue with route update...
172
+ }
173
+ ```
174
+
175
+ 3. Update Route Manager Communication:
176
+ ```typescript
177
+ // Add this method to smart-proxy.ts
178
+ private async addRouteWithoutRebinding(route: IRouteConfig): Promise<void> {
179
+ // Add route to configuration without triggering a port rebind
180
+ this.settings.routes.push(route);
181
+ this.routeManager.updateRoutes(this.settings.routes);
182
+
183
+ // Update HttpProxy if needed, but skip port binding updates
184
+ if (this.httpProxyBridge.getHttpProxy()) {
185
+ await this.httpProxyBridge.syncRoutesToHttpProxy(this.settings.routes);
186
+ }
187
+ }
188
+ ```
189
+
190
+ #### Phase 3: Enhance Proxy Route Management
191
+ 1. Modify `/ts/proxies/smart-proxy/smart-proxy.ts`:
192
+ - Refactor `updateRoutes()` to group routes by port
193
+ - Implement incremental updates that preserve port bindings
194
+ - Add orphaned port detection and cleanup
195
+
196
+ 2. Group Routes by Port:
197
+ ```typescript
198
+ private groupRoutesByPort(routes: IRouteConfig[]): Map<number, IRouteConfig[]> {
199
+ const portMap = new Map<number, IRouteConfig[]>();
200
+
201
+ for (const route of routes) {
202
+ const ports = Array.isArray(route.match.ports)
203
+ ? route.match.ports
204
+ : [route.match.ports];
205
+
206
+ for (const port of ports) {
207
+ if (!portMap.has(port)) {
208
+ portMap.set(port, []);
209
+ }
210
+ portMap.get(port)!.push(route);
211
+ }
212
+ }
213
+
214
+ return portMap;
215
+ }
216
+ ```
217
+
218
+ 3. Implement Port Usage Tracking:
219
+ ```typescript
220
+ private updatePortUsageMap(routes: IRouteConfig[]): Map<number, Set<string>> {
221
+ // Map of port -> Set of route names using that port
222
+ const portUsage = new Map<number, Set<string>>();
223
+
224
+ for (const route of routes) {
225
+ const ports = Array.isArray(route.match.ports)
226
+ ? route.match.ports
227
+ : [route.match.ports];
228
+
229
+ const routeName = route.name || `unnamed_${Math.random().toString(36).substring(2, 9)}`;
230
+
231
+ for (const port of ports) {
232
+ if (!portUsage.has(port)) {
233
+ portUsage.set(port, new Set());
234
+ }
235
+ portUsage.get(port)!.add(routeName);
236
+ }
237
+ }
238
+
239
+ return portUsage;
240
+ }
241
+
242
+ private findOrphanedPorts(oldUsage: Map<number, Set<string>>, newUsage: Map<number, Set<string>>): number[] {
243
+ // Find ports that have no routes in new configuration
244
+ const orphanedPorts: number[] = [];
245
+
246
+ for (const [port, routes] of oldUsage.entries()) {
247
+ if (!newUsage.has(port) || newUsage.get(port)!.size === 0) {
248
+ orphanedPorts.push(port);
249
+ logger.log('info', `Port ${port} no longer has any associated routes, will be released`, { port });
250
+ }
251
+ }
252
+
253
+ return orphanedPorts;
254
+ }
255
+ ```
256
+
257
+ 4. Implement Incremental Update Logic:
258
+ ```typescript
259
+ public async updateRoutesIncremental(newRoutes: IRouteConfig[]): Promise<void> {
260
+ // Track port usage before and after update
261
+ const oldPortUsage = this.updatePortUsageMap(this.settings.routes);
262
+ const newPortUsage = this.updatePortUsageMap(newRoutes);
263
+
264
+ // Find orphaned ports - ports that no longer have any routes
265
+ const orphanedPorts = this.findOrphanedPorts(oldPortUsage, newPortUsage);
266
+
267
+ // Ports that need new bindings - not in old configuration
268
+ const newBindingPorts = [...newPortUsage.keys()].filter(p => !oldPortUsage.has(p));
269
+
270
+ // Close orphaned ports
271
+ if (orphanedPorts.length > 0) {
272
+ logger.log('info', `Releasing ${orphanedPorts.length} orphaned ports: ${orphanedPorts.join(', ')}`, { ports: orphanedPorts });
273
+ await this.portManager.removePorts(orphanedPorts);
274
+ }
275
+
276
+ // Bind to new ports
277
+ if (newBindingPorts.length > 0) {
278
+ logger.log('info', `Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`, { ports: newBindingPorts });
279
+ await this.portManager.addPorts(newBindingPorts);
280
+ }
281
+
282
+ // Update route configuration
283
+ this.settings.routes = newRoutes;
284
+ this.routeManager.updateRoutes(newRoutes);
285
+
286
+ // Update other components...
287
+ }
288
+ ```
289
+
290
+ #### Phase 4: Improve Error Handling and Recovery
291
+ 1. Enhance Error Reporting:
292
+ ```typescript
293
+ private handlePortBindingError(port: number, error: any): void {
294
+ if (error.code === 'EADDRINUSE') {
295
+ const isInternalConflict = this.portManager.isPortBoundBySmartProxy(port);
296
+ if (isInternalConflict) {
297
+ logger.log('warn', `Port ${port} is already bound by SmartProxy. This is likely a route configuration issue.`, { port });
298
+ } else {
299
+ logger.log('error', `Port ${port} is in use by another application. Please choose a different port.`, { port });
300
+ }
301
+ } else {
302
+ logger.log('error', `Failed to bind to port ${port}: ${error.message}`, { port, error });
303
+ }
304
+ }
305
+ ```
306
+
307
+ 2. Implement ACME Port Fallback Strategy:
308
+ ```typescript
309
+ private async selectAcmePort(): Promise<number> {
310
+ const preferredPort = this.globalAcmeDefaults?.port || 80;
311
+
312
+ // Check if preferred port is already bound internally
313
+ if (this.portManager.isPortBoundBySmartProxy(preferredPort)) {
314
+ // We can use it without a new binding
315
+ return preferredPort;
316
+ }
317
+
318
+ // Try to bind to preferred port
319
+ try {
320
+ // Temporary test binding
321
+ const server = plugins.net.createServer();
322
+ await new Promise<void>((resolve, reject) => {
323
+ server.listen(preferredPort, () => {
324
+ server.close();
325
+ resolve();
326
+ }).on('error', reject);
327
+ });
328
+
329
+ // If we get here, port is available
330
+ return preferredPort;
331
+ } catch (error) {
332
+ if (error.code === 'EADDRINUSE') {
333
+ // Port is unavailable, try fallback ports
334
+ for (const fallbackPort of [8080, 8081, 8082, 8083, 8084]) {
335
+ try {
336
+ // Test if we can bind to fallback
337
+ const server = plugins.net.createServer();
338
+ await new Promise<void>((resolve, reject) => {
339
+ server.listen(fallbackPort, () => {
340
+ server.close();
341
+ resolve();
342
+ }).on('error', reject);
343
+ });
344
+
345
+ logger.log('warn', `Primary ACME port ${preferredPort} is unavailable, using fallback port ${fallbackPort}`);
346
+ return fallbackPort;
347
+ } catch {
348
+ // Try next fallback
349
+ }
350
+ }
351
+ }
352
+
353
+ // All attempts failed
354
+ throw new Error(`Could not find an available port for ACME challenges`);
355
+ }
356
+ }
357
+ ```
358
+
359
+ ### Testing Strategy
360
+ 1. **Unit Tests**:
361
+ - Test port binding intelligence
362
+ - Test route merging logic
363
+ - Test error handling mechanisms
364
+ - Test port reference counting
365
+ - Test orphaned port detection and cleanup
366
+
367
+ 2. **Integration Tests**:
368
+ - Test multiple routes on the same port
369
+ - Test ACME challenges on ports with existing routes
370
+ - Test dynamic route addition and removal
371
+ - Test port lifecycle (bind → share → release)
372
+ - Test various recovery scenarios
373
+
374
+ 3. **Stress Tests**:
375
+ - Test rapid route updates
376
+ - Test concurrent operations
377
+ - Test large scale route changes (add/remove many at once)
378
+ - Test frequent changes to see if ports are properly released
379
+ - Test recovery from port conflicts
380
+
381
+ ### Release Plan
382
+ 1. **19.4.0** - Phase 1 & 2: Port Manager and ACME Route Improvements
383
+ 2. **19.5.0** - Phase 3: Enhanced Route Management
384
+ 3. **19.6.0** - Phase 4: Improved Error Handling and Recovery
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '19.3.12',
6
+ version: '19.3.13',
7
7
  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.'
8
8
  }
@@ -93,6 +93,12 @@ export class SmartCertManager {
93
93
  */
94
94
  public setUpdateRoutesCallback(callback: (routes: IRouteConfig[]) => Promise<void>): void {
95
95
  this.updateRoutesCallback = callback;
96
+ try {
97
+ logger.log('debug', 'Route update callback set successfully', { component: 'certificate-manager' });
98
+ } catch (error) {
99
+ // Silently handle logging errors
100
+ console.log('[DEBUG] Route update callback set successfully');
101
+ }
96
102
  }
97
103
 
98
104
  /**
@@ -399,13 +405,23 @@ export class SmartCertManager {
399
405
  private async addChallengeRoute(): Promise<void> {
400
406
  // Check with state manager first
401
407
  if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
402
- logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
408
+ try {
409
+ logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
410
+ } catch (error) {
411
+ // Silently handle logging errors
412
+ console.log('[INFO] Challenge route already active in global state, skipping');
413
+ }
403
414
  this.challengeRouteActive = true;
404
415
  return;
405
416
  }
406
417
 
407
418
  if (this.challengeRouteActive) {
408
- logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' });
419
+ try {
420
+ logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' });
421
+ } catch (error) {
422
+ // Silently handle logging errors
423
+ console.log('[INFO] Challenge route already active locally, skipping');
424
+ }
409
425
  return;
410
426
  }
411
427
 
@@ -416,9 +432,53 @@ export class SmartCertManager {
416
432
  if (!this.challengeRoute) {
417
433
  throw new Error('Challenge route not initialized');
418
434
  }
435
+
436
+ // Get the challenge port
437
+ const challengePort = this.globalAcmeDefaults?.port || 80;
438
+
439
+ // Check if any existing routes are already using this port
440
+ const portInUseByRoutes = this.routes.some(route => {
441
+ const routePorts = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
442
+ return routePorts.some(p => {
443
+ // Handle both number and port range objects
444
+ if (typeof p === 'number') {
445
+ return p === challengePort;
446
+ } else if (typeof p === 'object' && 'from' in p && 'to' in p) {
447
+ // Port range case - check if challengePort is in range
448
+ return challengePort >= p.from && challengePort <= p.to;
449
+ }
450
+ return false;
451
+ });
452
+ });
453
+
454
+ if (portInUseByRoutes) {
455
+ logger.log('info', `Port ${challengePort} is already used by another route, merging ACME challenge route`, {
456
+ port: challengePort,
457
+ component: 'certificate-manager'
458
+ });
459
+ }
460
+
461
+ // Add the challenge route
419
462
  const challengeRoute = this.challengeRoute;
420
463
 
464
+ // If the port is already in use by other routes in this SmartProxy instance,
465
+ // we can safely add the ACME challenge route without trying to bind to the port again
421
466
  try {
467
+ // Check if we're already listening on the challenge port
468
+ const isPortAlreadyBound = portInUseByRoutes;
469
+
470
+ if (isPortAlreadyBound) {
471
+ try {
472
+ logger.log('info', `Port ${challengePort} is already bound by SmartProxy, adding ACME challenge route without rebinding`, {
473
+ port: challengePort,
474
+ component: 'certificate-manager'
475
+ });
476
+ } catch (error) {
477
+ // Silently handle logging errors
478
+ console.log(`[INFO] Port ${challengePort} is already bound by SmartProxy, adding ACME challenge route without rebinding`);
479
+ }
480
+ }
481
+
422
482
  const updatedRoutes = [...this.routes, challengeRoute];
423
483
  await this.updateRoutesCallback(updatedRoutes);
424
484
  this.challengeRouteActive = true;
@@ -428,11 +488,43 @@ export class SmartCertManager {
428
488
  this.acmeStateManager.addChallengeRoute(challengeRoute);
429
489
  }
430
490
 
431
- logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' });
491
+ try {
492
+ logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' });
493
+ } catch (error) {
494
+ // Silently handle logging errors
495
+ console.log('[INFO] ACME challenge route successfully added');
496
+ }
432
497
  } catch (error) {
433
- logger.log('error', `Failed to add challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
498
+ // Handle specific EADDRINUSE errors differently based on whether it's an internal conflict
434
499
  if ((error as any).code === 'EADDRINUSE') {
435
- throw new Error(`Port ${this.globalAcmeDefaults?.port || 80} is already in use for ACME challenges`);
500
+ try {
501
+ logger.log('error', `Failed to add challenge route on port ${challengePort}: ${error.message}`, {
502
+ error: (error as Error).message,
503
+ port: challengePort,
504
+ component: 'certificate-manager'
505
+ });
506
+ } catch (logError) {
507
+ // Silently handle logging errors
508
+ console.log(`[ERROR] Failed to add challenge route on port ${challengePort}: ${error.message}`);
509
+ }
510
+
511
+ // Provide a more informative error message
512
+ throw new Error(
513
+ `Port ${challengePort} is already in use. ` +
514
+ `If it's in use by an external process, configure a different port in the ACME settings. ` +
515
+ `If it's in use by SmartProxy, there may be a route configuration issue.`
516
+ );
517
+ }
518
+
519
+ // Log and rethrow other errors
520
+ try {
521
+ logger.log('error', `Failed to add challenge route: ${(error as Error).message}`, {
522
+ error: (error as Error).message,
523
+ component: 'certificate-manager'
524
+ });
525
+ } catch (logError) {
526
+ // Silently handle logging errors
527
+ console.log(`[ERROR] Failed to add challenge route: ${(error as Error).message}`);
436
528
  }
437
529
  throw error;
438
530
  }
@@ -443,7 +535,12 @@ export class SmartCertManager {
443
535
  */
444
536
  private async removeChallengeRoute(): Promise<void> {
445
537
  if (!this.challengeRouteActive) {
446
- logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' });
538
+ try {
539
+ logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' });
540
+ } catch (error) {
541
+ // Silently handle logging errors
542
+ console.log('[INFO] Challenge route not active, skipping removal');
543
+ }
447
544
  return;
448
545
  }
449
546
 
@@ -461,9 +558,19 @@ export class SmartCertManager {
461
558
  this.acmeStateManager.removeChallengeRoute('acme-challenge');
462
559
  }
463
560
 
464
- logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' });
561
+ try {
562
+ logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' });
563
+ } catch (error) {
564
+ // Silently handle logging errors
565
+ console.log('[INFO] ACME challenge route successfully removed');
566
+ }
465
567
  } catch (error) {
466
- logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
568
+ try {
569
+ logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
570
+ } catch (logError) {
571
+ // Silently handle logging errors
572
+ console.log(`[ERROR] Failed to remove challenge route: ${error.message}`);
573
+ }
467
574
  // Reset the flag even on error to avoid getting stuck
468
575
  this.challengeRouteActive = false;
469
576
  throw error;