@serve.zone/dcrouter 18.9.3 → 18.9.5
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/deno.json +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +5 -4
- package/dist_ts/db/documents/classes.authentication-event.doc.d.ts +0 -1
- package/dist_ts/db/documents/classes.authentication-event.doc.js +176 -259
- package/dist_ts/db/documents/classes.email-traffic-bucket.doc.js +110 -188
- package/dist_ts/dns/manager.dns-authority.d.ts +5 -0
- package/dist_ts/dns/manager.dns-authority.js +97 -53
- package/dist_ts/dns/manager.dns.js +23 -5
- package/dist_ts/email/classes.email-domain.manager.js +14 -3
- package/dist_ts/email/classes.email-settings.manager.js +26 -6
- package/dist_ts/email/classes.mail-dns-sync.js +43 -11
- package/dist_ts/monitoring/classes.metricscache.d.ts +28 -19
- package/dist_ts/monitoring/classes.metricscache.js +133 -62
- package/dist_ts/monitoring/classes.metricsmanager.js +7 -5
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +46 -9
- package/dist_ts/security/classes.authentication-event-manager.d.ts +5 -1
- package/dist_ts/security/classes.authentication-event-manager.js +32 -22
- package/dist_ts/vpn/classes.vpn-manager.js +34 -2
- package/dist_ts_migrations/index.d.ts +4 -1
- package/dist_ts_migrations/index.js +18 -3
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +7 -6
- package/readme.hints.md +84 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +4 -3
- package/ts/db/documents/classes.authentication-event.doc.ts +64 -87
- package/ts/db/documents/classes.email-traffic-bucket.doc.ts +10 -9
- package/ts/dns/manager.dns-authority.ts +92 -54
- package/ts/dns/manager.dns.ts +20 -4
- package/ts/email/classes.email-domain.manager.ts +12 -2
- package/ts/email/classes.email-settings.manager.ts +23 -5
- package/ts/email/classes.mail-dns-sync.ts +45 -10
- package/ts/monitoring/classes.metricscache.ts +133 -79
- package/ts/monitoring/classes.metricsmanager.ts +6 -4
- package/ts/plugins.ts +2 -0
- package/ts/remoteingress/classes.remoteingress-manager.ts +42 -8
- package/ts/security/classes.authentication-event-manager.ts +31 -20
- package/ts/vpn/classes.vpn-manager.ts +32 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import { logger } from '../logger.js';
|
|
3
3
|
import type { DcRouter } from '../classes.dcrouter.js';
|
|
4
|
+
import { MetricsCache } from '../monitoring/classes.metricscache.js';
|
|
4
5
|
import { AuthenticationEventDoc } from '../db/documents/classes.authentication-event.doc.js';
|
|
5
6
|
import type {
|
|
6
7
|
IAuthenticationEvent,
|
|
@@ -29,16 +30,23 @@ export class AuthenticationEventManager {
|
|
|
29
30
|
private recentEvents = new Map<string, IAuthenticationEvent>();
|
|
30
31
|
private flushInFlight?: Promise<void>;
|
|
31
32
|
private pruneInFlight?: Promise<number>;
|
|
32
|
-
private
|
|
33
|
+
private windowStatsCache = new MetricsCache(1_000, { maxAggregateEntries: 8, maxInFlight: 8 });
|
|
34
|
+
private lastPersistenceAvailable?: boolean;
|
|
33
35
|
|
|
34
36
|
constructor(private dcRouterRef: DcRouter) {}
|
|
35
37
|
|
|
38
|
+
public start(): void { this.windowStatsCache.start(); }
|
|
39
|
+
|
|
40
|
+
/** Drain observer reads; MetricsManager still owns the final persistence flush. */
|
|
41
|
+
public async stop(): Promise<void> { await this.windowStatsCache.stop(); }
|
|
42
|
+
|
|
36
43
|
private canPersist(): boolean {
|
|
37
44
|
return this.dcRouterRef.options.dbConfig?.enabled !== false
|
|
38
45
|
&& Boolean(this.dcRouterRef.dcRouterDb?.isReady());
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
private rememberEvent(eventArg: IAuthenticationEvent): void {
|
|
49
|
+
this.windowStatsCache.clear();
|
|
42
50
|
this.pendingEvents.set(eventArg.id, eventArg);
|
|
43
51
|
this.recentEvents.set(eventArg.id, eventArg);
|
|
44
52
|
while (this.pendingEvents.size > MAX_IN_MEMORY_EVENTS) {
|
|
@@ -108,7 +116,11 @@ export class AuthenticationEventManager {
|
|
|
108
116
|
|
|
109
117
|
public flushBuffered(): Promise<void> {
|
|
110
118
|
if (this.flushInFlight) return this.flushInFlight;
|
|
111
|
-
const
|
|
119
|
+
const mutates = this.canPersist() && this.pendingEvents.size > 0;
|
|
120
|
+
if (mutates) this.windowStatsCache.clear();
|
|
121
|
+
const run = this.flushBufferedUnlocked().finally(() => {
|
|
122
|
+
if (mutates) this.windowStatsCache.clear();
|
|
123
|
+
});
|
|
112
124
|
this.flushInFlight = run;
|
|
113
125
|
run.then(
|
|
114
126
|
() => { if (this.flushInFlight === run) this.flushInFlight = undefined; },
|
|
@@ -119,9 +131,10 @@ export class AuthenticationEventManager {
|
|
|
119
131
|
|
|
120
132
|
public pruneBefore(cutoffArg: number): Promise<number> {
|
|
121
133
|
if (this.pruneInFlight) return this.pruneInFlight;
|
|
122
|
-
|
|
134
|
+
this.windowStatsCache.clear();
|
|
135
|
+
const run = (this.canPersist()
|
|
123
136
|
? AuthenticationEventDoc.pruneBefore(cutoffArg)
|
|
124
|
-
: Promise.resolve(0);
|
|
137
|
+
: Promise.resolve(0)).finally(() => { this.windowStatsCache.clear(); });
|
|
125
138
|
this.pruneInFlight = run;
|
|
126
139
|
run.then(
|
|
127
140
|
() => { if (this.pruneInFlight === run) this.pruneInFlight = undefined; },
|
|
@@ -151,12 +164,6 @@ export class AuthenticationEventManager {
|
|
|
151
164
|
return this.getInMemoryWindow(cutoff, limitArg);
|
|
152
165
|
}
|
|
153
166
|
|
|
154
|
-
try {
|
|
155
|
-
await this.flushBuffered();
|
|
156
|
-
} catch (error) {
|
|
157
|
-
logger.log('warn', `Unable to flush authentication event buffer: ${(error as Error).message}`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
167
|
try {
|
|
161
168
|
const durable = await AuthenticationEventDoc.getWindowSummary(cutoff, limitArg);
|
|
162
169
|
const buffered = [...this.pendingEvents.values()]
|
|
@@ -190,7 +197,7 @@ export class AuthenticationEventManager {
|
|
|
190
197
|
}
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
public getWindowStats(
|
|
200
|
+
public async getWindowStats(
|
|
194
201
|
windowMsArg = 24 * 60 * 60 * 1000,
|
|
195
202
|
limitArg = 100,
|
|
196
203
|
): Promise<IAuthenticationWindowStats> {
|
|
@@ -200,21 +207,25 @@ export class AuthenticationEventManager {
|
|
|
200
207
|
if (!Number.isSafeInteger(limitArg) || limitArg <= 0 || limitArg > 500) {
|
|
201
208
|
return Promise.reject(new Error('Authentication event limit must be from 1 to 500'));
|
|
202
209
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
(
|
|
210
|
+
const persistenceAvailable = this.canPersist();
|
|
211
|
+
if (this.lastPersistenceAvailable !== persistenceAvailable) this.windowStatsCache.clear();
|
|
212
|
+
this.lastPersistenceAvailable = persistenceAvailable;
|
|
213
|
+
// Drain before entering the shared read boundary. Recording authentication itself never waits here.
|
|
214
|
+
try {
|
|
215
|
+
await this.flushBuffered();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logger.log('warn', `Unable to flush authentication event buffer: ${(error as Error).message}`);
|
|
218
|
+
}
|
|
219
|
+
return await this.windowStatsCache.get(
|
|
220
|
+
`${windowMsArg}:${limitArg}`,
|
|
221
|
+
() => this.getWindowStatsUnlocked(windowMsArg, limitArg),
|
|
211
222
|
);
|
|
212
|
-
return run;
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
/** Test-only reset for the DcRouter-owned in-process buffers. */
|
|
216
226
|
public async resetForTests(): Promise<void> {
|
|
217
227
|
await this.flushInFlight?.catch(() => undefined);
|
|
228
|
+
this.windowStatsCache.clear();
|
|
218
229
|
this.pendingEvents.clear();
|
|
219
230
|
this.recentEvents.clear();
|
|
220
231
|
}
|
|
@@ -811,6 +811,8 @@ export class VpnManager {
|
|
|
811
811
|
client: Pick<VpnClientDoc, 'useHostIp' | 'useDhcp' | 'staticIp' | 'forceVlan' | 'vlanId'>,
|
|
812
812
|
): void {
|
|
813
813
|
client.useHostIp = client.useHostIp === true;
|
|
814
|
+
client.staticIp = client.staticIp ?? undefined;
|
|
815
|
+
client.vlanId = client.vlanId ?? undefined;
|
|
814
816
|
|
|
815
817
|
if (!client.useHostIp) {
|
|
816
818
|
client.useDhcp = false;
|
|
@@ -883,6 +885,35 @@ export class VpnManager {
|
|
|
883
885
|
}
|
|
884
886
|
|
|
885
887
|
private async persistClient(client: VpnClientDoc): Promise<void> {
|
|
886
|
-
|
|
888
|
+
if (client.creationStatus === 'new') {
|
|
889
|
+
await client.save();
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const persisted = await VpnClientDoc.atomicUpdate({ clientId: client.clientId }, {
|
|
893
|
+
$set: {
|
|
894
|
+
enabled: client.enabled,
|
|
895
|
+
noisePublicKey: client.noisePublicKey,
|
|
896
|
+
wgPublicKey: client.wgPublicKey,
|
|
897
|
+
createdAt: client.createdAt,
|
|
898
|
+
updatedAt: client.updatedAt,
|
|
899
|
+
...(client.targetProfileIds !== undefined ? { targetProfileIds: client.targetProfileIds } : {}),
|
|
900
|
+
...(client.description !== undefined ? { description: client.description } : {}),
|
|
901
|
+
...(client.assignedIp !== undefined ? { assignedIp: client.assignedIp } : {}),
|
|
902
|
+
...(client.wgPrivateKey !== undefined ? { wgPrivateKey: client.wgPrivateKey } : {}),
|
|
903
|
+
...(client.expiresAt !== undefined ? { expiresAt: client.expiresAt } : {}),
|
|
904
|
+
...(client.destinationAllowList !== undefined ? { destinationAllowList: client.destinationAllowList } : {}),
|
|
905
|
+
...(client.destinationBlockList !== undefined ? { destinationBlockList: client.destinationBlockList } : {}),
|
|
906
|
+
...(client.useHostIp !== undefined ? { useHostIp: client.useHostIp } : {}),
|
|
907
|
+
...(client.useDhcp !== undefined ? { useDhcp: client.useDhcp } : {}),
|
|
908
|
+
...(client.staticIp !== undefined ? { staticIp: client.staticIp } : {}),
|
|
909
|
+
...(client.forceVlan !== undefined ? { forceVlan: client.forceVlan } : {}),
|
|
910
|
+
...(client.vlanId !== undefined ? { vlanId: client.vlanId } : {}),
|
|
911
|
+
},
|
|
912
|
+
$unset: {
|
|
913
|
+
...(client.staticIp === undefined ? { staticIp: true } : {}),
|
|
914
|
+
...(client.vlanId === undefined ? { vlanId: true } : {}),
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
if (persisted.matchedCount !== 1) throw new Error('VPN client disappeared during update');
|
|
887
918
|
}
|
|
888
919
|
}
|