@serve.zone/dcrouter 16.5.0 → 17.1.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/.smartconfig.json +5 -2
- package/deno.json +1 -1
- package/dist_serve/bundle.js +1316 -1151
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +6 -1
- package/dist_ts/classes.dcrouter.js +119 -41
- package/dist_ts/db/classes.cache.cleaner.d.ts +3 -0
- package/dist_ts/db/classes.cache.cleaner.js +7 -3
- package/dist_ts/db/classes.dcrouter-db.js +1 -2
- package/dist_ts/db/documents/classes.cached.email.d.ts +41 -4
- package/dist_ts/db/documents/classes.cached.email.js +36 -5
- package/dist_ts/db/documents/classes.dns-record.doc.js +3 -1
- package/dist_ts/db/documents/classes.smartmta-storage.doc.d.ts +8 -0
- package/dist_ts/db/documents/classes.smartmta-storage.doc.js +91 -0
- package/dist_ts/db/documents/index.d.ts +1 -0
- package/dist_ts/db/documents/index.js +2 -1
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +0 -1
- package/dist_ts/dns/classes.dns-server-runtime.js +4 -36
- package/dist_ts/dns/index.d.ts +1 -0
- package/dist_ts/dns/index.js +2 -1
- package/dist_ts/dns/manager.dns.d.ts +1 -0
- package/dist_ts/dns/manager.dns.js +49 -14
- package/dist_ts/dns/providers/cloudflare.provider.js +19 -4
- package/dist_ts/dns/txt-record-presentation.d.ts +20 -0
- package/dist_ts/dns/txt-record-presentation.js +131 -0
- package/dist_ts/email/classes.accepted-email-spool.d.ts +32 -3
- package/dist_ts/email/classes.accepted-email-spool.js +174 -13
- package/dist_ts/email/classes.email-domain.manager.d.ts +14 -1
- package/dist_ts/email/classes.email-domain.manager.js +151 -8
- package/dist_ts/email/classes.email-settings.manager.js +2 -1
- package/dist_ts/email/classes.mail-dns-sync.d.ts +31 -5
- package/dist_ts/email/classes.mail-dns-sync.js +508 -117
- package/dist_ts/email/classes.smartmta-blob-storage-manager.d.ts +27 -0
- package/dist_ts/email/classes.smartmta-blob-storage-manager.js +110 -0
- package/dist_ts/email/classes.smartmta-storage-manager.d.ts +6 -8
- package/dist_ts/email/classes.smartmta-storage-manager.js +63 -86
- package/dist_ts/email/classes.workapp-mail-manager.d.ts +1 -0
- package/dist_ts/email/classes.workapp-mail-manager.js +31 -4
- package/dist_ts/email/index.d.ts +4 -0
- package/dist_ts/email/index.js +5 -1
- package/dist_ts/email/mail-dns-txt-policy.d.ts +18 -0
- package/dist_ts/email/mail-dns-txt-policy.js +466 -0
- package/dist_ts/email/reconcile-mail-dns-singleton.d.ts +47 -0
- package/dist_ts/email/reconcile-mail-dns-singleton.js +159 -0
- package/dist_ts/email/smartmta-storage-policy.d.ts +9 -0
- package/dist_ts/email/smartmta-storage-policy.js +58 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +8 -0
- package/dist_ts/opsserver/classes.opsserver.js +34 -1
- package/dist_ts/opsserver/handlers/certificate.handler.js +25 -4
- package/dist_ts/opsserver/handlers/config.handler.js +9 -5
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +19 -2
- package/dist_ts/opsserver/handlers/dns-record.handler.js +25 -4
- package/dist_ts/opsserver/handlers/domain.handler.js +37 -3
- package/dist_ts/opsserver/handlers/email-domain.handler.js +5 -7
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +1 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +26 -7
- package/dist_ts/opsserver/handlers/index.d.ts +1 -0
- package/dist_ts/opsserver/handlers/index.js +2 -1
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +1 -1
- package/dist_ts/opsserver/handlers/logs.handler.js +3 -3
- package/dist_ts/opsserver/handlers/realtime.handler.d.ts +37 -0
- package/dist_ts/opsserver/handlers/realtime.handler.js +154 -0
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +22 -1
- package/dist_ts/opsserver/handlers/route-management.handler.js +47 -5
- package/dist_ts/opsserver/handlers/stats.handler.js +3 -5
- package/dist_ts/paths.d.ts +0 -11
- package/dist_ts/paths.js +1 -22
- package/dist_ts/plugins.d.ts +2 -35
- package/dist_ts/plugins.js +3 -63
- package/dist_ts_interfaces/data/email-domain.d.ts +14 -0
- package/dist_ts_interfaces/requests/config.d.ts +1 -1
- package/dist_ts_interfaces/requests/email-ops.d.ts +34 -1
- package/dist_ts_interfaces/requests/index.d.ts +1 -0
- package/dist_ts_interfaces/requests/index.js +2 -1
- package/dist_ts_interfaces/requests/realtime.d.ts +38 -0
- package/dist_ts_interfaces/requests/realtime.js +11 -0
- package/dist_ts_migrations/index.d.ts +8 -0
- package/dist_ts_migrations/index.js +489 -12
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate/certificates.js +9 -2
- package/dist_ts_web/appstate/domains.js +16 -5
- package/dist_ts_web/appstate/email-domains.d.ts +3 -0
- package/dist_ts_web/appstate/email-domains.js +47 -14
- package/dist_ts_web/appstate/email-ops.d.ts +1 -0
- package/dist_ts_web/appstate/email-ops.js +20 -2
- package/dist_ts_web/appstate/realtime.d.ts +27 -0
- package/dist_ts_web/appstate/realtime.js +71 -0
- package/dist_ts_web/appstate/remoteingress.js +9 -3
- package/dist_ts_web/appstate/routes.js +16 -5
- package/dist_ts_web/appstate/runtime.js +135 -7
- package/dist_ts_web/appstate.d.ts +1 -0
- package/dist_ts_web/appstate.js +2 -1
- package/dist_ts_web/elements/email/email-domain-creation-progress.d.ts +33 -0
- package/dist_ts_web/elements/email/email-domain-creation-progress.js +257 -0
- package/dist_ts_web/elements/email/ops-view-email-domains.js +233 -68
- package/dist_ts_web/elements/email/ops-view-emails.d.ts +4 -2
- package/dist_ts_web/elements/email/ops-view-emails.js +25 -13
- package/dist_ts_web/elements/overview/ops-view-config.js +5 -5
- package/package.json +23 -19
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +136 -44
- package/ts/db/classes.cache.cleaner.ts +18 -3
- package/ts/db/classes.dcrouter-db.ts +0 -2
- package/ts/db/documents/classes.cached.email.ts +78 -4
- package/ts/db/documents/classes.dns-record.doc.ts +1 -0
- package/ts/db/documents/classes.smartmta-storage.doc.ts +25 -0
- package/ts/db/documents/index.ts +1 -0
- package/ts/dns/classes.dns-server-runtime.ts +9 -45
- package/ts/dns/index.ts +1 -0
- package/ts/dns/manager.dns.ts +50 -16
- package/ts/dns/providers/cloudflare.provider.ts +21 -3
- package/ts/dns/txt-record-presentation.ts +134 -0
- package/ts/email/classes.accepted-email-spool.ts +220 -14
- package/ts/email/classes.email-domain.manager.ts +201 -7
- package/ts/email/classes.email-settings.manager.ts +1 -0
- package/ts/email/classes.mail-dns-sync.ts +588 -122
- package/ts/email/classes.smartmta-blob-storage-manager.ts +139 -0
- package/ts/email/classes.smartmta-storage-manager.ts +86 -83
- package/ts/email/classes.workapp-mail-manager.ts +47 -4
- package/ts/email/index.ts +4 -0
- package/ts/email/mail-dns-txt-policy.ts +505 -0
- package/ts/email/reconcile-mail-dns-singleton.ts +224 -0
- package/ts/email/smartmta-storage-policy.ts +73 -0
- package/ts/opsserver/classes.opsserver.ts +42 -0
- package/ts/opsserver/handlers/certificate.handler.ts +24 -3
- package/ts/opsserver/handlers/config.handler.ts +9 -5
- package/ts/opsserver/handlers/dns-provider.handler.ts +18 -1
- package/ts/opsserver/handlers/dns-record.handler.ts +24 -3
- package/ts/opsserver/handlers/domain.handler.ts +36 -2
- package/ts/opsserver/handlers/email-domain.handler.ts +4 -5
- package/ts/opsserver/handlers/email-ops.handler.ts +29 -6
- package/ts/opsserver/handlers/index.ts +1 -0
- package/ts/opsserver/handlers/logs.handler.ts +2 -2
- package/ts/opsserver/handlers/realtime.handler.ts +222 -0
- package/ts/opsserver/handlers/remoteingress.handler.ts +21 -0
- package/ts/opsserver/handlers/route-management.handler.ts +46 -4
- package/ts/opsserver/handlers/stats.handler.ts +2 -4
- package/ts/paths.ts +0 -25
- package/ts/plugins.ts +2 -69
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate/certificates.ts +6 -1
- package/ts_web/appstate/domains.ts +11 -4
- package/ts_web/appstate/email-domains.ts +68 -14
- package/ts_web/appstate/email-ops.ts +20 -1
- package/ts_web/appstate/realtime.ts +109 -0
- package/ts_web/appstate/remoteingress.ts +6 -2
- package/ts_web/appstate/routes.ts +11 -4
- package/ts_web/appstate/runtime.ts +176 -7
- package/ts_web/appstate.ts +1 -0
- package/ts_web/elements/email/email-domain-creation-progress.ts +340 -0
- package/ts_web/elements/email/ops-view-email-domains.ts +273 -74
- package/ts_web/elements/email/ops-view-emails.ts +23 -16
- package/ts_web/elements/overview/ops-view-config.ts +4 -4
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { IBlobStorageManager } from '@push.rocks/smartmta';
|
|
3
|
+
|
|
4
|
+
const SMARTMTA_BLOB_NAMESPACES = [
|
|
5
|
+
'/email/queue/',
|
|
6
|
+
'/email/attachments/',
|
|
7
|
+
'/email/messages/',
|
|
8
|
+
] as const;
|
|
9
|
+
const DEFAULT_OBJECT_PREFIX = 'dcrouter/smartmta/';
|
|
10
|
+
const MAX_STORAGE_KEY_BYTES = 1024;
|
|
11
|
+
|
|
12
|
+
type TBlobBucket = Pick<
|
|
13
|
+
plugins.smartbucket.Bucket,
|
|
14
|
+
'fastExists' | 'fastGet' | 'fastPut' | 'fastRemove' | 'listAllObjects'
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
export interface ISmartMtaBlobStorageConfig {
|
|
18
|
+
storageDescriptor: plugins.tsclass.storage.IStorageDescriptor;
|
|
19
|
+
bucketName: string;
|
|
20
|
+
objectPrefix?: string;
|
|
21
|
+
createBucketIfMissing?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeObjectPrefix(prefixArg = DEFAULT_OBJECT_PREFIX): string {
|
|
25
|
+
const prefix = prefixArg.trim().replace(/^\/+|\/+$/g, '');
|
|
26
|
+
if (!prefix || prefix.includes('\\') || prefix.includes('\0')) {
|
|
27
|
+
throw new Error('SmartMTA blob objectPrefix must be a non-empty object-key prefix');
|
|
28
|
+
}
|
|
29
|
+
const segments = prefix.split('/');
|
|
30
|
+
if (segments.some((segment) => !segment || segment === '.' || segment === '..')) {
|
|
31
|
+
throw new Error('SmartMTA blob objectPrefix contains an invalid path segment');
|
|
32
|
+
}
|
|
33
|
+
return `${prefix}/`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeBlobKey(keyArg: string, allowNamespaceRoot = false): string {
|
|
37
|
+
if (
|
|
38
|
+
typeof keyArg !== 'string' ||
|
|
39
|
+
!keyArg.startsWith('/') ||
|
|
40
|
+
keyArg.includes('\\') ||
|
|
41
|
+
keyArg.includes('\0') ||
|
|
42
|
+
Buffer.byteLength(keyArg, 'utf8') > MAX_STORAGE_KEY_BYTES
|
|
43
|
+
) {
|
|
44
|
+
throw new Error('Invalid SmartMTA blob storage key');
|
|
45
|
+
}
|
|
46
|
+
const namespace = SMARTMTA_BLOB_NAMESPACES.find((candidate) => keyArg.startsWith(candidate));
|
|
47
|
+
if (!namespace) {
|
|
48
|
+
throw new Error(`Unsupported SmartMTA blob storage namespace: ${keyArg}`);
|
|
49
|
+
}
|
|
50
|
+
const remainder = keyArg.slice(namespace.length);
|
|
51
|
+
if (!allowNamespaceRoot && !remainder) {
|
|
52
|
+
throw new Error('SmartMTA blob storage key must identify an object');
|
|
53
|
+
}
|
|
54
|
+
if (remainder && remainder.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {
|
|
55
|
+
throw new Error('SmartMTA blob storage key contains an invalid path segment');
|
|
56
|
+
}
|
|
57
|
+
return keyArg;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* SmartMTA blob persistence backed by a SmartBucket bucket.
|
|
62
|
+
*
|
|
63
|
+
* The adapter accepts only SmartMTA queue, attachment, and retained-message
|
|
64
|
+
* namespaces. Every key remains a SmartBucket object key.
|
|
65
|
+
*/
|
|
66
|
+
export class SmartMtaBlobStorageManager implements IBlobStorageManager {
|
|
67
|
+
private readonly objectPrefix: string;
|
|
68
|
+
|
|
69
|
+
public constructor(
|
|
70
|
+
private readonly bucketRef: TBlobBucket,
|
|
71
|
+
objectPrefix = DEFAULT_OBJECT_PREFIX,
|
|
72
|
+
) {
|
|
73
|
+
this.objectPrefix = normalizeObjectPrefix(objectPrefix);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public static async create(
|
|
77
|
+
configArg: ISmartMtaBlobStorageConfig,
|
|
78
|
+
): Promise<SmartMtaBlobStorageManager> {
|
|
79
|
+
const bucketName = configArg.bucketName?.trim();
|
|
80
|
+
if (!bucketName) {
|
|
81
|
+
throw new Error('SmartMTA blob storage requires a bucketName');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const smartBucket = new plugins.smartbucket.SmartBucket(configArg.storageDescriptor);
|
|
85
|
+
let bucket: plugins.smartbucket.Bucket;
|
|
86
|
+
if (configArg.createBucketIfMissing) {
|
|
87
|
+
bucket = await smartBucket.bucketExists(bucketName)
|
|
88
|
+
? await smartBucket.getBucketByName(bucketName)
|
|
89
|
+
: await smartBucket.createBucket(bucketName);
|
|
90
|
+
} else {
|
|
91
|
+
bucket = await smartBucket.getBucketByName(bucketName);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return new SmartMtaBlobStorageManager(bucket, configArg.objectPrefix);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public async get(keyArg: string): Promise<Buffer | null> {
|
|
98
|
+
const objectKey = this.toObjectKey(normalizeBlobKey(keyArg));
|
|
99
|
+
if (!(await this.bucketRef.fastExists({ path: objectKey }))) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return Buffer.from(await this.bucketRef.fastGet({ path: objectKey }));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async set(keyArg: string, valueArg: Buffer): Promise<void> {
|
|
106
|
+
if (!Buffer.isBuffer(valueArg)) {
|
|
107
|
+
throw new TypeError('SmartMTA blob storage values must be Buffers');
|
|
108
|
+
}
|
|
109
|
+
const objectKey = this.toObjectKey(normalizeBlobKey(keyArg));
|
|
110
|
+
await this.bucketRef.fastPut({
|
|
111
|
+
path: objectKey,
|
|
112
|
+
contents: Buffer.from(valueArg),
|
|
113
|
+
overwrite: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public async list(prefixArg: string): Promise<string[]> {
|
|
118
|
+
const prefix = normalizeBlobKey(prefixArg, true);
|
|
119
|
+
const objectPrefix = this.toObjectKey(prefix);
|
|
120
|
+
const keys: string[] = [];
|
|
121
|
+
for await (const objectKey of this.bucketRef.listAllObjects(objectPrefix)) {
|
|
122
|
+
if (!objectKey.startsWith(this.objectPrefix)) {
|
|
123
|
+
throw new Error(`SmartBucket returned an object outside the configured prefix: ${objectKey}`);
|
|
124
|
+
}
|
|
125
|
+
const key = `/${objectKey.slice(this.objectPrefix.length)}`;
|
|
126
|
+
keys.push(normalizeBlobKey(key));
|
|
127
|
+
}
|
|
128
|
+
return keys.sort();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async delete(keyArg: string): Promise<void> {
|
|
132
|
+
const objectKey = this.toObjectKey(normalizeBlobKey(keyArg));
|
|
133
|
+
await this.bucketRef.fastRemove({ path: objectKey });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private toObjectKey(keyArg: string): string {
|
|
137
|
+
return `${this.objectPrefix}${keyArg.slice(1)}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -1,108 +1,111 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { IStorageManager } from '@push.rocks/smartmta';
|
|
3
|
+
import {
|
|
4
|
+
getSmartMtaTextNamespace,
|
|
5
|
+
hashSmartMtaStorageKey,
|
|
6
|
+
normalizeSmartMtaStorageKey,
|
|
7
|
+
validateSmartMtaTextValue,
|
|
8
|
+
} from './smartmta-storage-policy.js';
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
10
|
+
interface ISmartMtaStorageDocument {
|
|
11
|
+
_id: string;
|
|
12
|
+
id: string;
|
|
13
|
+
key: string;
|
|
14
|
+
value: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
updatedAt: string;
|
|
17
|
+
}
|
|
6
18
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
plugins.fsUtils.ensureDirSync(this.resolvedRootDir);
|
|
10
|
-
}
|
|
19
|
+
export class SmartMtaStorageManager implements IStorageManager {
|
|
20
|
+
private readonly collection: any;
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
constructor(smartdataDb: plugins.smartdata.SmartdataDb) {
|
|
23
|
+
this.collection = smartdataDb.mongoDb.collection('SmartMtaStorageDoc');
|
|
14
24
|
}
|
|
15
25
|
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
private validateDocument(
|
|
27
|
+
document: ISmartMtaStorageDocument,
|
|
28
|
+
expectedKey?: string,
|
|
29
|
+
): ISmartMtaStorageDocument {
|
|
30
|
+
const normalizedKey = normalizeSmartMtaStorageKey(document.key);
|
|
31
|
+
const expectedId = hashSmartMtaStorageKey(normalizedKey);
|
|
19
32
|
if (
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
document._id !== expectedId
|
|
34
|
+
|| document.id !== expectedId
|
|
35
|
+
|| document.key !== normalizedKey
|
|
36
|
+
|| (expectedKey && document.key !== expectedKey)
|
|
37
|
+
|| typeof document.value !== 'string'
|
|
22
38
|
) {
|
|
23
|
-
throw new Error(`
|
|
39
|
+
throw new Error(`Corrupt SmartMTA storage document: ${document._id}`);
|
|
24
40
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
private toStorageKey(filePath: string): string {
|
|
29
|
-
const relativePath = plugins.path.relative(this.resolvedRootDir, filePath).split(plugins.path.sep).join('/');
|
|
30
|
-
return `/${relativePath}`;
|
|
41
|
+
validateSmartMtaTextValue(document.key, document.value);
|
|
42
|
+
return document;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
public async get(key: string): Promise<string | null> {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
46
|
+
const normalizedKey = normalizeSmartMtaStorageKey(key);
|
|
47
|
+
getSmartMtaTextNamespace(normalizedKey);
|
|
48
|
+
const id = hashSmartMtaStorageKey(normalizedKey);
|
|
49
|
+
const document = await this.collection.findOne({ _id: id });
|
|
50
|
+
return document ? this.validateDocument(document, normalizedKey).value : null;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
public async set(key: string, value: string): Promise<void> {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
const normalizedKey = normalizeSmartMtaStorageKey(key);
|
|
55
|
+
validateSmartMtaTextValue(normalizedKey, value);
|
|
56
|
+
const id = hashSmartMtaStorageKey(normalizedKey);
|
|
57
|
+
const existing = await this.collection.findOne({ _id: id });
|
|
58
|
+
if (existing) {
|
|
59
|
+
this.validateDocument(existing, normalizedKey);
|
|
60
|
+
}
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const now = new Date().toISOString();
|
|
63
|
+
await this.collection.updateOne(
|
|
64
|
+
{ _id: id },
|
|
65
|
+
{
|
|
66
|
+
$set: {
|
|
67
|
+
id,
|
|
68
|
+
key: normalizedKey,
|
|
69
|
+
value,
|
|
70
|
+
updatedAt: now,
|
|
71
|
+
},
|
|
72
|
+
$setOnInsert: {
|
|
73
|
+
createdAt: now,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{ upsert: true },
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const stored = await this.collection.findOne({ _id: id });
|
|
80
|
+
if (!stored || this.validateDocument(stored, normalizedKey).value !== value) {
|
|
81
|
+
throw new Error(`SmartMTA storage write verification failed: ${normalizedKey}`);
|
|
63
82
|
}
|
|
83
|
+
}
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
85
|
+
public async list(prefix: string): Promise<string[]> {
|
|
86
|
+
const normalizedPrefix = normalizeSmartMtaStorageKey(prefix);
|
|
87
|
+
getSmartMtaTextNamespace(normalizedPrefix);
|
|
88
|
+
const documents = await this.collection.find({
|
|
89
|
+
key: {
|
|
90
|
+
$gte: normalizedPrefix,
|
|
91
|
+
$lt: `${normalizedPrefix}\uffff`,
|
|
92
|
+
},
|
|
93
|
+
}).toArray();
|
|
77
94
|
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
return documents
|
|
96
|
+
.map((document: ISmartMtaStorageDocument) => this.validateDocument(document).key)
|
|
97
|
+
.filter((key: string) => key.startsWith(normalizedPrefix))
|
|
98
|
+
.sort();
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
public async delete(key: string): Promise<void> {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
} catch (error: unknown) {
|
|
92
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let currentDir = plugins.path.dirname(targetPath);
|
|
99
|
-
while (currentDir.startsWith(this.resolvedRootDir) && currentDir !== this.resolvedRootDir) {
|
|
100
|
-
const entries = await plugins.fs.promises.readdir(currentDir);
|
|
101
|
-
if (entries.length > 0) {
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
await plugins.fs.promises.rmdir(currentDir);
|
|
105
|
-
currentDir = plugins.path.dirname(currentDir);
|
|
102
|
+
const normalizedKey = normalizeSmartMtaStorageKey(key);
|
|
103
|
+
getSmartMtaTextNamespace(normalizedKey);
|
|
104
|
+
const id = hashSmartMtaStorageKey(normalizedKey);
|
|
105
|
+
const existing = await this.collection.findOne({ _id: id });
|
|
106
|
+
if (existing) {
|
|
107
|
+
this.validateDocument(existing, normalizedKey);
|
|
108
|
+
await this.collection.deleteOne({ _id: id });
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
}
|
|
@@ -4,10 +4,12 @@ import type {
|
|
|
4
4
|
IMessageAcceptanceContext,
|
|
5
5
|
IMessageAcceptanceDecision,
|
|
6
6
|
IUnifiedEmailServerOptions,
|
|
7
|
+
IStorageManagerLike,
|
|
7
8
|
} from '@push.rocks/smartmta';
|
|
8
9
|
import type { Buffer } from 'node:buffer';
|
|
9
10
|
import * as plugins from '../plugins.js';
|
|
10
11
|
import { CachedEmail } from '../db/index.js';
|
|
12
|
+
import type { ICachedEmailSmtpTransaction } from '../db/documents/classes.cached.email.js';
|
|
11
13
|
import type { DcRouter } from '../classes.dcrouter.js';
|
|
12
14
|
import type * as interfaces from '../../ts_interfaces/index.js';
|
|
13
15
|
|
|
@@ -42,6 +44,7 @@ type TSmartMtaStatusQueueItem = {
|
|
|
42
44
|
attempts?: number;
|
|
43
45
|
nextAttempt?: Date;
|
|
44
46
|
lastError?: string;
|
|
47
|
+
smtpTransactions?: ICachedEmailSmtpTransaction[];
|
|
45
48
|
processingResult?: {
|
|
46
49
|
headers?: Record<string, string>;
|
|
47
50
|
email?: {
|
|
@@ -611,11 +614,20 @@ export class WorkAppMailManager {
|
|
|
611
614
|
await storageManager.set(this.storageKey, JSON.stringify(state, null, 2));
|
|
612
615
|
}
|
|
613
616
|
|
|
614
|
-
private getRequiredStorageManager()
|
|
615
|
-
|
|
617
|
+
private getRequiredStorageManager(): IStorageManagerLike & Required<
|
|
618
|
+
Pick<IStorageManagerLike, 'get' | 'set'>
|
|
619
|
+
> {
|
|
620
|
+
const storageManager = this.dcRouterRef.storageManager;
|
|
621
|
+
if (
|
|
622
|
+
!storageManager
|
|
623
|
+
|| typeof storageManager.get !== 'function'
|
|
624
|
+
|| typeof storageManager.set !== 'function'
|
|
625
|
+
) {
|
|
616
626
|
throw new Error('DcRouter storage manager does not support managed mail identity persistence');
|
|
617
627
|
}
|
|
618
|
-
return
|
|
628
|
+
return storageManager as IStorageManagerLike & Required<
|
|
629
|
+
Pick<IStorageManagerLike, 'get' | 'set'>
|
|
630
|
+
>;
|
|
619
631
|
}
|
|
620
632
|
|
|
621
633
|
private mergeIdentitiesIntoEmailConfig<TConfig extends IUnifiedEmailServerOptions>(
|
|
@@ -1044,10 +1056,41 @@ export class WorkAppMailManager {
|
|
|
1044
1056
|
createdAt,
|
|
1045
1057
|
updatedAt,
|
|
1046
1058
|
},
|
|
1047
|
-
attempts:
|
|
1059
|
+
attempts: this.mapDeliveryAttempts(
|
|
1060
|
+
queueItem?.smtpTransactions || cachedEmailArg.smtpTransactions || [],
|
|
1061
|
+
cachedEmailArg.id,
|
|
1062
|
+
),
|
|
1048
1063
|
};
|
|
1049
1064
|
}
|
|
1050
1065
|
|
|
1066
|
+
private mapDeliveryAttempts(
|
|
1067
|
+
transactionsArg: ICachedEmailSmtpTransaction[],
|
|
1068
|
+
spoolItemIdArg: string,
|
|
1069
|
+
): plugins.servezoneInterfaces.data.IMailDeliveryAttempt[] {
|
|
1070
|
+
return transactionsArg.map((transaction) => ({
|
|
1071
|
+
id: transaction.id,
|
|
1072
|
+
spoolItemId: spoolItemIdArg,
|
|
1073
|
+
target: {
|
|
1074
|
+
type: 'mtaRelay',
|
|
1075
|
+
label: transaction.recipientDomain || `${transaction.targetHost}:${transaction.targetPort}`,
|
|
1076
|
+
host: transaction.targetHost,
|
|
1077
|
+
port: transaction.targetPort,
|
|
1078
|
+
},
|
|
1079
|
+
status: transaction.outcome === 'succeeded'
|
|
1080
|
+
? 'delivered'
|
|
1081
|
+
: transaction.retryable
|
|
1082
|
+
? 'deferred'
|
|
1083
|
+
: 'failed',
|
|
1084
|
+
startedAt: this.toTimestamp(transaction.startedAt) || 0,
|
|
1085
|
+
completedAt: this.toTimestamp(transaction.completedAt),
|
|
1086
|
+
smtpCode: transaction.smtpCode,
|
|
1087
|
+
response: transaction.recipientResults?.map((result) => {
|
|
1088
|
+
return `${result.recipient}: ${result.responseCode} ${result.accepted ? 'accepted' : 'rejected'}`;
|
|
1089
|
+
}).join('; '),
|
|
1090
|
+
error: transaction.error,
|
|
1091
|
+
}));
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1051
1094
|
private findQueueItemForCachedEmail(cachedEmailIdArg: string): TSmartMtaStatusQueueItem | undefined {
|
|
1052
1095
|
const emailServer = this.dcRouterRef.emailServer;
|
|
1053
1096
|
if (!emailServer?.getQueueItems) {
|
package/ts/email/index.ts
CHANGED
|
@@ -2,9 +2,13 @@ export * from './classes.accepted-email-spool.js';
|
|
|
2
2
|
export * from './classes.email-domain.manager.js';
|
|
3
3
|
export * from './classes.email-route-builder.js';
|
|
4
4
|
export * from './classes.mail-dns-sync.js';
|
|
5
|
+
export * from './mail-dns-txt-policy.js';
|
|
6
|
+
export * from './reconcile-mail-dns-singleton.js';
|
|
5
7
|
export * from './classes.mail-egress-coordinator.js';
|
|
6
8
|
export * from './classes.mail-edge-eligibility.js';
|
|
7
9
|
export * from './classes.email-settings.manager.js';
|
|
10
|
+
export * from './classes.smartmta-blob-storage-manager.js';
|
|
8
11
|
export * from './classes.smartmta-storage-manager.js';
|
|
12
|
+
export * from './smartmta-storage-policy.js';
|
|
9
13
|
export * from './classes.workapp-mail-manager.js';
|
|
10
14
|
export * from './email-dns-records.js';
|