@serve.zone/dcrouter 18.3.0 → 18.4.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/deno.json +1 -1
- package/dist_serve/bundle.js +1770 -1677
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +4 -1
- package/dist_ts/db/documents/classes.cached.email.d.ts +44 -0
- package/dist_ts/db/documents/classes.cached.email.js +24 -4
- package/dist_ts/email/classes.accepted-email-spool.js +16 -13
- package/dist_ts/email/classes.mail-egress-coordinator.js +10 -1
- package/dist_ts/email/helpers.inbound-security.js +7 -8
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +3 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +89 -22
- package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +4 -0
- package/dist_ts/remoteingress/classes.tunnel-manager.js +10 -1
- package/dist_ts_interfaces/requests/email-ops.d.ts +44 -0
- package/dist_ts_migrations/index.js +111 -11
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate/email-ops.d.ts +15 -0
- package/dist_ts_web/appstate/email-ops.js +24 -1
- package/dist_ts_web/elements/email/ops-view-emails.d.ts +7 -0
- package/dist_ts_web/elements/email/ops-view-emails.js +63 -13
- package/package.json +5 -5
- package/readme.md +19 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +7 -0
- package/ts/db/documents/classes.cached.email.ts +60 -2
- package/ts/email/classes.accepted-email-spool.ts +20 -14
- package/ts/email/classes.mail-egress-coordinator.ts +8 -0
- package/ts/email/helpers.inbound-security.ts +6 -7
- package/ts/opsserver/handlers/email-ops.handler.ts +105 -18
- package/ts/remoteingress/classes.tunnel-manager.ts +14 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate/email-ops.ts +30 -0
- package/ts_web/elements/email/ops-view-emails.ts +67 -11
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/dcrouter',
|
|
6
|
-
version: '18.
|
|
6
|
+
version: '18.4.0',
|
|
7
7
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViLzAwX2NvbW1pdGluZm9fZGF0YS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4QixJQUFJLEVBQUUsc0JBQXNCO0lBQzVCLE9BQU8sRUFBRSxRQUFRO0lBQ2pCLFdBQVcsRUFBRSwwRUFBMEU7Q0FDeEYsQ0FBQSJ9
|
|
@@ -4,6 +4,10 @@ export interface IEmailOpsState {
|
|
|
4
4
|
emails: interfaces.requests.IEmail[];
|
|
5
5
|
traffic: interfaces.requests.IEmailLogTraffic | null;
|
|
6
6
|
searchQuery: string;
|
|
7
|
+
selectedRange: {
|
|
8
|
+
from: number;
|
|
9
|
+
to: number;
|
|
10
|
+
} | null;
|
|
7
11
|
securityFindings: interfaces.requests.IEmailSecurityFinding[];
|
|
8
12
|
securityFindingsLoading: boolean;
|
|
9
13
|
securityFindingsError: string | null;
|
|
@@ -15,8 +19,19 @@ export interface IEmailOpsState {
|
|
|
15
19
|
export declare const emailOpsStatePart: plugins.deesElement.domtools.plugins.smartstate.StatePart<string, IEmailOpsState>;
|
|
16
20
|
export interface IEmailOpsFetchOptions {
|
|
17
21
|
searchQuery?: string;
|
|
22
|
+
selectedRange?: {
|
|
23
|
+
from: number;
|
|
24
|
+
to: number;
|
|
25
|
+
} | null;
|
|
18
26
|
}
|
|
19
27
|
export declare function setActiveEmailSearchQuery(searchQueryArg: string): string;
|
|
28
|
+
export declare function setActiveEmailSelectedRange(rangeArg: {
|
|
29
|
+
from: number;
|
|
30
|
+
to: number;
|
|
31
|
+
} | null): {
|
|
32
|
+
from: number;
|
|
33
|
+
to: number;
|
|
34
|
+
} | null;
|
|
20
35
|
export declare const fetchAllEmailsAction: plugins.deesElement.domtools.plugins.smartstate.StateAction<IEmailOpsState, IEmailOpsFetchOptions | null>;
|
|
21
36
|
export declare const fetchEmailSecurityFindingsAction: plugins.deesElement.domtools.plugins.smartstate.StateAction<IEmailOpsState, void>;
|
|
22
37
|
export declare function fetchEmailDetailSnapshot(emailIdArg: string): Promise<interfaces.requests.IEmailDetail | null>;
|
|
@@ -7,6 +7,7 @@ export const emailOpsStatePart = await appState.getStatePart('emailOps', {
|
|
|
7
7
|
emails: [],
|
|
8
8
|
traffic: null,
|
|
9
9
|
searchQuery: '',
|
|
10
|
+
selectedRange: null,
|
|
10
11
|
securityFindings: [],
|
|
11
12
|
securityFindingsLoading: false,
|
|
12
13
|
securityFindingsError: null,
|
|
@@ -18,6 +19,7 @@ export const emailOpsStatePart = await appState.getStatePart('emailOps', {
|
|
|
18
19
|
// Email Operations Actions
|
|
19
20
|
// ============================================================================
|
|
20
21
|
let activeSearchQuery = '';
|
|
22
|
+
let activeSelectedRange = null;
|
|
21
23
|
let emailListRequestGeneration = 0;
|
|
22
24
|
let securityFindingsRequestGeneration = 0;
|
|
23
25
|
export function setActiveEmailSearchQuery(searchQueryArg) {
|
|
@@ -33,6 +35,22 @@ export function setActiveEmailSearchQuery(searchQueryArg) {
|
|
|
33
35
|
});
|
|
34
36
|
return activeSearchQuery;
|
|
35
37
|
}
|
|
38
|
+
export function setActiveEmailSelectedRange(rangeArg) {
|
|
39
|
+
if (rangeArg && (!Number.isSafeInteger(rangeArg.from)
|
|
40
|
+
|| !Number.isSafeInteger(rangeArg.to)
|
|
41
|
+
|| rangeArg.from < 0
|
|
42
|
+
|| rangeArg.to <= rangeArg.from)) {
|
|
43
|
+
throw new Error('Invalid email time range');
|
|
44
|
+
}
|
|
45
|
+
activeSelectedRange = rangeArg ? { ...rangeArg } : null;
|
|
46
|
+
emailListRequestGeneration++;
|
|
47
|
+
const currentState = emailOpsStatePart.getState();
|
|
48
|
+
emailOpsStatePart.setState({
|
|
49
|
+
...currentState,
|
|
50
|
+
selectedRange: activeSelectedRange,
|
|
51
|
+
});
|
|
52
|
+
return activeSelectedRange;
|
|
53
|
+
}
|
|
36
54
|
// Fetch All Emails Action
|
|
37
55
|
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg, dataArg) => {
|
|
38
56
|
const context = getActionContext();
|
|
@@ -42,6 +60,9 @@ export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (stateP
|
|
|
42
60
|
const searchQuery = dataArg?.searchQuery !== undefined
|
|
43
61
|
? setActiveEmailSearchQuery(dataArg.searchQuery)
|
|
44
62
|
: activeSearchQuery;
|
|
63
|
+
const selectedRange = dataArg?.selectedRange !== undefined
|
|
64
|
+
? setActiveEmailSelectedRange(dataArg.selectedRange)
|
|
65
|
+
: activeSelectedRange;
|
|
45
66
|
const requestGeneration = ++emailListRequestGeneration;
|
|
46
67
|
const ticket = captureRealtimeTicket(['emailQueue', 'smtpAttempts']);
|
|
47
68
|
try {
|
|
@@ -49,6 +70,7 @@ export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (stateP
|
|
|
49
70
|
const response = await request.fire({
|
|
50
71
|
identity: context.identity,
|
|
51
72
|
...(searchQuery ? { search: searchQuery } : {}),
|
|
73
|
+
...(selectedRange ? selectedRange : {}),
|
|
52
74
|
});
|
|
53
75
|
if (requestGeneration !== emailListRequestGeneration
|
|
54
76
|
|| !isRealtimeTicketCurrent(ticket))
|
|
@@ -59,6 +81,7 @@ export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (stateP
|
|
|
59
81
|
traffic: response.traffic,
|
|
60
82
|
searchQuery,
|
|
61
83
|
isLoading: false,
|
|
84
|
+
selectedRange,
|
|
62
85
|
error: null,
|
|
63
86
|
lastUpdated: Date.now(),
|
|
64
87
|
};
|
|
@@ -128,4 +151,4 @@ export async function fetchEmailDetailSnapshot(emailIdArg) {
|
|
|
128
151
|
});
|
|
129
152
|
return response.email;
|
|
130
153
|
}
|
|
131
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
154
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1haWwtb3BzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHNfd2ViL2FwcHN0YXRlL2VtYWlsLW9wcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssVUFBVSxNQUFNLDhCQUE4QixDQUFDO0FBQzNELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdkMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzlDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQWdCL0UsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxRQUFRLENBQUMsWUFBWSxDQUMxRCxVQUFVLEVBQ1Y7SUFDRSxNQUFNLEVBQUUsRUFBRTtJQUNWLE9BQU8sRUFBRSxJQUFJO0lBQ2IsV0FBVyxFQUFFLEVBQUU7SUFDZixhQUFhLEVBQUUsSUFBSTtJQUNuQixnQkFBZ0IsRUFBRSxFQUFFO0lBQ3BCLHVCQUF1QixFQUFFLEtBQUs7SUFDOUIscUJBQXFCLEVBQUUsSUFBSTtJQUMzQixTQUFTLEVBQUUsS0FBSztJQUNoQixLQUFLLEVBQUUsSUFBSTtJQUNYLFdBQVcsRUFBRSxDQUFDO0lBQ2QsbUJBQW1CLEVBQUUsQ0FBQztDQUN2QixFQUNELE1BQU0sQ0FDUCxDQUFDO0FBRUYsMkJBQTJCO0FBQzNCLCtFQUErRTtBQUUvRSxJQUFJLGlCQUFpQixHQUFHLEVBQUUsQ0FBQztBQUMzQixJQUFJLG1CQUFtQixHQUF3QyxJQUFJLENBQUM7QUFDcEUsSUFBSSwwQkFBMEIsR0FBRyxDQUFDLENBQUM7QUFDbkMsSUFBSSxpQ0FBaUMsR0FBRyxDQUFDLENBQUM7QUFPMUMsTUFBTSxVQUFVLHlCQUF5QixDQUFDLGNBQXNCO0lBQzlELE1BQU0sZUFBZSxHQUFHLGNBQWMsQ0FBQyxJQUFJLEVBQUUs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
|
|
@@ -12,6 +12,10 @@ export declare class OpsViewEmails extends DeesElement {
|
|
|
12
12
|
accessor isLoading: boolean;
|
|
13
13
|
accessor traffic: interfaces.requests.IEmailLogTraffic | null;
|
|
14
14
|
accessor searchQuery: string;
|
|
15
|
+
accessor selectedRange: {
|
|
16
|
+
from: number;
|
|
17
|
+
to: number;
|
|
18
|
+
} | null;
|
|
15
19
|
private stateSubscription;
|
|
16
20
|
private lastEmailOpsUpdate;
|
|
17
21
|
private detailRequestGeneration;
|
|
@@ -23,6 +27,9 @@ export declare class OpsViewEmails extends DeesElement {
|
|
|
23
27
|
private refreshEmailDetail;
|
|
24
28
|
private handleEmailClick;
|
|
25
29
|
private handleSearchChange;
|
|
30
|
+
private handleRangeChange;
|
|
31
|
+
private clearSelectedRange;
|
|
32
|
+
private formatSelectedRange;
|
|
26
33
|
private getEmailTrafficSeries;
|
|
27
34
|
private handleBack;
|
|
28
35
|
}
|
|
@@ -60,6 +60,9 @@ let OpsViewEmails = (() => {
|
|
|
60
60
|
let _searchQuery_decorators;
|
|
61
61
|
let _searchQuery_initializers = [];
|
|
62
62
|
let _searchQuery_extraInitializers = [];
|
|
63
|
+
let _selectedRange_decorators;
|
|
64
|
+
let _selectedRange_initializers = [];
|
|
65
|
+
let _selectedRange_extraInitializers = [];
|
|
63
66
|
var OpsViewEmails = class extends _classSuper {
|
|
64
67
|
static { _classThis = this; }
|
|
65
68
|
static {
|
|
@@ -70,12 +73,14 @@ let OpsViewEmails = (() => {
|
|
|
70
73
|
_isLoading_decorators = [state()];
|
|
71
74
|
_traffic_decorators = [state()];
|
|
72
75
|
_searchQuery_decorators = [state()];
|
|
76
|
+
_selectedRange_decorators = [state()];
|
|
73
77
|
__esDecorate(this, null, _emails_decorators, { kind: "accessor", name: "emails", static: false, private: false, access: { has: obj => "emails" in obj, get: obj => obj.emails, set: (obj, value) => { obj.emails = value; } }, metadata: _metadata }, _emails_initializers, _emails_extraInitializers);
|
|
74
78
|
__esDecorate(this, null, _selectedEmail_decorators, { kind: "accessor", name: "selectedEmail", static: false, private: false, access: { has: obj => "selectedEmail" in obj, get: obj => obj.selectedEmail, set: (obj, value) => { obj.selectedEmail = value; } }, metadata: _metadata }, _selectedEmail_initializers, _selectedEmail_extraInitializers);
|
|
75
79
|
__esDecorate(this, null, _currentView_decorators, { kind: "accessor", name: "currentView", static: false, private: false, access: { has: obj => "currentView" in obj, get: obj => obj.currentView, set: (obj, value) => { obj.currentView = value; } }, metadata: _metadata }, _currentView_initializers, _currentView_extraInitializers);
|
|
76
80
|
__esDecorate(this, null, _isLoading_decorators, { kind: "accessor", name: "isLoading", static: false, private: false, access: { has: obj => "isLoading" in obj, get: obj => obj.isLoading, set: (obj, value) => { obj.isLoading = value; } }, metadata: _metadata }, _isLoading_initializers, _isLoading_extraInitializers);
|
|
77
81
|
__esDecorate(this, null, _traffic_decorators, { kind: "accessor", name: "traffic", static: false, private: false, access: { has: obj => "traffic" in obj, get: obj => obj.traffic, set: (obj, value) => { obj.traffic = value; } }, metadata: _metadata }, _traffic_initializers, _traffic_extraInitializers);
|
|
78
82
|
__esDecorate(this, null, _searchQuery_decorators, { kind: "accessor", name: "searchQuery", static: false, private: false, access: { has: obj => "searchQuery" in obj, get: obj => obj.searchQuery, set: (obj, value) => { obj.searchQuery = value; } }, metadata: _metadata }, _searchQuery_initializers, _searchQuery_extraInitializers);
|
|
83
|
+
__esDecorate(this, null, _selectedRange_decorators, { kind: "accessor", name: "selectedRange", static: false, private: false, access: { has: obj => "selectedRange" in obj, get: obj => obj.selectedRange, set: (obj, value) => { obj.selectedRange = value; } }, metadata: _metadata }, _selectedRange_initializers, _selectedRange_extraInitializers);
|
|
79
84
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
80
85
|
OpsViewEmails = _classThis = _classDescriptor.value;
|
|
81
86
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
@@ -98,7 +103,10 @@ let OpsViewEmails = (() => {
|
|
|
98
103
|
#searchQuery_accessor_storage = (__runInitializers(this, _traffic_extraInitializers), __runInitializers(this, _searchQuery_initializers, ''));
|
|
99
104
|
get searchQuery() { return this.#searchQuery_accessor_storage; }
|
|
100
105
|
set searchQuery(value) { this.#searchQuery_accessor_storage = value; }
|
|
101
|
-
|
|
106
|
+
#selectedRange_accessor_storage = (__runInitializers(this, _searchQuery_extraInitializers), __runInitializers(this, _selectedRange_initializers, null));
|
|
107
|
+
get selectedRange() { return this.#selectedRange_accessor_storage; }
|
|
108
|
+
set selectedRange(value) { this.#selectedRange_accessor_storage = value; }
|
|
109
|
+
stateSubscription = __runInitializers(this, _selectedRange_extraInitializers);
|
|
102
110
|
lastEmailOpsUpdate = 0;
|
|
103
111
|
detailRequestGeneration = 0;
|
|
104
112
|
searchDebounceTimer;
|
|
@@ -108,6 +116,7 @@ let OpsViewEmails = (() => {
|
|
|
108
116
|
this.emails = state.emails;
|
|
109
117
|
this.traffic = state.traffic;
|
|
110
118
|
this.searchQuery = state.searchQuery;
|
|
119
|
+
this.selectedRange = state.selectedRange;
|
|
111
120
|
const hasNewSnapshot = state.lastUpdated > this.lastEmailOpsUpdate;
|
|
112
121
|
this.lastEmailOpsUpdate = Math.max(this.lastEmailOpsUpdate, state.lastUpdated);
|
|
113
122
|
if (hasNewSnapshot && this.selectedEmail) {
|
|
@@ -141,7 +150,23 @@ let OpsViewEmails = (() => {
|
|
|
141
150
|
height: 100%;
|
|
142
151
|
}
|
|
143
152
|
|
|
144
|
-
|
|
153
|
+
.chartStack {
|
|
154
|
+
display: flex;
|
|
155
|
+
flex-direction: column;
|
|
156
|
+
gap: 8px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.rangeSummary {
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
justify-content: space-between;
|
|
163
|
+
min-height: 32px;
|
|
164
|
+
padding: 0 4px;
|
|
165
|
+
color: ${cssManager.bdTheme('#374151', '#d1d5db')};
|
|
166
|
+
font-size: 13px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.chartStack dees-chart-area {
|
|
145
170
|
min-height: 360px;
|
|
146
171
|
}
|
|
147
172
|
`,
|
|
@@ -166,16 +191,26 @@ let OpsViewEmails = (() => {
|
|
|
166
191
|
@email-click=${this.handleEmailClick}
|
|
167
192
|
>
|
|
168
193
|
${this.traffic ? html `
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
<div slot="above-table" class="chartStack">
|
|
195
|
+
${this.selectedRange ? html `
|
|
196
|
+
<div class="rangeSummary">
|
|
197
|
+
<span>${this.formatSelectedRange()}</span>
|
|
198
|
+
<dees-button @click=${this.clearSelectedRange}>Clear time range</dees-button>
|
|
199
|
+
</div>
|
|
200
|
+
` : html ``}
|
|
201
|
+
<dees-chart-area
|
|
202
|
+
.label=${this.traffic.label}
|
|
203
|
+
.series=${this.getEmailTrafficSeries()}
|
|
204
|
+
.realtimeMode=${true}
|
|
205
|
+
.rollingWindow=${this.traffic.durationMs}
|
|
206
|
+
.legendStats=${['latest']}
|
|
207
|
+
.chartLines=${[]}
|
|
208
|
+
.rangeSelectionEnabled=${true}
|
|
209
|
+
.selectedRange=${this.selectedRange}
|
|
210
|
+
.yAxisFormatter=${(valueArg) => `${valueArg}`}
|
|
211
|
+
@range-change=${this.handleRangeChange}
|
|
212
|
+
></dees-chart-area>
|
|
213
|
+
</div>
|
|
179
214
|
` : html ``}
|
|
180
215
|
</sz-mta-list-view>
|
|
181
216
|
`}
|
|
@@ -215,6 +250,21 @@ let OpsViewEmails = (() => {
|
|
|
215
250
|
void appstate.emailOpsStatePart.dispatchAction(appstate.fetchAllEmailsAction, { searchQuery });
|
|
216
251
|
}, 300);
|
|
217
252
|
}
|
|
253
|
+
handleRangeChange = (eventArg) => {
|
|
254
|
+
void appstate.emailOpsStatePart.dispatchAction(appstate.fetchAllEmailsAction, { selectedRange: eventArg.detail });
|
|
255
|
+
};
|
|
256
|
+
clearSelectedRange = () => {
|
|
257
|
+
void appstate.emailOpsStatePart.dispatchAction(appstate.fetchAllEmailsAction, { selectedRange: null });
|
|
258
|
+
};
|
|
259
|
+
formatSelectedRange() {
|
|
260
|
+
if (!this.selectedRange)
|
|
261
|
+
return '';
|
|
262
|
+
const formatter = new Intl.DateTimeFormat(undefined, {
|
|
263
|
+
dateStyle: 'medium',
|
|
264
|
+
timeStyle: 'short',
|
|
265
|
+
});
|
|
266
|
+
return `Showing emails from ${formatter.format(this.selectedRange.from)} to ${formatter.format(this.selectedRange.to)}`;
|
|
267
|
+
}
|
|
218
268
|
getEmailTrafficSeries() {
|
|
219
269
|
if (!this.traffic)
|
|
220
270
|
return [];
|
|
@@ -248,4 +298,4 @@ let OpsViewEmails = (() => {
|
|
|
248
298
|
return OpsViewEmails = _classThis;
|
|
249
299
|
})();
|
|
250
300
|
export { OpsViewEmails };
|
|
251
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
301
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctZW1haWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL2VtYWlsL29wcy12aWV3LWVtYWlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDdkcsT0FBTyxLQUFLLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUM5QyxPQUFPLEtBQUssTUFBTSxNQUFNLG9CQUFvQixDQUFDO0FBQzdDLE9BQU8sS0FBSyxVQUFVLE1BQU0sc0NBQXNDLENBQUM7SUFTdEQsYUFBYTs0QkFEekIsYUFBYSxDQUFDLGlCQUFpQixDQUFDOzs7O3NCQUNFLFdBQVc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7NkJBQW5CLFNBQVEsV0FBVzs7OztrQ0FDM0MsS0FBSyxFQUFFO3lDQUdQLEtBQUssRUFBRTt1Q0FHUCxLQUFLLEVBQUU7cUNBR1AsS0FBSyxFQUFFO21DQUdQLEtBQUssRUFBRTt1Q0FHUCxLQUFLLEVBQUU7eUNBR1AsS0FBSyxFQUFFO1lBakJSLHVLQUFTLE1BQU0sNkJBQU4sTUFBTSx1RkFBb0M7WUFHbkQsNExBQVMsYUFBYSw2QkFBYixhQUFhLHFHQUFpRDtZQUd2RSxzTEFBUyxXQUFXLDZCQUFYLFdBQVcsaUdBQTZCO1lBR2pELGdMQUFTLFNBQVMsNkJBQVQsU0FBUyw2RkFBUztZQUczQiwwS0FBUyxPQUFPLDZCQUFQLE9BQU8seUZBQXFEO1lBR3JFLHNMQUFTLFdBQVcsNkJBQVgsV0FBVyxpR0FBTTtZQUcxQiw0TEFBUyxhQUFhLDZCQUFiLGFBQWEscUdBQTZDO1lBcEJyRSw2S0F1T0M7Ozs7UUFyT0MseUVBQWdELEVBQUUsRUFBQztRQUFuRCxJQUFTLE1BQU0sNENBQW9D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
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/dcrouter",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.4.0",
|
|
5
5
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@api.global/typedserver": "^8.6.4",
|
|
32
32
|
"@api.global/typedsocket": "^5.0.0",
|
|
33
33
|
"@apiclient.xyz/cloudflare": "^7.1.0",
|
|
34
|
-
"@design.estate/dees-catalog": "^3.
|
|
34
|
+
"@design.estate/dees-catalog": "^3.99.0",
|
|
35
35
|
"@design.estate/dees-element": "^2.4.0",
|
|
36
36
|
"@idp.global/sdk": "^14.3.0",
|
|
37
37
|
"@push.rocks/lik": "^6.4.1",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@push.rocks/smartmcp": "^0.3.0",
|
|
51
51
|
"@push.rocks/smartmetrics": "^3.0.3",
|
|
52
52
|
"@push.rocks/smartmigration": "1.4.1",
|
|
53
|
-
"@push.rocks/smartmta": "^9.
|
|
53
|
+
"@push.rocks/smartmta": "^9.4.0",
|
|
54
54
|
"@push.rocks/smartnetwork": "^4.8.0",
|
|
55
55
|
"@push.rocks/smartpath": "^6.0.0",
|
|
56
56
|
"@push.rocks/smartpromise": "^4.2.4",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"@push.rocks/smartunique": "^3.0.10",
|
|
64
64
|
"@push.rocks/smartvpn": "1.22.0",
|
|
65
65
|
"@push.rocks/taskbuffer": "^8.0.2",
|
|
66
|
-
"@serve.zone/catalog": "^2.
|
|
66
|
+
"@serve.zone/catalog": "^2.21.0",
|
|
67
67
|
"@serve.zone/interfaces": "^19.2.0",
|
|
68
|
-
"@serve.zone/remoteingress": "^5.0
|
|
68
|
+
"@serve.zone/remoteingress": "^5.1.0",
|
|
69
69
|
"@tsclass/tsclass": "^9.5.1",
|
|
70
70
|
"@types/qrcode": "^1.5.6",
|
|
71
71
|
"ipaddr.js": "^2.4.0",
|
package/readme.md
CHANGED
|
@@ -474,6 +474,25 @@ Use `@serve.zone/dcrouter/interfaces` or `@serve.zone/dcrouter-interfaces` when
|
|
|
474
474
|
|
|
475
475
|
Gateway-client mail contracts let Cloudly or Onebox claim exact app addresses, attach inbound `smtpForward` targets, enable managed outbound SMTP credentials, rotate those credentials, enqueue service mail, and query delivery status through TypedRequest. Managed SMTP users can only send as their exact claimed address; mismatched envelope or header `From` values are rejected before relay. Delivery status queries return the dcrouter spool item state, including deferred SmartMTA errors and next retry timestamps when outbound delivery is temporarily delayed.
|
|
476
476
|
|
|
477
|
+
### Email Operations Views
|
|
478
|
+
|
|
479
|
+
The Email Log keeps its search controls sticky and renders the traffic chart
|
|
480
|
+
above the message table. With no query, the chart covers the latest 24 hours.
|
|
481
|
+
Searches expand the chart window to the smallest supported period that contains
|
|
482
|
+
matching retained messages (7, 14, or 30 days). Dragging across the chart
|
|
483
|
+
selects an epoch-millisecond time range for the table while the chart continues
|
|
484
|
+
to show the complete search context; clearing the selection restores the full
|
|
485
|
+
result list.
|
|
486
|
+
|
|
487
|
+
Inbound messages persist a separate security disposition:
|
|
488
|
+
`accepted`, `flagged`, or `rejected`. SPF, DKIM, or DMARC findings therefore
|
|
489
|
+
remain visible in Email Security without overwriting the transport/delivery
|
|
490
|
+
status. The security table shows time, sender, recipients, subject, and the
|
|
491
|
+
specific authentication failure. Sent-message detail exposes the asynchronous
|
|
492
|
+
outbound authentication self-check based on the exact post-DKIM bytes and the
|
|
493
|
+
actual RemoteIngress source address. It is diagnostic evidence, not a receipt
|
|
494
|
+
from the destination server.
|
|
495
|
+
|
|
477
496
|
### RemoteIngress SMTP Egress
|
|
478
497
|
|
|
479
498
|
The email settings API and Ops UI expose `outboundMode`. Keep it at `direct` to let SmartMTA dial destination MX hosts from the dcrouter hub. Set it to `remoteIngress` to make SmartMTA request a one-shot RemoteIngress TCP egress proxy for each outbound SMTP connection while keeping the logical MX host and port for SMTP/TLS identity.
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -2142,6 +2142,13 @@ export class DcRouter {
|
|
|
2142
2142
|
await this.acceptedEmailSpool.trackSmtpTransaction(transaction, emailServer);
|
|
2143
2143
|
},
|
|
2144
2144
|
);
|
|
2145
|
+
this.addEmailEventSubscription(
|
|
2146
|
+
emailServer.deliverySystem,
|
|
2147
|
+
'smtpTransactionUpdated',
|
|
2148
|
+
async (transaction: import('./db/documents/classes.cached.email.js').ICachedEmailSmtpTransaction) => {
|
|
2149
|
+
await this.acceptedEmailSpool.trackSmtpTransaction(transaction, emailServer);
|
|
2150
|
+
},
|
|
2151
|
+
);
|
|
2145
2152
|
|
|
2146
2153
|
// Wire delivery events to MetricsManager and logger using smartmta's public queue APIs.
|
|
2147
2154
|
if (this.metricsManager) {
|
|
@@ -14,11 +14,14 @@ export type TCachedEmailStatus = 'pending' | 'processing' | 'queued' | 'delivere
|
|
|
14
14
|
* 'outbound' = locally submitted for delivery.
|
|
15
15
|
*/
|
|
16
16
|
export type TCachedEmailDirection = 'inbound' | 'outbound';
|
|
17
|
+
export type TCachedEmailSecurityDisposition = 'accepted' | 'flagged' | 'rejected';
|
|
17
18
|
|
|
18
19
|
export interface ICachedEmailListFilter {
|
|
19
20
|
direction?: TCachedEmailDirection;
|
|
20
21
|
recipientDomain?: string;
|
|
21
22
|
search?: string;
|
|
23
|
+
from?: number;
|
|
24
|
+
to?: number;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export interface ICachedEmailTrafficBucket {
|
|
@@ -67,6 +70,7 @@ export interface ICachedEmailSmtpTransaction {
|
|
|
67
70
|
}>;
|
|
68
71
|
failurePhase?: TCachedEmailSmtpDeliveryPhase;
|
|
69
72
|
error?: string;
|
|
73
|
+
outboundAuthentication?: ICachedEmailOutboundAuthenticationCheck;
|
|
70
74
|
transcript: Array<{
|
|
71
75
|
timestampMs: number;
|
|
72
76
|
phase: TCachedEmailSmtpDeliveryPhase;
|
|
@@ -76,6 +80,40 @@ export interface ICachedEmailSmtpTransaction {
|
|
|
76
80
|
}>;
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
export interface ICachedEmailOutboundAuthenticationCheck {
|
|
84
|
+
state: 'pending' | 'completed' | 'indeterminate' | 'not-evaluated';
|
|
85
|
+
overall: 'pass' | 'fail' | 'indeterminate';
|
|
86
|
+
checkedAt: string;
|
|
87
|
+
signedMessageSha256?: string;
|
|
88
|
+
error?: string;
|
|
89
|
+
identity?: {
|
|
90
|
+
sourceIp: string;
|
|
91
|
+
addressFamily: 'ipv4' | 'ipv6';
|
|
92
|
+
heloDomain: string;
|
|
93
|
+
receiverHostname: string;
|
|
94
|
+
envelopeFrom: string;
|
|
95
|
+
dkimDomain?: string;
|
|
96
|
+
dkimSelector?: string;
|
|
97
|
+
};
|
|
98
|
+
dkim?: Array<{
|
|
99
|
+
is_valid: boolean;
|
|
100
|
+
domain: string | null;
|
|
101
|
+
selector: string | null;
|
|
102
|
+
status: string;
|
|
103
|
+
details: string | null;
|
|
104
|
+
}>;
|
|
105
|
+
spf?: { result: string; domain: string; ip: string; explanation: string | null } | null;
|
|
106
|
+
dmarc?: {
|
|
107
|
+
passed: boolean;
|
|
108
|
+
policy: string;
|
|
109
|
+
domain: string;
|
|
110
|
+
dkim_result: string;
|
|
111
|
+
spf_result: string;
|
|
112
|
+
action: string;
|
|
113
|
+
details: string | null;
|
|
114
|
+
} | null;
|
|
115
|
+
}
|
|
116
|
+
|
|
79
117
|
/**
|
|
80
118
|
* Helper to get the smartdata database instance
|
|
81
119
|
*/
|
|
@@ -175,6 +213,11 @@ export class CachedEmail extends plugins.smartdata.SmartdataCachedDocument<Cache
|
|
|
175
213
|
@plugins.smartdata.svDb()
|
|
176
214
|
public inboundSecurityResults?: string;
|
|
177
215
|
|
|
216
|
+
/** Authentication disposition, kept separate from delivery/lifecycle status. */
|
|
217
|
+
@plugins.smartdata.index()
|
|
218
|
+
@plugins.smartdata.svDb()
|
|
219
|
+
public inboundSecurityDisposition?: TCachedEmailSecurityDisposition;
|
|
220
|
+
|
|
178
221
|
/**
|
|
179
222
|
* Number of delivery attempts
|
|
180
223
|
*/
|
|
@@ -291,6 +334,9 @@ export class CachedEmail extends plugins.smartdata.SmartdataCachedDocument<Cache
|
|
|
291
334
|
if (this.rawContentObjectKey) {
|
|
292
335
|
delete savableObject.rawContent;
|
|
293
336
|
}
|
|
337
|
+
if (this.inboundSecurityDisposition === undefined) {
|
|
338
|
+
delete savableObject.inboundSecurityDisposition;
|
|
339
|
+
}
|
|
294
340
|
return savableObject;
|
|
295
341
|
}
|
|
296
342
|
|
|
@@ -405,6 +451,19 @@ export class CachedEmail extends plugins.smartdata.SmartdataCachedDocument<Cache
|
|
|
405
451
|
{ messageId: { $regex: escapedSearch, $options: 'i' } },
|
|
406
452
|
];
|
|
407
453
|
}
|
|
454
|
+
if (filterArg.from !== undefined || filterArg.to !== undefined) {
|
|
455
|
+
const from = filterArg.from;
|
|
456
|
+
const to = filterArg.to;
|
|
457
|
+
if (
|
|
458
|
+
!Number.isSafeInteger(from)
|
|
459
|
+
|| !Number.isSafeInteger(to)
|
|
460
|
+
|| (from as number) < 0
|
|
461
|
+
|| (to as number) < (from as number)
|
|
462
|
+
) {
|
|
463
|
+
throw new Error('Invalid CachedEmail time range');
|
|
464
|
+
}
|
|
465
|
+
query.acceptedAt = { $gte: from, $lte: to };
|
|
466
|
+
}
|
|
408
467
|
return query;
|
|
409
468
|
}
|
|
410
469
|
|
|
@@ -511,8 +570,7 @@ export class CachedEmail extends plugins.smartdata.SmartdataCachedDocument<Cache
|
|
|
511
570
|
}
|
|
512
571
|
return await CachedEmail.findLimited({
|
|
513
572
|
direction: 'inbound',
|
|
514
|
-
|
|
515
|
-
inboundSecurityResults: { $exists: true, $ne: '' },
|
|
573
|
+
inboundSecurityDisposition: { $in: ['flagged', 'rejected'] },
|
|
516
574
|
acceptedAt: { $gte: cutoffArg },
|
|
517
575
|
}, safeLimit, { acceptedAt: -1 });
|
|
518
576
|
}
|
|
@@ -405,7 +405,21 @@ export class AcceptedEmailSpool {
|
|
|
405
405
|
const plan = emailServer.createAcceptedEnvelopeDispatchPlan(context, idempotencyKeys);
|
|
406
406
|
const nonStoreEntries = plan.filter((entry) => entry.action.type !== 'store');
|
|
407
407
|
|
|
408
|
-
|
|
408
|
+
cachedEmail.direction = this.deriveDirectionFromResolvedRoutes(
|
|
409
|
+
context.resolvedRecipientRoutes,
|
|
410
|
+
context.envelope.rcptTo,
|
|
411
|
+
);
|
|
412
|
+
// Submission/relay authentication is not a receiver verdict. Only locally
|
|
413
|
+
// received mail is classified or enforced here; outbound mail gets its
|
|
414
|
+
// post-signing, actual-egress self-check from the delivery transaction.
|
|
415
|
+
const acceptance = evaluateInboundAcceptance(
|
|
416
|
+
cachedEmail.direction === 'inbound' ? context.securityResults : null,
|
|
417
|
+
);
|
|
418
|
+
if (cachedEmail.direction === 'inbound' && context.securityResults) {
|
|
419
|
+
cachedEmail.inboundSecurityDisposition = acceptance.dmarcReject
|
|
420
|
+
? 'rejected'
|
|
421
|
+
: acceptance.status;
|
|
422
|
+
}
|
|
409
423
|
const startedAtMs = Date.now();
|
|
410
424
|
|
|
411
425
|
const persistedRawMessage = this.setDcRouterCacheIdHeader(
|
|
@@ -413,10 +427,6 @@ export class AcceptedEmailSpool {
|
|
|
413
427
|
cachedEmail.id,
|
|
414
428
|
);
|
|
415
429
|
await this.persistRawMessage(cachedEmail, persistedRawMessage);
|
|
416
|
-
cachedEmail.direction = this.deriveDirectionFromResolvedRoutes(
|
|
417
|
-
context.resolvedRecipientRoutes,
|
|
418
|
-
context.envelope.rcptTo,
|
|
419
|
-
);
|
|
420
430
|
cachedEmail.acceptedAt = startedAtMs;
|
|
421
431
|
|
|
422
432
|
if (acceptance.dmarcReject) {
|
|
@@ -446,12 +456,9 @@ export class AcceptedEmailSpool {
|
|
|
446
456
|
throw new AcceptEnvelopeRejectionError(550, rejectionMessage);
|
|
447
457
|
}
|
|
448
458
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
} else {
|
|
453
|
-
cachedEmail.status = 'accepted';
|
|
454
|
-
}
|
|
459
|
+
// Security disposition is orthogonal to the delivery lifecycle. The Ops
|
|
460
|
+
// API synthesizes the visible flagged badge from inboundSecurityDisposition.
|
|
461
|
+
cachedEmail.status = 'accepted';
|
|
455
462
|
if (nonStoreEntries.length > 0) {
|
|
456
463
|
// Relay recipients are dispatched after this row is committed. Park the
|
|
457
464
|
// row in a status the spool actually scans so a crash between the commit
|
|
@@ -657,12 +664,11 @@ export class AcceptedEmailSpool {
|
|
|
657
664
|
cachedEmail.routeData = JSON.stringify(routeData);
|
|
658
665
|
|
|
659
666
|
const stillPending = outcomeArg.pendingRecipients.length > 0;
|
|
660
|
-
const acceptanceStatus = cachedEmail.lastError && !stillPending ? 'flagged' : 'accepted';
|
|
661
667
|
if (stillPending) {
|
|
662
668
|
cachedEmail.status = 'deferred';
|
|
663
669
|
cachedEmail.nextAttempt = new Date(Date.now() + ENVELOPE_DISPATCH_RETRY_DELAY_MS);
|
|
664
670
|
} else if (cachedEmail.status === 'deferred') {
|
|
665
|
-
cachedEmail.status =
|
|
671
|
+
cachedEmail.status = 'accepted';
|
|
666
672
|
cachedEmail.nextAttempt = new Date();
|
|
667
673
|
}
|
|
668
674
|
await cachedEmail.save();
|
|
@@ -701,7 +707,7 @@ export class AcceptedEmailSpool {
|
|
|
701
707
|
// Nothing left to dispatch: settle the row back onto its acceptance status
|
|
702
708
|
// so a fully dispatched envelope does not linger as deferred.
|
|
703
709
|
if (cachedEmailArg.status === 'deferred') {
|
|
704
|
-
cachedEmailArg.status =
|
|
710
|
+
cachedEmailArg.status = 'accepted';
|
|
705
711
|
cachedEmailArg.nextAttempt = new Date();
|
|
706
712
|
await cachedEmailArg.save();
|
|
707
713
|
await this.notifyEmailQueuePersisted(cachedEmailArg, 'envelope-dispatch-settled');
|
|
@@ -134,6 +134,14 @@ export class MailEgressCoordinator {
|
|
|
134
134
|
connectPort: proxy.listenPort,
|
|
135
135
|
poolKey: `remoteIngress:${proxy.edgeId}:${context.host}:${context.port}`,
|
|
136
136
|
ehloHostname: selected.hostname,
|
|
137
|
+
sourceEvidence: tunnelManager.waitForEgressConnectionEvidence(proxy.proxyId, proxy.edgeId)
|
|
138
|
+
.then((eventArg) => {
|
|
139
|
+
if (!eventArg?.sourceIp || !eventArg.addressFamily) return null;
|
|
140
|
+
return {
|
|
141
|
+
sourceIp: eventArg.sourceIp,
|
|
142
|
+
addressFamily: eventArg.addressFamily,
|
|
143
|
+
};
|
|
144
|
+
}),
|
|
137
145
|
close: async () => {
|
|
138
146
|
if (closed) {
|
|
139
147
|
return;
|
|
@@ -24,18 +24,17 @@ export function evaluateInboundAcceptance(securityResults: any): IInboundAccepta
|
|
|
24
24
|
doubts.push('DKIM signature(s) present but none verified');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// A failed DMARC alignment is a security finding even when the sender
|
|
28
|
+
// publishes p=none. Policy controls enforcement, not the authentication result.
|
|
28
29
|
const dmarc = securityResults?.dmarc;
|
|
29
30
|
if (dmarc && dmarc.passed === false) {
|
|
30
31
|
const policy = String(dmarc.policy || '').toLowerCase();
|
|
31
32
|
const action = String(dmarc.action || '').toLowerCase();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
dmarcReject = true;
|
|
36
|
-
}
|
|
37
|
-
doubts.push(`DMARC failed for ${dmarc.domain || 'sender domain'} (policy ${dmarc.policy || 'unknown'})`);
|
|
33
|
+
dmarcDomain = dmarc.domain || undefined;
|
|
34
|
+
if (action === 'reject' || policy === 'reject') {
|
|
35
|
+
dmarcReject = true;
|
|
38
36
|
}
|
|
37
|
+
doubts.push(`DMARC failed for ${dmarc.domain || 'sender domain'} (policy ${dmarc.policy || 'unknown'})`);
|
|
39
38
|
}
|
|
40
39
|
return {
|
|
41
40
|
status: doubts.length > 0 ? 'flagged' : 'accepted',
|