@serve.zone/dcrouter 11.10.4 → 11.11.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 +5102 -5102
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/cache/classes.cache.cleaner.js +1 -1
- package/dist_ts/cache/classes.cached.document.js +1 -1
- package/dist_ts/cache/classes.cachedb.d.ts +5 -5
- package/dist_ts/cache/classes.cachedb.js +14 -14
- package/dist_ts/cache/documents/classes.cached.email.js +1 -1
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +5 -0
- package/dist_ts/classes.dcrouter.js +50 -4
- package/dist_ts/config/classes.route-config-manager.d.ts +1 -1
- package/dist_ts/config/validator.js +1 -1
- package/dist_ts/errors/base.errors.js +2 -2
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +6 -1
- package/dist_ts/monitoring/classes.metricsmanager.js +1 -1
- package/dist_ts/opsserver/classes.opsserver.js +2 -2
- package/dist_ts/opsserver/handlers/admin.handler.js +1 -1
- package/dist_ts/opsserver/handlers/certificate.handler.js +1 -1
- package/dist_ts/opsserver/handlers/radius.handler.js +1 -1
- package/dist_ts/opsserver/handlers/stats.handler.js +1 -1
- package/dist_ts/plugins.d.ts +3 -3
- package/dist_ts/plugins.js +5 -5
- package/dist_ts/radius/classes.accounting.manager.js +1 -1
- package/dist_ts/radius/classes.radius.server.js +1 -1
- package/dist_ts/radius/classes.vlan.manager.js +1 -1
- package/dist_ts/security/classes.contentscanner.js +3 -3
- package/dist_ts/security/classes.ipreputationchecker.js +4 -4
- package/dist_ts/security/classes.securitylogger.js +5 -3
- package/dist_ts/sms/classes.smsservice.js +2 -2
- package/dist_ts/storage/classes.storagemanager.d.ts +1 -1
- package/dist_ts/storage/classes.storagemanager.js +2 -4
- package/dist_ts_oci_container/index.js +22 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.js +9 -6
- package/dist_ts_web/elements/ops-dashboard.js +3 -2
- package/dist_ts_web/elements/ops-view-certificates.js +1 -1
- package/dist_ts_web/elements/ops-view-config.js +1 -1
- package/dist_ts_web/elements/ops-view-emails.js +1 -1
- package/dist_ts_web/elements/ops-view-network.js +1 -1
- package/dist_ts_web/elements/ops-view-remoteingress.js +1 -1
- package/dist_ts_web/router.js +1 -1
- package/license +21 -0
- package/package.json +18 -18
- package/readme.hints.md +1 -1
- package/readme.md +42 -21
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/cache/classes.cache.cleaner.ts +10 -10
- package/ts/cache/classes.cached.document.ts +1 -1
- package/ts/cache/classes.cachedb.ts +18 -18
- package/ts/cache/documents/classes.cached.email.ts +14 -14
- package/ts/cache/documents/classes.cached.ip.reputation.ts +10 -10
- package/ts/classes.dcrouter.ts +77 -32
- package/ts/config/validator.ts +5 -5
- package/ts/errors/base.errors.ts +1 -1
- package/ts/monitoring/classes.metricsmanager.ts +3 -3
- package/ts/opsserver/classes.opsserver.ts +13 -13
- package/ts/opsserver/handlers/admin.handler.ts +1 -1
- package/ts/opsserver/handlers/certificate.handler.ts +6 -6
- package/ts/opsserver/handlers/radius.handler.ts +4 -4
- package/ts/opsserver/handlers/stats.handler.ts +1 -1
- package/ts/plugins.ts +4 -4
- package/ts/radius/classes.accounting.manager.ts +10 -10
- package/ts/radius/classes.radius.server.ts +2 -2
- package/ts/radius/classes.vlan.manager.ts +5 -5
- package/ts/readme.md +1 -1
- package/ts/security/classes.contentscanner.ts +12 -12
- package/ts/security/classes.ipreputationchecker.ts +26 -26
- package/ts/security/classes.securitylogger.ts +6 -4
- package/ts/sms/classes.smsservice.ts +3 -3
- package/ts/storage/classes.storagemanager.ts +23 -25
- package/ts_apiclient/readme.md +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +136 -133
- package/ts_web/elements/ops-dashboard.ts +15 -14
- package/ts_web/elements/ops-view-certificates.ts +10 -10
- package/ts_web/elements/ops-view-config.ts +8 -8
- package/ts_web/elements/ops-view-emails.ts +2 -2
- package/ts_web/elements/ops-view-network.ts +2 -2
- package/ts_web/elements/ops-view-remoteingress.ts +6 -6
- package/ts_web/readme.md +1 -1
- package/ts_web/router.ts +3 -3
- /package/{npmextra.json → .smartconfig.json} +0 -0
|
@@ -15,16 +15,16 @@ export interface ICacheDbOptions {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* CacheDb - Wrapper around
|
|
18
|
+
* CacheDb - Wrapper around LocalSmartDb and smartdata
|
|
19
19
|
*
|
|
20
20
|
* Provides persistent caching using smartdata as the ORM layer
|
|
21
|
-
* and
|
|
21
|
+
* and LocalSmartDb as the embedded database engine.
|
|
22
22
|
*/
|
|
23
23
|
export class CacheDb {
|
|
24
24
|
private static instance: CacheDb | null = null;
|
|
25
25
|
|
|
26
|
-
private
|
|
27
|
-
private smartdataDb
|
|
26
|
+
private localSmartDb!: plugins.smartdb.LocalSmartDb;
|
|
27
|
+
private smartdataDb!: plugins.smartdata.SmartdataDb;
|
|
28
28
|
private options: Required<ICacheDbOptions>;
|
|
29
29
|
private isStarted: boolean = false;
|
|
30
30
|
|
|
@@ -55,8 +55,8 @@ export class CacheDb {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Start the cache database
|
|
58
|
-
* - Initializes
|
|
59
|
-
* - Connects smartdata to the
|
|
58
|
+
* - Initializes LocalSmartDb with file persistence
|
|
59
|
+
* - Connects smartdata to the LocalSmartDb via Unix socket
|
|
60
60
|
*/
|
|
61
61
|
public async start(): Promise<void> {
|
|
62
62
|
if (this.isStarted) {
|
|
@@ -68,16 +68,16 @@ export class CacheDb {
|
|
|
68
68
|
// Ensure storage directory exists
|
|
69
69
|
await plugins.fsUtils.ensureDir(this.options.storagePath);
|
|
70
70
|
|
|
71
|
-
// Create
|
|
72
|
-
this.
|
|
71
|
+
// Create LocalSmartDb instance
|
|
72
|
+
this.localSmartDb = new plugins.smartdb.LocalSmartDb({
|
|
73
73
|
folderPath: this.options.storagePath,
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
// Start
|
|
77
|
-
const connectionInfo = await this.
|
|
76
|
+
// Start LocalSmartDb and get connection info
|
|
77
|
+
const connectionInfo = await this.localSmartDb.start();
|
|
78
78
|
|
|
79
79
|
if (this.options.debug) {
|
|
80
|
-
logger.log('debug', `
|
|
80
|
+
logger.log('debug', `LocalSmartDb started with URI: ${connectionInfo.connectionUri}`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Initialize smartdata with the connection URI
|
|
@@ -89,8 +89,8 @@ export class CacheDb {
|
|
|
89
89
|
|
|
90
90
|
this.isStarted = true;
|
|
91
91
|
logger.log('info', `CacheDb started at ${this.options.storagePath}`);
|
|
92
|
-
} catch (error) {
|
|
93
|
-
logger.log('error', `Failed to start CacheDb: ${error.message}`);
|
|
92
|
+
} catch (error: unknown) {
|
|
93
|
+
logger.log('error', `Failed to start CacheDb: ${(error as Error).message}`);
|
|
94
94
|
throw error;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -109,15 +109,15 @@ export class CacheDb {
|
|
|
109
109
|
await this.smartdataDb.close();
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// Stop
|
|
113
|
-
if (this.
|
|
114
|
-
await this.
|
|
112
|
+
// Stop LocalSmartDb
|
|
113
|
+
if (this.localSmartDb) {
|
|
114
|
+
await this.localSmartDb.stop();
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
this.isStarted = false;
|
|
118
118
|
logger.log('info', 'CacheDb stopped');
|
|
119
|
-
} catch (error) {
|
|
120
|
-
logger.log('error', `Error stopping CacheDb: ${error.message}`);
|
|
119
|
+
} catch (error: unknown) {
|
|
120
|
+
logger.log('error', `Error stopping CacheDb: ${(error as Error).message}`);
|
|
121
121
|
throw error;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -35,55 +35,55 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
35
35
|
*/
|
|
36
36
|
@plugins.smartdata.unI()
|
|
37
37
|
@plugins.smartdata.svDb()
|
|
38
|
-
public id
|
|
38
|
+
public id!: string;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Email message ID (RFC 822 Message-ID header)
|
|
42
42
|
*/
|
|
43
43
|
@plugins.smartdata.svDb()
|
|
44
|
-
public messageId
|
|
44
|
+
public messageId!: string;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Sender email address (envelope from)
|
|
48
48
|
*/
|
|
49
49
|
@plugins.smartdata.svDb()
|
|
50
|
-
public from
|
|
50
|
+
public from!: string;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Recipient email addresses
|
|
54
54
|
*/
|
|
55
55
|
@plugins.smartdata.svDb()
|
|
56
|
-
public to
|
|
56
|
+
public to!: string[];
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* CC recipients
|
|
60
60
|
*/
|
|
61
61
|
@plugins.smartdata.svDb()
|
|
62
|
-
public cc
|
|
62
|
+
public cc!: string[];
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* BCC recipients
|
|
66
66
|
*/
|
|
67
67
|
@plugins.smartdata.svDb()
|
|
68
|
-
public bcc
|
|
68
|
+
public bcc!: string[];
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Email subject
|
|
72
72
|
*/
|
|
73
73
|
@plugins.smartdata.svDb()
|
|
74
|
-
public subject
|
|
74
|
+
public subject!: string;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Raw RFC822 email content
|
|
78
78
|
*/
|
|
79
79
|
@plugins.smartdata.svDb()
|
|
80
|
-
public rawContent
|
|
80
|
+
public rawContent!: string;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Current status of the email
|
|
84
84
|
*/
|
|
85
85
|
@plugins.smartdata.svDb()
|
|
86
|
-
public status
|
|
86
|
+
public status!: TCachedEmailStatus;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Number of delivery attempts
|
|
@@ -101,25 +101,25 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
101
101
|
* Timestamp for next delivery attempt
|
|
102
102
|
*/
|
|
103
103
|
@plugins.smartdata.svDb()
|
|
104
|
-
public nextAttempt
|
|
104
|
+
public nextAttempt!: Date;
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Last error message if delivery failed
|
|
108
108
|
*/
|
|
109
109
|
@plugins.smartdata.svDb()
|
|
110
|
-
public lastError
|
|
110
|
+
public lastError!: string;
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Timestamp when the email was successfully delivered
|
|
114
114
|
*/
|
|
115
115
|
@plugins.smartdata.svDb()
|
|
116
|
-
public deliveredAt
|
|
116
|
+
public deliveredAt!: Date;
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Sender domain (for querying/filtering)
|
|
120
120
|
*/
|
|
121
121
|
@plugins.smartdata.svDb()
|
|
122
|
-
public senderDomain
|
|
122
|
+
public senderDomain!: string;
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* Priority level (higher = more important)
|
|
@@ -131,7 +131,7 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
131
131
|
* JSON-serialized route data
|
|
132
132
|
*/
|
|
133
133
|
@plugins.smartdata.svDb()
|
|
134
|
-
public routeData
|
|
134
|
+
public routeData!: string;
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* DKIM signature status
|
|
@@ -45,61 +45,61 @@ export class CachedIPReputation extends CachedDocument<CachedIPReputation> {
|
|
|
45
45
|
*/
|
|
46
46
|
@plugins.smartdata.unI()
|
|
47
47
|
@plugins.smartdata.svDb()
|
|
48
|
-
public ipAddress
|
|
48
|
+
public ipAddress!: string;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Reputation score (0-100, higher = better)
|
|
52
52
|
*/
|
|
53
53
|
@plugins.smartdata.svDb()
|
|
54
|
-
public score
|
|
54
|
+
public score!: number;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Whether the IP is flagged as spam source
|
|
58
58
|
*/
|
|
59
59
|
@plugins.smartdata.svDb()
|
|
60
|
-
public isSpam
|
|
60
|
+
public isSpam!: boolean;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Whether the IP is a known proxy
|
|
64
64
|
*/
|
|
65
65
|
@plugins.smartdata.svDb()
|
|
66
|
-
public isProxy
|
|
66
|
+
public isProxy!: boolean;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Whether the IP is a Tor exit node
|
|
70
70
|
*/
|
|
71
71
|
@plugins.smartdata.svDb()
|
|
72
|
-
public isTor
|
|
72
|
+
public isTor!: boolean;
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Whether the IP is a VPN endpoint
|
|
76
76
|
*/
|
|
77
77
|
@plugins.smartdata.svDb()
|
|
78
|
-
public isVPN
|
|
78
|
+
public isVPN!: boolean;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Country code (ISO 3166-1 alpha-2)
|
|
82
82
|
*/
|
|
83
83
|
@plugins.smartdata.svDb()
|
|
84
|
-
public country
|
|
84
|
+
public country!: string;
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Autonomous System Number
|
|
88
88
|
*/
|
|
89
89
|
@plugins.smartdata.svDb()
|
|
90
|
-
public asn
|
|
90
|
+
public asn!: string;
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Organization name
|
|
94
94
|
*/
|
|
95
95
|
@plugins.smartdata.svDb()
|
|
96
|
-
public org
|
|
96
|
+
public org!: string;
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* List of blacklists the IP appears on
|
|
100
100
|
*/
|
|
101
101
|
@plugins.smartdata.svDb()
|
|
102
|
-
public blacklists
|
|
102
|
+
public blacklists!: string[];
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Number of times this IP has been checked
|
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -215,7 +215,7 @@ export class DcRouter {
|
|
|
215
215
|
public emailServer?: UnifiedEmailServer;
|
|
216
216
|
public radiusServer?: RadiusServer;
|
|
217
217
|
public storageManager: StorageManager;
|
|
218
|
-
public opsServer
|
|
218
|
+
public opsServer!: OpsServer;
|
|
219
219
|
public metricsManager?: MetricsManager;
|
|
220
220
|
|
|
221
221
|
// Cache system (smartdata + LocalTsmDb)
|
|
@@ -448,7 +448,7 @@ export class DcRouter {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
// DNS Server: optional, depends on SmartProxy
|
|
451
|
-
if (this.options.dnsNsDomains
|
|
451
|
+
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0 && this.options.dnsScopes && this.options.dnsScopes.length > 0) {
|
|
452
452
|
this.serviceManager.addService(
|
|
453
453
|
new plugins.taskbuffer.Service('DnsServer')
|
|
454
454
|
.optional()
|
|
@@ -528,10 +528,36 @@ export class DcRouter {
|
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
public async start() {
|
|
531
|
+
await this.checkSystemLimits();
|
|
531
532
|
logger.log('info', 'Starting DcRouter Services');
|
|
532
533
|
await this.serviceManager.start();
|
|
533
534
|
this.logStartupSummary();
|
|
534
535
|
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Detect OS-level resource limits and warn if they are too low for production use.
|
|
539
|
+
* This is detection only — no attempts to raise limits.
|
|
540
|
+
*/
|
|
541
|
+
private async checkSystemLimits(): Promise<void> {
|
|
542
|
+
try {
|
|
543
|
+
const fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
544
|
+
const limitsContent = await fs.file('/proc/self/limits').encoding('utf8').read() as string;
|
|
545
|
+
const nofileLine = limitsContent.split('\n').find((line: string) => line.startsWith('Max open files'));
|
|
546
|
+
if (nofileLine) {
|
|
547
|
+
const parts = nofileLine.split(/\s{2,}/);
|
|
548
|
+
const softLimit = parseInt(parts[1], 10);
|
|
549
|
+
const hardLimit = parseInt(parts[2], 10);
|
|
550
|
+
if (softLimit < 65536) {
|
|
551
|
+
logger.log('warn', `File descriptor soft limit is ${softLimit} (hard: ${hardLimit}). ` +
|
|
552
|
+
`For production use, set --ulimit nofile=65536:65536 on the container runtime.`);
|
|
553
|
+
} else {
|
|
554
|
+
logger.log('info', `File descriptor limits: soft=${softLimit}, hard=${hardLimit}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
// Non-Linux or /proc not available — silently skip
|
|
559
|
+
}
|
|
560
|
+
}
|
|
535
561
|
|
|
536
562
|
/**
|
|
537
563
|
* Log comprehensive startup summary
|
|
@@ -708,9 +734,28 @@ export class DcRouter {
|
|
|
708
734
|
// Track cert entries loaded from cert store so we can populate certificateStatusMap after start
|
|
709
735
|
const loadedCertEntries: Array<{domain: string; publicKey: string; validUntil?: number; validFrom?: number}> = [];
|
|
710
736
|
|
|
711
|
-
// Create SmartProxy configuration
|
|
737
|
+
// Create SmartProxy configuration with sensible gateway defaults.
|
|
738
|
+
// User's smartProxyConfig overrides these defaults via spread.
|
|
712
739
|
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
|
|
740
|
+
// --- dcrouter gateway defaults ---
|
|
741
|
+
maxConnectionsPerIP: 100,
|
|
742
|
+
connectionRateLimitPerMinute: 600,
|
|
743
|
+
socketTimeout: 120_000,
|
|
744
|
+
inactivityTimeout: 120_000,
|
|
745
|
+
keepAlive: true,
|
|
746
|
+
noDelay: true,
|
|
747
|
+
gracefulShutdownTimeout: 30_000,
|
|
748
|
+
// --- user overrides ---
|
|
713
749
|
...this.options.smartProxyConfig,
|
|
750
|
+
// --- deep-merge defaults.security so user can override maxConnections ---
|
|
751
|
+
defaults: {
|
|
752
|
+
...this.options.smartProxyConfig?.defaults,
|
|
753
|
+
security: {
|
|
754
|
+
maxConnections: 50_000,
|
|
755
|
+
...this.options.smartProxyConfig?.defaults?.security,
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
// --- always set by dcrouter (after spread) ---
|
|
714
759
|
routes,
|
|
715
760
|
acme: acmeConfig,
|
|
716
761
|
certStore: {
|
|
@@ -787,7 +832,7 @@ export class DcRouter {
|
|
|
787
832
|
eventComms.log(`Attempting DNS-01 via SmartAcme for ${domain}`);
|
|
788
833
|
eventComms.setSource('smartacme-dns-01');
|
|
789
834
|
const isWildcardDomain = domain.startsWith('*.');
|
|
790
|
-
const cert = await this.smartAcme
|
|
835
|
+
const cert = await this.smartAcme!.getCertificateForDomain(domain, {
|
|
791
836
|
includeWildcard: !isWildcardDomain,
|
|
792
837
|
});
|
|
793
838
|
if (cert.validUntil) {
|
|
@@ -806,10 +851,10 @@ export class DcRouter {
|
|
|
806
851
|
// Success — clear any backoff
|
|
807
852
|
await scheduler.clearBackoff(domain);
|
|
808
853
|
return result;
|
|
809
|
-
} catch (err) {
|
|
854
|
+
} catch (err: unknown) {
|
|
810
855
|
// Record failure for backoff tracking
|
|
811
|
-
await scheduler.recordFailure(domain, err.message);
|
|
812
|
-
eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${err.message}, falling back to http-01`);
|
|
856
|
+
await scheduler.recordFailure(domain, (err as Error).message);
|
|
857
|
+
eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${(err as Error).message}, falling back to http-01`);
|
|
813
858
|
return 'http01';
|
|
814
859
|
}
|
|
815
860
|
};
|
|
@@ -1248,21 +1293,21 @@ export class DcRouter {
|
|
|
1248
1293
|
// Wire delivery events to MetricsManager and logger
|
|
1249
1294
|
if (this.metricsManager && this.emailServer.deliverySystem) {
|
|
1250
1295
|
this.emailServer.deliverySystem.on('deliveryStart', (item: any) => {
|
|
1251
|
-
this.metricsManager
|
|
1296
|
+
this.metricsManager!.trackEmailReceived(item?.from);
|
|
1252
1297
|
logger.log('info', `Email delivery started: ${item?.from} → ${item?.to}`, { zone: 'email' });
|
|
1253
1298
|
});
|
|
1254
1299
|
this.emailServer.deliverySystem.on('deliverySuccess', (item: any) => {
|
|
1255
|
-
this.metricsManager
|
|
1300
|
+
this.metricsManager!.trackEmailSent(item?.to);
|
|
1256
1301
|
logger.log('info', `Email delivered to ${item?.to}`, { zone: 'email' });
|
|
1257
1302
|
});
|
|
1258
1303
|
this.emailServer.deliverySystem.on('deliveryFailed', (item: any, error: any) => {
|
|
1259
|
-
this.metricsManager
|
|
1304
|
+
this.metricsManager!.trackEmailFailed(item?.to, error?.message);
|
|
1260
1305
|
logger.log('warn', `Email delivery failed to ${item?.to}: ${error?.message}`, { zone: 'email' });
|
|
1261
1306
|
});
|
|
1262
1307
|
}
|
|
1263
1308
|
if (this.metricsManager && this.emailServer) {
|
|
1264
1309
|
this.emailServer.on('bounceProcessed', () => {
|
|
1265
|
-
this.metricsManager
|
|
1310
|
+
this.metricsManager!.trackEmailBounced();
|
|
1266
1311
|
logger.log('warn', 'Email bounce processed', { zone: 'email' });
|
|
1267
1312
|
});
|
|
1268
1313
|
}
|
|
@@ -1305,12 +1350,12 @@ export class DcRouter {
|
|
|
1305
1350
|
}
|
|
1306
1351
|
|
|
1307
1352
|
logger.log('info', 'All unified email components stopped');
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
logger.log('error', `Error stopping unified email components: ${error.message}`);
|
|
1353
|
+
} catch (error: unknown) {
|
|
1354
|
+
logger.log('error', `Error stopping unified email components: ${(error as Error).message}`);
|
|
1310
1355
|
throw error;
|
|
1311
1356
|
}
|
|
1312
1357
|
}
|
|
1313
|
-
|
|
1358
|
+
|
|
1314
1359
|
/**
|
|
1315
1360
|
* Update domain rules for email routing
|
|
1316
1361
|
* @param rules New domain rules to apply
|
|
@@ -1468,7 +1513,7 @@ export class DcRouter {
|
|
|
1468
1513
|
this.dnsServer.on('query', (event: plugins.smartdns.dnsServerMod.IDnsQueryCompletedEvent) => {
|
|
1469
1514
|
// Metrics tracking
|
|
1470
1515
|
for (const question of event.questions) {
|
|
1471
|
-
this.metricsManager
|
|
1516
|
+
this.metricsManager?.trackDnsQuery(
|
|
1472
1517
|
question.type,
|
|
1473
1518
|
question.name,
|
|
1474
1519
|
false,
|
|
@@ -1553,8 +1598,8 @@ export class DcRouter {
|
|
|
1553
1598
|
// Use the built-in socket handler from smartdns
|
|
1554
1599
|
// This handles HTTP/2, DoH protocol, etc.
|
|
1555
1600
|
await (this.dnsServer as any).handleHttpsSocket(socket);
|
|
1556
|
-
} catch (error) {
|
|
1557
|
-
logger.log('error', `DNS socket handler error: ${error.message}`);
|
|
1601
|
+
} catch (error: unknown) {
|
|
1602
|
+
logger.log('error', `DNS socket handler error: ${(error as Error).message}`);
|
|
1558
1603
|
if (!socket.destroyed) {
|
|
1559
1604
|
socket.destroy();
|
|
1560
1605
|
}
|
|
@@ -1695,14 +1740,14 @@ export class DcRouter {
|
|
|
1695
1740
|
} else {
|
|
1696
1741
|
logger.log('warn', `Invalid DKIM record structure in ${file}`);
|
|
1697
1742
|
}
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
logger.log('error', `Failed to load DKIM record from ${file}: ${error.message}`);
|
|
1743
|
+
} catch (error: unknown) {
|
|
1744
|
+
logger.log('error', `Failed to load DKIM record from ${file}: ${(error as Error).message}`);
|
|
1700
1745
|
}
|
|
1701
1746
|
}
|
|
1702
|
-
} catch (error) {
|
|
1703
|
-
logger.log('error', `Failed to load DKIM records: ${error.message}`);
|
|
1747
|
+
} catch (error: unknown) {
|
|
1748
|
+
logger.log('error', `Failed to load DKIM records: ${(error as Error).message}`);
|
|
1704
1749
|
}
|
|
1705
|
-
|
|
1750
|
+
|
|
1706
1751
|
return records;
|
|
1707
1752
|
}
|
|
1708
1753
|
|
|
@@ -1734,11 +1779,11 @@ export class DcRouter {
|
|
|
1734
1779
|
// This ensures keys are ready even if DNS mode changes later
|
|
1735
1780
|
await dkimCreator.handleDKIMKeysForDomain(domainConfig.domain);
|
|
1736
1781
|
logger.log('info', `DKIM keys initialized for ${domainConfig.domain}`);
|
|
1737
|
-
} catch (error) {
|
|
1738
|
-
logger.log('error', `Failed to initialize DKIM for ${domainConfig.domain}: ${error.message}`);
|
|
1782
|
+
} catch (error: unknown) {
|
|
1783
|
+
logger.log('error', `Failed to initialize DKIM for ${domainConfig.domain}: ${(error as Error).message}`);
|
|
1739
1784
|
}
|
|
1740
1785
|
}
|
|
1741
|
-
|
|
1786
|
+
|
|
1742
1787
|
logger.log('info', 'DKIM initialization complete');
|
|
1743
1788
|
}
|
|
1744
1789
|
|
|
@@ -1779,10 +1824,10 @@ export class DcRouter {
|
|
|
1779
1824
|
} else {
|
|
1780
1825
|
logger.log('warn', 'Could not auto-discover public IPv4 address');
|
|
1781
1826
|
}
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
logger.log('error', `Failed to auto-discover public IP: ${error.message}`);
|
|
1827
|
+
} catch (error: unknown) {
|
|
1828
|
+
logger.log('error', `Failed to auto-discover public IP: ${(error as Error).message}`);
|
|
1784
1829
|
}
|
|
1785
|
-
|
|
1830
|
+
|
|
1786
1831
|
if (!publicIp) {
|
|
1787
1832
|
logger.log('warn', 'No public IP available. Nameserver A records require either proxyIps, publicIp, or successful auto-discovery.');
|
|
1788
1833
|
}
|
|
@@ -1876,8 +1921,8 @@ export class DcRouter {
|
|
|
1876
1921
|
}
|
|
1877
1922
|
|
|
1878
1923
|
return null;
|
|
1879
|
-
} catch (error) {
|
|
1880
|
-
logger.log('warn', `Failed to detect public IP: ${error.message}`);
|
|
1924
|
+
} catch (error: unknown) {
|
|
1925
|
+
logger.log('warn', `Failed to detect public IP: ${(error as Error).message}`);
|
|
1881
1926
|
return null;
|
|
1882
1927
|
}
|
|
1883
1928
|
}
|
|
@@ -1911,8 +1956,8 @@ export class DcRouter {
|
|
|
1911
1956
|
const keyPem = plugins.fs.readFileSync(riCfg.tls.keyPath, 'utf8');
|
|
1912
1957
|
tlsConfig = { certPem, keyPem };
|
|
1913
1958
|
logger.log('info', 'Using explicit TLS cert/key for RemoteIngress tunnel');
|
|
1914
|
-
} catch (err) {
|
|
1915
|
-
logger.log('warn', `Failed to read RemoteIngress TLS cert/key files: ${err.message}`);
|
|
1959
|
+
} catch (err: unknown) {
|
|
1960
|
+
logger.log('warn', `Failed to read RemoteIngress TLS cert/key files: ${(err as Error).message}`);
|
|
1916
1961
|
}
|
|
1917
1962
|
}
|
|
1918
1963
|
|
package/ts/config/validator.ts
CHANGED
|
@@ -170,7 +170,7 @@ export class ConfigValidator {
|
|
|
170
170
|
} else if (rules.items.schema && itemType === 'object') {
|
|
171
171
|
const itemResult = this.validate(value[i], rules.items.schema);
|
|
172
172
|
if (!itemResult.valid) {
|
|
173
|
-
errors.push(...itemResult.errors
|
|
173
|
+
errors.push(...itemResult.errors!.map(err => `${key}[${i}].${err}`));
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -181,7 +181,7 @@ export class ConfigValidator {
|
|
|
181
181
|
if (rules.schema) {
|
|
182
182
|
const nestedResult = this.validate(value, rules.schema);
|
|
183
183
|
if (!nestedResult.valid) {
|
|
184
|
-
errors.push(...nestedResult.errors
|
|
184
|
+
errors.push(...nestedResult.errors!.map(err => `${key}.${err}`));
|
|
185
185
|
}
|
|
186
186
|
validatedConfig[key] = nestedResult.config;
|
|
187
187
|
}
|
|
@@ -233,8 +233,8 @@ export class ConfigValidator {
|
|
|
233
233
|
|
|
234
234
|
// Apply defaults to array items
|
|
235
235
|
if (result[key] && rules.type === 'array' && rules.items && rules.items.schema) {
|
|
236
|
-
result[key] = result[key].map(item =>
|
|
237
|
-
typeof item === 'object' ? this.applyDefaults(item, rules.items
|
|
236
|
+
result[key] = result[key].map(item =>
|
|
237
|
+
typeof item === 'object' ? this.applyDefaults(item, rules.items!.schema!) : item
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -255,7 +255,7 @@ export class ConfigValidator {
|
|
|
255
255
|
|
|
256
256
|
if (!result.valid) {
|
|
257
257
|
throw new ValidationError(
|
|
258
|
-
`Configuration validation failed: ${result.errors
|
|
258
|
+
`Configuration validation failed: ${result.errors!.join(', ')}`,
|
|
259
259
|
'CONFIG_VALIDATION_ERROR',
|
|
260
260
|
{ data: { errors: result.errors } }
|
|
261
261
|
);
|
package/ts/errors/base.errors.ts
CHANGED
|
@@ -296,11 +296,11 @@ export class MetricsManager {
|
|
|
296
296
|
const proxyMetrics = this.dcRouter.smartProxy ? this.dcRouter.smartProxy.getMetrics() : null;
|
|
297
297
|
|
|
298
298
|
if (!proxyMetrics) {
|
|
299
|
-
return [];
|
|
299
|
+
return [] as Array<{ type: string; count: number; source: string; lastActivity: Date }>;
|
|
300
300
|
}
|
|
301
|
-
|
|
301
|
+
|
|
302
302
|
const connectionsByRoute = proxyMetrics.connections.byRoute();
|
|
303
|
-
const connectionInfo = [];
|
|
303
|
+
const connectionInfo: Array<{ type: string; count: number; source: string; lastActivity: Date }> = [];
|
|
304
304
|
|
|
305
305
|
for (const [routeName, count] of connectionsByRoute) {
|
|
306
306
|
connectionInfo.push({
|
|
@@ -7,7 +7,7 @@ import { requireValidIdentity, requireAdminIdentity } from './helpers/guards.js'
|
|
|
7
7
|
|
|
8
8
|
export class OpsServer {
|
|
9
9
|
public dcRouterRef: DcRouter;
|
|
10
|
-
public server
|
|
10
|
+
public server!: plugins.typedserver.utilityservers.UtilityWebsiteServer;
|
|
11
11
|
|
|
12
12
|
// Main TypedRouter — unauthenticated endpoints (login/logout/verify) and own-auth handlers
|
|
13
13
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
@@ -17,17 +17,17 @@ export class OpsServer {
|
|
|
17
17
|
public adminRouter = new plugins.typedrequest.TypedRouter<{ request: { identity: interfaces.data.IIdentity } }>();
|
|
18
18
|
|
|
19
19
|
// Handler instances
|
|
20
|
-
public adminHandler
|
|
21
|
-
private configHandler
|
|
22
|
-
private logsHandler
|
|
23
|
-
private securityHandler
|
|
24
|
-
private statsHandler
|
|
25
|
-
private radiusHandler
|
|
26
|
-
private emailOpsHandler
|
|
27
|
-
private certificateHandler
|
|
28
|
-
private remoteIngressHandler
|
|
29
|
-
private routeManagementHandler
|
|
30
|
-
private apiTokenHandler
|
|
20
|
+
public adminHandler!: handlers.AdminHandler;
|
|
21
|
+
private configHandler!: handlers.ConfigHandler;
|
|
22
|
+
private logsHandler!: handlers.LogsHandler;
|
|
23
|
+
private securityHandler!: handlers.SecurityHandler;
|
|
24
|
+
private statsHandler!: handlers.StatsHandler;
|
|
25
|
+
private radiusHandler!: handlers.RadiusHandler;
|
|
26
|
+
private emailOpsHandler!: handlers.EmailOpsHandler;
|
|
27
|
+
private certificateHandler!: handlers.CertificateHandler;
|
|
28
|
+
private remoteIngressHandler!: handlers.RemoteIngressHandler;
|
|
29
|
+
private routeManagementHandler!: handlers.RouteManagementHandler;
|
|
30
|
+
private apiTokenHandler!: handlers.ApiTokenHandler;
|
|
31
31
|
|
|
32
32
|
constructor(dcRouterRefArg: DcRouter) {
|
|
33
33
|
this.dcRouterRef = dcRouterRefArg;
|
|
@@ -39,7 +39,7 @@ export class OpsServer {
|
|
|
39
39
|
public async start() {
|
|
40
40
|
this.server = new plugins.typedserver.utilityservers.UtilityWebsiteServer({
|
|
41
41
|
domain: 'localhost',
|
|
42
|
-
feedMetadata:
|
|
42
|
+
feedMetadata: undefined,
|
|
43
43
|
serveDir: paths.distServe,
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -12,7 +12,7 @@ export class AdminHandler {
|
|
|
12
12
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
13
13
|
|
|
14
14
|
// JWT instance
|
|
15
|
-
public smartjwtInstance
|
|
15
|
+
public smartjwtInstance!: plugins.smartjwt.SmartJwt<IJwtData>;
|
|
16
16
|
|
|
17
17
|
// Simple in-memory user storage (in production, use proper database)
|
|
18
18
|
private users = new Map<string, {
|
|
@@ -311,8 +311,8 @@ export class CertificateHandler {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
return { success: true, message: `Certificate reprovisioning triggered for route '${routeName}'` };
|
|
314
|
-
} catch (err) {
|
|
315
|
-
return { success: false, message: err.message || 'Failed to reprovision certificate' };
|
|
314
|
+
} catch (err: unknown) {
|
|
315
|
+
return { success: false, message: (err as Error).message || 'Failed to reprovision certificate' };
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
@@ -340,8 +340,8 @@ export class CertificateHandler {
|
|
|
340
340
|
try {
|
|
341
341
|
await dcRouter.smartAcme.getCertificateForDomain(domain);
|
|
342
342
|
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}'` };
|
|
343
|
-
} catch (err) {
|
|
344
|
-
return { success: false, message: err.message || `Failed to reprovision certificate for ${domain}` };
|
|
343
|
+
} catch (err: unknown) {
|
|
344
|
+
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
|
|
@@ -351,8 +351,8 @@ export class CertificateHandler {
|
|
|
351
351
|
try {
|
|
352
352
|
await smartProxy.provisionCertificate(routeNames[0]);
|
|
353
353
|
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}' via route '${routeNames[0]}'` };
|
|
354
|
-
} catch (err) {
|
|
355
|
-
return { success: false, message: err.message || `Failed to reprovision certificate for ${domain}` };
|
|
354
|
+
} catch (err: unknown) {
|
|
355
|
+
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
|
|
@@ -52,8 +52,8 @@ export class RadiusHandler {
|
|
|
52
52
|
try {
|
|
53
53
|
await radiusServer.addClient(dataArg.client);
|
|
54
54
|
return { success: true };
|
|
55
|
-
} catch (error) {
|
|
56
|
-
return { success: false, message: error.message };
|
|
55
|
+
} catch (error: unknown) {
|
|
56
|
+
return { success: false, message: (error as Error).message };
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
)
|
|
@@ -144,8 +144,8 @@ export class RadiusHandler {
|
|
|
144
144
|
updatedAt: mapping.updatedAt,
|
|
145
145
|
},
|
|
146
146
|
};
|
|
147
|
-
} catch (error) {
|
|
148
|
-
return { success: false, message: error.message };
|
|
147
|
+
} catch (error: unknown) {
|
|
148
|
+
return { success: false, message: (error as Error).message };
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
)
|