@serve.zone/dcrouter 11.8.11 → 11.9.0
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/dist_serve/bundle.js +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.dcrouter.d.ts +7 -0
- package/dist_ts/classes.dcrouter.js +236 -119
- package/dist_ts/opsserver/handlers/stats.handler.js +31 -28
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/package.json +14 -21
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +278 -148
- package/ts/opsserver/handlers/stats.handler.ts +34 -37
- package/ts/plugins.ts +2 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -252,6 +252,10 @@ export class DcRouter {
|
|
|
252
252
|
// Certificate provisioning scheduler with per-domain backoff
|
|
253
253
|
public certProvisionScheduler?: CertProvisionScheduler;
|
|
254
254
|
|
|
255
|
+
// Service lifecycle management
|
|
256
|
+
public serviceManager: plugins.taskbuffer.ServiceManager;
|
|
257
|
+
public smartAcmeReady = false;
|
|
258
|
+
|
|
255
259
|
// TypedRouter for API endpoints
|
|
256
260
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
257
261
|
|
|
@@ -279,67 +283,253 @@ export class DcRouter {
|
|
|
279
283
|
|
|
280
284
|
// Initialize storage manager
|
|
281
285
|
this.storageManager = new StorageManager(this.options.storage);
|
|
286
|
+
|
|
287
|
+
// Initialize service manager and register all services
|
|
288
|
+
this.serviceManager = new plugins.taskbuffer.ServiceManager({
|
|
289
|
+
name: 'dcrouter',
|
|
290
|
+
startupTimeoutMs: 120_000,
|
|
291
|
+
shutdownTimeoutMs: 30_000,
|
|
292
|
+
});
|
|
293
|
+
this.registerServices();
|
|
282
294
|
}
|
|
283
295
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Register all dcrouter services with the ServiceManager.
|
|
298
|
+
* Services are started in dependency order, with failure isolation for optional services.
|
|
299
|
+
*/
|
|
300
|
+
private registerServices(): void {
|
|
301
|
+
// OpsServer: critical, no dependencies — provides visibility
|
|
302
|
+
this.serviceManager.addService(
|
|
303
|
+
new plugins.taskbuffer.Service('OpsServer')
|
|
304
|
+
.critical()
|
|
305
|
+
.withStart(async () => {
|
|
306
|
+
this.opsServer = new OpsServer(this);
|
|
307
|
+
await this.opsServer.start();
|
|
308
|
+
})
|
|
309
|
+
.withStop(async () => {
|
|
310
|
+
await this.opsServer?.stop();
|
|
311
|
+
})
|
|
312
|
+
.withRetry({ maxRetries: 0 }),
|
|
313
|
+
);
|
|
287
314
|
|
|
288
|
-
|
|
289
|
-
|
|
315
|
+
// CacheDb: optional, no dependencies
|
|
316
|
+
if (this.options.cacheConfig?.enabled !== false) {
|
|
317
|
+
this.serviceManager.addService(
|
|
318
|
+
new plugins.taskbuffer.Service('CacheDb')
|
|
319
|
+
.optional()
|
|
320
|
+
.withStart(async () => {
|
|
321
|
+
await this.setupCacheDb();
|
|
322
|
+
})
|
|
323
|
+
.withStop(async () => {
|
|
324
|
+
if (this.cacheCleaner) {
|
|
325
|
+
this.cacheCleaner.stop();
|
|
326
|
+
this.cacheCleaner = undefined;
|
|
327
|
+
}
|
|
328
|
+
if (this.cacheDb) {
|
|
329
|
+
await this.cacheDb.stop();
|
|
330
|
+
CacheDb.resetInstance();
|
|
331
|
+
this.cacheDb = undefined;
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
.withRetry({ maxRetries: 2, baseDelayMs: 1000, maxDelayMs: 5000 }),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
290
337
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
338
|
+
// MetricsManager: optional, depends on OpsServer
|
|
339
|
+
this.serviceManager.addService(
|
|
340
|
+
new plugins.taskbuffer.Service('MetricsManager')
|
|
341
|
+
.optional()
|
|
342
|
+
.dependsOn('OpsServer')
|
|
343
|
+
.withStart(async () => {
|
|
344
|
+
this.metricsManager = new MetricsManager(this);
|
|
345
|
+
await this.metricsManager.start();
|
|
346
|
+
})
|
|
347
|
+
.withStop(async () => {
|
|
348
|
+
if (this.metricsManager) {
|
|
349
|
+
await this.metricsManager.stop();
|
|
350
|
+
this.metricsManager = undefined;
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
.withRetry({ maxRetries: 1, baseDelayMs: 1000 }),
|
|
354
|
+
);
|
|
296
355
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
() =>
|
|
356
|
+
// SmartProxy: critical, depends on CacheDb (if enabled)
|
|
357
|
+
const smartProxyDeps: string[] = [];
|
|
358
|
+
if (this.options.cacheConfig?.enabled !== false) {
|
|
359
|
+
smartProxyDeps.push('CacheDb');
|
|
360
|
+
}
|
|
361
|
+
this.serviceManager.addService(
|
|
362
|
+
new plugins.taskbuffer.Service('SmartProxy')
|
|
363
|
+
.critical()
|
|
364
|
+
.dependsOn(...smartProxyDeps)
|
|
365
|
+
.withStart(async () => {
|
|
366
|
+
await this.setupSmartProxy();
|
|
367
|
+
})
|
|
368
|
+
.withStop(async () => {
|
|
369
|
+
if (this.smartProxy) {
|
|
370
|
+
this.smartProxy.removeAllListeners();
|
|
371
|
+
await this.smartProxy.stop();
|
|
372
|
+
this.smartProxy = undefined;
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
.withRetry({ maxRetries: 0 }),
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// SmartAcme: optional, depends on SmartProxy — aggressive retry for rate limits
|
|
379
|
+
// Only registered if DNS challenge is configured
|
|
380
|
+
if (this.options.dnsChallenge?.cloudflareApiKey) {
|
|
381
|
+
this.serviceManager.addService(
|
|
382
|
+
new plugins.taskbuffer.Service('SmartAcme')
|
|
383
|
+
.optional()
|
|
384
|
+
.dependsOn('SmartProxy')
|
|
385
|
+
.withStart(async () => {
|
|
386
|
+
if (this.smartAcme) {
|
|
387
|
+
await this.smartAcme.start();
|
|
388
|
+
this.smartAcmeReady = true;
|
|
389
|
+
logger.log('info', 'SmartAcme DNS-01 provider is now ready');
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
.withStop(async () => {
|
|
393
|
+
this.smartAcmeReady = false;
|
|
394
|
+
if (this.smartAcme) {
|
|
395
|
+
await this.smartAcme.stop();
|
|
396
|
+
this.smartAcme = undefined;
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
.withRetry({ maxRetries: 20, baseDelayMs: 5000, maxDelayMs: 3_600_000, backoffFactor: 2 }),
|
|
310
400
|
);
|
|
311
|
-
|
|
312
|
-
await this.apiTokenManager.initialize();
|
|
313
|
-
await this.routeConfigManager.initialize();
|
|
401
|
+
}
|
|
314
402
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
403
|
+
// ConfigManagers: optional, depends on SmartProxy
|
|
404
|
+
this.serviceManager.addService(
|
|
405
|
+
new plugins.taskbuffer.Service('ConfigManagers')
|
|
406
|
+
.optional()
|
|
407
|
+
.dependsOn('SmartProxy')
|
|
408
|
+
.withStart(async () => {
|
|
409
|
+
this.routeConfigManager = new RouteConfigManager(
|
|
410
|
+
this.storageManager,
|
|
411
|
+
() => this.getConstructorRoutes(),
|
|
412
|
+
() => this.smartProxy,
|
|
413
|
+
() => this.options.http3,
|
|
414
|
+
);
|
|
415
|
+
this.apiTokenManager = new ApiTokenManager(this.storageManager);
|
|
416
|
+
await this.apiTokenManager.initialize();
|
|
417
|
+
await this.routeConfigManager.initialize();
|
|
418
|
+
})
|
|
419
|
+
.withStop(async () => {
|
|
420
|
+
this.routeConfigManager = undefined;
|
|
421
|
+
this.apiTokenManager = undefined;
|
|
422
|
+
})
|
|
423
|
+
.withRetry({ maxRetries: 2, baseDelayMs: 1000 }),
|
|
424
|
+
);
|
|
325
425
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
426
|
+
// Email Server: optional, depends on SmartProxy
|
|
427
|
+
if (this.options.emailConfig) {
|
|
428
|
+
this.serviceManager.addService(
|
|
429
|
+
new plugins.taskbuffer.Service('EmailServer')
|
|
430
|
+
.optional()
|
|
431
|
+
.dependsOn('SmartProxy')
|
|
432
|
+
.withStart(async () => {
|
|
433
|
+
await this.setupUnifiedEmailHandling();
|
|
434
|
+
})
|
|
435
|
+
.withStop(async () => {
|
|
436
|
+
if (this.emailServer) {
|
|
437
|
+
if ((this.emailServer as any).deliverySystem) {
|
|
438
|
+
(this.emailServer as any).deliverySystem.removeAllListeners();
|
|
439
|
+
}
|
|
440
|
+
this.emailServer.removeAllListeners();
|
|
441
|
+
await this.emailServer.stop();
|
|
442
|
+
this.emailServer = undefined;
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
.withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
|
|
446
|
+
);
|
|
447
|
+
}
|
|
330
448
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
449
|
+
// DNS Server: optional, depends on SmartProxy
|
|
450
|
+
if (this.options.dnsNsDomains?.length > 0 && this.options.dnsScopes?.length > 0) {
|
|
451
|
+
this.serviceManager.addService(
|
|
452
|
+
new plugins.taskbuffer.Service('DnsServer')
|
|
453
|
+
.optional()
|
|
454
|
+
.dependsOn('SmartProxy')
|
|
455
|
+
.withStart(async () => {
|
|
456
|
+
await this.setupDnsWithSocketHandler();
|
|
457
|
+
})
|
|
458
|
+
.withStop(async () => {
|
|
459
|
+
// Flush pending DNS batch log
|
|
460
|
+
if (this.dnsBatchTimer) {
|
|
461
|
+
clearTimeout(this.dnsBatchTimer);
|
|
462
|
+
if (this.dnsBatchCount > 0) {
|
|
463
|
+
logger.log('info', `DNS: ${this.dnsBatchCount} queries processed (final flush)`, { zone: 'dns' });
|
|
464
|
+
}
|
|
465
|
+
this.dnsBatchTimer = null;
|
|
466
|
+
this.dnsBatchCount = 0;
|
|
467
|
+
this.dnsLogWindowSecond = 0;
|
|
468
|
+
this.dnsLogWindowCount = 0;
|
|
469
|
+
}
|
|
470
|
+
if (this.dnsServer) {
|
|
471
|
+
this.dnsServer.removeAllListeners();
|
|
472
|
+
await this.dnsServer.stop();
|
|
473
|
+
this.dnsServer = undefined;
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
.withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
|
|
477
|
+
);
|
|
478
|
+
}
|
|
335
479
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
480
|
+
// RADIUS Server: optional, no dependency on SmartProxy
|
|
481
|
+
if (this.options.radiusConfig) {
|
|
482
|
+
this.serviceManager.addService(
|
|
483
|
+
new plugins.taskbuffer.Service('RadiusServer')
|
|
484
|
+
.optional()
|
|
485
|
+
.withStart(async () => {
|
|
486
|
+
await this.setupRadiusServer();
|
|
487
|
+
})
|
|
488
|
+
.withStop(async () => {
|
|
489
|
+
if (this.radiusServer) {
|
|
490
|
+
await this.radiusServer.stop();
|
|
491
|
+
this.radiusServer = undefined;
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
.withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
|
|
495
|
+
);
|
|
342
496
|
}
|
|
497
|
+
|
|
498
|
+
// Remote Ingress: optional, depends on SmartProxy
|
|
499
|
+
if (this.options.remoteIngressConfig?.enabled) {
|
|
500
|
+
this.serviceManager.addService(
|
|
501
|
+
new plugins.taskbuffer.Service('RemoteIngress')
|
|
502
|
+
.optional()
|
|
503
|
+
.dependsOn('SmartProxy')
|
|
504
|
+
.withStart(async () => {
|
|
505
|
+
await this.setupRemoteIngress();
|
|
506
|
+
})
|
|
507
|
+
.withStop(async () => {
|
|
508
|
+
if (this.tunnelManager) {
|
|
509
|
+
await this.tunnelManager.stop();
|
|
510
|
+
this.tunnelManager = undefined;
|
|
511
|
+
}
|
|
512
|
+
this.remoteIngressManager = undefined;
|
|
513
|
+
})
|
|
514
|
+
.withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Wire up aggregated events for logging
|
|
519
|
+
this.serviceManager.serviceSubject.subscribe((event) => {
|
|
520
|
+
const level = event.type === 'failed' ? 'error' : event.type === 'retrying' ? 'warn' : 'info';
|
|
521
|
+
logger.log(level as any, `Service '${event.serviceName}': ${event.type}`, {
|
|
522
|
+
state: event.state,
|
|
523
|
+
...(event.error ? { error: event.error } : {}),
|
|
524
|
+
...(event.attempt ? { attempt: event.attempt } : {}),
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
public async start() {
|
|
530
|
+
logger.log('info', 'Starting DcRouter Services');
|
|
531
|
+
await this.serviceManager.start();
|
|
532
|
+
this.logStartupSummary();
|
|
343
533
|
}
|
|
344
534
|
|
|
345
535
|
/**
|
|
@@ -399,7 +589,21 @@ export class DcRouter {
|
|
|
399
589
|
logger.log('info', `Cache Database: storage=${this.cacheDb.getStoragePath()}, db=${this.cacheDb.getDbName()}, cleaner=${this.cacheCleaner?.isActive() ? 'active' : 'inactive'} (${(this.options.cacheConfig?.cleanupIntervalHours || 1)}h interval)`);
|
|
400
590
|
}
|
|
401
591
|
|
|
402
|
-
|
|
592
|
+
// Service status summary from ServiceManager
|
|
593
|
+
const health = this.serviceManager.getHealth();
|
|
594
|
+
const statuses = health.services;
|
|
595
|
+
const running = statuses.filter(s => s.state === 'running').length;
|
|
596
|
+
const failed = statuses.filter(s => s.state === 'failed').length;
|
|
597
|
+
const retrying = statuses.filter(s => s.state === 'starting' || s.state === 'degraded').length;
|
|
598
|
+
|
|
599
|
+
if (failed > 0) {
|
|
600
|
+
const failedNames = statuses.filter(s => s.state === 'failed').map(s => `${s.name}: ${s.lastError || 'unknown'}`);
|
|
601
|
+
logger.log('warn', `DcRouter started in degraded mode — ${running} running, ${failed} failed: ${failedNames.join('; ')}`);
|
|
602
|
+
} else if (retrying > 0) {
|
|
603
|
+
logger.log('info', `DcRouter started — ${running} running, ${retrying} still initializing`);
|
|
604
|
+
} else {
|
|
605
|
+
logger.log('info', `All ${running} services are running`);
|
|
606
|
+
}
|
|
403
607
|
}
|
|
404
608
|
|
|
405
609
|
/**
|
|
@@ -535,10 +739,13 @@ export class DcRouter {
|
|
|
535
739
|
// Initialize cert provision scheduler
|
|
536
740
|
this.certProvisionScheduler = new CertProvisionScheduler(this.storageManager);
|
|
537
741
|
|
|
538
|
-
// If we have DNS challenge handlers, create SmartAcme and wire
|
|
742
|
+
// If we have DNS challenge handlers, create SmartAcme instance and wire certProvisionFunction
|
|
743
|
+
// Note: SmartAcme.start() is NOT called here — it runs as a separate optional service
|
|
744
|
+
// via the ServiceManager, with aggressive retry for rate-limit resilience.
|
|
539
745
|
if (challengeHandlers.length > 0) {
|
|
540
746
|
// Stop old SmartAcme if it exists (e.g., during updateSmartProxyConfig)
|
|
541
747
|
if (this.smartAcme) {
|
|
748
|
+
this.smartAcmeReady = false;
|
|
542
749
|
await this.smartAcme.stop().catch(err =>
|
|
543
750
|
logger.log('error', 'Error stopping old SmartAcme', { error: String(err) })
|
|
544
751
|
);
|
|
@@ -550,10 +757,15 @@ export class DcRouter {
|
|
|
550
757
|
challengeHandlers: challengeHandlers,
|
|
551
758
|
challengePriority: ['dns-01'],
|
|
552
759
|
});
|
|
553
|
-
await this.smartAcme.start();
|
|
554
760
|
|
|
555
761
|
const scheduler = this.certProvisionScheduler;
|
|
556
762
|
smartProxyConfig.certProvisionFunction = async (domain, eventComms) => {
|
|
763
|
+
// If SmartAcme is not yet ready (still starting or retrying), fall back to HTTP-01
|
|
764
|
+
if (!this.smartAcmeReady) {
|
|
765
|
+
eventComms.warn(`SmartAcme not yet initialized, falling back to http-01 for ${domain}`);
|
|
766
|
+
return 'http01';
|
|
767
|
+
}
|
|
768
|
+
|
|
557
769
|
// Check backoff before attempting provision
|
|
558
770
|
if (await scheduler.isInBackoff(domain)) {
|
|
559
771
|
const info = await scheduler.getBackoffInfo(domain);
|
|
@@ -914,105 +1126,23 @@ export class DcRouter {
|
|
|
914
1126
|
public async stop() {
|
|
915
1127
|
logger.log('info', 'Stopping DcRouter services...');
|
|
916
1128
|
|
|
917
|
-
//
|
|
918
|
-
|
|
919
|
-
clearTimeout(this.dnsBatchTimer);
|
|
920
|
-
if (this.dnsBatchCount > 0) {
|
|
921
|
-
logger.log('info', `DNS: ${this.dnsBatchCount} queries processed (rate limited, final flush)`, { zone: 'dns' });
|
|
922
|
-
}
|
|
923
|
-
this.dnsBatchTimer = null;
|
|
924
|
-
this.dnsBatchCount = 0;
|
|
925
|
-
this.dnsLogWindowSecond = 0;
|
|
926
|
-
this.dnsLogWindowCount = 0;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
await this.opsServer.stop();
|
|
930
|
-
|
|
931
|
-
try {
|
|
932
|
-
// Remove event listeners before stopping services to prevent leaks
|
|
933
|
-
if (this.smartProxy) {
|
|
934
|
-
this.smartProxy.removeAllListeners();
|
|
935
|
-
}
|
|
936
|
-
if (this.emailServer) {
|
|
937
|
-
if ((this.emailServer as any).deliverySystem) {
|
|
938
|
-
(this.emailServer as any).deliverySystem.removeAllListeners();
|
|
939
|
-
}
|
|
940
|
-
this.emailServer.removeAllListeners();
|
|
941
|
-
}
|
|
942
|
-
if (this.dnsServer) {
|
|
943
|
-
this.dnsServer.removeAllListeners();
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// Stop all services in parallel for faster shutdown
|
|
947
|
-
await Promise.all([
|
|
948
|
-
// Stop cache cleaner if running
|
|
949
|
-
this.cacheCleaner ? Promise.resolve(this.cacheCleaner.stop()) : Promise.resolve(),
|
|
950
|
-
|
|
951
|
-
// Stop metrics manager if running
|
|
952
|
-
this.metricsManager ? this.metricsManager.stop().catch(err => logger.log('error', 'Error stopping MetricsManager', { error: String(err) })) : Promise.resolve(),
|
|
953
|
-
|
|
954
|
-
// Stop unified email server if running
|
|
955
|
-
this.emailServer ? this.emailServer.stop().catch(err => logger.log('error', 'Error stopping email server', { error: String(err) })) : Promise.resolve(),
|
|
956
|
-
|
|
957
|
-
// Stop SmartAcme if running
|
|
958
|
-
this.smartAcme ? this.smartAcme.stop().catch(err => logger.log('error', 'Error stopping SmartAcme', { error: String(err) })) : Promise.resolve(),
|
|
959
|
-
|
|
960
|
-
// Stop HTTP SmartProxy if running
|
|
961
|
-
this.smartProxy ? this.smartProxy.stop().catch(err => logger.log('error', 'Error stopping SmartProxy', { error: String(err) })) : Promise.resolve(),
|
|
962
|
-
|
|
963
|
-
// Stop DNS server if running
|
|
964
|
-
this.dnsServer ?
|
|
965
|
-
this.dnsServer.stop().catch(err => logger.log('error', 'Error stopping DNS server', { error: String(err) })) :
|
|
966
|
-
Promise.resolve(),
|
|
967
|
-
|
|
968
|
-
// Stop RADIUS server if running
|
|
969
|
-
this.radiusServer ?
|
|
970
|
-
this.radiusServer.stop().catch(err => logger.log('error', 'Error stopping RADIUS server', { error: String(err) })) :
|
|
971
|
-
Promise.resolve(),
|
|
1129
|
+
// ServiceManager handles reverse-dependency-ordered shutdown
|
|
1130
|
+
await this.serviceManager.stop();
|
|
972
1131
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
Promise.resolve()
|
|
977
|
-
]);
|
|
978
|
-
|
|
979
|
-
// Stop cache database after other services (they may need it during shutdown)
|
|
980
|
-
if (this.cacheDb) {
|
|
981
|
-
await this.cacheDb.stop().catch(err => logger.log('error', 'Error stopping CacheDb', { error: String(err) }));
|
|
982
|
-
CacheDb.resetInstance();
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Clear backoff cache in cert scheduler
|
|
986
|
-
if (this.certProvisionScheduler) {
|
|
987
|
-
this.certProvisionScheduler.clear();
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// Allow GC of stopped services by nulling references
|
|
991
|
-
this.smartProxy = undefined;
|
|
992
|
-
this.emailServer = undefined;
|
|
993
|
-
this.dnsServer = undefined;
|
|
994
|
-
this.metricsManager = undefined;
|
|
995
|
-
this.cacheCleaner = undefined;
|
|
996
|
-
this.cacheDb = undefined;
|
|
997
|
-
this.tunnelManager = undefined;
|
|
998
|
-
this.radiusServer = undefined;
|
|
999
|
-
this.smartAcme = undefined;
|
|
1132
|
+
// Clear backoff cache in cert scheduler
|
|
1133
|
+
if (this.certProvisionScheduler) {
|
|
1134
|
+
this.certProvisionScheduler.clear();
|
|
1000
1135
|
this.certProvisionScheduler = undefined;
|
|
1001
|
-
|
|
1002
|
-
this.routeConfigManager = undefined;
|
|
1003
|
-
this.apiTokenManager = undefined;
|
|
1004
|
-
this.certificateStatusMap.clear();
|
|
1136
|
+
}
|
|
1005
1137
|
|
|
1006
|
-
|
|
1007
|
-
SecurityLogger.resetInstance();
|
|
1008
|
-
ContentScanner.resetInstance();
|
|
1009
|
-
IPReputationChecker.resetInstance();
|
|
1138
|
+
this.certificateStatusMap.clear();
|
|
1010
1139
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1140
|
+
// Reset security singletons to allow GC
|
|
1141
|
+
SecurityLogger.resetInstance();
|
|
1142
|
+
ContentScanner.resetInstance();
|
|
1143
|
+
IPReputationChecker.resetInstance();
|
|
1144
|
+
|
|
1145
|
+
logger.log('info', 'All DcRouter services stopped');
|
|
1016
1146
|
}
|
|
1017
1147
|
|
|
1018
1148
|
/**
|
|
@@ -489,44 +489,41 @@ export class StatsHandler {
|
|
|
489
489
|
message?: string;
|
|
490
490
|
}>;
|
|
491
491
|
}> {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
services.push({
|
|
524
|
-
name: 'OpsServer',
|
|
525
|
-
status: 'healthy',
|
|
492
|
+
const dcRouter = this.opsServerRef.dcRouterRef;
|
|
493
|
+
const health = dcRouter.serviceManager.getHealth();
|
|
494
|
+
|
|
495
|
+
const services = health.services.map((svc) => {
|
|
496
|
+
let status: 'healthy' | 'degraded' | 'unhealthy';
|
|
497
|
+
switch (svc.state) {
|
|
498
|
+
case 'running':
|
|
499
|
+
status = 'healthy';
|
|
500
|
+
break;
|
|
501
|
+
case 'starting':
|
|
502
|
+
case 'degraded':
|
|
503
|
+
status = 'degraded';
|
|
504
|
+
break;
|
|
505
|
+
case 'failed':
|
|
506
|
+
status = svc.criticality === 'critical' ? 'unhealthy' : 'degraded';
|
|
507
|
+
break;
|
|
508
|
+
case 'stopped':
|
|
509
|
+
case 'stopping':
|
|
510
|
+
default:
|
|
511
|
+
status = 'degraded';
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let message: string | undefined;
|
|
516
|
+
if (svc.state === 'failed' && svc.lastError) {
|
|
517
|
+
message = svc.lastError;
|
|
518
|
+
} else if (svc.retryCount > 0 && svc.state !== 'running') {
|
|
519
|
+
message = `Retry attempt ${svc.retryCount}`;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return { name: svc.name, status, message };
|
|
526
523
|
});
|
|
527
|
-
|
|
528
|
-
const healthy =
|
|
529
|
-
|
|
524
|
+
|
|
525
|
+
const healthy = health.overall === 'healthy';
|
|
526
|
+
|
|
530
527
|
return {
|
|
531
528
|
healthy,
|
|
532
529
|
services,
|
package/ts/plugins.ts
CHANGED
|
@@ -62,8 +62,9 @@ import * as smartradius from '@push.rocks/smartradius';
|
|
|
62
62
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
63
63
|
import * as smartrx from '@push.rocks/smartrx';
|
|
64
64
|
import * as smartunique from '@push.rocks/smartunique';
|
|
65
|
+
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
65
66
|
|
|
66
|
-
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmetrics, smartmongo, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique };
|
|
67
|
+
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmetrics, smartmongo, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
|
|
67
68
|
|
|
68
69
|
// Define SmartLog types for use in error handling
|
|
69
70
|
export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';
|