@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.
- package/dist_serve/bundle.js +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.cert-provision-scheduler.d.ts +6 -8
- package/dist_ts/classes.cert-provision-scheduler.js +37 -17
- package/dist_ts/classes.dcrouter.d.ts +15 -29
- package/dist_ts/classes.dcrouter.js +96 -91
- package/dist_ts/classes.storage-cert-manager.d.ts +3 -6
- package/dist_ts/classes.storage-cert-manager.js +35 -25
- package/dist_ts/config/classes.api-token-manager.d.ts +1 -3
- package/dist_ts/config/classes.api-token-manager.js +45 -15
- package/dist_ts/config/classes.route-config-manager.d.ts +1 -3
- package/dist_ts/config/classes.route-config-manager.js +63 -25
- package/dist_ts/{cache → db}/classes.cache.cleaner.d.ts +3 -3
- package/dist_ts/db/classes.cache.cleaner.js +130 -0
- package/dist_ts/{cache → db}/classes.cached.document.js +1 -1
- package/dist_ts/db/classes.dcrouter-db.d.ts +70 -0
- package/dist_ts/db/classes.dcrouter-db.js +146 -0
- package/dist_ts/db/documents/classes.accounting-session.doc.d.ts +32 -0
- package/dist_ts/db/documents/classes.accounting-session.doc.js +214 -0
- package/dist_ts/db/documents/classes.acme-cert.doc.d.ts +13 -0
- package/dist_ts/db/documents/classes.acme-cert.doc.js +109 -0
- package/dist_ts/db/documents/classes.api-token.doc.d.ts +18 -0
- package/dist_ts/db/documents/classes.api-token.doc.js +127 -0
- package/dist_ts/{cache → db}/documents/classes.cached.email.js +3 -3
- package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.js +3 -3
- package/dist_ts/db/documents/classes.cert-backoff.doc.d.ts +11 -0
- package/dist_ts/db/documents/classes.cert-backoff.doc.js +97 -0
- package/dist_ts/db/documents/classes.proxy-cert.doc.d.ts +12 -0
- package/dist_ts/db/documents/classes.proxy-cert.doc.js +103 -0
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.d.ts +17 -0
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.js +130 -0
- package/dist_ts/db/documents/classes.route-override.doc.d.ts +10 -0
- package/dist_ts/db/documents/classes.route-override.doc.js +91 -0
- package/dist_ts/db/documents/classes.stored-route.doc.d.ts +12 -0
- package/dist_ts/db/documents/classes.stored-route.doc.js +103 -0
- package/dist_ts/db/documents/classes.vlan-mappings.doc.d.ts +15 -0
- package/dist_ts/db/documents/classes.vlan-mappings.doc.js +77 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +18 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.js +136 -0
- package/dist_ts/db/documents/classes.vpn-server-keys.doc.d.ts +10 -0
- package/dist_ts/db/documents/classes.vpn-server-keys.doc.js +94 -0
- package/dist_ts/db/documents/index.d.ts +13 -0
- package/dist_ts/db/documents/index.js +20 -0
- package/dist_ts/{cache → db}/index.d.ts +1 -1
- package/dist_ts/db/index.js +9 -0
- package/dist_ts/opsserver/handlers/certificate.handler.js +66 -66
- package/dist_ts/opsserver/handlers/config.handler.js +14 -15
- package/dist_ts/paths.d.ts +0 -1
- package/dist_ts/paths.js +1 -2
- package/dist_ts/radius/classes.accounting.manager.d.ts +4 -12
- package/dist_ts/radius/classes.accounting.manager.js +80 -93
- package/dist_ts/radius/classes.radius.server.d.ts +1 -3
- package/dist_ts/radius/classes.radius.server.js +4 -6
- package/dist_ts/radius/classes.vlan.manager.d.ts +3 -7
- package/dist_ts/radius/classes.vlan.manager.js +21 -28
- package/dist_ts/radius/index.d.ts +1 -1
- package/dist_ts/radius/index.js +1 -1
- package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +3 -5
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +41 -21
- package/dist_ts/security/classes.ipreputationchecker.d.ts +6 -21
- package/dist_ts/security/classes.ipreputationchecker.js +59 -138
- package/dist_ts/vpn/classes.vpn-manager.d.ts +4 -22
- package/dist_ts/vpn/classes.vpn-manager.js +40 -45
- package/dist_ts_oci_container/index.js +4 -4
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/package.json +1 -1
- package/readme.storage.md +55 -91
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.cert-provision-scheduler.ts +35 -17
- package/ts/classes.dcrouter.ts +113 -125
- package/ts/classes.storage-cert-manager.ts +34 -22
- package/ts/config/classes.api-token-manager.ts +42 -11
- package/ts/config/classes.route-config-manager.ts +57 -22
- package/ts/{cache → db}/classes.cache.cleaner.ts +6 -6
- package/ts/db/classes.dcrouter-db.ts +179 -0
- package/ts/db/documents/classes.accounting-session.doc.ts +106 -0
- package/ts/db/documents/classes.acme-cert.doc.ts +41 -0
- package/ts/db/documents/classes.api-token.doc.ts +56 -0
- package/ts/{cache → db}/documents/classes.cached.email.ts +2 -2
- package/ts/{cache → db}/documents/classes.cached.ip.reputation.ts +2 -2
- package/ts/db/documents/classes.cert-backoff.doc.ts +35 -0
- package/ts/db/documents/classes.proxy-cert.doc.ts +38 -0
- package/ts/db/documents/classes.remote-ingress-edge.doc.ts +54 -0
- package/ts/db/documents/classes.route-override.doc.ts +32 -0
- package/ts/db/documents/classes.stored-route.doc.ts +38 -0
- package/ts/db/documents/classes.vlan-mappings.doc.ts +32 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +57 -0
- package/ts/db/documents/classes.vpn-server-keys.doc.ts +31 -0
- package/ts/db/documents/index.ts +24 -0
- package/ts/{cache → db}/index.ts +6 -2
- package/ts/opsserver/handlers/certificate.handler.ts +67 -65
- package/ts/opsserver/handlers/config.handler.ts +13 -14
- package/ts/paths.ts +0 -1
- package/ts/radius/classes.accounting.manager.ts +81 -103
- package/ts/radius/classes.radius.server.ts +3 -6
- package/ts/radius/classes.vlan.manager.ts +20 -32
- package/ts/radius/index.ts +1 -1
- package/ts/remoteingress/classes.remoteingress-manager.ts +40 -22
- package/ts/security/classes.ipreputationchecker.ts +103 -196
- package/ts/vpn/classes.vpn-manager.ts +44 -75
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/dist_ts/cache/classes.cache.cleaner.js +0 -130
- package/dist_ts/cache/classes.cachedb.d.ts +0 -60
- package/dist_ts/cache/classes.cachedb.js +0 -126
- package/dist_ts/cache/documents/index.d.ts +0 -2
- package/dist_ts/cache/documents/index.js +0 -3
- package/dist_ts/cache/index.js +0 -7
- package/dist_ts/storage/classes.storagemanager.d.ts +0 -83
- package/dist_ts/storage/classes.storagemanager.js +0 -348
- package/dist_ts/storage/index.d.ts +0 -1
- package/dist_ts/storage/index.js +0 -3
- package/ts/cache/classes.cachedb.ts +0 -155
- package/ts/cache/documents/index.ts +0 -2
- package/ts/storage/classes.storagemanager.ts +0 -404
- package/ts/storage/index.ts +0 -2
- /package/dist_ts/{cache → db}/classes.cached.document.d.ts +0 -0
- /package/dist_ts/{cache → db}/documents/classes.cached.email.d.ts +0 -0
- /package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.d.ts +0 -0
- /package/ts/{cache → db}/classes.cached.document.ts +0 -0
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -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
|
|
19
|
-
import {
|
|
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
|
-
*
|
|
130
|
-
*
|
|
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
|
-
|
|
133
|
-
/** Enable
|
|
129
|
+
dbConfig?: {
|
|
130
|
+
/** Enable database (default: true). Set to false in tests to skip DB startup. */
|
|
134
131
|
enabled?: boolean;
|
|
135
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
256
|
-
public
|
|
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
|
-
//
|
|
354
|
-
if (this.options.
|
|
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('
|
|
338
|
+
new plugins.taskbuffer.Service('DcRouterDb')
|
|
357
339
|
.optional()
|
|
358
340
|
.withStart(async () => {
|
|
359
|
-
await this.
|
|
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.
|
|
367
|
-
await this.
|
|
368
|
-
|
|
369
|
-
this.
|
|
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
|
|
376
|
+
// SmartProxy: critical, depends on DcRouterDb (if enabled)
|
|
395
377
|
const smartProxyDeps: string[] = [];
|
|
396
|
-
if (this.options.
|
|
397
|
-
smartProxyDeps.push('
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
//
|
|
699
|
-
if (this.
|
|
700
|
-
logger.log('info', `
|
|
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
|
|
705
|
+
* Set up the unified database (smartdata + LocalSmartDb or external MongoDB)
|
|
727
706
|
*/
|
|
728
|
-
private async
|
|
729
|
-
logger.log('info', 'Setting up
|
|
707
|
+
private async setupDcRouterDb(): Promise<void> {
|
|
708
|
+
logger.log('info', 'Setting up DcRouterDb...');
|
|
730
709
|
|
|
731
|
-
const
|
|
710
|
+
const dbConfig = this.options.dbConfig || {};
|
|
732
711
|
|
|
733
|
-
// Initialize
|
|
734
|
-
this.
|
|
735
|
-
|
|
736
|
-
|
|
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.
|
|
720
|
+
await this.dcRouterDb.start();
|
|
741
721
|
|
|
742
|
-
// Start the cache cleaner
|
|
743
|
-
const cleanupIntervalMs = (
|
|
744
|
-
this.cacheCleaner = new CacheCleaner(this.
|
|
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', `
|
|
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
|
|
833
|
+
const docs = await ProxyCertDoc.findAll();
|
|
854
834
|
const certs: Array<{ domain: string; publicKey: string; privateKey: string; ca?: string }> = [];
|
|
855
|
-
for (const
|
|
856
|
-
|
|
857
|
-
|
|
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
|
|
873
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
1028
|
+
// Try SmartAcme AcmeCertDoc metadata as secondary source
|
|
1041
1029
|
if (!expiryDate) {
|
|
1042
1030
|
try {
|
|
1043
1031
|
const cleanDomain = entry.domain.replace(/^\*\.?/, '');
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1046
|
-
expiryDate = new Date(
|
|
1032
|
+
const certDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
|
1033
|
+
if (certDoc?.validUntil) {
|
|
1034
|
+
expiryDate = new Date(certDoc.validUntil).toISOString();
|
|
1047
1035
|
}
|
|
1048
|
-
if (
|
|
1049
|
-
issuedAt = new Date(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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 {
|
|
2
|
+
import { AcmeCertDoc } from './db/index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* ICertManager implementation backed by
|
|
6
|
-
* Persists SmartAcme certificates
|
|
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
|
-
|
|
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
|
|
18
|
-
if (!
|
|
19
|
-
return new plugins.smartacme.Cert(
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
|
42
|
-
for (const
|
|
43
|
-
await
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
161
|
-
for (const
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
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
|
}
|