@serve.zone/dcrouter 11.23.4 → 12.0.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.
Files changed (119) hide show
  1. package/dist_serve/bundle.js +1 -1
  2. package/dist_ts/00_commitinfo_data.js +2 -2
  3. package/dist_ts/classes.cert-provision-scheduler.d.ts +6 -8
  4. package/dist_ts/classes.cert-provision-scheduler.js +37 -17
  5. package/dist_ts/classes.dcrouter.d.ts +15 -29
  6. package/dist_ts/classes.dcrouter.js +96 -91
  7. package/dist_ts/classes.storage-cert-manager.d.ts +3 -6
  8. package/dist_ts/classes.storage-cert-manager.js +35 -25
  9. package/dist_ts/config/classes.api-token-manager.d.ts +1 -3
  10. package/dist_ts/config/classes.api-token-manager.js +45 -15
  11. package/dist_ts/config/classes.route-config-manager.d.ts +1 -3
  12. package/dist_ts/config/classes.route-config-manager.js +63 -25
  13. package/dist_ts/{cache → db}/classes.cache.cleaner.d.ts +3 -3
  14. package/dist_ts/db/classes.cache.cleaner.js +130 -0
  15. package/dist_ts/{cache → db}/classes.cached.document.js +1 -1
  16. package/dist_ts/db/classes.dcrouter-db.d.ts +70 -0
  17. package/dist_ts/db/classes.dcrouter-db.js +146 -0
  18. package/dist_ts/db/documents/classes.accounting-session.doc.d.ts +32 -0
  19. package/dist_ts/db/documents/classes.accounting-session.doc.js +214 -0
  20. package/dist_ts/db/documents/classes.acme-cert.doc.d.ts +13 -0
  21. package/dist_ts/db/documents/classes.acme-cert.doc.js +109 -0
  22. package/dist_ts/db/documents/classes.api-token.doc.d.ts +18 -0
  23. package/dist_ts/db/documents/classes.api-token.doc.js +127 -0
  24. package/dist_ts/{cache → db}/documents/classes.cached.email.js +3 -3
  25. package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.js +3 -3
  26. package/dist_ts/db/documents/classes.cert-backoff.doc.d.ts +11 -0
  27. package/dist_ts/db/documents/classes.cert-backoff.doc.js +97 -0
  28. package/dist_ts/db/documents/classes.proxy-cert.doc.d.ts +12 -0
  29. package/dist_ts/db/documents/classes.proxy-cert.doc.js +103 -0
  30. package/dist_ts/db/documents/classes.remote-ingress-edge.doc.d.ts +17 -0
  31. package/dist_ts/db/documents/classes.remote-ingress-edge.doc.js +130 -0
  32. package/dist_ts/db/documents/classes.route-override.doc.d.ts +10 -0
  33. package/dist_ts/db/documents/classes.route-override.doc.js +91 -0
  34. package/dist_ts/db/documents/classes.stored-route.doc.d.ts +12 -0
  35. package/dist_ts/db/documents/classes.stored-route.doc.js +103 -0
  36. package/dist_ts/db/documents/classes.vlan-mappings.doc.d.ts +15 -0
  37. package/dist_ts/db/documents/classes.vlan-mappings.doc.js +77 -0
  38. package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +18 -0
  39. package/dist_ts/db/documents/classes.vpn-client.doc.js +136 -0
  40. package/dist_ts/db/documents/classes.vpn-server-keys.doc.d.ts +10 -0
  41. package/dist_ts/db/documents/classes.vpn-server-keys.doc.js +94 -0
  42. package/dist_ts/db/documents/index.d.ts +13 -0
  43. package/dist_ts/db/documents/index.js +20 -0
  44. package/dist_ts/{cache → db}/index.d.ts +1 -1
  45. package/dist_ts/db/index.js +9 -0
  46. package/dist_ts/opsserver/handlers/certificate.handler.js +66 -66
  47. package/dist_ts/opsserver/handlers/config.handler.js +14 -15
  48. package/dist_ts/paths.d.ts +0 -1
  49. package/dist_ts/paths.js +1 -2
  50. package/dist_ts/radius/classes.accounting.manager.d.ts +4 -12
  51. package/dist_ts/radius/classes.accounting.manager.js +80 -93
  52. package/dist_ts/radius/classes.radius.server.d.ts +1 -3
  53. package/dist_ts/radius/classes.radius.server.js +4 -6
  54. package/dist_ts/radius/classes.vlan.manager.d.ts +3 -7
  55. package/dist_ts/radius/classes.vlan.manager.js +21 -28
  56. package/dist_ts/radius/index.d.ts +1 -1
  57. package/dist_ts/radius/index.js +1 -1
  58. package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +3 -5
  59. package/dist_ts/remoteingress/classes.remoteingress-manager.js +41 -21
  60. package/dist_ts/security/classes.ipreputationchecker.d.ts +6 -21
  61. package/dist_ts/security/classes.ipreputationchecker.js +59 -138
  62. package/dist_ts/vpn/classes.vpn-manager.d.ts +4 -22
  63. package/dist_ts/vpn/classes.vpn-manager.js +40 -45
  64. package/dist_ts_oci_container/index.js +4 -4
  65. package/dist_ts_web/00_commitinfo_data.js +2 -2
  66. package/package.json +1 -1
  67. package/readme.storage.md +55 -91
  68. package/ts/00_commitinfo_data.ts +1 -1
  69. package/ts/classes.cert-provision-scheduler.ts +35 -17
  70. package/ts/classes.dcrouter.ts +113 -125
  71. package/ts/classes.storage-cert-manager.ts +34 -22
  72. package/ts/config/classes.api-token-manager.ts +42 -11
  73. package/ts/config/classes.route-config-manager.ts +57 -22
  74. package/ts/{cache → db}/classes.cache.cleaner.ts +6 -6
  75. package/ts/db/classes.dcrouter-db.ts +179 -0
  76. package/ts/db/documents/classes.accounting-session.doc.ts +106 -0
  77. package/ts/db/documents/classes.acme-cert.doc.ts +41 -0
  78. package/ts/db/documents/classes.api-token.doc.ts +56 -0
  79. package/ts/{cache → db}/documents/classes.cached.email.ts +2 -2
  80. package/ts/{cache → db}/documents/classes.cached.ip.reputation.ts +2 -2
  81. package/ts/db/documents/classes.cert-backoff.doc.ts +35 -0
  82. package/ts/db/documents/classes.proxy-cert.doc.ts +38 -0
  83. package/ts/db/documents/classes.remote-ingress-edge.doc.ts +54 -0
  84. package/ts/db/documents/classes.route-override.doc.ts +32 -0
  85. package/ts/db/documents/classes.stored-route.doc.ts +38 -0
  86. package/ts/db/documents/classes.vlan-mappings.doc.ts +32 -0
  87. package/ts/db/documents/classes.vpn-client.doc.ts +57 -0
  88. package/ts/db/documents/classes.vpn-server-keys.doc.ts +31 -0
  89. package/ts/db/documents/index.ts +24 -0
  90. package/ts/{cache → db}/index.ts +6 -2
  91. package/ts/opsserver/handlers/certificate.handler.ts +67 -65
  92. package/ts/opsserver/handlers/config.handler.ts +13 -14
  93. package/ts/paths.ts +0 -1
  94. package/ts/radius/classes.accounting.manager.ts +81 -103
  95. package/ts/radius/classes.radius.server.ts +3 -6
  96. package/ts/radius/classes.vlan.manager.ts +20 -32
  97. package/ts/radius/index.ts +1 -1
  98. package/ts/remoteingress/classes.remoteingress-manager.ts +40 -22
  99. package/ts/security/classes.ipreputationchecker.ts +103 -196
  100. package/ts/vpn/classes.vpn-manager.ts +44 -75
  101. package/ts_web/00_commitinfo_data.ts +1 -1
  102. package/dist_ts/cache/classes.cache.cleaner.js +0 -130
  103. package/dist_ts/cache/classes.cachedb.d.ts +0 -60
  104. package/dist_ts/cache/classes.cachedb.js +0 -126
  105. package/dist_ts/cache/documents/index.d.ts +0 -2
  106. package/dist_ts/cache/documents/index.js +0 -3
  107. package/dist_ts/cache/index.js +0 -7
  108. package/dist_ts/storage/classes.storagemanager.d.ts +0 -83
  109. package/dist_ts/storage/classes.storagemanager.js +0 -348
  110. package/dist_ts/storage/index.d.ts +0 -1
  111. package/dist_ts/storage/index.js +0 -3
  112. package/ts/cache/classes.cachedb.ts +0 -155
  113. package/ts/cache/documents/index.ts +0 -2
  114. package/ts/storage/classes.storagemanager.ts +0 -404
  115. package/ts/storage/index.ts +0 -2
  116. /package/dist_ts/{cache → db}/classes.cached.document.d.ts +0 -0
  117. /package/dist_ts/{cache → db}/documents/classes.cached.email.d.ts +0 -0
  118. /package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.d.ts +0 -0
  119. /package/ts/{cache → db}/classes.cached.document.ts +0 -0
@@ -11,12 +11,10 @@ import {
11
11
  type IEmailDomainConfig,
12
12
  } from '@push.rocks/smartmta';
13
13
  import { logger } from './logger.js';
14
- // Import storage manager
15
- import { StorageManager, type IStorageConfig } from './storage/index.js';
16
14
  import { StorageBackedCertManager } from './classes.storage-cert-manager.js';
17
15
  import { CertProvisionScheduler } from './classes.cert-provision-scheduler.js';
18
- // Import cache system
19
- import { CacheDb, CacheCleaner, type ICacheDbOptions } from './cache/index.js';
16
+ // Import unified database
17
+ import { DcRouterDb, type IDcRouterDbConfig, CacheCleaner, ProxyCertDoc, AcmeCertDoc } from './db/index.js';
20
18
 
21
19
  import { OpsServer } from './opsserver/index.js';
22
20
  import { MetricsManager } from './monitoring/index.js';
@@ -122,37 +120,23 @@ export interface IDcRouterOptions {
122
120
  /** Other DNS providers can be added here */
123
121
  };
124
122
 
125
- /** Storage configuration */
126
- storage?: IStorageConfig;
127
-
128
123
  /**
129
- * Cache database configuration using smartdata and LocalTsmDb
130
- * Provides persistent caching for emails, IP reputation, bounces, etc.
124
+ * Unified database configuration.
125
+ * All persistent data (config, certs, VPN, cache, etc.) is stored via smartdata.
126
+ * If mongoDbUrl is provided, connects to external MongoDB.
127
+ * Otherwise, starts an embedded LocalSmartDb automatically.
131
128
  */
132
- cacheConfig?: {
133
- /** Enable cache database (default: true) */
129
+ dbConfig?: {
130
+ /** Enable database (default: true). Set to false in tests to skip DB startup. */
134
131
  enabled?: boolean;
135
- /** Storage path for TsmDB data (default: ~/.serve.zone/dcrouter/tsmdb) */
132
+ /** External MongoDB connection URL. If absent, uses embedded LocalSmartDb. */
133
+ mongoDbUrl?: string;
134
+ /** Storage path for embedded database data (default: ~/.serve.zone/dcrouter/tsmdb) */
136
135
  storagePath?: string;
137
136
  /** Database name (default: dcrouter) */
138
137
  dbName?: string;
139
- /** Default TTL in days for cached items (default: 30) */
140
- defaultTTLDays?: number;
141
- /** Cleanup interval in hours (default: 1) */
138
+ /** Cache cleanup interval in hours (default: 1) */
142
139
  cleanupIntervalHours?: number;
143
- /** TTL configuration per data type (in days) */
144
- ttlConfig?: {
145
- /** Email cache TTL (default: 30 days) */
146
- emails?: number;
147
- /** IP reputation cache TTL (default: 1 day) */
148
- ipReputation?: number;
149
- /** Bounce records TTL (default: 30 days) */
150
- bounces?: number;
151
- /** DKIM keys TTL (default: 90 days) */
152
- dkimKeys?: number;
153
- /** Suppression list TTL (default: 30 days, can be permanent) */
154
- suppression?: number;
155
- };
156
140
  };
157
141
 
158
142
  /**
@@ -248,12 +232,20 @@ export class DcRouter {
248
232
  public dnsServer?: plugins.smartdns.dnsServerMod.DnsServer;
249
233
  public emailServer?: UnifiedEmailServer;
250
234
  public radiusServer?: RadiusServer;
251
- public storageManager: StorageManager;
252
235
  public opsServer!: OpsServer;
253
236
  public metricsManager?: MetricsManager;
254
237
 
255
- // Cache system (smartdata + LocalTsmDb)
256
- public cacheDb?: CacheDb;
238
+ // Compatibility shim for smartmta's DkimManager which calls dcRouter.storageManager.set()
239
+ public storageManager: any = {
240
+ get: async (_key: string) => null,
241
+ set: async (_key: string, _value: string) => {
242
+ // DKIM keys from smartmta — logged but not yet migrated to smartdata
243
+ logger.log('debug', `storageManager.set() called (compat shim) for key: ${_key}`);
244
+ },
245
+ };
246
+
247
+ // Unified database (smartdata + LocalSmartDb or external MongoDB)
248
+ public dcRouterDb?: DcRouterDb;
257
249
  public cacheCleaner?: CacheCleaner;
258
250
 
259
251
  // Remote Ingress
@@ -312,16 +304,6 @@ export class DcRouter {
312
304
  // Resolve all data paths from baseDir
313
305
  this.resolvedPaths = paths.resolvePaths(this.options.baseDir);
314
306
 
315
- // Default storage to filesystem if not configured
316
- if (!this.options.storage) {
317
- this.options.storage = {
318
- fsPath: this.resolvedPaths.defaultStoragePath,
319
- };
320
- }
321
-
322
- // Initialize storage manager
323
- this.storageManager = new StorageManager(this.options.storage);
324
-
325
307
  // Initialize service manager and register all services
326
308
  this.serviceManager = new plugins.taskbuffer.ServiceManager({
327
309
  name: 'dcrouter',
@@ -350,23 +332,23 @@ export class DcRouter {
350
332
  .withRetry({ maxRetries: 0 }),
351
333
  );
352
334
 
353
- // CacheDb: optional, no dependencies
354
- if (this.options.cacheConfig?.enabled !== false) {
335
+ // DcRouterDb: optional, no dependencies — unified database for all persistence
336
+ if (this.options.dbConfig?.enabled !== false) {
355
337
  this.serviceManager.addService(
356
- new plugins.taskbuffer.Service('CacheDb')
338
+ new plugins.taskbuffer.Service('DcRouterDb')
357
339
  .optional()
358
340
  .withStart(async () => {
359
- await this.setupCacheDb();
341
+ await this.setupDcRouterDb();
360
342
  })
361
343
  .withStop(async () => {
362
344
  if (this.cacheCleaner) {
363
345
  this.cacheCleaner.stop();
364
346
  this.cacheCleaner = undefined;
365
347
  }
366
- if (this.cacheDb) {
367
- await this.cacheDb.stop();
368
- CacheDb.resetInstance();
369
- this.cacheDb = undefined;
348
+ if (this.dcRouterDb) {
349
+ await this.dcRouterDb.stop();
350
+ DcRouterDb.resetInstance();
351
+ this.dcRouterDb = undefined;
370
352
  }
371
353
  })
372
354
  .withRetry({ maxRetries: 2, baseDelayMs: 1000, maxDelayMs: 5000 }),
@@ -391,10 +373,10 @@ export class DcRouter {
391
373
  .withRetry({ maxRetries: 1, baseDelayMs: 1000 }),
392
374
  );
393
375
 
394
- // SmartProxy: critical, depends on CacheDb (if enabled)
376
+ // SmartProxy: critical, depends on DcRouterDb (if enabled)
395
377
  const smartProxyDeps: string[] = [];
396
- if (this.options.cacheConfig?.enabled !== false) {
397
- smartProxyDeps.push('CacheDb');
378
+ if (this.options.dbConfig?.enabled !== false) {
379
+ smartProxyDeps.push('DcRouterDb');
398
380
  }
399
381
  this.serviceManager.addService(
400
382
  new plugins.taskbuffer.Service('SmartProxy')
@@ -455,36 +437,38 @@ export class DcRouter {
455
437
  );
456
438
  }
457
439
 
458
- // ConfigManagers: optional, depends on SmartProxy
459
- this.serviceManager.addService(
460
- new plugins.taskbuffer.Service('ConfigManagers')
461
- .optional()
462
- .dependsOn('SmartProxy')
463
- .withStart(async () => {
464
- this.routeConfigManager = new RouteConfigManager(
465
- this.storageManager,
466
- () => this.getConstructorRoutes(),
467
- () => this.smartProxy,
468
- () => this.options.http3,
469
- this.options.vpnConfig?.enabled
470
- ? (tags?: string[]) => {
471
- if (tags?.length && this.vpnManager) {
472
- return this.vpnManager.getClientIpsForServerDefinedTags(tags);
440
+ // ConfigManagers: optional, depends on SmartProxy + DcRouterDb
441
+ // Requires DcRouterDb to be enabled (document classes need the database)
442
+ if (this.options.dbConfig?.enabled !== false) {
443
+ this.serviceManager.addService(
444
+ new plugins.taskbuffer.Service('ConfigManagers')
445
+ .optional()
446
+ .dependsOn('SmartProxy', 'DcRouterDb')
447
+ .withStart(async () => {
448
+ this.routeConfigManager = new RouteConfigManager(
449
+ () => this.getConstructorRoutes(),
450
+ () => this.smartProxy,
451
+ () => this.options.http3,
452
+ this.options.vpnConfig?.enabled
453
+ ? (tags?: string[]) => {
454
+ if (tags?.length && this.vpnManager) {
455
+ return this.vpnManager.getClientIpsForServerDefinedTags(tags);
456
+ }
457
+ return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
473
458
  }
474
- return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
475
- }
476
- : undefined,
477
- );
478
- this.apiTokenManager = new ApiTokenManager(this.storageManager);
479
- await this.apiTokenManager.initialize();
480
- await this.routeConfigManager.initialize();
481
- })
482
- .withStop(async () => {
483
- this.routeConfigManager = undefined;
484
- this.apiTokenManager = undefined;
485
- })
486
- .withRetry({ maxRetries: 2, baseDelayMs: 1000 }),
487
- );
459
+ : undefined,
460
+ );
461
+ this.apiTokenManager = new ApiTokenManager();
462
+ await this.apiTokenManager.initialize();
463
+ await this.routeConfigManager.initialize();
464
+ })
465
+ .withStop(async () => {
466
+ this.routeConfigManager = undefined;
467
+ this.apiTokenManager = undefined;
468
+ })
469
+ .withRetry({ maxRetries: 2, baseDelayMs: 1000 }),
470
+ );
471
+ }
488
472
 
489
473
  // Email Server: optional, depends on SmartProxy
490
474
  if (this.options.emailConfig) {
@@ -695,14 +679,9 @@ export class DcRouter {
695
679
  logger.log('info', `Remote Ingress: tunnel port=${this.options.remoteIngressConfig.tunnelPort || 8443}, edges=${edgeCount} registered/${connectedCount} connected`);
696
680
  }
697
681
 
698
- // Storage summary
699
- if (this.storageManager && this.options.storage) {
700
- logger.log('info', `Storage: path=${this.options.storage.fsPath || 'default'}`);
701
- }
702
-
703
- // Cache database summary
704
- if (this.cacheDb) {
705
- 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)`);
682
+ // Database summary
683
+ if (this.dcRouterDb) {
684
+ logger.log('info', `Database: ${this.dcRouterDb.isEmbedded() ? 'embedded' : 'external'}, db=${this.dcRouterDb.getDbName()}, cleaner=${this.cacheCleaner?.isActive() ? 'active' : 'inactive'} (${(this.options.dbConfig?.cleanupIntervalHours || 1)}h interval)`);
706
685
  }
707
686
 
708
687
  // Service status summary from ServiceManager
@@ -723,31 +702,32 @@ export class DcRouter {
723
702
  }
724
703
 
725
704
  /**
726
- * Set up the cache database (smartdata + LocalTsmDb)
705
+ * Set up the unified database (smartdata + LocalSmartDb or external MongoDB)
727
706
  */
728
- private async setupCacheDb(): Promise<void> {
729
- logger.log('info', 'Setting up CacheDb...');
707
+ private async setupDcRouterDb(): Promise<void> {
708
+ logger.log('info', 'Setting up DcRouterDb...');
730
709
 
731
- const cacheConfig = this.options.cacheConfig || {};
710
+ const dbConfig = this.options.dbConfig || {};
732
711
 
733
- // Initialize CacheDb singleton
734
- this.cacheDb = CacheDb.getInstance({
735
- storagePath: cacheConfig.storagePath || this.resolvedPaths.defaultTsmDbPath,
736
- dbName: cacheConfig.dbName || 'dcrouter',
712
+ // Initialize DcRouterDb singleton
713
+ this.dcRouterDb = DcRouterDb.getInstance({
714
+ mongoDbUrl: dbConfig.mongoDbUrl,
715
+ storagePath: dbConfig.storagePath || this.resolvedPaths.defaultTsmDbPath,
716
+ dbName: dbConfig.dbName || 'dcrouter',
737
717
  debug: false,
738
718
  });
739
719
 
740
- await this.cacheDb.start();
720
+ await this.dcRouterDb.start();
741
721
 
742
- // Start the cache cleaner
743
- const cleanupIntervalMs = (cacheConfig.cleanupIntervalHours || 1) * 60 * 60 * 1000;
744
- this.cacheCleaner = new CacheCleaner(this.cacheDb, {
722
+ // Start the cache cleaner for TTL-based document cleanup
723
+ const cleanupIntervalMs = (dbConfig.cleanupIntervalHours || 1) * 60 * 60 * 1000;
724
+ this.cacheCleaner = new CacheCleaner(this.dcRouterDb, {
745
725
  intervalMs: cleanupIntervalMs,
746
726
  verbose: false,
747
727
  });
748
728
  this.cacheCleaner.start();
749
729
 
750
- logger.log('info', `CacheDb initialized at ${this.cacheDb.getStoragePath()}`);
730
+ logger.log('info', `DcRouterDb ready (${this.dcRouterDb.isEmbedded() ? 'embedded' : 'external'})`);
751
731
  }
752
732
 
753
733
  /**
@@ -850,14 +830,11 @@ export class DcRouter {
850
830
  acme: acmeConfig,
851
831
  certStore: {
852
832
  loadAll: async () => {
853
- const keys = await this.storageManager.list('/proxy-certs/');
833
+ const docs = await ProxyCertDoc.findAll();
854
834
  const certs: Array<{ domain: string; publicKey: string; privateKey: string; ca?: string }> = [];
855
- for (const key of keys) {
856
- const data = await this.storageManager.getJSON(key);
857
- if (data) {
858
- certs.push(data);
859
- loadedCertEntries.push({ domain: data.domain, publicKey: data.publicKey, validUntil: data.validUntil, validFrom: data.validFrom });
860
- }
835
+ for (const doc of docs) {
836
+ certs.push({ domain: doc.domain, publicKey: doc.publicKey, privateKey: doc.privateKey, ca: doc.ca });
837
+ loadedCertEntries.push({ domain: doc.domain, publicKey: doc.publicKey, validUntil: doc.validUntil, validFrom: doc.validFrom });
861
838
  }
862
839
  return certs;
863
840
  },
@@ -869,18 +846,29 @@ export class DcRouter {
869
846
  validUntil = new Date(x509.validTo).getTime();
870
847
  validFrom = new Date(x509.validFrom).getTime();
871
848
  } catch { /* PEM parsing failed */ }
872
- await this.storageManager.setJSON(`/proxy-certs/${domain}`, {
873
- domain, publicKey, privateKey, ca, validUntil, validFrom,
874
- });
849
+ let doc = await ProxyCertDoc.findByDomain(domain);
850
+ if (!doc) {
851
+ doc = new ProxyCertDoc();
852
+ doc.domain = domain;
853
+ }
854
+ doc.publicKey = publicKey;
855
+ doc.privateKey = privateKey;
856
+ doc.ca = ca || '';
857
+ doc.validUntil = validUntil || 0;
858
+ doc.validFrom = validFrom || 0;
859
+ await doc.save();
875
860
  },
876
861
  remove: async (domain: string) => {
877
- await this.storageManager.delete(`/proxy-certs/${domain}`);
862
+ const doc = await ProxyCertDoc.findByDomain(domain);
863
+ if (doc) {
864
+ await doc.delete();
865
+ }
878
866
  },
879
867
  },
880
868
  };
881
869
 
882
870
  // Initialize cert provision scheduler
883
- this.certProvisionScheduler = new CertProvisionScheduler(this.storageManager);
871
+ this.certProvisionScheduler = new CertProvisionScheduler();
884
872
 
885
873
  // If we have DNS challenge handlers, create SmartAcme instance and wire certProvisionFunction
886
874
  // Note: SmartAcme.start() is NOT called here — it runs as a separate optional service
@@ -895,7 +883,7 @@ export class DcRouter {
895
883
  }
896
884
  this.smartAcme = new plugins.smartacme.SmartAcme({
897
885
  accountEmail: acmeConfig?.accountEmail || this.options.tls?.contactEmail || 'admin@example.com',
898
- certManager: new StorageBackedCertManager(this.storageManager),
886
+ certManager: new StorageBackedCertManager(),
899
887
  environment: 'production',
900
888
  challengeHandlers: challengeHandlers,
901
889
  challengePriority: ['dns-01'],
@@ -1037,16 +1025,16 @@ export class DcRouter {
1037
1025
  issuedAt = new Date(entry.validFrom).toISOString();
1038
1026
  }
1039
1027
 
1040
- // Try SmartAcme /certs/ metadata as secondary source
1028
+ // Try SmartAcme AcmeCertDoc metadata as secondary source
1041
1029
  if (!expiryDate) {
1042
1030
  try {
1043
1031
  const cleanDomain = entry.domain.replace(/^\*\.?/, '');
1044
- const certMeta = await this.storageManager.getJSON(`/certs/${cleanDomain}`);
1045
- if (certMeta?.validUntil) {
1046
- expiryDate = new Date(certMeta.validUntil).toISOString();
1032
+ const certDoc = await AcmeCertDoc.findByDomain(cleanDomain);
1033
+ if (certDoc?.validUntil) {
1034
+ expiryDate = new Date(certDoc.validUntil).toISOString();
1047
1035
  }
1048
- if (certMeta?.created && !issuedAt) {
1049
- issuedAt = new Date(certMeta.created).toISOString();
1036
+ if (certDoc?.created && !issuedAt) {
1037
+ issuedAt = new Date(certDoc.created).toISOString();
1050
1038
  }
1051
1039
  } catch { /* no metadata available */ }
1052
1040
  }
@@ -2030,7 +2018,7 @@ export class DcRouter {
2030
2018
  logger.log('info', 'Setting up Remote Ingress hub...');
2031
2019
 
2032
2020
  // Initialize the edge registration manager
2033
- this.remoteIngressManager = new RemoteIngressManager(this.storageManager);
2021
+ this.remoteIngressManager = new RemoteIngressManager();
2034
2022
  await this.remoteIngressManager.initialize();
2035
2023
 
2036
2024
  // Pass current routes so the manager can derive edge ports from remoteIngress-tagged routes
@@ -2056,7 +2044,7 @@ export class DcRouter {
2056
2044
  // Priority 2: Existing cert from SmartProxy cert store for hubDomain
2057
2045
  if (!tlsConfig && riCfg.hubDomain) {
2058
2046
  try {
2059
- const stored = await this.storageManager.getJSON(`/proxy-certs/${riCfg.hubDomain}`);
2047
+ const stored = await ProxyCertDoc.findByDomain(riCfg.hubDomain);
2060
2048
  if (stored?.publicKey && stored?.privateKey) {
2061
2049
  tlsConfig = { certPem: stored.publicKey, keyPem: stored.privateKey };
2062
2050
  logger.log('info', `Using stored ACME cert for RemoteIngress tunnel TLS: ${riCfg.hubDomain}`);
@@ -2090,7 +2078,7 @@ export class DcRouter {
2090
2078
 
2091
2079
  logger.log('info', 'Setting up VPN server...');
2092
2080
 
2093
- this.vpnManager = new VpnManager(this.storageManager, {
2081
+ this.vpnManager = new VpnManager({
2094
2082
  subnet: this.options.vpnConfig.subnet,
2095
2083
  wgListenPort: this.options.vpnConfig.wgListenPort,
2096
2084
  dns: this.options.vpnConfig.dns,
@@ -2180,7 +2168,7 @@ export class DcRouter {
2180
2168
 
2181
2169
  logger.log('info', 'Setting up RADIUS server...');
2182
2170
 
2183
- this.radiusServer = new RadiusServer(this.options.radiusConfig, this.storageManager);
2171
+ this.radiusServer = new RadiusServer(this.options.radiusConfig);
2184
2172
  await this.radiusServer.start();
2185
2173
 
2186
2174
  logger.log('info', `RADIUS server started on ports ${this.options.radiusConfig.authPort || 1812} (auth) and ${this.options.radiusConfig.acctPort || 1813} (acct)`);
@@ -1,46 +1,58 @@
1
1
  import * as plugins from './plugins.js';
2
- import { StorageManager } from './storage/index.js';
2
+ import { AcmeCertDoc } from './db/index.js';
3
3
 
4
4
  /**
5
- * ICertManager implementation backed by StorageManager.
6
- * Persists SmartAcme certificates under a /certs/ key prefix so they
5
+ * ICertManager implementation backed by smartdata document classes.
6
+ * Persists SmartAcme certificates via AcmeCertDoc so they
7
7
  * survive process restarts without re-hitting ACME.
8
8
  */
9
9
  export class StorageBackedCertManager implements plugins.smartacme.ICertManager {
10
- private keyPrefix = '/certs/';
11
-
12
- constructor(private storageManager: StorageManager) {}
10
+ constructor() {}
13
11
 
14
12
  async init(): Promise<void> {}
15
13
 
16
14
  async retrieveCertificate(domainName: string): Promise<plugins.smartacme.Cert | null> {
17
- const data = await this.storageManager.getJSON(this.keyPrefix + domainName);
18
- if (!data) return null;
19
- return new plugins.smartacme.Cert(data);
15
+ const doc = await AcmeCertDoc.findByDomain(domainName);
16
+ if (!doc) return null;
17
+ return new plugins.smartacme.Cert({
18
+ id: doc.id,
19
+ domainName: doc.domainName,
20
+ created: doc.created,
21
+ privateKey: doc.privateKey,
22
+ publicKey: doc.publicKey,
23
+ csr: doc.csr,
24
+ validUntil: doc.validUntil,
25
+ });
20
26
  }
21
27
 
22
28
  async storeCertificate(cert: plugins.smartacme.Cert): Promise<void> {
23
- await this.storageManager.setJSON(this.keyPrefix + cert.domainName, {
24
- id: cert.id,
25
- domainName: cert.domainName,
26
- created: cert.created,
27
- privateKey: cert.privateKey,
28
- publicKey: cert.publicKey,
29
- csr: cert.csr,
30
- validUntil: cert.validUntil,
31
- });
29
+ let doc = await AcmeCertDoc.findByDomain(cert.domainName);
30
+ if (!doc) {
31
+ doc = new AcmeCertDoc();
32
+ doc.domainName = cert.domainName;
33
+ }
34
+ doc.id = cert.id;
35
+ doc.created = cert.created;
36
+ doc.privateKey = cert.privateKey;
37
+ doc.publicKey = cert.publicKey;
38
+ doc.csr = cert.csr;
39
+ doc.validUntil = cert.validUntil;
40
+ await doc.save();
32
41
  }
33
42
 
34
43
  async deleteCertificate(domainName: string): Promise<void> {
35
- await this.storageManager.delete(this.keyPrefix + domainName);
44
+ const doc = await AcmeCertDoc.findByDomain(domainName);
45
+ if (doc) {
46
+ await doc.delete();
47
+ }
36
48
  }
37
49
 
38
50
  async close(): Promise<void> {}
39
51
 
40
52
  async wipe(): Promise<void> {
41
- const keys = await this.storageManager.list(this.keyPrefix);
42
- for (const key of keys) {
43
- await this.storageManager.delete(key);
53
+ const docs = await AcmeCertDoc.findAll();
54
+ for (const doc of docs) {
55
+ await doc.delete();
44
56
  }
45
57
  }
46
58
  }
@@ -1,19 +1,18 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import { logger } from '../logger.js';
3
- import type { StorageManager } from '../storage/index.js';
3
+ import { ApiTokenDoc } from '../db/index.js';
4
4
  import type {
5
5
  IStoredApiToken,
6
6
  IApiTokenInfo,
7
7
  TApiTokenScope,
8
8
  } from '../../ts_interfaces/data/route-management.js';
9
9
 
10
- const TOKENS_PREFIX = '/config-api/tokens/';
11
10
  const TOKEN_PREFIX_STR = 'dcr_';
12
11
 
13
12
  export class ApiTokenManager {
14
13
  private tokens = new Map<string, IStoredApiToken>();
15
14
 
16
- constructor(private storageManager: StorageManager) {}
15
+ constructor() {}
17
16
 
18
17
  public async initialize(): Promise<void> {
19
18
  await this.loadTokens();
@@ -117,7 +116,8 @@ export class ApiTokenManager {
117
116
  if (!this.tokens.has(id)) return false;
118
117
  const token = this.tokens.get(id)!;
119
118
  this.tokens.delete(id);
120
- await this.storageManager.delete(`${TOKENS_PREFIX}${id}.json`);
119
+ const doc = await ApiTokenDoc.findById(id);
120
+ if (doc) await doc.delete();
121
121
  logger.log('info', `API token '${token.name}' revoked (id: ${id})`);
122
122
  return true;
123
123
  }
@@ -157,17 +157,48 @@ export class ApiTokenManager {
157
157
  // =========================================================================
158
158
 
159
159
  private async loadTokens(): Promise<void> {
160
- const keys = await this.storageManager.list(TOKENS_PREFIX);
161
- for (const key of keys) {
162
- if (!key.endsWith('.json')) continue;
163
- const stored = await this.storageManager.getJSON<IStoredApiToken>(key);
164
- if (stored?.id) {
165
- this.tokens.set(stored.id, stored);
160
+ const docs = await ApiTokenDoc.findAll();
161
+ for (const doc of docs) {
162
+ if (doc.id) {
163
+ this.tokens.set(doc.id, {
164
+ id: doc.id,
165
+ name: doc.name,
166
+ tokenHash: doc.tokenHash,
167
+ scopes: doc.scopes,
168
+ createdAt: doc.createdAt,
169
+ expiresAt: doc.expiresAt,
170
+ lastUsedAt: doc.lastUsedAt,
171
+ createdBy: doc.createdBy,
172
+ enabled: doc.enabled,
173
+ });
166
174
  }
167
175
  }
168
176
  }
169
177
 
170
178
  private async persistToken(stored: IStoredApiToken): Promise<void> {
171
- await this.storageManager.setJSON(`${TOKENS_PREFIX}${stored.id}.json`, stored);
179
+ const existing = await ApiTokenDoc.findById(stored.id);
180
+ if (existing) {
181
+ existing.name = stored.name;
182
+ existing.tokenHash = stored.tokenHash;
183
+ existing.scopes = stored.scopes;
184
+ existing.createdAt = stored.createdAt;
185
+ existing.expiresAt = stored.expiresAt;
186
+ existing.lastUsedAt = stored.lastUsedAt;
187
+ existing.createdBy = stored.createdBy;
188
+ existing.enabled = stored.enabled;
189
+ await existing.save();
190
+ } else {
191
+ const doc = new ApiTokenDoc();
192
+ doc.id = stored.id;
193
+ doc.name = stored.name;
194
+ doc.tokenHash = stored.tokenHash;
195
+ doc.scopes = stored.scopes;
196
+ doc.createdAt = stored.createdAt;
197
+ doc.expiresAt = stored.expiresAt;
198
+ doc.lastUsedAt = stored.lastUsedAt;
199
+ doc.createdBy = stored.createdBy;
200
+ doc.enabled = stored.enabled;
201
+ await doc.save();
202
+ }
172
203
  }
173
204
  }