fluxmail 0.8.1 → 0.10.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 +9 -3
- package/dist/accounts/googleAuth.d.ts.map +1 -1
- package/dist/accounts/googleAuth.js +23 -85
- package/dist/accounts/googleAuth.js.map +1 -1
- package/dist/accounts/microsoftAuth.d.ts +2 -1
- package/dist/accounts/microsoftAuth.d.ts.map +1 -1
- package/dist/accounts/microsoftAuth.js +16 -80
- package/dist/accounts/microsoftAuth.js.map +1 -1
- package/dist/accounts/oauthCallback.d.ts +52 -0
- package/dist/accounts/oauthCallback.d.ts.map +1 -0
- package/dist/accounts/oauthCallback.js +237 -0
- package/dist/accounts/oauthCallback.js.map +1 -0
- 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/accounts/telemetry.d.ts +3 -0
- package/dist/accounts/telemetry.d.ts.map +1 -1
- package/dist/accounts/telemetry.js +1 -0
- package/dist/accounts/telemetry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +100 -12
- 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 +77 -2
- package/dist/cliMail.js.map +1 -1
- package/dist/http/rest.d.ts.map +1 -1
- package/dist/http/rest.js +261 -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 +79 -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 +36 -2
- package/dist/service/emailService.d.ts.map +1 -1
- package/dist/service/emailService.js +285 -35
- package/dist/service/emailService.js.map +1 -1
- package/dist/service/searchCursor.d.ts +4 -0
- package/dist/service/searchCursor.d.ts.map +1 -1
- package/dist/service/searchCursor.js +7 -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,32 @@ 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
|
+
includeSearchContext?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export interface BatchSearchError {
|
|
68
|
+
code: string;
|
|
69
|
+
message: string;
|
|
70
|
+
data?: Record<string, unknown>;
|
|
71
|
+
exhausted: false;
|
|
72
|
+
}
|
|
73
|
+
export interface BatchSearchGroup {
|
|
74
|
+
accountId: string;
|
|
75
|
+
page?: MessageSearchPage;
|
|
76
|
+
error?: BatchSearchError;
|
|
77
|
+
}
|
|
78
|
+
export interface BatchSearchResult {
|
|
79
|
+
groups: BatchSearchGroup[];
|
|
80
|
+
exhausted: boolean;
|
|
81
|
+
}
|
|
54
82
|
/** Parse and validate a sendAt timestamp; returns epoch ms. */
|
|
55
83
|
export declare function resolveSendAt(sendAtIso: string, now?: number): number;
|
|
56
84
|
/**
|
|
@@ -98,7 +126,10 @@ export declare class EmailService {
|
|
|
98
126
|
private scheduledStatus;
|
|
99
127
|
listFolders(accountId?: string): Promise<Folder[]>;
|
|
100
128
|
listLabels(accountId?: string): Promise<Label[]>;
|
|
101
|
-
|
|
129
|
+
listSendAs(accountId?: string): Promise<SendAsIdentity[]>;
|
|
130
|
+
replaceSendAs(accountId: string, identities: ConfiguredSendAsInput[]): Promise<SendAsIdentity[]>;
|
|
131
|
+
listMessages(accountId: string | undefined, q: EmailQuery, page?: PageOpts): Promise<MessageSearchPage>;
|
|
132
|
+
searchMessagesBatch(input: BatchSearchInput): Promise<BatchSearchResult>;
|
|
102
133
|
getMessage(accountId: string | undefined, id: string): Promise<Message>;
|
|
103
134
|
getThread(accountId: string | undefined, threadId: string): Promise<Thread>;
|
|
104
135
|
createDraft(accountId: string | undefined, d: SendInput): Promise<Message>;
|
|
@@ -127,6 +158,9 @@ export declare class EmailService {
|
|
|
127
158
|
* reply; plus original To/Cc (minus our own address) for reply-all.
|
|
128
159
|
*/
|
|
129
160
|
private resolveRecipients;
|
|
161
|
+
private sendAsIdentities;
|
|
162
|
+
private validateSender;
|
|
163
|
+
private selectReplySender;
|
|
130
164
|
forward(accountId: string | undefined, input: ForwardInput): Promise<SendResult>;
|
|
131
165
|
modify(accountId: string | undefined, ids: string[], action: ModifyAction): Promise<void>;
|
|
132
166
|
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(' '), {
|
|
@@ -260,36 +327,144 @@ export class EmailService {
|
|
|
260
327
|
});
|
|
261
328
|
}
|
|
262
329
|
const query = normalized.query;
|
|
330
|
+
if (page.includeSearchContext && !query.text) {
|
|
331
|
+
throw new EmailError('invalid_request', 'includeSearchContext requires a portable text query.');
|
|
332
|
+
}
|
|
263
333
|
const pageSize = Math.min(Math.max(page.pageSize ?? 25, 1), 100);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
334
|
+
const ownsController = page.signal === undefined;
|
|
335
|
+
const controller = ownsController ? new AbortController() : undefined;
|
|
336
|
+
const signal = page.signal ?? controller.signal;
|
|
337
|
+
const startedAt = Date.now();
|
|
338
|
+
const softDeadlineAt = page.softDeadlineAt ?? startedAt + SEARCH_SOFT_BUDGET_MS;
|
|
339
|
+
const hardDeadlineAt = startedAt + SEARCH_HARD_DEADLINE_MS;
|
|
340
|
+
const timeout = ownsController
|
|
341
|
+
? setTimeout(() => controller.abort(searchTimeoutError()), Math.max(0, hardDeadlineAt - Date.now()))
|
|
342
|
+
: undefined;
|
|
343
|
+
let rejectOnAbort;
|
|
344
|
+
try {
|
|
345
|
+
const operation = this.withProvider(accountId, async (provider, resolvedId, account) => {
|
|
346
|
+
assertSearchCapabilities(provider.capabilities.search, query);
|
|
347
|
+
if (page.includeSearchContext && provider.capabilities.searchContext !== true) {
|
|
348
|
+
throw new EmailError('unsupported_capability', 'This provider does not support search context.');
|
|
349
|
+
}
|
|
350
|
+
const providerToken = page.pageToken
|
|
351
|
+
? this.cursorCodec.decode(page.pageToken, {
|
|
352
|
+
accountId: resolvedId,
|
|
353
|
+
provider: account.provider,
|
|
354
|
+
query,
|
|
355
|
+
pageSize,
|
|
356
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
357
|
+
...(page.includeSearchContext !== undefined ? { includeSearchContext: page.includeSearchContext } : {}),
|
|
358
|
+
})
|
|
359
|
+
: undefined;
|
|
360
|
+
const result = await provider.listMessages(query, {
|
|
271
361
|
pageSize,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
362
|
+
...(providerToken ? { pageToken: providerToken } : {}),
|
|
363
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
364
|
+
...(page.includeSearchContext !== undefined ? { includeSearchContext: page.includeSearchContext } : {}),
|
|
365
|
+
signal,
|
|
366
|
+
softDeadlineAt,
|
|
367
|
+
});
|
|
368
|
+
return {
|
|
369
|
+
...result,
|
|
370
|
+
exhausted: result.exhausted ?? (!result.nextPageToken && !result.incomplete),
|
|
371
|
+
...(result.nextPageToken
|
|
372
|
+
? {
|
|
373
|
+
nextPageToken: this.cursorCodec.encode({
|
|
374
|
+
accountId: resolvedId,
|
|
375
|
+
provider: account.provider,
|
|
376
|
+
query,
|
|
377
|
+
pageSize,
|
|
378
|
+
...(page.includeSnippet !== undefined ? { includeSnippet: page.includeSnippet } : {}),
|
|
379
|
+
...(page.includeSearchContext !== undefined
|
|
380
|
+
? { includeSearchContext: page.includeSearchContext }
|
|
381
|
+
: {}),
|
|
382
|
+
providerToken: result.nextPageToken,
|
|
383
|
+
}),
|
|
384
|
+
}
|
|
385
|
+
: { nextPageToken: undefined }),
|
|
386
|
+
};
|
|
277
387
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
388
|
+
const aborted = new Promise((_, reject) => {
|
|
389
|
+
rejectOnAbort = () => reject(searchTimeoutError());
|
|
390
|
+
signal.addEventListener('abort', rejectOnAbort, { once: true });
|
|
391
|
+
if (signal.aborted)
|
|
392
|
+
rejectOnAbort();
|
|
393
|
+
});
|
|
394
|
+
return await Promise.race([operation, aborted]);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (signal.aborted)
|
|
398
|
+
throw searchTimeoutError();
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
if (timeout)
|
|
403
|
+
clearTimeout(timeout);
|
|
404
|
+
if (rejectOnAbort)
|
|
405
|
+
signal.removeEventListener('abort', rejectOnAbort);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async searchMessagesBatch(input) {
|
|
409
|
+
if (input.accounts.length < 1 || input.accounts.length > 20) {
|
|
410
|
+
throw new EmailError('invalid_request', 'Batch search requires between 1 and 20 accounts.');
|
|
411
|
+
}
|
|
412
|
+
const seen = new Set();
|
|
413
|
+
for (const account of input.accounts) {
|
|
414
|
+
if (seen.has(account.accountId))
|
|
415
|
+
throw new EmailError('invalid_request', 'Batch account IDs must be distinct.');
|
|
416
|
+
seen.add(account.accountId);
|
|
417
|
+
}
|
|
418
|
+
const normalized = normalizeEmailQuery(input.query);
|
|
419
|
+
if (!normalized.success) {
|
|
420
|
+
throw new EmailError('invalid_request', normalized.diagnostics.map((item) => item.message).join(' '), {
|
|
421
|
+
diagnostics: normalized.diagnostics,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
const pageSize = Math.min(Math.max(input.pageSize ?? 25, 1), 100);
|
|
425
|
+
if (input.includeSearchContext && !normalized.query.text) {
|
|
426
|
+
throw new EmailError('invalid_request', 'includeSearchContext requires a portable text query.');
|
|
427
|
+
}
|
|
428
|
+
const controller = new AbortController();
|
|
429
|
+
const startedAt = Date.now();
|
|
430
|
+
const softDeadlineAt = startedAt + SEARCH_SOFT_BUDGET_MS;
|
|
431
|
+
const timeout = setTimeout(() => controller.abort(searchTimeoutError()), SEARCH_HARD_DEADLINE_MS);
|
|
432
|
+
const groups = [];
|
|
433
|
+
let nextIndex = 0;
|
|
434
|
+
const worker = async () => {
|
|
435
|
+
for (;;) {
|
|
436
|
+
const index = nextIndex++;
|
|
437
|
+
if (index >= input.accounts.length)
|
|
438
|
+
return;
|
|
439
|
+
const account = input.accounts[index];
|
|
440
|
+
if (controller.signal.aborted) {
|
|
441
|
+
groups[index] = { accountId: account.accountId, error: batchError(searchTimeoutError()) };
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
this.assertAccountAccess(account.accountId);
|
|
446
|
+
const page = await this.listMessages(account.accountId, normalized.query, {
|
|
447
|
+
pageSize,
|
|
448
|
+
...(account.pageToken ? { pageToken: account.pageToken } : {}),
|
|
449
|
+
...(input.includeSnippet !== undefined ? { includeSnippet: input.includeSnippet } : {}),
|
|
450
|
+
...(input.includeSearchContext !== undefined ? { includeSearchContext: input.includeSearchContext } : {}),
|
|
451
|
+
signal: controller.signal,
|
|
452
|
+
softDeadlineAt,
|
|
453
|
+
});
|
|
454
|
+
groups[index] = { accountId: account.accountId, page };
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
groups[index] = { accountId: account.accountId, error: batchError(error) };
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
try {
|
|
462
|
+
await Promise.all(Array.from({ length: Math.min(BATCH_SEARCH_CONCURRENCY, input.accounts.length) }, () => worker()));
|
|
463
|
+
}
|
|
464
|
+
finally {
|
|
465
|
+
clearTimeout(timeout);
|
|
466
|
+
}
|
|
467
|
+
return { groups, exhausted: groups.every((group) => group.page?.exhausted === true) };
|
|
293
468
|
}
|
|
294
469
|
getMessage(accountId, id) {
|
|
295
470
|
return this.withProvider(accountId, (p) => p.getMessage(id));
|
|
@@ -313,6 +488,10 @@ export class EmailService {
|
|
|
313
488
|
send(accountId, input) {
|
|
314
489
|
return this.withProvider(accountId, async (p, resolvedId, account) => {
|
|
315
490
|
if ('draftId' in input) {
|
|
491
|
+
const draft = await p.getDraft(input.draftId);
|
|
492
|
+
if (draft.from?.email && draft.from.email.toLowerCase() !== account.email.toLowerCase()) {
|
|
493
|
+
await this.validateSender(p, resolvedId, account, draft.from.email);
|
|
494
|
+
}
|
|
316
495
|
const result = await p.send({ draftId: input.draftId });
|
|
317
496
|
// Sending a scheduled draft now supersedes its schedule.
|
|
318
497
|
const pending = findPendingByDraft(this.db, resolvedId, input.draftId);
|
|
@@ -392,17 +571,87 @@ export class EmailService {
|
|
|
392
571
|
*/
|
|
393
572
|
async resolveRecipients(p, account, input) {
|
|
394
573
|
const { replyAll, ...draft } = input;
|
|
395
|
-
if (!draft.replyToMessageId
|
|
574
|
+
if (!draft.replyToMessageId) {
|
|
575
|
+
if (draft.from)
|
|
576
|
+
draft.from = (await this.validateSender(p, account.id, account, draft.from)).email;
|
|
396
577
|
return draft;
|
|
578
|
+
}
|
|
397
579
|
const original = await p.getMessage(draft.replyToMessageId);
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
580
|
+
const identities = await this.sendAsIdentities(p, account.id, account);
|
|
581
|
+
const requestedSender = draft.from;
|
|
582
|
+
const selected = requestedSender !== undefined
|
|
583
|
+
? await this.validateSender(p, account.id, account, requestedSender, identities)
|
|
584
|
+
: this.selectReplySender(original, identities);
|
|
585
|
+
if (requestedSender !== undefined || !selected.isPrimary)
|
|
586
|
+
draft.from = selected.email;
|
|
587
|
+
else
|
|
588
|
+
delete draft.from;
|
|
589
|
+
if (!draft.to?.length) {
|
|
590
|
+
const recipients = computeReplyRecipients(original, identities.map((identity) => identity.email), replyAll ?? false);
|
|
591
|
+
draft.to = recipients.to;
|
|
592
|
+
if (recipients.cc.length && !draft.cc?.length)
|
|
593
|
+
draft.cc = recipients.cc;
|
|
594
|
+
}
|
|
402
595
|
return draft;
|
|
403
596
|
}
|
|
597
|
+
async sendAsIdentities(provider, accountId, account) {
|
|
598
|
+
if (provider.listSendAs) {
|
|
599
|
+
const discovered = await provider.listSendAs();
|
|
600
|
+
const primaryIndex = discovered.findIndex((identity) => identity.email.toLowerCase() === account.email.toLowerCase());
|
|
601
|
+
if (primaryIndex >= 0) {
|
|
602
|
+
return discovered.map((identity, index) => ({ ...identity, isPrimary: index === primaryIndex }));
|
|
603
|
+
}
|
|
604
|
+
return [
|
|
605
|
+
{
|
|
606
|
+
email: account.email,
|
|
607
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
608
|
+
isPrimary: true,
|
|
609
|
+
source: 'provider',
|
|
610
|
+
},
|
|
611
|
+
...discovered.map((identity) => ({ ...identity, isPrimary: false })),
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
return [
|
|
615
|
+
{
|
|
616
|
+
email: account.email,
|
|
617
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
618
|
+
isPrimary: true,
|
|
619
|
+
source: 'configured',
|
|
620
|
+
},
|
|
621
|
+
...listConfiguredSendAs(this.db, accountId),
|
|
622
|
+
];
|
|
623
|
+
}
|
|
624
|
+
async validateSender(provider, accountId, account, sender, known) {
|
|
625
|
+
const parsed = parseSingleAddress(sender.trim());
|
|
626
|
+
if (!parsed)
|
|
627
|
+
throw new EmailError('invalid_request', `Could not parse sender address: "${sender}"`);
|
|
628
|
+
if (parsed.email.toLowerCase() === account.email.toLowerCase()) {
|
|
629
|
+
return {
|
|
630
|
+
email: account.email,
|
|
631
|
+
...(account.displayName ? { name: account.displayName } : {}),
|
|
632
|
+
isPrimary: true,
|
|
633
|
+
source: account.provider === 'gmail' ? 'provider' : 'configured',
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const identity = (known ?? (await this.sendAsIdentities(provider, accountId, account))).find((candidate) => candidate.email.toLowerCase() === parsed.email.toLowerCase());
|
|
637
|
+
if (!identity)
|
|
638
|
+
throw new EmailError('invalid_request', `Sender address "${parsed.email}" is not available.`);
|
|
639
|
+
return identity;
|
|
640
|
+
}
|
|
641
|
+
selectReplySender(original, identities) {
|
|
642
|
+
const byEmail = new Map(identities.map((identity) => [identity.email.toLowerCase(), identity]));
|
|
643
|
+
const originalSender = original.from ? byEmail.get(original.from.email.toLowerCase()) : undefined;
|
|
644
|
+
if (originalSender)
|
|
645
|
+
return originalSender;
|
|
646
|
+
for (const recipient of [...original.to, ...(original.cc ?? [])]) {
|
|
647
|
+
const match = byEmail.get(recipient.email.toLowerCase());
|
|
648
|
+
if (match)
|
|
649
|
+
return match;
|
|
650
|
+
}
|
|
651
|
+
return identities.find((identity) => identity.isPrimary) ?? identities[0];
|
|
652
|
+
}
|
|
404
653
|
forward(accountId, input) {
|
|
405
|
-
return this.withProvider(accountId, async (p) => {
|
|
654
|
+
return this.withProvider(accountId, async (p, resolvedId, account) => {
|
|
406
655
|
const original = await p.getMessage(input.messageId);
|
|
407
656
|
const includeAttachments = input.includeAttachments ?? true;
|
|
408
657
|
const attachments = [];
|
|
@@ -419,6 +668,7 @@ export class EmailService {
|
|
|
419
668
|
}
|
|
420
669
|
}
|
|
421
670
|
const draft = {
|
|
671
|
+
...(input.from ? { from: (await this.validateSender(p, resolvedId, account, input.from)).email } : {}),
|
|
422
672
|
to: input.to,
|
|
423
673
|
...(input.cc?.length ? { cc: input.cc } : {}),
|
|
424
674
|
subject: forwardSubject(original.subject),
|