fluxmail 0.8.1 → 0.9.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/accounts/googleAuth.d.ts +5 -1
- package/dist/accounts/googleAuth.d.ts.map +1 -1
- package/dist/accounts/googleAuth.js +5 -1
- package/dist/accounts/googleAuth.js.map +1 -1
- package/dist/accounts/registry.d.ts +1 -0
- package/dist/accounts/registry.d.ts.map +1 -1
- package/dist/accounts/registry.js +11 -0
- package/dist/accounts/registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -1
- package/dist/cliMail.d.ts +1 -0
- package/dist/cliMail.d.ts.map +1 -1
- package/dist/cliMail.js +71 -2
- package/dist/cliMail.js.map +1 -1
- package/dist/http/rest.d.ts.map +1 -1
- package/dist/http/rest.js +241 -12
- package/dist/http/rest.js.map +1 -1
- package/dist/mcp/buildServer.d.ts +1 -0
- package/dist/mcp/buildServer.d.ts.map +1 -1
- package/dist/mcp/buildServer.js +73 -3
- package/dist/mcp/buildServer.js.map +1 -1
- package/dist/permissions.js +1 -1
- package/dist/permissions.js.map +1 -1
- package/dist/scheduler/sendScheduler.js +1 -1
- package/dist/scheduler/sendScheduler.js.map +1 -1
- package/dist/service/emailService.d.ts +35 -2
- package/dist/service/emailService.d.ts.map +1 -1
- package/dist/service/emailService.js +270 -35
- package/dist/service/emailService.js.map +1 -1
- package/dist/service/searchCursor.d.ts +2 -0
- package/dist/service/searchCursor.d.ts.map +1 -1
- package/dist/service/searchCursor.js +4 -1
- package/dist/service/searchCursor.js.map +1 -1
- package/dist/storage/db.d.ts +66 -2
- package/dist/storage/db.d.ts.map +1 -1
- package/dist/storage/db.js +25 -1
- package/dist/storage/db.js.map +1 -1
- package/dist/storage/sendAs.d.ts +9 -0
- package/dist/storage/sendAs.d.ts.map +1 -0
- package/dist/storage/sendAs.js +26 -0
- package/dist/storage/sendAs.js.map +1 -0
- package/package.json +5 -5
package/dist/permissions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,aAAa;IACb,eAAe;IACf,YAAY;IACZ,aAAa;IACb,WAAW;CACH,CAAC;AAIX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,aAAa;CACL,CAAC;AAIX,MAAM,CAAC,MAAM,2BAA2B,GAAkC;IACxE,WAAW,EACT
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ const MAX_ARM_MS = 15 * 60_000;
|
|
|
6
6
|
const BASE_BACKOFF_MS = 30_000;
|
|
7
7
|
const CLAIM_LEASE_MS = 5 * 60_000;
|
|
8
8
|
/** Errors that no amount of retrying will fix (e.g. the draft is gone). */
|
|
9
|
-
const PERMANENT_CODES = new Set(['not_found', 'invalid_request', 'unsupported_capability']);
|
|
9
|
+
const PERMANENT_CODES = new Set(['not_found', 'invalid_request', 'permission_denied', 'unsupported_capability']);
|
|
10
10
|
/**
|
|
11
11
|
* Fires scheduled sends when their time comes. Owned by the long-lived
|
|
12
12
|
* processes (serve/stdio); one-shot CLI commands construct it but never
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendScheduler.js","sourceRoot":"","sources":["../../src/scheduler/sendScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,SAAS,EACT,WAAW,EACX,UAAU,EACV,aAAa,EACb,UAAU,GAEX,MAAM,8BAA8B,CAAC;AAStC,mGAAmG;AACnG,MAAM,UAAU,GAAG,EAAE,GAAG,MAAM,CAAC;AAC/B,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC;AAElC,2EAA2E;AAC3E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,EAAE,wBAAwB,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"sendScheduler.js","sourceRoot":"","sources":["../../src/scheduler/sendScheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type Account, type AttachmentMeta, type DraftInput, type EmailAddress, type EmailQuery, type Folder, type Label, type Message, type
|
|
1
|
+
import { type Account, type AttachmentMeta, type DraftInput, type EmailAddress, type EmailQuery, type Folder, type Label, type Message, type MessageSearchPage, type ModifyAction, type PageOpts, type PortableEmailQuery, type SendResult, type SendAsIdentity, type Thread } from '@fluxmail/core';
|
|
2
2
|
import type { AccountRegistry } from '../accounts/registry.js';
|
|
3
3
|
import { type Entitlements } from '../licensing/entitlements.js';
|
|
4
4
|
import type { FluxmailDb } from '../storage/db.js';
|
|
5
5
|
import type { Principal } from '../auth.js';
|
|
6
6
|
import { type ScheduledSendStatus } from '../storage/scheduledSends.js';
|
|
7
|
+
import { type ConfiguredSendAsInput } from '../storage/sendAs.js';
|
|
7
8
|
export interface SendInput extends DraftInput {
|
|
8
9
|
/** With replyToMessageId: compute recipients from the original (reply-all semantics). */
|
|
9
10
|
replyAll?: boolean;
|
|
@@ -15,6 +16,7 @@ export interface ForwardInput {
|
|
|
15
16
|
/** Optional note placed above the forwarded content. */
|
|
16
17
|
comment?: string;
|
|
17
18
|
includeAttachments?: boolean;
|
|
19
|
+
from?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface ServiceStatus {
|
|
20
22
|
accounts: Array<Pick<Account, 'id' | 'provider' | 'email' | 'status' | 'ownerMemberId'> & {
|
|
@@ -51,6 +53,31 @@ export interface ScheduledSendInfo {
|
|
|
51
53
|
sentMessageId?: string;
|
|
52
54
|
sentThreadId?: string;
|
|
53
55
|
}
|
|
56
|
+
export interface BatchSearchAccount {
|
|
57
|
+
accountId: string;
|
|
58
|
+
pageToken?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface BatchSearchInput {
|
|
61
|
+
accounts: BatchSearchAccount[];
|
|
62
|
+
query: PortableEmailQuery;
|
|
63
|
+
pageSize?: number;
|
|
64
|
+
includeSnippet?: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface BatchSearchError {
|
|
67
|
+
code: string;
|
|
68
|
+
message: string;
|
|
69
|
+
data?: Record<string, unknown>;
|
|
70
|
+
exhausted: false;
|
|
71
|
+
}
|
|
72
|
+
export interface BatchSearchGroup {
|
|
73
|
+
accountId: string;
|
|
74
|
+
page?: MessageSearchPage;
|
|
75
|
+
error?: BatchSearchError;
|
|
76
|
+
}
|
|
77
|
+
export interface BatchSearchResult {
|
|
78
|
+
groups: BatchSearchGroup[];
|
|
79
|
+
exhausted: boolean;
|
|
80
|
+
}
|
|
54
81
|
/** Parse and validate a sendAt timestamp; returns epoch ms. */
|
|
55
82
|
export declare function resolveSendAt(sendAtIso: string, now?: number): number;
|
|
56
83
|
/**
|
|
@@ -98,7 +125,10 @@ export declare class EmailService {
|
|
|
98
125
|
private scheduledStatus;
|
|
99
126
|
listFolders(accountId?: string): Promise<Folder[]>;
|
|
100
127
|
listLabels(accountId?: string): Promise<Label[]>;
|
|
101
|
-
|
|
128
|
+
listSendAs(accountId?: string): Promise<SendAsIdentity[]>;
|
|
129
|
+
replaceSendAs(accountId: string, identities: ConfiguredSendAsInput[]): Promise<SendAsIdentity[]>;
|
|
130
|
+
listMessages(accountId: string | undefined, q: EmailQuery, page?: PageOpts): Promise<MessageSearchPage>;
|
|
131
|
+
searchMessagesBatch(input: BatchSearchInput): Promise<BatchSearchResult>;
|
|
102
132
|
getMessage(accountId: string | undefined, id: string): Promise<Message>;
|
|
103
133
|
getThread(accountId: string | undefined, threadId: string): Promise<Thread>;
|
|
104
134
|
createDraft(accountId: string | undefined, d: SendInput): Promise<Message>;
|
|
@@ -127,6 +157,9 @@ export declare class EmailService {
|
|
|
127
157
|
* reply; plus original To/Cc (minus our own address) for reply-all.
|
|
128
158
|
*/
|
|
129
159
|
private resolveRecipients;
|
|
160
|
+
private sendAsIdentities;
|
|
161
|
+
private validateSender;
|
|
162
|
+
private selectReplySender;
|
|
130
163
|
forward(accountId: string | undefined, input: ForwardInput): Promise<SendResult>;
|
|
131
164
|
modify(accountId: string | undefined, ids: string[], action: ModifyAction): Promise<void>;
|
|
132
165
|
getAttachment(accountId: string | undefined, messageId: string, attachmentId: string, maxBytes?: number): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emailService.d.ts","sourceRoot":"","sources":["../../src/service/emailService.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"emailService.d.ts","sourceRoot":"","sources":["../../src/service/emailService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
-
import { computeReplyRecipients, EmailError, formatAddressList, forwardSubject, isEmailError, isPortableFolderRole, normalizeEmailQuery, supportsPortableEmailQuery, } from '@fluxmail/core';
|
|
1
|
+
import { computeReplyRecipients, EmailError, formatAddressList, forwardSubject, isEmailError, isPortableFolderRole, normalizeEmailQuery, parseSingleAddress, supportsPortableEmailQuery, } from '@fluxmail/core';
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { assertWithinQuota, checkLicenseState } from '../licensing/entitlements.js';
|
|
4
4
|
import { listMembers } from '../storage/members.js';
|
|
5
5
|
import { canAccessAccount, canAdminister, canSeeAccountMetadata } from '../authorization.js';
|
|
6
6
|
import { cancelScheduledSend, countPending, createScheduledSend, findPendingByDraft, getScheduledSend, listScheduledSends, markSent, } from '../storage/scheduledSends.js';
|
|
7
7
|
import { SearchCursorCodec } from './searchCursor.js';
|
|
8
|
+
import { listConfiguredSendAs, replaceConfiguredSendAs } from '../storage/sendAs.js';
|
|
8
9
|
const SCHEDULE_GRACE_MS = 60_000;
|
|
9
10
|
const SCHEDULE_MAX_HORIZON_MS = 365 * 24 * 3_600_000;
|
|
11
|
+
const SEARCH_SOFT_BUDGET_MS = 10_000;
|
|
12
|
+
const SEARCH_HARD_DEADLINE_MS = 15_000;
|
|
13
|
+
const BATCH_SEARCH_CONCURRENCY = 3;
|
|
14
|
+
function searchTimeoutError() {
|
|
15
|
+
return new EmailError('provider_unavailable', 'The search did not finish before the provider deadline.', {
|
|
16
|
+
reason: 'search_timeout',
|
|
17
|
+
exhausted: false,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function batchError(error) {
|
|
21
|
+
if (isEmailError(error)) {
|
|
22
|
+
return {
|
|
23
|
+
code: error.code,
|
|
24
|
+
message: error.message,
|
|
25
|
+
...(error.data ? { data: error.data } : {}),
|
|
26
|
+
exhausted: false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
code: 'provider_unavailable',
|
|
31
|
+
message: 'The email provider could not complete the search.',
|
|
32
|
+
exhausted: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
10
35
|
function assertSearchCapabilities(capabilities, query) {
|
|
11
36
|
const portableQuery = {
|
|
12
37
|
...(query.text !== undefined ? { text: query.text } : {}),
|
|
@@ -252,7 +277,49 @@ export class EmailService {
|
|
|
252
277
|
listLabels(accountId) {
|
|
253
278
|
return this.withProvider(accountId, (p) => p.listLabels());
|
|
254
279
|
}
|
|
255
|
-
|
|
280
|
+
listSendAs(accountId) {
|
|
281
|
+
return this.withProvider(accountId, (provider, resolvedId, account) => this.sendAsIdentities(provider, resolvedId, account));
|
|
282
|
+
}
|
|
283
|
+
async replaceSendAs(accountId, identities) {
|
|
284
|
+
if (!this.principal || !canAdminister(this.principal, 'admin.accounts')) {
|
|
285
|
+
throw new EmailError('permission_denied', 'This operation requires admin.accounts.');
|
|
286
|
+
}
|
|
287
|
+
const account = this.registry.getAccount(accountId);
|
|
288
|
+
if (!canSeeAccountMetadata(this.principal, account)) {
|
|
289
|
+
throw new EmailError('not_found', `No account with id "${accountId}"`);
|
|
290
|
+
}
|
|
291
|
+
const resolvedId = account.id;
|
|
292
|
+
if (account.provider === 'gmail') {
|
|
293
|
+
throw new EmailError('unsupported_capability', 'Manage Gmail send-as addresses in Gmail.');
|
|
294
|
+
}
|
|
295
|
+
const seen = new Set();
|
|
296
|
+
const normalized = identities.map((identity) => {
|
|
297
|
+
const parsed = parseSingleAddress(identity.email.trim());
|
|
298
|
+
if (!parsed)
|
|
299
|
+
throw new EmailError('invalid_request', `Could not parse sender address: "${identity.email}"`);
|
|
300
|
+
const email = parsed.email;
|
|
301
|
+
const key = email.toLowerCase();
|
|
302
|
+
if (key === account.email.toLowerCase()) {
|
|
303
|
+
throw new EmailError('invalid_request', 'Do not include the connected account address in configured aliases.');
|
|
304
|
+
}
|
|
305
|
+
if (seen.has(key))
|
|
306
|
+
throw new EmailError('invalid_request', `Duplicate sender address: "${email}"`);
|
|
307
|
+
seen.add(key);
|
|
308
|
+
const name = identity.name?.trim();
|
|
309
|
+
return { email, ...(name ? { name } : {}) };
|
|
310
|
+
});
|
|
311
|
+
replaceConfiguredSendAs(this.db, resolvedId, normalized);
|
|
312
|
+
return [
|
|
313
|
+
{
|
|
314
|
+
email: account.email,
|
|
315
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
316
|
+
isPrimary: true,
|
|
317
|
+
source: 'configured',
|
|
318
|
+
},
|
|
319
|
+
...listConfiguredSendAs(this.db, resolvedId),
|
|
320
|
+
];
|
|
321
|
+
}
|
|
322
|
+
async listMessages(accountId, q, page = {}) {
|
|
256
323
|
const normalized = normalizeEmailQuery(q);
|
|
257
324
|
if (!normalized.success) {
|
|
258
325
|
throw new EmailError('invalid_request', normalized.diagnostics.map((item) => item.message).join(' '), {
|
|
@@ -261,35 +328,128 @@ export class EmailService {
|
|
|
261
328
|
}
|
|
262
329
|
const query = normalized.query;
|
|
263
330
|
const pageSize = Math.min(Math.max(page.pageSize ?? 25, 1), 100);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
331
|
+
const ownsController = page.signal === undefined;
|
|
332
|
+
const controller = ownsController ? new AbortController() : undefined;
|
|
333
|
+
const signal = page.signal ?? controller.signal;
|
|
334
|
+
const startedAt = Date.now();
|
|
335
|
+
const softDeadlineAt = page.softDeadlineAt ?? startedAt + SEARCH_SOFT_BUDGET_MS;
|
|
336
|
+
const hardDeadlineAt = startedAt + SEARCH_HARD_DEADLINE_MS;
|
|
337
|
+
const timeout = ownsController
|
|
338
|
+
? setTimeout(() => controller.abort(searchTimeoutError()), Math.max(0, hardDeadlineAt - Date.now()))
|
|
339
|
+
: undefined;
|
|
340
|
+
let rejectOnAbort;
|
|
341
|
+
try {
|
|
342
|
+
const operation = this.withProvider(accountId, async (provider, resolvedId, account) => {
|
|
343
|
+
assertSearchCapabilities(provider.capabilities.search, query);
|
|
344
|
+
const providerToken = page.pageToken
|
|
345
|
+
? this.cursorCodec.decode(page.pageToken, {
|
|
346
|
+
accountId: resolvedId,
|
|
347
|
+
provider: account.provider,
|
|
348
|
+
query,
|
|
349
|
+
pageSize,
|
|
350
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
351
|
+
})
|
|
352
|
+
: undefined;
|
|
353
|
+
const result = await provider.listMessages(query, {
|
|
271
354
|
pageSize,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
355
|
+
...(providerToken ? { pageToken: providerToken } : {}),
|
|
356
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
357
|
+
signal,
|
|
358
|
+
softDeadlineAt,
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
...result,
|
|
362
|
+
exhausted: result.exhausted ?? (!result.nextPageToken && !result.incomplete),
|
|
363
|
+
...(result.nextPageToken
|
|
364
|
+
? {
|
|
365
|
+
nextPageToken: this.cursorCodec.encode({
|
|
366
|
+
accountId: resolvedId,
|
|
367
|
+
provider: account.provider,
|
|
368
|
+
query,
|
|
369
|
+
pageSize,
|
|
370
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
371
|
+
providerToken: result.nextPageToken,
|
|
372
|
+
}),
|
|
373
|
+
}
|
|
374
|
+
: { nextPageToken: undefined }),
|
|
375
|
+
};
|
|
277
376
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
377
|
+
const aborted = new Promise((_, reject) => {
|
|
378
|
+
rejectOnAbort = () => reject(searchTimeoutError());
|
|
379
|
+
signal.addEventListener('abort', rejectOnAbort, { once: true });
|
|
380
|
+
if (signal.aborted)
|
|
381
|
+
rejectOnAbort();
|
|
382
|
+
});
|
|
383
|
+
return await Promise.race([operation, aborted]);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
if (signal.aborted)
|
|
387
|
+
throw searchTimeoutError();
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
finally {
|
|
391
|
+
if (timeout)
|
|
392
|
+
clearTimeout(timeout);
|
|
393
|
+
if (rejectOnAbort)
|
|
394
|
+
signal.removeEventListener('abort', rejectOnAbort);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async searchMessagesBatch(input) {
|
|
398
|
+
if (input.accounts.length < 1 || input.accounts.length > 20) {
|
|
399
|
+
throw new EmailError('invalid_request', 'Batch search requires between 1 and 20 accounts.');
|
|
400
|
+
}
|
|
401
|
+
const seen = new Set();
|
|
402
|
+
for (const account of input.accounts) {
|
|
403
|
+
if (seen.has(account.accountId))
|
|
404
|
+
throw new EmailError('invalid_request', 'Batch account IDs must be distinct.');
|
|
405
|
+
seen.add(account.accountId);
|
|
406
|
+
}
|
|
407
|
+
const normalized = normalizeEmailQuery(input.query);
|
|
408
|
+
if (!normalized.success) {
|
|
409
|
+
throw new EmailError('invalid_request', normalized.diagnostics.map((item) => item.message).join(' '), {
|
|
410
|
+
diagnostics: normalized.diagnostics,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
const pageSize = Math.min(Math.max(input.pageSize ?? 25, 1), 100);
|
|
414
|
+
const controller = new AbortController();
|
|
415
|
+
const startedAt = Date.now();
|
|
416
|
+
const softDeadlineAt = startedAt + SEARCH_SOFT_BUDGET_MS;
|
|
417
|
+
const timeout = setTimeout(() => controller.abort(searchTimeoutError()), SEARCH_HARD_DEADLINE_MS);
|
|
418
|
+
const groups = [];
|
|
419
|
+
let nextIndex = 0;
|
|
420
|
+
const worker = async () => {
|
|
421
|
+
for (;;) {
|
|
422
|
+
const index = nextIndex++;
|
|
423
|
+
if (index >= input.accounts.length)
|
|
424
|
+
return;
|
|
425
|
+
const account = input.accounts[index];
|
|
426
|
+
if (controller.signal.aborted) {
|
|
427
|
+
groups[index] = { accountId: account.accountId, error: batchError(searchTimeoutError()) };
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
this.assertAccountAccess(account.accountId);
|
|
432
|
+
const page = await this.listMessages(account.accountId, normalized.query, {
|
|
433
|
+
pageSize,
|
|
434
|
+
...(account.pageToken ? { pageToken: account.pageToken } : {}),
|
|
435
|
+
...(input.includeSnippet !== undefined ? { includeSnippet: input.includeSnippet } : {}),
|
|
436
|
+
signal: controller.signal,
|
|
437
|
+
softDeadlineAt,
|
|
438
|
+
});
|
|
439
|
+
groups[index] = { accountId: account.accountId, page };
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
groups[index] = { accountId: account.accountId, error: batchError(error) };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
try {
|
|
447
|
+
await Promise.all(Array.from({ length: Math.min(BATCH_SEARCH_CONCURRENCY, input.accounts.length) }, () => worker()));
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
clearTimeout(timeout);
|
|
451
|
+
}
|
|
452
|
+
return { groups, exhausted: groups.every((group) => group.page?.exhausted === true) };
|
|
293
453
|
}
|
|
294
454
|
getMessage(accountId, id) {
|
|
295
455
|
return this.withProvider(accountId, (p) => p.getMessage(id));
|
|
@@ -313,6 +473,10 @@ export class EmailService {
|
|
|
313
473
|
send(accountId, input) {
|
|
314
474
|
return this.withProvider(accountId, async (p, resolvedId, account) => {
|
|
315
475
|
if ('draftId' in input) {
|
|
476
|
+
const draft = await p.getDraft(input.draftId);
|
|
477
|
+
if (draft.from?.email && draft.from.email.toLowerCase() !== account.email.toLowerCase()) {
|
|
478
|
+
await this.validateSender(p, resolvedId, account, draft.from.email);
|
|
479
|
+
}
|
|
316
480
|
const result = await p.send({ draftId: input.draftId });
|
|
317
481
|
// Sending a scheduled draft now supersedes its schedule.
|
|
318
482
|
const pending = findPendingByDraft(this.db, resolvedId, input.draftId);
|
|
@@ -392,17 +556,87 @@ export class EmailService {
|
|
|
392
556
|
*/
|
|
393
557
|
async resolveRecipients(p, account, input) {
|
|
394
558
|
const { replyAll, ...draft } = input;
|
|
395
|
-
if (!draft.replyToMessageId
|
|
559
|
+
if (!draft.replyToMessageId) {
|
|
560
|
+
if (draft.from)
|
|
561
|
+
draft.from = (await this.validateSender(p, account.id, account, draft.from)).email;
|
|
396
562
|
return draft;
|
|
563
|
+
}
|
|
397
564
|
const original = await p.getMessage(draft.replyToMessageId);
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
565
|
+
const identities = await this.sendAsIdentities(p, account.id, account);
|
|
566
|
+
const requestedSender = draft.from;
|
|
567
|
+
const selected = requestedSender !== undefined
|
|
568
|
+
? await this.validateSender(p, account.id, account, requestedSender, identities)
|
|
569
|
+
: this.selectReplySender(original, identities);
|
|
570
|
+
if (requestedSender !== undefined || !selected.isPrimary)
|
|
571
|
+
draft.from = selected.email;
|
|
572
|
+
else
|
|
573
|
+
delete draft.from;
|
|
574
|
+
if (!draft.to?.length) {
|
|
575
|
+
const recipients = computeReplyRecipients(original, identities.map((identity) => identity.email), replyAll ?? false);
|
|
576
|
+
draft.to = recipients.to;
|
|
577
|
+
if (recipients.cc.length && !draft.cc?.length)
|
|
578
|
+
draft.cc = recipients.cc;
|
|
579
|
+
}
|
|
402
580
|
return draft;
|
|
403
581
|
}
|
|
582
|
+
async sendAsIdentities(provider, accountId, account) {
|
|
583
|
+
if (provider.listSendAs) {
|
|
584
|
+
const discovered = await provider.listSendAs();
|
|
585
|
+
const primaryIndex = discovered.findIndex((identity) => identity.email.toLowerCase() === account.email.toLowerCase());
|
|
586
|
+
if (primaryIndex >= 0) {
|
|
587
|
+
return discovered.map((identity, index) => ({ ...identity, isPrimary: index === primaryIndex }));
|
|
588
|
+
}
|
|
589
|
+
return [
|
|
590
|
+
{
|
|
591
|
+
email: account.email,
|
|
592
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
593
|
+
isPrimary: true,
|
|
594
|
+
source: 'provider',
|
|
595
|
+
},
|
|
596
|
+
...discovered.map((identity) => ({ ...identity, isPrimary: false })),
|
|
597
|
+
];
|
|
598
|
+
}
|
|
599
|
+
return [
|
|
600
|
+
{
|
|
601
|
+
email: account.email,
|
|
602
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
603
|
+
isPrimary: true,
|
|
604
|
+
source: 'configured',
|
|
605
|
+
},
|
|
606
|
+
...listConfiguredSendAs(this.db, accountId),
|
|
607
|
+
];
|
|
608
|
+
}
|
|
609
|
+
async validateSender(provider, accountId, account, sender, known) {
|
|
610
|
+
const parsed = parseSingleAddress(sender.trim());
|
|
611
|
+
if (!parsed)
|
|
612
|
+
throw new EmailError('invalid_request', `Could not parse sender address: "${sender}"`);
|
|
613
|
+
if (parsed.email.toLowerCase() === account.email.toLowerCase()) {
|
|
614
|
+
return {
|
|
615
|
+
email: account.email,
|
|
616
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
617
|
+
isPrimary: true,
|
|
618
|
+
source: account.provider === 'gmail' ? 'provider' : 'configured',
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
const identity = (known ?? (await this.sendAsIdentities(provider, accountId, account))).find((candidate) => candidate.email.toLowerCase() === parsed.email.toLowerCase());
|
|
622
|
+
if (!identity)
|
|
623
|
+
throw new EmailError('invalid_request', `Sender address "${parsed.email}" is not available.`);
|
|
624
|
+
return identity;
|
|
625
|
+
}
|
|
626
|
+
selectReplySender(original, identities) {
|
|
627
|
+
const byEmail = new Map(identities.map((identity) => [identity.email.toLowerCase(), identity]));
|
|
628
|
+
const originalSender = original.from ? byEmail.get(original.from.email.toLowerCase()) : undefined;
|
|
629
|
+
if (originalSender)
|
|
630
|
+
return originalSender;
|
|
631
|
+
for (const recipient of [...original.to, ...(original.cc ?? [])]) {
|
|
632
|
+
const match = byEmail.get(recipient.email.toLowerCase());
|
|
633
|
+
if (match)
|
|
634
|
+
return match;
|
|
635
|
+
}
|
|
636
|
+
return identities.find((identity) => identity.isPrimary) ?? identities[0];
|
|
637
|
+
}
|
|
404
638
|
forward(accountId, input) {
|
|
405
|
-
return this.withProvider(accountId, async (p) => {
|
|
639
|
+
return this.withProvider(accountId, async (p, resolvedId, account) => {
|
|
406
640
|
const original = await p.getMessage(input.messageId);
|
|
407
641
|
const includeAttachments = input.includeAttachments ?? true;
|
|
408
642
|
const attachments = [];
|
|
@@ -419,6 +653,7 @@ export class EmailService {
|
|
|
419
653
|
}
|
|
420
654
|
}
|
|
421
655
|
const draft = {
|
|
656
|
+
...(input.from ? { from: (await this.validateSender(p, resolvedId, account, input.from)).email } : {}),
|
|
422
657
|
to: input.to,
|
|
423
658
|
...(input.cc?.length ? { cc: input.cc } : {}),
|
|
424
659
|
subject: forwardSubject(original.subject),
|