@serve.zone/dcrouter 11.8.11 → 11.9.1

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.
@@ -252,6 +252,11 @@ 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
+ private serviceSubjectSubscription?: plugins.smartrx.rxjs.Subscription;
258
+ public smartAcmeReady = false;
259
+
255
260
  // TypedRouter for API endpoints
256
261
  public typedrouter = new plugins.typedrequest.TypedRouter();
257
262
 
@@ -279,67 +284,253 @@ export class DcRouter {
279
284
 
280
285
  // Initialize storage manager
281
286
  this.storageManager = new StorageManager(this.options.storage);
287
+
288
+ // Initialize service manager and register all services
289
+ this.serviceManager = new plugins.taskbuffer.ServiceManager({
290
+ name: 'dcrouter',
291
+ startupTimeoutMs: 120_000,
292
+ shutdownTimeoutMs: 30_000,
293
+ });
294
+ this.registerServices();
282
295
  }
283
296
 
284
- public async start() {
285
- logger.log('info', 'Starting DcRouter Services');
286
-
297
+ /**
298
+ * Register all dcrouter services with the ServiceManager.
299
+ * Services are started in dependency order, with failure isolation for optional services.
300
+ */
301
+ private registerServices(): void {
302
+ // OpsServer: critical, no dependencies — provides visibility
303
+ this.serviceManager.addService(
304
+ new plugins.taskbuffer.Service('OpsServer')
305
+ .critical()
306
+ .withStart(async () => {
307
+ this.opsServer = new OpsServer(this);
308
+ await this.opsServer.start();
309
+ })
310
+ .withStop(async () => {
311
+ await this.opsServer?.stop();
312
+ })
313
+ .withRetry({ maxRetries: 0 }),
314
+ );
315
+
316
+ // CacheDb: optional, no dependencies
317
+ if (this.options.cacheConfig?.enabled !== false) {
318
+ this.serviceManager.addService(
319
+ new plugins.taskbuffer.Service('CacheDb')
320
+ .optional()
321
+ .withStart(async () => {
322
+ await this.setupCacheDb();
323
+ })
324
+ .withStop(async () => {
325
+ if (this.cacheCleaner) {
326
+ this.cacheCleaner.stop();
327
+ this.cacheCleaner = undefined;
328
+ }
329
+ if (this.cacheDb) {
330
+ await this.cacheDb.stop();
331
+ CacheDb.resetInstance();
332
+ this.cacheDb = undefined;
333
+ }
334
+ })
335
+ .withRetry({ maxRetries: 2, baseDelayMs: 1000, maxDelayMs: 5000 }),
336
+ );
337
+ }
287
338
 
288
- this.opsServer = new OpsServer(this);
289
- await this.opsServer.start();
339
+ // MetricsManager: optional, depends on OpsServer
340
+ this.serviceManager.addService(
341
+ new plugins.taskbuffer.Service('MetricsManager')
342
+ .optional()
343
+ .dependsOn('OpsServer')
344
+ .withStart(async () => {
345
+ this.metricsManager = new MetricsManager(this);
346
+ await this.metricsManager.start();
347
+ })
348
+ .withStop(async () => {
349
+ if (this.metricsManager) {
350
+ await this.metricsManager.stop();
351
+ this.metricsManager = undefined;
352
+ }
353
+ })
354
+ .withRetry({ maxRetries: 1, baseDelayMs: 1000 }),
355
+ );
290
356
 
291
- try {
292
- // Initialize cache database if enabled (default: enabled)
293
- if (this.options.cacheConfig?.enabled !== false) {
294
- await this.setupCacheDb();
295
- }
357
+ // SmartProxy: critical, depends on CacheDb (if enabled)
358
+ const smartProxyDeps: string[] = [];
359
+ if (this.options.cacheConfig?.enabled !== false) {
360
+ smartProxyDeps.push('CacheDb');
361
+ }
362
+ this.serviceManager.addService(
363
+ new plugins.taskbuffer.Service('SmartProxy')
364
+ .critical()
365
+ .dependsOn(...smartProxyDeps)
366
+ .withStart(async () => {
367
+ await this.setupSmartProxy();
368
+ })
369
+ .withStop(async () => {
370
+ if (this.smartProxy) {
371
+ this.smartProxy.removeAllListeners();
372
+ await this.smartProxy.stop();
373
+ this.smartProxy = undefined;
374
+ }
375
+ })
376
+ .withRetry({ maxRetries: 0 }),
377
+ );
296
378
 
297
- // Initialize MetricsManager
298
- this.metricsManager = new MetricsManager(this);
299
- await this.metricsManager.start();
300
-
301
- // Set up SmartProxy for HTTP/HTTPS and all traffic including email routes
302
- await this.setupSmartProxy();
303
-
304
- // Initialize programmatic config API managers
305
- this.routeConfigManager = new RouteConfigManager(
306
- this.storageManager,
307
- () => this.getConstructorRoutes(),
308
- () => this.smartProxy,
309
- () => this.options.http3,
379
+ // SmartAcme: optional, depends on SmartProxy — aggressive retry for rate limits
380
+ // Only registered if DNS challenge is configured
381
+ if (this.options.dnsChallenge?.cloudflareApiKey) {
382
+ this.serviceManager.addService(
383
+ new plugins.taskbuffer.Service('SmartAcme')
384
+ .optional()
385
+ .dependsOn('SmartProxy')
386
+ .withStart(async () => {
387
+ if (this.smartAcme) {
388
+ await this.smartAcme.start();
389
+ this.smartAcmeReady = true;
390
+ logger.log('info', 'SmartAcme DNS-01 provider is now ready');
391
+ }
392
+ })
393
+ .withStop(async () => {
394
+ this.smartAcmeReady = false;
395
+ if (this.smartAcme) {
396
+ await this.smartAcme.stop();
397
+ this.smartAcme = undefined;
398
+ }
399
+ })
400
+ .withRetry({ maxRetries: 20, baseDelayMs: 5000, maxDelayMs: 3_600_000, backoffFactor: 2 }),
310
401
  );
311
- this.apiTokenManager = new ApiTokenManager(this.storageManager);
312
- await this.apiTokenManager.initialize();
313
- await this.routeConfigManager.initialize();
402
+ }
314
403
 
315
- // Set up unified email handling if configured
316
- if (this.options.emailConfig) {
317
- await this.setupUnifiedEmailHandling();
318
- }
319
-
320
- // Set up DNS server if configured with nameservers and scopes
321
- if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0 &&
322
- this.options.dnsScopes && this.options.dnsScopes.length > 0) {
323
- await this.setupDnsWithSocketHandler();
324
- }
404
+ // ConfigManagers: optional, depends on SmartProxy
405
+ this.serviceManager.addService(
406
+ new plugins.taskbuffer.Service('ConfigManagers')
407
+ .optional()
408
+ .dependsOn('SmartProxy')
409
+ .withStart(async () => {
410
+ this.routeConfigManager = new RouteConfigManager(
411
+ this.storageManager,
412
+ () => this.getConstructorRoutes(),
413
+ () => this.smartProxy,
414
+ () => this.options.http3,
415
+ );
416
+ this.apiTokenManager = new ApiTokenManager(this.storageManager);
417
+ await this.apiTokenManager.initialize();
418
+ await this.routeConfigManager.initialize();
419
+ })
420
+ .withStop(async () => {
421
+ this.routeConfigManager = undefined;
422
+ this.apiTokenManager = undefined;
423
+ })
424
+ .withRetry({ maxRetries: 2, baseDelayMs: 1000 }),
425
+ );
325
426
 
326
- // Set up RADIUS server if configured
327
- if (this.options.radiusConfig) {
328
- await this.setupRadiusServer();
329
- }
427
+ // Email Server: optional, depends on SmartProxy
428
+ if (this.options.emailConfig) {
429
+ this.serviceManager.addService(
430
+ new plugins.taskbuffer.Service('EmailServer')
431
+ .optional()
432
+ .dependsOn('SmartProxy')
433
+ .withStart(async () => {
434
+ await this.setupUnifiedEmailHandling();
435
+ })
436
+ .withStop(async () => {
437
+ if (this.emailServer) {
438
+ if ((this.emailServer as any).deliverySystem) {
439
+ (this.emailServer as any).deliverySystem.removeAllListeners();
440
+ }
441
+ this.emailServer.removeAllListeners();
442
+ await this.emailServer.stop();
443
+ this.emailServer = undefined;
444
+ }
445
+ })
446
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
447
+ );
448
+ }
330
449
 
331
- // Set up Remote Ingress hub if configured
332
- if (this.options.remoteIngressConfig?.enabled) {
333
- await this.setupRemoteIngress();
334
- }
450
+ // DNS Server: optional, depends on SmartProxy
451
+ if (this.options.dnsNsDomains?.length > 0 && this.options.dnsScopes?.length > 0) {
452
+ this.serviceManager.addService(
453
+ new plugins.taskbuffer.Service('DnsServer')
454
+ .optional()
455
+ .dependsOn('SmartProxy')
456
+ .withStart(async () => {
457
+ await this.setupDnsWithSocketHandler();
458
+ })
459
+ .withStop(async () => {
460
+ // Flush pending DNS batch log
461
+ if (this.dnsBatchTimer) {
462
+ clearTimeout(this.dnsBatchTimer);
463
+ if (this.dnsBatchCount > 0) {
464
+ logger.log('info', `DNS: ${this.dnsBatchCount} queries processed (final flush)`, { zone: 'dns' });
465
+ }
466
+ this.dnsBatchTimer = null;
467
+ this.dnsBatchCount = 0;
468
+ this.dnsLogWindowSecond = 0;
469
+ this.dnsLogWindowCount = 0;
470
+ }
471
+ if (this.dnsServer) {
472
+ this.dnsServer.removeAllListeners();
473
+ await this.dnsServer.stop();
474
+ this.dnsServer = undefined;
475
+ }
476
+ })
477
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
478
+ );
479
+ }
335
480
 
336
- this.logStartupSummary();
337
- } catch (error) {
338
- logger.log('error', 'Error starting DcRouter', { error: String(error) });
339
- // Try to clean up any services that may have started
340
- await this.stop();
341
- throw error;
481
+ // RADIUS Server: optional, no dependency on SmartProxy
482
+ if (this.options.radiusConfig) {
483
+ this.serviceManager.addService(
484
+ new plugins.taskbuffer.Service('RadiusServer')
485
+ .optional()
486
+ .withStart(async () => {
487
+ await this.setupRadiusServer();
488
+ })
489
+ .withStop(async () => {
490
+ if (this.radiusServer) {
491
+ await this.radiusServer.stop();
492
+ this.radiusServer = undefined;
493
+ }
494
+ })
495
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
496
+ );
342
497
  }
498
+
499
+ // Remote Ingress: optional, depends on SmartProxy
500
+ if (this.options.remoteIngressConfig?.enabled) {
501
+ this.serviceManager.addService(
502
+ new plugins.taskbuffer.Service('RemoteIngress')
503
+ .optional()
504
+ .dependsOn('SmartProxy')
505
+ .withStart(async () => {
506
+ await this.setupRemoteIngress();
507
+ })
508
+ .withStop(async () => {
509
+ if (this.tunnelManager) {
510
+ await this.tunnelManager.stop();
511
+ this.tunnelManager = undefined;
512
+ }
513
+ this.remoteIngressManager = undefined;
514
+ })
515
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
516
+ );
517
+ }
518
+
519
+ // Wire up aggregated events for logging
520
+ this.serviceSubjectSubscription = this.serviceManager.serviceSubject.subscribe((event) => {
521
+ const level = event.type === 'failed' ? 'error' : event.type === 'retrying' ? 'warn' : 'info';
522
+ logger.log(level as any, `Service '${event.serviceName}': ${event.type}`, {
523
+ state: event.state,
524
+ ...(event.error ? { error: event.error } : {}),
525
+ ...(event.attempt ? { attempt: event.attempt } : {}),
526
+ });
527
+ });
528
+ }
529
+
530
+ public async start() {
531
+ logger.log('info', 'Starting DcRouter Services');
532
+ await this.serviceManager.start();
533
+ this.logStartupSummary();
343
534
  }
344
535
 
345
536
  /**
@@ -399,7 +590,21 @@ export class DcRouter {
399
590
  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
591
  }
401
592
 
402
- logger.log('info', 'All services are running');
593
+ // Service status summary from ServiceManager
594
+ const health = this.serviceManager.getHealth();
595
+ const statuses = health.services;
596
+ const running = statuses.filter(s => s.state === 'running').length;
597
+ const failed = statuses.filter(s => s.state === 'failed').length;
598
+ const retrying = statuses.filter(s => s.state === 'starting' || s.state === 'degraded').length;
599
+
600
+ if (failed > 0) {
601
+ const failedNames = statuses.filter(s => s.state === 'failed').map(s => `${s.name}: ${s.lastError || 'unknown'}`);
602
+ logger.log('warn', `DcRouter started in degraded mode — ${running} running, ${failed} failed: ${failedNames.join('; ')}`);
603
+ } else if (retrying > 0) {
604
+ logger.log('info', `DcRouter started — ${running} running, ${retrying} still initializing`);
605
+ } else {
606
+ logger.log('info', `All ${running} services are running`);
607
+ }
403
608
  }
404
609
 
405
610
  /**
@@ -435,6 +640,13 @@ export class DcRouter {
435
640
  */
436
641
  private async setupSmartProxy(): Promise<void> {
437
642
  logger.log('info', 'Setting up SmartProxy...');
643
+
644
+ // Clean up any existing SmartProxy instance (e.g. from a retry)
645
+ if (this.smartProxy) {
646
+ this.smartProxy.removeAllListeners();
647
+ this.smartProxy = undefined;
648
+ }
649
+
438
650
  let routes: plugins.smartproxy.IRouteConfig[] = [];
439
651
  let acmeConfig: plugins.smartproxy.IAcmeOptions | undefined;
440
652
 
@@ -535,10 +747,13 @@ export class DcRouter {
535
747
  // Initialize cert provision scheduler
536
748
  this.certProvisionScheduler = new CertProvisionScheduler(this.storageManager);
537
749
 
538
- // If we have DNS challenge handlers, create SmartAcme and wire to certProvisionFunction
750
+ // If we have DNS challenge handlers, create SmartAcme instance and wire certProvisionFunction
751
+ // Note: SmartAcme.start() is NOT called here — it runs as a separate optional service
752
+ // via the ServiceManager, with aggressive retry for rate-limit resilience.
539
753
  if (challengeHandlers.length > 0) {
540
754
  // Stop old SmartAcme if it exists (e.g., during updateSmartProxyConfig)
541
755
  if (this.smartAcme) {
756
+ this.smartAcmeReady = false;
542
757
  await this.smartAcme.stop().catch(err =>
543
758
  logger.log('error', 'Error stopping old SmartAcme', { error: String(err) })
544
759
  );
@@ -550,10 +765,15 @@ export class DcRouter {
550
765
  challengeHandlers: challengeHandlers,
551
766
  challengePriority: ['dns-01'],
552
767
  });
553
- await this.smartAcme.start();
554
768
 
555
769
  const scheduler = this.certProvisionScheduler;
556
770
  smartProxyConfig.certProvisionFunction = async (domain, eventComms) => {
771
+ // If SmartAcme is not yet ready (still starting or retrying), fall back to HTTP-01
772
+ if (!this.smartAcmeReady) {
773
+ eventComms.warn(`SmartAcme not yet initialized, falling back to http-01 for ${domain}`);
774
+ return 'http01';
775
+ }
776
+
557
777
  // Check backoff before attempting provision
558
778
  if (await scheduler.isInBackoff(domain)) {
559
779
  const info = await scheduler.getBackoffInfo(domain);
@@ -914,105 +1134,29 @@ export class DcRouter {
914
1134
  public async stop() {
915
1135
  logger.log('info', 'Stopping DcRouter services...');
916
1136
 
917
- // Flush pending DNS batch log
918
- if (this.dnsBatchTimer) {
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;
1137
+ // Unsubscribe from service events before stopping services
1138
+ if (this.serviceSubjectSubscription) {
1139
+ this.serviceSubjectSubscription.unsubscribe();
1140
+ this.serviceSubjectSubscription = undefined;
927
1141
  }
928
1142
 
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(),
1143
+ // ServiceManager handles reverse-dependency-ordered shutdown
1144
+ await this.serviceManager.stop();
972
1145
 
973
- // Stop Remote Ingress tunnel manager if running
974
- this.tunnelManager ?
975
- this.tunnelManager.stop().catch(err => logger.log('error', 'Error stopping TunnelManager', { error: String(err) })) :
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;
1146
+ // Clear backoff cache in cert scheduler
1147
+ if (this.certProvisionScheduler) {
1148
+ this.certProvisionScheduler.clear();
1000
1149
  this.certProvisionScheduler = undefined;
1001
- this.remoteIngressManager = undefined;
1002
- this.routeConfigManager = undefined;
1003
- this.apiTokenManager = undefined;
1004
- this.certificateStatusMap.clear();
1150
+ }
1005
1151
 
1006
- // Reset security singletons to allow GC
1007
- SecurityLogger.resetInstance();
1008
- ContentScanner.resetInstance();
1009
- IPReputationChecker.resetInstance();
1152
+ this.certificateStatusMap.clear();
1010
1153
 
1011
- logger.log('info', 'All DcRouter services stopped');
1012
- } catch (error) {
1013
- logger.log('error', 'Error during DcRouter shutdown', { error: String(error) });
1014
- throw error;
1015
- }
1154
+ // Reset security singletons to allow GC
1155
+ SecurityLogger.resetInstance();
1156
+ ContentScanner.resetInstance();
1157
+ IPReputationChecker.resetInstance();
1158
+
1159
+ logger.log('info', 'All DcRouter services stopped');
1016
1160
  }
1017
1161
 
1018
1162
  /**
package/ts/index.ts CHANGED
@@ -35,6 +35,6 @@ export const runCli = async () => {
35
35
  await dcRouter.stop();
36
36
  process.exit(0);
37
37
  };
38
- process.on('SIGINT', shutdown);
39
- process.on('SIGTERM', shutdown);
38
+ process.once('SIGINT', shutdown);
39
+ process.once('SIGTERM', shutdown);
40
40
  };
@@ -489,44 +489,41 @@ export class StatsHandler {
489
489
  message?: string;
490
490
  }>;
491
491
  }> {
492
- const services: Array<{
493
- name: string;
494
- status: 'healthy' | 'degraded' | 'unhealthy';
495
- message?: string;
496
- }> = [];
497
-
498
- // Check HTTP Proxy
499
- if (this.opsServerRef.dcRouterRef.smartProxy) {
500
- services.push({
501
- name: 'HTTP/HTTPS Proxy',
502
- status: 'healthy',
503
- });
504
- }
505
-
506
- // Check Email Server
507
- if (this.opsServerRef.dcRouterRef.emailServer) {
508
- services.push({
509
- name: 'Email Server',
510
- status: 'healthy',
511
- });
512
- }
513
-
514
- // Check DNS Server
515
- if (this.opsServerRef.dcRouterRef.dnsServer) {
516
- services.push({
517
- name: 'DNS Server',
518
- status: 'healthy',
519
- });
520
- }
521
-
522
- // Check OpsServer
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 = services.every(s => s.status === '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';