@remit/backend 0.0.144 → 0.0.145
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/src/api.d.ts +0 -2
- package/dist/src/api.js +9 -80
- package/dist/src/auth.d.ts +13 -19
- package/dist/src/auth.js +51 -43
- package/dist/src/config/api-config.d.ts +20 -0
- package/dist/src/config/api-config.js +1 -0
- package/dist/src/config/from-env.d.ts +2 -0
- package/dist/src/config/from-env.js +65 -0
- package/dist/src/config/msoauth-secrets-manager.d.ts +2 -0
- package/dist/src/config/msoauth-secrets-manager.js +28 -0
- package/dist/src/config/msoauth.d.ts +18 -29
- package/dist/src/config/msoauth.js +4 -28
- package/dist/src/derive/contentSignature.d.ts +3 -4
- package/dist/src/derive/contentSignature.js +4 -13
- package/dist/src/derive/contentUrl.d.ts +0 -13
- package/dist/src/derive/contentUrl.js +0 -19
- package/dist/src/handlers/account-oauth.d.ts +4 -3
- package/dist/src/handlers/account-oauth.js +197 -250
- package/dist/src/handlers/account.d.ts +4 -3
- package/dist/src/handlers/account.js +26 -39
- package/dist/src/handlers/address.d.ts +2 -2
- package/dist/src/handlers/address.js +11 -19
- package/dist/src/handlers/calendar-event.js +18 -26
- package/dist/src/handlers/calendar-feed.d.ts +2 -2
- package/dist/src/handlers/calendar-feed.js +24 -32
- package/dist/src/handlers/calendar-suggestion.js +18 -26
- package/dist/src/handlers/calendar.d.ts +1 -1
- package/dist/src/handlers/calendar.js +15 -22
- package/dist/src/handlers/config.d.ts +5 -5
- package/dist/src/handlers/config.js +23 -35
- package/dist/src/handlers/filter.d.ts +2 -2
- package/dist/src/handlers/filter.js +17 -32
- package/dist/src/handlers/folder-role.js +3 -6
- package/dist/src/handlers/index.d.ts +88 -2
- package/dist/src/handlers/index.js +6 -4
- package/dist/src/handlers/label.js +15 -22
- package/dist/src/handlers/mailbox.d.ts +3 -3
- package/dist/src/handlers/mailbox.js +18 -26
- package/dist/src/handlers/me.d.ts +1 -1
- package/dist/src/handlers/me.js +26 -47
- package/dist/src/handlers/message.d.ts +3 -5
- package/dist/src/handlers/message.js +34 -46
- package/dist/src/handlers/organize.d.ts +2 -2
- package/dist/src/handlers/organize.js +11 -20
- package/dist/src/handlers/outbox-attachment.d.ts +5 -3
- package/dist/src/handlers/outbox-attachment.js +11 -15
- package/dist/src/handlers/outbox.d.ts +3 -3
- package/dist/src/handlers/outbox.js +27 -35
- package/dist/src/handlers/sync.d.ts +5 -5
- package/dist/src/handlers/sync.js +13 -22
- package/dist/src/handlers/system-update.js +14 -27
- package/dist/src/handlers/thread.js +9 -14
- package/dist/src/handlers/unified-threads.js +3 -6
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +18 -2
- package/dist/src/jwt-auth.d.ts +9 -12
- package/dist/src/jwt-auth.js +19 -71
- package/dist/src/lambda.d.ts +13 -0
- package/dist/src/lambda.js +33 -0
- package/dist/src/request.d.ts +8 -2
- package/dist/src/request.js +16 -6
- package/dist/src/response.d.ts +20 -3
- package/dist/src/response.js +15 -22
- package/dist/src/serve.d.ts +31 -0
- package/dist/src/serve.js +117 -0
- package/dist/src/service/account-deletion.d.ts +2 -4
- package/dist/src/service/account-deletion.js +3 -9
- package/dist/src/service/compose-sqlite.d.ts +2 -1
- package/dist/src/service/compose-sqlite.js +5 -3
- package/dist/src/service/create-remit-client.d.ts +25 -11
- package/dist/src/service/create-remit-client.js +96 -115
- package/dist/src/service/data-client.js +3 -5
- package/dist/src/service/export-identity.d.ts +2 -1
- package/dist/src/service/export-identity.js +4 -6
- package/dist/src/service/fire-and-forget.js +1 -2
- package/dist/src/service/organize.d.ts +7 -10
- package/dist/src/service/organize.js +10 -27
- package/dist/src/service/queues.d.ts +12 -0
- package/dist/src/service/queues.js +1 -0
- package/dist/src/service/shared-deps.d.ts +6 -0
- package/dist/src/service/shared-deps.js +24 -0
- package/dist/src/service/trigger-sync.d.ts +11 -5
- package/dist/src/service/trigger-sync.js +10 -18
- package/dist/src/types.d.ts +24 -1
- package/package.json +20 -8
- package/dist/src/data-backend.d.ts +0 -12
- package/dist/src/data-backend.js +0 -12
- package/dist/src/request-context.d.ts +0 -9
- package/dist/src/request-context.js +0 -22
- package/dist/src/service/sqs.d.ts +0 -1
- package/dist/src/service/sqs.js +0 -3
|
@@ -4,12 +4,10 @@ import { ContentDisposition, MediaType, MessageCategory, SenderTrust, } from "@r
|
|
|
4
4
|
import { logger } from "@remit/logger";
|
|
5
5
|
import { guardConnectionCursor, isCursorRebuildNeeded, isMessageBodySyncBroken, MailboxCursorPausedError, MoveNotSettledError, materializeBodyParts, NoJunkMailboxError, ownedMessages, resolveFolderPlacement, settledFolder, } from "@remit/mailbox-service";
|
|
6
6
|
import { isStorageNotFoundError as isStorageNotFoundErrorFromService, parseStorageUri, } from "@remit/storage-service";
|
|
7
|
-
import { getAccountConfigIdFromEvent, getSubFromEvent } from "../auth.js";
|
|
8
7
|
import { deriveAutoMoved } from "../derive/autoMoved.js";
|
|
9
8
|
import { getContentSigner, } from "../derive/contentSignature.js";
|
|
10
|
-
import { buildContentUrl
|
|
9
|
+
import { buildContentUrl } from "../derive/contentUrl.js";
|
|
11
10
|
import { deriveSenderTrust } from "../derive/senderTrust.js";
|
|
12
|
-
import { getClient } from "../service/data-client.js";
|
|
13
11
|
import { unwrap } from "../service/refusal.js";
|
|
14
12
|
export const isStorageNotFoundError = (error) => isStorageNotFoundErrorFromService(error);
|
|
15
13
|
/**
|
|
@@ -144,11 +142,10 @@ export const settleSpamReportBulk = async (messageIds, run) => {
|
|
|
144
142
|
};
|
|
145
143
|
};
|
|
146
144
|
export const MessageOperations = {
|
|
147
|
-
MessageOperations_describeMessage: async (context,
|
|
148
|
-
const
|
|
149
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
145
|
+
MessageOperations_describeMessage: async (context, ctx) => {
|
|
146
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
150
147
|
const { messageId } = context.request.params;
|
|
151
|
-
const client =
|
|
148
|
+
const client = ctx.client;
|
|
152
149
|
const ownedAccountId = unwrap(await ownedMessages(client, [messageId], accountConfigId, "read"));
|
|
153
150
|
const description = await client.message.describe(messageId);
|
|
154
151
|
const message = description.message[0];
|
|
@@ -335,11 +332,11 @@ export const MessageOperations = {
|
|
|
335
332
|
logger,
|
|
336
333
|
});
|
|
337
334
|
const bodyParts = buildBodyPartResponses(bodyPartRows, {
|
|
338
|
-
contentDeliveryDomain:
|
|
335
|
+
contentDeliveryDomain: ctx.config.contentDeliveryDomain,
|
|
339
336
|
accountConfigId,
|
|
340
337
|
accountId,
|
|
341
338
|
messageId,
|
|
342
|
-
sign: getContentSigner(),
|
|
339
|
+
sign: getContentSigner(ctx.config),
|
|
343
340
|
});
|
|
344
341
|
const references = description.messageReference.map((ref) => ({
|
|
345
342
|
messageIdValue: ref.messageIdValue,
|
|
@@ -354,11 +351,10 @@ export const MessageOperations = {
|
|
|
354
351
|
references,
|
|
355
352
|
};
|
|
356
353
|
},
|
|
357
|
-
MessageOperations_getRawMessage: async (context,
|
|
358
|
-
const
|
|
359
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
354
|
+
MessageOperations_getRawMessage: async (context, ctx) => {
|
|
355
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
360
356
|
const { messageId } = context.request.params;
|
|
361
|
-
const client =
|
|
357
|
+
const client = ctx.client;
|
|
362
358
|
// Cross-tenant ownership guard. Returning raw RFC822 bytes is sensitive,
|
|
363
359
|
// so resolve the owning account and prove the caller owns it BEFORE any
|
|
364
360
|
// storage read or backfill. `read` mode throws NotFoundError (→ 404) on a
|
|
@@ -421,12 +417,11 @@ export const MessageOperations = {
|
|
|
421
417
|
const body = await client.storage.retrieve(stored.bodyStorageKey);
|
|
422
418
|
return { raw: decodeRawEml(body) };
|
|
423
419
|
},
|
|
424
|
-
MessageOperations_updateMessageFlags: async (context,
|
|
425
|
-
const
|
|
426
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
420
|
+
MessageOperations_updateMessageFlags: async (context, ctx) => {
|
|
421
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
427
422
|
const { messageId } = context.request.params;
|
|
428
423
|
const { isRead, isStarred, starColor } = context.request.requestBody;
|
|
429
|
-
const client =
|
|
424
|
+
const client = ctx.client;
|
|
430
425
|
const accountId = unwrap(await ownedMessages(client, [messageId], accountConfigId, "act"));
|
|
431
426
|
// FlagQueueService handles: MessageFlag + ThreadMessage updates + SQS event
|
|
432
427
|
const result = await client.flagQueue.updateFlags(accountConfigId, messageId, accountId, {
|
|
@@ -442,15 +437,14 @@ export const MessageOperations = {
|
|
|
442
437
|
},
|
|
443
438
|
};
|
|
444
439
|
export const MessageBulkOperations = {
|
|
445
|
-
MessageBulkOperations_updateFlags: async (context,
|
|
446
|
-
const
|
|
447
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
440
|
+
MessageBulkOperations_updateFlags: async (context, ctx) => {
|
|
441
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
448
442
|
const { messageIds, isRead, isStarred, starColor } = context.request
|
|
449
443
|
.requestBody;
|
|
450
444
|
if (messageIds.length === 0) {
|
|
451
445
|
return { successCount: 0, failureCount: 0 };
|
|
452
446
|
}
|
|
453
|
-
const client =
|
|
447
|
+
const client = ctx.client;
|
|
454
448
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
455
449
|
// FlagQueueService handles: MessageFlag + ThreadMessage updates + SQS events
|
|
456
450
|
// Process each message individually (same pattern as moveMessages)
|
|
@@ -466,14 +460,13 @@ export const MessageBulkOperations = {
|
|
|
466
460
|
failureCount: 0,
|
|
467
461
|
};
|
|
468
462
|
},
|
|
469
|
-
MessageBulkOperations_deleteMessages: async (context,
|
|
470
|
-
const
|
|
471
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
463
|
+
MessageBulkOperations_deleteMessages: async (context, ctx) => {
|
|
464
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
472
465
|
const { messageIds, permanent } = context.request.requestBody;
|
|
473
466
|
if (messageIds.length === 0) {
|
|
474
467
|
return { successCount: 0, failureCount: 0 };
|
|
475
468
|
}
|
|
476
|
-
const client =
|
|
469
|
+
const client = ctx.client;
|
|
477
470
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
478
471
|
// MessageMoveService handles: Message + ThreadMessage updates + SQS events
|
|
479
472
|
const { refusedMessageIds } = await client.messageMove.deleteMessages(accountConfigId, messageIds, accountId, {
|
|
@@ -489,15 +482,14 @@ export const MessageBulkOperations = {
|
|
|
489
482
|
failureCount: refusedMessageIds.length,
|
|
490
483
|
};
|
|
491
484
|
},
|
|
492
|
-
MessageBulkOperations_moveMessages: async (context,
|
|
493
|
-
const
|
|
494
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
485
|
+
MessageBulkOperations_moveMessages: async (context, ctx) => {
|
|
486
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
495
487
|
const { messageIds, destinationMailboxId } = context.request
|
|
496
488
|
.requestBody;
|
|
497
489
|
if (messageIds.length === 0) {
|
|
498
490
|
return { successCount: 0, failureCount: 0 };
|
|
499
491
|
}
|
|
500
|
-
const client =
|
|
492
|
+
const client = ctx.client;
|
|
501
493
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
502
494
|
// Destination must belong to the same (caller-owned) account, and must be
|
|
503
495
|
// a folder the mail server has settled: the row records the destination
|
|
@@ -515,15 +507,14 @@ export const MessageBulkOperations = {
|
|
|
515
507
|
failureCount: 0,
|
|
516
508
|
};
|
|
517
509
|
},
|
|
518
|
-
MessageBulkOperations_copyMessages: async (context,
|
|
519
|
-
const
|
|
520
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
510
|
+
MessageBulkOperations_copyMessages: async (context, ctx) => {
|
|
511
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
521
512
|
const { messageIds, destinationMailboxId } = context.request
|
|
522
513
|
.requestBody;
|
|
523
514
|
if (messageIds.length === 0) {
|
|
524
515
|
return { successCount: 0, failureCount: 0 };
|
|
525
516
|
}
|
|
526
|
-
const client =
|
|
517
|
+
const client = ctx.client;
|
|
527
518
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
528
519
|
// Destination must belong to the same (caller-owned) account, and must be
|
|
529
520
|
// a folder the mail server has settled: the row records the destination
|
|
@@ -541,14 +532,13 @@ export const MessageBulkOperations = {
|
|
|
541
532
|
failureCount: 0,
|
|
542
533
|
};
|
|
543
534
|
},
|
|
544
|
-
MessageBulkOperations_updateMessageLabels: async (context,
|
|
545
|
-
const
|
|
546
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
535
|
+
MessageBulkOperations_updateMessageLabels: async (context, ctx) => {
|
|
536
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
547
537
|
const { messageIds, labelId, action } = context.request.requestBody;
|
|
548
538
|
if (messageIds.length === 0) {
|
|
549
539
|
return { successCount: 0, failureCount: 0 };
|
|
550
540
|
}
|
|
551
|
-
const client =
|
|
541
|
+
const client = ctx.client;
|
|
552
542
|
unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
553
543
|
// Scoped by accountConfigId (RFC 030) — a foreign labelId 404s here rather
|
|
554
544
|
// than silently applying to messages under someone else's label.
|
|
@@ -574,17 +564,16 @@ export const MessageBulkOperations = {
|
|
|
574
564
|
failureCount: 0,
|
|
575
565
|
};
|
|
576
566
|
},
|
|
577
|
-
MessageBulkOperations_reportSpam: async (context,
|
|
578
|
-
const
|
|
579
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
567
|
+
MessageBulkOperations_reportSpam: async (context, ctx) => {
|
|
568
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
580
569
|
const { messageIds: requestedIds } = context.request.requestBody;
|
|
581
570
|
const messageIds = [...new Set(requestedIds)];
|
|
582
571
|
if (messageIds.length === 0) {
|
|
583
572
|
return { successCount: 0, failureCount: 0 };
|
|
584
573
|
}
|
|
585
|
-
const client =
|
|
574
|
+
const client = ctx.client;
|
|
586
575
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
587
|
-
const setBy =
|
|
576
|
+
const setBy = ctx.identity.sub;
|
|
588
577
|
return settleSpamReportBulk(messageIds, (messageId) => client.spamReport.reportSpam({
|
|
589
578
|
accountConfigId,
|
|
590
579
|
accountId,
|
|
@@ -592,15 +581,14 @@ export const MessageBulkOperations = {
|
|
|
592
581
|
setBy,
|
|
593
582
|
}));
|
|
594
583
|
},
|
|
595
|
-
MessageBulkOperations_notSpam: async (context,
|
|
596
|
-
const
|
|
597
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
584
|
+
MessageBulkOperations_notSpam: async (context, ctx) => {
|
|
585
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
598
586
|
const { messageIds: requestedIds } = context.request.requestBody;
|
|
599
587
|
const messageIds = [...new Set(requestedIds)];
|
|
600
588
|
if (messageIds.length === 0) {
|
|
601
589
|
return { successCount: 0, failureCount: 0 };
|
|
602
590
|
}
|
|
603
|
-
const client =
|
|
591
|
+
const client = ctx.client;
|
|
604
592
|
const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
|
|
605
593
|
return settleSpamReportBulk(messageIds, (messageId) => client.spamReport.notSpam({ accountConfigId, accountId, messageId }));
|
|
606
594
|
},
|
|
@@ -6,5 +6,5 @@ import type { OperationHandler, OrganizeJobDetailOperationIds, OrganizeOperation
|
|
|
6
6
|
* matcher share.
|
|
7
7
|
*/
|
|
8
8
|
export declare const predicateFromInput: (input: OrganizeInput) => OrganizePredicate;
|
|
9
|
-
export declare const OrganizeOperations: Record<OrganizeOperationIds, OperationHandler<OrganizeOperationIds>>;
|
|
10
|
-
export declare const OrganizeJobDetailOperations: Record<OrganizeJobDetailOperationIds, OperationHandler<OrganizeJobDetailOperationIds>>;
|
|
9
|
+
export declare const OrganizeOperations: Record<OrganizeOperationIds, OperationHandler<OrganizeOperationIds, "accountFanout">>;
|
|
10
|
+
export declare const OrganizeJobDetailOperations: Record<OrganizeJobDetailOperationIds, OperationHandler<OrganizeJobDetailOperationIds, "accountFanout">>;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
|
|
2
2
|
import { logger } from "@remit/logger";
|
|
3
3
|
import { ownedAccount, queueOrganizeJob, } from "@remit/mailbox-service";
|
|
4
|
-
import { env } from "expect-env";
|
|
5
|
-
import { getAccountConfigIdFromEvent, getSubFromEvent } from "../auth.js";
|
|
6
|
-
import { getClient } from "../service/data-client.js";
|
|
7
4
|
import { buildOrganizeMatchDeps, matchOrganize, ORGANIZE_MATCH_LIMIT, organizePredicateRejection, } from "../service/organize.js";
|
|
8
5
|
import { unwrap } from "../service/refusal.js";
|
|
9
6
|
/**
|
|
@@ -55,20 +52,16 @@ const requestedJob = async (client, owner, input) => {
|
|
|
55
52
|
return { ...owner, predicate };
|
|
56
53
|
};
|
|
57
54
|
export const OrganizeOperations = {
|
|
58
|
-
OrganizeOperations_createOrganizeJob: async (context,
|
|
59
|
-
const
|
|
60
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
55
|
+
OrganizeOperations_createOrganizeJob: async (context, ctx) => {
|
|
56
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
61
57
|
const { accountId } = context.request.params;
|
|
62
58
|
const input = context.request.requestBody;
|
|
63
|
-
const userId =
|
|
64
|
-
|
|
65
|
-
throw new Error("Missing Cognito `sub`: cannot attribute an organize job to a user");
|
|
66
|
-
}
|
|
67
|
-
const client = await getClient();
|
|
59
|
+
const userId = ctx.identity.sub;
|
|
60
|
+
const client = ctx.client;
|
|
68
61
|
await assertAccount(client, accountId, accountConfigId, "act");
|
|
69
62
|
const job = await queueOrganizeJob({
|
|
70
63
|
organizeJobRequest: client.organizeJobRequest,
|
|
71
|
-
|
|
64
|
+
queue: client.queues.accountFanout,
|
|
72
65
|
logger,
|
|
73
66
|
}, await requestedJob(client, { accountConfigId, userId }, input));
|
|
74
67
|
return {
|
|
@@ -77,12 +70,11 @@ export const OrganizeOperations = {
|
|
|
77
70
|
state: job.state,
|
|
78
71
|
};
|
|
79
72
|
},
|
|
80
|
-
OrganizeOperations_previewOrganize: async (context,
|
|
81
|
-
const
|
|
82
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
73
|
+
OrganizeOperations_previewOrganize: async (context, ctx) => {
|
|
74
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
83
75
|
const { accountId } = context.request.params;
|
|
84
76
|
const input = context.request.requestBody;
|
|
85
|
-
const client =
|
|
77
|
+
const client = ctx.client;
|
|
86
78
|
await assertAccount(client, accountId, accountConfigId, "read");
|
|
87
79
|
if (input.filterId) {
|
|
88
80
|
throw new BadRequestError("Preview takes a rule, not a saved filter. Run the filter to apply it.");
|
|
@@ -97,11 +89,10 @@ export const OrganizeOperations = {
|
|
|
97
89
|
},
|
|
98
90
|
};
|
|
99
91
|
export const OrganizeJobDetailOperations = {
|
|
100
|
-
OrganizeJobDetailOperations_getOrganizeJob: async (context,
|
|
101
|
-
const
|
|
102
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
92
|
+
OrganizeJobDetailOperations_getOrganizeJob: async (context, ctx) => {
|
|
93
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
103
94
|
const { accountId, organizeJobId } = context.request.params;
|
|
104
|
-
const client =
|
|
95
|
+
const client = ctx.client;
|
|
105
96
|
await assertAccount(client, accountId, accountConfigId, "read");
|
|
106
97
|
const job = await client.organizeJobRequest.get(organizeJobId);
|
|
107
98
|
if (job.accountConfigId !== accountConfigId) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { MintOutboxAttachmentResponse, OutboxAttachmentRejection, OutboxAttachmentResponse } from "@remit/api-openapi-types";
|
|
2
2
|
import type { OutboxAttachmentItem } from "@remit/data-ports";
|
|
3
3
|
import type { Context } from "openapi-backend";
|
|
4
|
-
|
|
4
|
+
import type { ApiConfig } from "../config/api-config.js";
|
|
5
|
+
import type { HandlerContext } from "../types.js";
|
|
6
|
+
export declare const toOutboxAttachmentResponse: (config: ApiConfig) => (item: OutboxAttachmentItem) => OutboxAttachmentResponse;
|
|
5
7
|
interface RejectionResponse {
|
|
6
8
|
statusCode: number;
|
|
7
9
|
body: OutboxAttachmentRejection;
|
|
@@ -12,12 +14,12 @@ interface RejectionResponse {
|
|
|
12
14
|
* lets the upload live on its own fleet and keeps this operation under any
|
|
13
15
|
* payload ceiling the REST tier has.
|
|
14
16
|
*/
|
|
15
|
-
export declare const mintOutboxAttachment: (context: Context,
|
|
17
|
+
export declare const mintOutboxAttachment: (context: Context, ctx: HandlerContext) => Promise<MintOutboxAttachmentResponse | RejectionResponse>;
|
|
16
18
|
/**
|
|
17
19
|
* Confirm the upload landed. The size in the response is read back from
|
|
18
20
|
* storage — on a deployment where the browser PUT straight to block storage
|
|
19
21
|
* this call is the first the API hears of the bytes, and the client's word for
|
|
20
22
|
* how many there are is not evidence.
|
|
21
23
|
*/
|
|
22
|
-
export declare const completeOutboxAttachment: (context: Context,
|
|
24
|
+
export declare const completeOutboxAttachment: (context: Context, ctx: HandlerContext) => Promise<OutboxAttachmentResponse | RejectionResponse>;
|
|
23
25
|
export {};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { OutboxAttachmentRejectionReason } from "@remit/domain-enums";
|
|
2
2
|
import { outboxAttachmentContentId } from "@remit/mailbox-service/outbox-attachment-content";
|
|
3
|
-
import { getAccountConfigIdFromEvent } from "../auth.js";
|
|
4
3
|
import { getContentSigner } from "../derive/contentSignature.js";
|
|
5
|
-
import { buildStorageContentUrl
|
|
6
|
-
import { getClient } from "../service/data-client.js";
|
|
4
|
+
import { buildStorageContentUrl } from "../derive/contentUrl.js";
|
|
7
5
|
// Every error body the API emits carries a `code` (issue #371). A refusal here
|
|
8
6
|
// is one kind, so the code is one value and `reason` says which refusal it was.
|
|
9
7
|
const ATTACHMENT_REJECTED_CODE = "attachment_rejected";
|
|
@@ -12,7 +10,7 @@ const TOO_LARGE = new Set([
|
|
|
12
10
|
OutboxAttachmentRejectionReason.MessageTooLarge,
|
|
13
11
|
OutboxAttachmentRejectionReason.TooManyAttachments,
|
|
14
12
|
]);
|
|
15
|
-
export const toOutboxAttachmentResponse = (item) => ({
|
|
13
|
+
export const toOutboxAttachmentResponse = (config) => (item) => ({
|
|
16
14
|
outboxAttachmentId: item.outboxAttachmentId,
|
|
17
15
|
outboxMessageId: item.outboxMessageId,
|
|
18
16
|
filename: item.filename,
|
|
@@ -21,9 +19,9 @@ export const toOutboxAttachmentResponse = (item) => ({
|
|
|
21
19
|
state: item.state,
|
|
22
20
|
contentId: outboxAttachmentContentId(item.outboxAttachmentId),
|
|
23
21
|
contentUrl: buildStorageContentUrl({
|
|
24
|
-
domain:
|
|
22
|
+
domain: config.contentDeliveryDomain,
|
|
25
23
|
storageKey: item.storageKey,
|
|
26
|
-
sign: getContentSigner(),
|
|
24
|
+
sign: getContentSigner(config),
|
|
27
25
|
}),
|
|
28
26
|
});
|
|
29
27
|
const refuse = (detail) => ({
|
|
@@ -42,12 +40,11 @@ const refuse = (detail) => ({
|
|
|
42
40
|
* lets the upload live on its own fleet and keeps this operation under any
|
|
43
41
|
* payload ceiling the REST tier has.
|
|
44
42
|
*/
|
|
45
|
-
export const mintOutboxAttachment = async (context,
|
|
46
|
-
const
|
|
47
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
43
|
+
export const mintOutboxAttachment = async (context, ctx) => {
|
|
44
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
48
45
|
const { outboxMessageId } = context.request.params;
|
|
49
46
|
const input = context.request.requestBody;
|
|
50
|
-
const client =
|
|
47
|
+
const client = ctx.client;
|
|
51
48
|
const result = await client.outboxAttachment.mint({
|
|
52
49
|
accountConfigId,
|
|
53
50
|
outboxMessageId,
|
|
@@ -65,11 +62,10 @@ export const mintOutboxAttachment = async (context, ...args) => {
|
|
|
65
62
|
* this call is the first the API hears of the bytes, and the client's word for
|
|
66
63
|
* how many there are is not evidence.
|
|
67
64
|
*/
|
|
68
|
-
export const completeOutboxAttachment = async (context,
|
|
69
|
-
const
|
|
70
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
65
|
+
export const completeOutboxAttachment = async (context, ctx) => {
|
|
66
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
71
67
|
const { outboxMessageId, outboxAttachmentId } = context.request.params;
|
|
72
|
-
const client =
|
|
68
|
+
const client = ctx.client;
|
|
73
69
|
const result = await client.outboxAttachment.complete({
|
|
74
70
|
accountConfigId,
|
|
75
71
|
outboxMessageId,
|
|
@@ -77,5 +73,5 @@ export const completeOutboxAttachment = async (context, ...args) => {
|
|
|
77
73
|
});
|
|
78
74
|
if (result.outcome === "Rejected")
|
|
79
75
|
return refuse(result.rejection);
|
|
80
|
-
return toOutboxAttachmentResponse(result.attachment);
|
|
76
|
+
return toOutboxAttachmentResponse(ctx.config)(result.attachment);
|
|
81
77
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { OperationHandler, OutboxAttachmentOperationIds, OutboxDetailOperationIds, OutboxOperationIds } from "../types.js";
|
|
2
|
-
export declare const OutboxOperations: Record<OutboxOperationIds, OperationHandler<OutboxOperationIds>>;
|
|
3
|
-
export declare const OutboxDetailOperations: Record<OutboxDetailOperationIds, OperationHandler<OutboxDetailOperationIds>>;
|
|
4
|
-
export declare const OutboxAttachmentOperations: Record<OutboxAttachmentOperationIds, OperationHandler<OutboxAttachmentOperationIds>>;
|
|
2
|
+
export declare const OutboxOperations: Record<OutboxOperationIds, OperationHandler<OutboxOperationIds, "smtp">>;
|
|
3
|
+
export declare const OutboxDetailOperations: Record<OutboxDetailOperationIds, OperationHandler<OutboxDetailOperationIds, "smtp">>;
|
|
4
|
+
export declare const OutboxAttachmentOperations: Record<OutboxAttachmentOperationIds, OperationHandler<OutboxAttachmentOperationIds, "smtp">>;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { ForbiddenError } from "@remit/data-ports/errors";
|
|
2
|
-
import { getAccountConfigIdFromEvent } from "../auth.js";
|
|
3
|
-
import { getClient } from "../service/data-client.js";
|
|
4
2
|
import { completeOutboxAttachment, mintOutboxAttachment, toOutboxAttachmentResponse, } from "./outbox-attachment.js";
|
|
5
|
-
const toOutboxMessageResponse = (item, attachments = []) => ({
|
|
6
|
-
attachments: attachments.map(toOutboxAttachmentResponse),
|
|
3
|
+
const toOutboxMessageResponse = (config, item, attachments = []) => ({
|
|
4
|
+
attachments: attachments.map(toOutboxAttachmentResponse(config)),
|
|
7
5
|
outboxMessageId: item.outboxMessageId,
|
|
8
6
|
accountId: item.accountId,
|
|
9
7
|
fromAddress: item.fromAddress,
|
|
@@ -23,11 +21,10 @@ const toOutboxMessageResponse = (item, attachments = []) => ({
|
|
|
23
21
|
updatedAt: item.updatedAt,
|
|
24
22
|
});
|
|
25
23
|
export const OutboxOperations = {
|
|
26
|
-
OutboxOperations_createOutboxMessage: async (
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const client = await getClient();
|
|
24
|
+
OutboxOperations_createOutboxMessage: async (context, ctx) => {
|
|
25
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
26
|
+
const input = context.request.requestBody;
|
|
27
|
+
const client = ctx.client;
|
|
31
28
|
const account = await client.account.get(input.accountId);
|
|
32
29
|
if (account.accountConfigId !== accountConfigId) {
|
|
33
30
|
throw new ForbiddenError(`Account ${input.accountId} not in account config`);
|
|
@@ -47,7 +44,7 @@ export const OutboxOperations = {
|
|
|
47
44
|
inReplyTo: input.inReplyTo,
|
|
48
45
|
references: input.references,
|
|
49
46
|
});
|
|
50
|
-
return toOutboxMessageResponse(outbox);
|
|
47
|
+
return toOutboxMessageResponse(ctx.config, outbox);
|
|
51
48
|
}
|
|
52
49
|
const outbox = await client.outboxQueue.createDraft({
|
|
53
50
|
accountId: input.accountId,
|
|
@@ -62,41 +59,38 @@ export const OutboxOperations = {
|
|
|
62
59
|
inReplyTo: input.inReplyTo,
|
|
63
60
|
references: input.references,
|
|
64
61
|
});
|
|
65
|
-
return toOutboxMessageResponse(outbox);
|
|
62
|
+
return toOutboxMessageResponse(ctx.config, outbox);
|
|
66
63
|
},
|
|
67
|
-
OutboxOperations_listOutboxMessages: async (context,
|
|
68
|
-
const
|
|
69
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
64
|
+
OutboxOperations_listOutboxMessages: async (context, ctx) => {
|
|
65
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
70
66
|
const { continuationToken } = context.request.query;
|
|
71
|
-
const client =
|
|
67
|
+
const client = ctx.client;
|
|
72
68
|
const accounts = await client.account.listAllByAccountConfig(accountConfigId);
|
|
73
69
|
if (accounts.length === 0) {
|
|
74
70
|
return { items: [], continuationToken: null };
|
|
75
71
|
}
|
|
76
72
|
const result = await client.outboxMessage.listByAccounts(accounts.map((account) => account.accountId), { continuationToken });
|
|
77
73
|
return {
|
|
78
|
-
items: result.items.map((item) => toOutboxMessageResponse(item)),
|
|
74
|
+
items: result.items.map((item) => toOutboxMessageResponse(ctx.config, item)),
|
|
79
75
|
continuationToken: result.continuationToken,
|
|
80
76
|
};
|
|
81
77
|
},
|
|
82
78
|
};
|
|
83
79
|
export const OutboxDetailOperations = {
|
|
84
|
-
OutboxDetailOperations_getOutboxMessage: async (context,
|
|
85
|
-
const
|
|
86
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
80
|
+
OutboxDetailOperations_getOutboxMessage: async (context, ctx) => {
|
|
81
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
87
82
|
const { outboxMessageId } = context.request.params;
|
|
88
|
-
const client =
|
|
83
|
+
const client = ctx.client;
|
|
89
84
|
// Default mode "read": the scoped get refuses a foreign message with
|
|
90
85
|
// NotFound (404, no existence leak) — no separate check needed.
|
|
91
86
|
const outbox = await client.outboxMessage.get(accountConfigId, outboxMessageId);
|
|
92
|
-
return toOutboxMessageResponse(outbox, await client.outboxAttachment.listFor(accountConfigId, outboxMessageId));
|
|
87
|
+
return toOutboxMessageResponse(ctx.config, outbox, await client.outboxAttachment.listFor(accountConfigId, outboxMessageId));
|
|
93
88
|
},
|
|
94
|
-
OutboxDetailOperations_updateOutboxMessage: async (context,
|
|
95
|
-
const
|
|
96
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
89
|
+
OutboxDetailOperations_updateOutboxMessage: async (context, ctx) => {
|
|
90
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
97
91
|
const { outboxMessageId } = context.request.params;
|
|
98
92
|
const input = context.request.requestBody;
|
|
99
|
-
const client =
|
|
93
|
+
const client = ctx.client;
|
|
100
94
|
// updateDraft's own scoped get uses mode "act": a foreign message is
|
|
101
95
|
// denied with Forbidden (403), not feigned as NotFound.
|
|
102
96
|
const updated = await client.outboxQueue.updateDraft(accountConfigId, outboxMessageId, {
|
|
@@ -115,27 +109,25 @@ export const OutboxDetailOperations = {
|
|
|
115
109
|
if (input.attachmentIds !== undefined) {
|
|
116
110
|
await client.outboxAttachment.retainOnly(accountConfigId, updated.accountId, outboxMessageId, input.attachmentIds);
|
|
117
111
|
}
|
|
118
|
-
return toOutboxMessageResponse(updated, await client.outboxAttachment.listFor(accountConfigId, outboxMessageId));
|
|
112
|
+
return toOutboxMessageResponse(ctx.config, updated, await client.outboxAttachment.listFor(accountConfigId, outboxMessageId));
|
|
119
113
|
},
|
|
120
|
-
OutboxDetailOperations_deleteOutboxMessage: async (context,
|
|
121
|
-
const
|
|
122
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
114
|
+
OutboxDetailOperations_deleteOutboxMessage: async (context, ctx) => {
|
|
115
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
123
116
|
const { outboxMessageId } = context.request.params;
|
|
124
|
-
const client =
|
|
117
|
+
const client = ctx.client;
|
|
125
118
|
// deleteDraft's own scoped get uses mode "act": a foreign message is
|
|
126
119
|
// denied with Forbidden (403), not feigned as NotFound.
|
|
127
120
|
await client.outboxQueue.deleteDraft(accountConfigId, outboxMessageId);
|
|
128
121
|
return { statusCode: 204 };
|
|
129
122
|
},
|
|
130
|
-
OutboxDetailOperations_sendOutboxMessage: async (context,
|
|
131
|
-
const
|
|
132
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
123
|
+
OutboxDetailOperations_sendOutboxMessage: async (context, ctx) => {
|
|
124
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
133
125
|
const { outboxMessageId } = context.request.params;
|
|
134
|
-
const client =
|
|
126
|
+
const client = ctx.client;
|
|
135
127
|
// send's own scoped get uses mode "act": a foreign message is denied
|
|
136
128
|
// with Forbidden (403), not feigned as NotFound.
|
|
137
129
|
const sent = await client.outboxQueue.send(accountConfigId, outboxMessageId);
|
|
138
|
-
return toOutboxMessageResponse(sent);
|
|
130
|
+
return toOutboxMessageResponse(ctx.config, sent);
|
|
139
131
|
},
|
|
140
132
|
OutboxDetailOperations_mintOutboxAttachment: mintOutboxAttachment,
|
|
141
133
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FifoQueueProducer } from "@remit/data-ports";
|
|
2
2
|
import { type FireAndForgetLogger } from "../service/fire-and-forget.js";
|
|
3
|
+
import { type SyncMailboxesEvent } from "../service/trigger-sync.js";
|
|
3
4
|
import type { OperationHandler, SyncOperationIds } from "../types.js";
|
|
4
5
|
interface SyncTriggerDeps {
|
|
5
|
-
|
|
6
|
-
queueUrl: string;
|
|
6
|
+
queue: FifoQueueProducer<SyncMailboxesEvent>;
|
|
7
7
|
logger: FireAndForgetLogger & {
|
|
8
8
|
info: (fields: Record<string, unknown>, message: string) => void;
|
|
9
9
|
};
|
|
@@ -23,6 +23,6 @@ interface SyncTriggerDeps {
|
|
|
23
23
|
* loudly with the alertable structured fields, and resolves to void. A genuine
|
|
24
24
|
* account-lookup / ownership failure still propagates and 500s as normal.
|
|
25
25
|
*/
|
|
26
|
-
export declare const triggerSyncSafe: (accountId: string, accountConfigId: string, deps
|
|
27
|
-
export declare const SyncOperations: Record<SyncOperationIds, OperationHandler<SyncOperationIds>>;
|
|
26
|
+
export declare const triggerSyncSafe: (accountId: string, accountConfigId: string, deps: SyncTriggerDeps) => Promise<void>;
|
|
27
|
+
export declare const SyncOperations: Record<SyncOperationIds, OperationHandler<SyncOperationIds, "sync">>;
|
|
28
28
|
export {};
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { logger } from "@remit/logger";
|
|
2
2
|
import { ownedAccount } from "@remit/mailbox-service";
|
|
3
|
-
import { env } from "expect-env";
|
|
4
|
-
import { getAccountConfigIdFromEvent } from "../auth.js";
|
|
5
|
-
import { getClient } from "../service/data-client.js";
|
|
6
3
|
import { fireAndForget, } from "../service/fire-and-forget.js";
|
|
7
4
|
import { unwrap } from "../service/refusal.js";
|
|
8
|
-
import {
|
|
9
|
-
import { triggerAccountSync } from "../service/trigger-sync.js";
|
|
5
|
+
import { triggerAccountSync, } from "../service/trigger-sync.js";
|
|
10
6
|
import { toMailboxSyncProgress } from "./sync-progress.js";
|
|
11
|
-
const defaultSyncTriggerDeps = () => ({
|
|
12
|
-
sqsClient,
|
|
13
|
-
queueUrl: env.SQS_QUEUE_URL,
|
|
14
|
-
logger,
|
|
15
|
-
});
|
|
16
7
|
/**
|
|
17
8
|
* Fire-and-forget enqueue for the POST /sync kick.
|
|
18
9
|
*
|
|
@@ -28,11 +19,10 @@ const defaultSyncTriggerDeps = () => ({
|
|
|
28
19
|
* loudly with the alertable structured fields, and resolves to void. A genuine
|
|
29
20
|
* account-lookup / ownership failure still propagates and 500s as normal.
|
|
30
21
|
*/
|
|
31
|
-
export const triggerSyncSafe = async (accountId, accountConfigId, deps
|
|
22
|
+
export const triggerSyncSafe = async (accountId, accountConfigId, deps) => {
|
|
32
23
|
await fireAndForget(async () => {
|
|
33
24
|
const { eventId } = await triggerAccountSync({
|
|
34
|
-
|
|
35
|
-
queueUrl: deps.queueUrl,
|
|
25
|
+
queue: deps.queue,
|
|
36
26
|
accountId,
|
|
37
27
|
// POST /sync asks for a sync of this account by name — the refresh
|
|
38
28
|
// control, pull-to-refresh, and the client's automatic poll all land
|
|
@@ -49,23 +39,24 @@ export const triggerSyncSafe = async (accountId, accountConfigId, deps = default
|
|
|
49
39
|
});
|
|
50
40
|
};
|
|
51
41
|
export const SyncOperations = {
|
|
52
|
-
SyncOperations_triggerSync: async (context,
|
|
53
|
-
const
|
|
54
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
42
|
+
SyncOperations_triggerSync: async (context, ctx) => {
|
|
43
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
55
44
|
const { accountId } = context.request.params;
|
|
56
|
-
const account = await
|
|
45
|
+
const account = await ctx.client.account.get(accountId);
|
|
57
46
|
unwrap(ownedAccount(account, accountConfigId, "act"));
|
|
58
|
-
void triggerSyncSafe(account.accountId, accountConfigId
|
|
47
|
+
void triggerSyncSafe(account.accountId, accountConfigId, {
|
|
48
|
+
queue: ctx.client.queues.sync,
|
|
49
|
+
logger,
|
|
50
|
+
});
|
|
59
51
|
return {
|
|
60
52
|
triggered: true,
|
|
61
53
|
message: `Sync triggered for account ${accountId}`,
|
|
62
54
|
};
|
|
63
55
|
},
|
|
64
|
-
SyncOperations_getSyncStatus: async (context,
|
|
65
|
-
const
|
|
66
|
-
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
56
|
+
SyncOperations_getSyncStatus: async (context, ctx) => {
|
|
57
|
+
const accountConfigId = ctx.identity.accountConfigId;
|
|
67
58
|
const { accountId } = context.request.params;
|
|
68
|
-
const client =
|
|
59
|
+
const client = ctx.client;
|
|
69
60
|
const account = await client.account.get(accountId);
|
|
70
61
|
unwrap(ownedAccount(account, accountConfigId, "read"));
|
|
71
62
|
// Collect all mailboxes (read-only — does not write or contend with sync lock)
|