@remit/backend 0.0.143 → 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/dev-server/content-handler.d.ts +38 -0
- package/dist/dev-server/content-handler.js +46 -0
- package/dist/src/api.d.ts +5 -0
- package/dist/src/api.js +73 -0
- package/dist/src/auth.d.ts +24 -0
- package/dist/src/auth.js +81 -0
- 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 +19 -0
- package/dist/src/config/msoauth.js +6 -0
- package/{src/derive/autoMoved.ts → dist/src/derive/autoMoved.d.ts} +1 -29
- package/dist/src/derive/autoMoved.js +49 -0
- package/dist/src/derive/contentSignature.d.ts +40 -0
- package/{src/derive/contentSignature.ts → dist/src/derive/contentSignature.js} +23 -77
- package/dist/src/derive/contentUrl.d.ts +33 -0
- package/dist/src/derive/contentUrl.js +27 -0
- package/{src/derive/deriveMuted.ts → dist/src/derive/deriveMuted.d.ts} +1 -3
- package/dist/src/derive/deriveMuted.js +7 -0
- package/dist/src/derive/enrichThreadRows.d.ts +118 -0
- package/dist/src/derive/enrichThreadRows.js +151 -0
- package/dist/src/derive/filterThreadCriteria.d.ts +23 -0
- package/dist/src/derive/filterThreadCriteria.js +24 -0
- package/{src/derive/pendingMoveCounts.ts → dist/src/derive/pendingMoveCounts.d.ts} +5 -53
- package/dist/src/derive/pendingMoveCounts.js +53 -0
- package/{src/derive/senderTrust.ts → dist/src/derive/senderTrust.d.ts} +1 -9
- package/dist/src/derive/senderTrust.js +19 -0
- package/dist/src/error.d.ts +21 -0
- package/dist/src/error.js +85 -0
- package/dist/src/handlers/account-guards.d.ts +36 -0
- package/dist/src/handlers/account-guards.js +107 -0
- package/dist/src/handlers/account-oauth.d.ts +15 -0
- package/dist/src/handlers/account-oauth.js +334 -0
- package/dist/src/handlers/account-overrides.d.ts +53 -0
- package/dist/src/handlers/account-overrides.js +233 -0
- package/dist/src/handlers/account-signature.d.ts +28 -0
- package/dist/src/handlers/account-signature.js +85 -0
- package/dist/src/handlers/account.d.ts +7 -0
- package/dist/src/handlers/account.js +337 -0
- package/dist/src/handlers/address.d.ts +35 -0
- package/dist/src/handlers/address.js +133 -0
- package/dist/src/handlers/calendar-event.d.ts +70 -0
- package/dist/src/handlers/calendar-event.js +417 -0
- package/dist/src/handlers/calendar-feed.d.ts +47 -0
- package/dist/src/handlers/calendar-feed.js +184 -0
- package/dist/src/handlers/calendar-suggestion.d.ts +47 -0
- package/dist/src/handlers/calendar-suggestion.js +204 -0
- package/dist/src/handlers/calendar.d.ts +116 -0
- package/dist/src/handlers/calendar.js +323 -0
- package/dist/src/handlers/config.d.ts +43 -0
- package/dist/src/handlers/config.js +164 -0
- package/dist/src/handlers/ensure-account-config.d.ts +9 -0
- package/dist/src/handlers/ensure-account-config.js +27 -0
- package/dist/src/handlers/filter.d.ts +3 -0
- package/dist/src/handlers/filter.js +88 -0
- package/dist/src/handlers/folder-role-appointments.d.ts +68 -0
- package/dist/src/handlers/folder-role-appointments.js +168 -0
- package/dist/src/handlers/folder-role-labels.d.ts +26 -0
- package/dist/src/handlers/folder-role-labels.js +40 -0
- package/dist/src/handlers/folder-role.d.ts +9 -0
- package/dist/src/handlers/folder-role.js +37 -0
- package/dist/src/handlers/index.d.ts +88 -0
- package/dist/src/handlers/index.js +62 -0
- package/dist/src/handlers/label.d.ts +43 -0
- package/dist/src/handlers/label.js +107 -0
- package/dist/src/handlers/mailbox-bindings.d.ts +41 -0
- package/dist/src/handlers/mailbox-bindings.js +61 -0
- package/dist/src/handlers/mailbox.d.ts +23 -0
- package/dist/src/handlers/mailbox.js +211 -0
- package/dist/src/handlers/me.d.ts +2 -0
- package/dist/src/handlers/me.js +117 -0
- package/dist/src/handlers/message.d.ts +82 -0
- package/dist/src/handlers/message.js +595 -0
- package/dist/src/handlers/organize.d.ts +10 -0
- package/dist/src/handlers/organize.js +103 -0
- package/dist/src/handlers/outbox-attachment.d.ts +25 -0
- package/dist/src/handlers/outbox-attachment.js +77 -0
- package/dist/src/handlers/outbox.d.ts +4 -0
- package/dist/src/handlers/outbox.js +136 -0
- package/dist/src/handlers/sync-progress.d.ts +46 -0
- package/dist/src/handlers/sync-progress.js +63 -0
- package/dist/src/handlers/sync.d.ts +28 -0
- package/dist/src/handlers/sync.js +73 -0
- package/dist/src/handlers/system-update.d.ts +2 -0
- package/dist/src/handlers/system-update.js +159 -0
- package/dist/src/handlers/thread.d.ts +148 -0
- package/dist/src/handlers/thread.js +228 -0
- package/dist/src/handlers/unified-threads.d.ts +247 -0
- package/dist/src/handlers/unified-threads.js +406 -0
- package/{src/handlers/vip-suggestions.ts → dist/src/handlers/vip-suggestions.d.ts} +1 -13
- package/dist/src/handlers/vip-suggestions.js +16 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +53 -0
- package/{src/json.ts → dist/src/json.d.ts} +1 -4
- package/dist/src/json.js +10 -0
- package/dist/src/jwt-auth.d.ts +15 -0
- package/dist/src/jwt-auth.js +45 -0
- package/dist/src/lambda.d.ts +13 -0
- package/dist/src/lambda.js +33 -0
- package/dist/src/request.d.ts +9 -0
- package/dist/src/request.js +17 -0
- package/dist/src/response.d.ts +44 -0
- package/dist/src/response.js +80 -0
- package/dist/src/serve.d.ts +31 -0
- package/dist/src/serve.js +117 -0
- package/dist/src/service/account-deletion.d.ts +20 -0
- package/dist/src/service/account-deletion.js +33 -0
- package/dist/src/service/compose-sqlite.d.ts +3 -0
- package/dist/src/service/compose-sqlite.js +13 -0
- package/dist/src/service/create-remit-client.d.ts +109 -0
- package/dist/src/service/create-remit-client.js +188 -0
- package/dist/src/service/data-client.d.ts +12 -0
- package/dist/src/service/data-client.js +42 -0
- package/dist/src/service/export-identity.d.ts +4 -0
- package/dist/src/service/export-identity.js +33 -0
- package/dist/src/service/fire-and-forget.d.ts +54 -0
- package/dist/src/service/fire-and-forget.js +37 -0
- package/dist/src/service/organize.d.ts +181 -0
- package/dist/src/service/organize.js +367 -0
- package/dist/src/service/queues.d.ts +12 -0
- package/dist/src/service/queues.js +1 -0
- package/dist/src/service/refusal.d.ts +4 -0
- package/dist/src/service/refusal.js +20 -0
- package/dist/src/service/sender-category.d.ts +51 -0
- package/{src/service/sender-category.ts → dist/src/service/sender-category.js} +40 -81
- 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 +57 -0
- package/dist/src/service/trigger-sync.js +31 -0
- package/dist/src/types.d.ts +72 -0
- package/dist/src/types.js +1 -0
- package/package.json +125 -98
- package/.env.test +0 -4
- package/dev-server/content-auth.test.ts +0 -113
- package/dev-server/content-auth.ts +0 -54
- package/dev-server/content-handler.test.ts +0 -105
- package/dev-server/content-handler.ts +0 -79
- package/dev-server/content-path.test.ts +0 -37
- package/dev-server/content-path.ts +0 -27
- package/dev-server/cors.test.ts +0 -48
- package/dev-server/cors.ts +0 -25
- package/dev-server/keep-alive.test.ts +0 -24
- package/dev-server/lambda-helpers.ts +0 -69
- package/dev-server/log-output.test.ts +0 -173
- package/dev-server/oauth-callback-route.test.ts +0 -66
- package/dev-server/relational-health.test.ts +0 -83
- package/dev-server/relational-health.ts +0 -39
- package/dev-server/server.ts +0 -384
- package/dev-server/sync-age.test.ts +0 -91
- package/dev-server/sync-age.ts +0 -55
- package/dev-server/upload-handler.ts +0 -132
- package/dev-server/upload-route.test.ts +0 -196
- package/scripts/backfill-classification.ts +0 -91
- package/scripts/backfill-list-id.ts +0 -83
- package/scripts/config-save.ts +0 -128
- package/src/auth.test.ts +0 -12
- package/src/auth.ts +0 -92
- package/src/config/msoauth.test.ts +0 -48
- package/src/config/msoauth.ts +0 -48
- package/src/data-backend.test.ts +0 -23
- package/src/data-backend.ts +0 -14
- package/src/derive/autoMoved.test.ts +0 -104
- package/src/derive/contentSignature.test.ts +0 -161
- package/src/derive/contentUrl.test.ts +0 -172
- package/src/derive/contentUrl.ts +0 -86
- package/src/derive/deriveMuted.test.ts +0 -34
- package/src/derive/enrichThreadRows.test.ts +0 -360
- package/src/derive/enrichThreadRows.ts +0 -234
- package/src/derive/filterThreadCriteria.test.ts +0 -126
- package/src/derive/filterThreadCriteria.ts +0 -51
- package/src/derive/senderTrust.test.ts +0 -67
- package/src/error.test.ts +0 -201
- package/src/error.ts +0 -123
- package/src/handlers/account-guards.test.ts +0 -67
- package/src/handlers/account-guards.ts +0 -169
- package/src/handlers/account-oauth-callback.test.ts +0 -155
- package/src/handlers/account-oauth-consent.test.ts +0 -336
- package/src/handlers/account-oauth-start.test.ts +0 -191
- package/src/handlers/account-oauth.test.ts +0 -439
- package/src/handlers/account-oauth.ts +0 -635
- package/src/handlers/account-overrides.test.ts +0 -88
- package/src/handlers/account-overrides.ts +0 -323
- package/src/handlers/account-ownership.ts +0 -25
- package/src/handlers/account-signature.ts +0 -117
- package/src/handlers/account-synced-services.test.ts +0 -245
- package/src/handlers/account.ts +0 -552
- package/src/handlers/address.test.ts +0 -381
- package/src/handlers/address.ts +0 -200
- package/src/handlers/calendar-event.test.ts +0 -685
- package/src/handlers/calendar-event.ts +0 -738
- package/src/handlers/calendar-feed-validators.test.ts +0 -228
- package/src/handlers/calendar-feed.test.ts +0 -439
- package/src/handlers/calendar-feed.ts +0 -291
- package/src/handlers/calendar-sqlite-fixture.ts +0 -36
- package/src/handlers/calendar-suggestion.test.ts +0 -1269
- package/src/handlers/calendar-suggestion.ts +0 -425
- package/src/handlers/calendar.test.ts +0 -1080
- package/src/handlers/calendar.ts +0 -517
- package/src/handlers/config.test.ts +0 -245
- package/src/handlers/config.ts +0 -332
- package/src/handlers/ensure-account-config.ts +0 -30
- package/src/handlers/filter.test.ts +0 -644
- package/src/handlers/filter.ts +0 -518
- package/src/handlers/folder-role-appointments.test.ts +0 -438
- package/src/handlers/folder-role-appointments.ts +0 -249
- package/src/handlers/folder-role-labels.ts +0 -63
- package/src/handlers/folder-role.test.ts +0 -209
- package/src/handlers/folder-role.ts +0 -80
- package/src/handlers/index.ts +0 -83
- package/src/handlers/label.test.ts +0 -112
- package/src/handlers/label.ts +0 -210
- package/src/handlers/mailbox-bindings.ts +0 -111
- package/src/handlers/mailbox-rename-refusal.test.ts +0 -247
- package/src/handlers/mailbox-settled-target.test.ts +0 -251
- package/src/handlers/mailbox.test.ts +0 -280
- package/src/handlers/mailbox.ts +0 -581
- package/src/handlers/me.test.ts +0 -81
- package/src/handlers/me.ts +0 -203
- package/src/handlers/message-read-backfill.test.ts +0 -103
- package/src/handlers/message.test.ts +0 -131
- package/src/handlers/message.ts +0 -1131
- package/src/handlers/organize.test.ts +0 -277
- package/src/handlers/organize.ts +0 -197
- package/src/handlers/outbox-attachment.test.ts +0 -765
- package/src/handlers/outbox-attachment.ts +0 -126
- package/src/handlers/outbox-list.test.ts +0 -179
- package/src/handlers/outbox.test.ts +0 -772
- package/src/handlers/outbox.ts +0 -258
- package/src/handlers/search.test.ts +0 -161
- package/src/handlers/search.ts +0 -105
- package/src/handlers/sync-progress.test.ts +0 -205
- package/src/handlers/sync-progress.ts +0 -99
- package/src/handlers/sync.test.ts +0 -168
- package/src/handlers/sync.ts +0 -118
- package/src/handlers/system-update.test.ts +0 -497
- package/src/handlers/system-update.ts +0 -230
- package/src/handlers/thread.test.ts +0 -396
- package/src/handlers/thread.ts +0 -427
- package/src/handlers/unified-threads-mail-sources.sqlite.test.ts +0 -197
- package/src/handlers/unified-threads.test.ts +0 -785
- package/src/handlers/unified-threads.ts +0 -669
- package/src/index.test.ts +0 -83
- package/src/index.ts +0 -228
- package/src/jwt-auth.test.ts +0 -152
- package/src/jwt-auth.ts +0 -132
- package/src/log-redaction.test.ts +0 -144
- package/src/openapi-spec.test.ts +0 -118
- package/src/request-context.test.ts +0 -61
- package/src/request-context.ts +0 -33
- package/src/request-pipeline.test.ts +0 -88
- package/src/request.ts +0 -10
- package/src/response.test.ts +0 -130
- package/src/response.ts +0 -144
- package/src/service/compose-sqlite.ts +0 -98
- package/src/service/config-import.ts +0 -39
- package/src/service/create-remit-client.ts +0 -522
- package/src/service/data-client.test.ts +0 -97
- package/src/service/data-client.ts +0 -58
- package/src/service/export-identity.ts +0 -39
- package/src/service/filter.ts +0 -40
- package/src/service/fire-and-forget.test.ts +0 -126
- package/src/service/fire-and-forget.ts +0 -79
- package/src/service/localhost-env-config.test.ts +0 -36
- package/src/service/organize-queue.ts +0 -98
- package/src/service/organize.test.ts +0 -1451
- package/src/service/organize.ts +0 -1025
- package/src/service/read-path-filter-config.test.ts +0 -243
- package/src/service/semantic-capability.test.ts +0 -78
- package/src/service/semantic-capability.ts +0 -95
- package/src/service/sender-category.test.ts +0 -235
- package/src/service/sqs.ts +0 -4
- package/src/service/trigger-sync.test.ts +0 -205
- package/src/service/trigger-sync.ts +0 -100
- package/src/stdout-capture-fixture.ts +0 -40
- package/src/types.ts +0 -271
- package/tsconfig.json +0 -7
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FifoQueueMessage, FifoQueueProducer } from "@remit/data-ports";
|
|
2
|
+
export interface SyncMailboxesEvent {
|
|
3
|
+
type: "SYNC_MAILBOXES";
|
|
4
|
+
eventId: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
accountId: string;
|
|
7
|
+
explicitRequest?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface TriggerAccountSyncInput {
|
|
10
|
+
queue: FifoQueueProducer<SyncMailboxesEvent>;
|
|
11
|
+
accountId: string;
|
|
12
|
+
/**
|
|
13
|
+
* Set by POST /sync, whose callers ask for a sync of one named account: the
|
|
14
|
+
* refresh control, pull-to-refresh, and the web client's automatic poll
|
|
15
|
+
* (`useStaleAccountSync`) — a timer, not a person. It travels on the event
|
|
16
|
+
* and makes the worker's fan-out sync every mailbox even if one just ran.
|
|
17
|
+
*
|
|
18
|
+
* Everything else triggers a sync as a side effect of doing something else
|
|
19
|
+
* (config load, OAuth connect, account create, the scheduled tick), leaves
|
|
20
|
+
* this unset, and takes the freshness gate — see `mailboxNeedsSync` in
|
|
21
|
+
* imap-worker's sync-mailboxes handler.
|
|
22
|
+
*
|
|
23
|
+
* How often a timer may do that is bounded on the client, by the poll's own
|
|
24
|
+
* floor (`MIN_POLL_INTERVAL_MS` in the client hook, 30s) rather than by the
|
|
25
|
+
* gate: `mailboxPollIntervalSeconds` can lengthen the interval, never
|
|
26
|
+
* shorten it past that.
|
|
27
|
+
*/
|
|
28
|
+
explicitRequest?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Override the queue deduplication id. Defaults to the event's own
|
|
31
|
+
* id, so the queue suppresses a re-send of one event and nothing else —
|
|
32
|
+
* every manual call site (POST /sync, OAuth connect, config load,
|
|
33
|
+
* pull-to-refresh, the client's online-poll) always enqueues. A shared,
|
|
34
|
+
* time-bucketed id instead turned SQS's 5-minute window into a rate limiter
|
|
35
|
+
* and silently discarded a sync the user asked for whenever one had run
|
|
36
|
+
* recently (issue #37). What a trigger costs is bounded in the worker's
|
|
37
|
+
* fan-out (`mailboxNeedsSync`), by skipping mailboxes not worth
|
|
38
|
+
* re-enumerating — never by dropping the trigger.
|
|
39
|
+
*
|
|
40
|
+
* The scheduled-sync tick (#1247) passes `buildScheduledSyncDedupId()`,
|
|
41
|
+
* which is bucketed by the tick's own cadence: there it collapses a
|
|
42
|
+
* re-invocation of a single tick, never two ticks or a manual trigger.
|
|
43
|
+
*/
|
|
44
|
+
dedupId?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the scheduler's own dedup namespace, bucketed by tick interval so
|
|
48
|
+
* consecutive ticks each get a fresh id (never colliding with each other)
|
|
49
|
+
* while still deduping a genuine double-invocation of the same tick (e.g. a
|
|
50
|
+
* retried Lambda) — see `dedupId` above.
|
|
51
|
+
*/
|
|
52
|
+
export declare const buildScheduledSyncDedupId: (accountId: string, now: number, bucketMs: number) => string;
|
|
53
|
+
export declare const buildSyncMailboxesMessage: (input: Omit<TriggerAccountSyncInput, "queue">) => FifoQueueMessage<SyncMailboxesEvent>;
|
|
54
|
+
export declare const triggerAccountSync: (input: TriggerAccountSyncInput) => Promise<{
|
|
55
|
+
eventId: string;
|
|
56
|
+
}>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Build the scheduler's own dedup namespace, bucketed by tick interval so
|
|
4
|
+
* consecutive ticks each get a fresh id (never colliding with each other)
|
|
5
|
+
* while still deduping a genuine double-invocation of the same tick (e.g. a
|
|
6
|
+
* retried Lambda) — see `dedupId` above.
|
|
7
|
+
*/
|
|
8
|
+
export const buildScheduledSyncDedupId = (accountId, now, bucketMs) => {
|
|
9
|
+
const bucket = Math.floor(now / bucketMs);
|
|
10
|
+
return `SYNC_MAILBOXES:scheduled:${accountId}:${bucket}`;
|
|
11
|
+
};
|
|
12
|
+
export const buildSyncMailboxesMessage = (input) => {
|
|
13
|
+
const { accountId, dedupId, explicitRequest } = input;
|
|
14
|
+
const event = {
|
|
15
|
+
type: "SYNC_MAILBOXES",
|
|
16
|
+
eventId: randomUUID(),
|
|
17
|
+
timestamp: Date.now(),
|
|
18
|
+
accountId,
|
|
19
|
+
...(explicitRequest && { explicitRequest }),
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
body: event,
|
|
23
|
+
groupId: accountId,
|
|
24
|
+
deduplicationId: dedupId ?? event.eventId,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export const triggerAccountSync = async (input) => {
|
|
28
|
+
const message = buildSyncMailboxesMessage(input);
|
|
29
|
+
await input.queue.send(message);
|
|
30
|
+
return { eventId: message.body.eventId };
|
|
31
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Context } from "openapi-backend";
|
|
2
|
+
import type { ApiConfig } from "./config/api-config.js";
|
|
3
|
+
import type { RemitClient } from "./service/create-remit-client.js";
|
|
4
|
+
import type { QueueName } from "./service/queues.js";
|
|
5
|
+
type MatchPrefix<Prefix extends string, S extends string> = S extends `${Prefix}${infer _}` ? S : never;
|
|
6
|
+
export type OperationIds = "MeOperations_deleteMe" | "MeOperations_listVipSuggestions" | "MeOperations_createExport" | "MeOperations_getExport" | "MeOperations_listQuarantine" | "ConfigOperations_getConfig" | "ConfigOperations_exportConfig" | "ConfigOperations_importConfig" | "SystemOperations_getSystemUpdate" | "SystemOperations_applySystemUpdate" | "AccountOperations_createAccount" | "AccountOperations_testConnection" | "AccountDetailOperations_updateAccount" | "AccountDetailOperations_deleteAccount" | "MicrosoftOAuthOperations_microsoftOAuthStart" | "MicrosoftOAuthOperations_microsoftOAuthCallback" | "MailboxOperations_listMailboxes" | "MailboxOperations_createMailbox" | "MailboxDetailOperations_getMailbox" | "MailboxDetailOperations_renameMailbox" | "MailboxDetailOperations_deleteMailbox" | "FolderRoleOperations_appointFolderRole" | "FilterOperations_listFilters" | "FilterOperations_createFilter" | "FilterDetailOperations_getFilter" | "FilterDetailOperations_updateFilter" | "FilterDetailOperations_deleteFilter" | "LabelOperations_listLabels" | "LabelOperations_createLabel" | "LabelDetailOperations_getLabel" | "LabelDetailOperations_updateLabel" | "LabelDetailOperations_deleteLabel" | "OrganizeOperations_createOrganizeJob" | "OrganizeOperations_previewOrganize" | "OrganizeJobDetailOperations_getOrganizeJob" | "SyncOperations_triggerSync" | "SyncOperations_getSyncStatus" | "ThreadDetailOperations_listThreadMessages" | "UnifiedThreadOperations_listAllThreads" | "ThreadOperations_listThreads" | "ThreadOperations_searchThreads" | "MessageOperations_describeMessage" | "MessageOperations_getRawMessage" | "MessageOperations_updateMessageFlags" | "MessageBulkOperations_deleteMessages" | "MessageBulkOperations_moveMessages" | "MessageBulkOperations_updateFlags" | "MessageBulkOperations_copyMessages" | "MessageBulkOperations_updateMessageLabels" | "MessageBulkOperations_reportSpam" | "MessageBulkOperations_notSpam" | "CalendarOperations_listCalendars" | "CalendarOperations_createCalendar" | "CalendarDetailOperations_getCalendar" | "CalendarDetailOperations_updateCalendar" | "CalendarDetailOperations_deleteCalendar" | "CalendarDetailOperations_getCalendarFeed" | "CalendarDetailOperations_putCalendarFeed" | "CalendarDetailOperations_revokeCalendarFeed" | "CalendarFeedOperations_getCalendarFeedIcal" | "CalendarEventOperations_listCalendarEvents" | "CalendarEventOperations_createCalendarEvent" | "CalendarEventDetailOperations_getCalendarEvent" | "CalendarEventDetailOperations_updateCalendarEvent" | "CalendarEventDetailOperations_deleteCalendarEvent" | "CalendarFreeBusyOperations_listCalendarFreeBusy" | "TrashOperations_emptyTrash" | "OutboxOperations_createOutboxMessage" | "OutboxOperations_listOutboxMessages" | "OutboxDetailOperations_getOutboxMessage" | "OutboxDetailOperations_updateOutboxMessage" | "OutboxDetailOperations_deleteOutboxMessage" | "OutboxDetailOperations_sendOutboxMessage" | "OutboxDetailOperations_mintOutboxAttachment" | "OutboxAttachmentOperations_completeOutboxAttachment" | "AddressOperations_searchAddresses" | "AddressDetailOperations_updateAddress" | "CalendarSuggestionOperations_listCalendarSuggestions" | "MessageCalendarSuggestionOperations_listMessageCalendarSuggestions" | "CalendarSuggestionActionOperations_acceptCalendarSuggestion" | "CalendarSuggestionActionOperations_declineCalendarSuggestion" | "CalendarSuggestionActionOperations_dismissCalendarSuggestion" | "CalendarSuggestionActionOperations_reopenCalendarSuggestion";
|
|
7
|
+
export type MeOperationIds = MatchPrefix<"MeOperations_", OperationIds>;
|
|
8
|
+
export type ConfigOperationIds = MatchPrefix<"ConfigOperations_", OperationIds>;
|
|
9
|
+
export type SystemOperationIds = MatchPrefix<"SystemOperations_", OperationIds>;
|
|
10
|
+
export type AccountOperationIds = MatchPrefix<"AccountOperations_", OperationIds>;
|
|
11
|
+
export type AccountDetailOperationIds = MatchPrefix<"AccountDetailOperations_", OperationIds>;
|
|
12
|
+
export type MailboxOperationIds = MatchPrefix<"MailboxOperations_", OperationIds>;
|
|
13
|
+
export type MailboxDetailOperationIds = MatchPrefix<"MailboxDetailOperations_", OperationIds>;
|
|
14
|
+
export type FolderRoleOperationIds = MatchPrefix<"FolderRoleOperations_", OperationIds>;
|
|
15
|
+
export type FilterOperationIds = MatchPrefix<"FilterOperations_", OperationIds>;
|
|
16
|
+
export type FilterDetailOperationIds = MatchPrefix<"FilterDetailOperations_", OperationIds>;
|
|
17
|
+
export type LabelOperationIds = MatchPrefix<"LabelOperations_", OperationIds>;
|
|
18
|
+
export type LabelDetailOperationIds = MatchPrefix<"LabelDetailOperations_", OperationIds>;
|
|
19
|
+
export type OrganizeOperationIds = MatchPrefix<"OrganizeOperations_", OperationIds>;
|
|
20
|
+
export type OrganizeJobDetailOperationIds = MatchPrefix<"OrganizeJobDetailOperations_", OperationIds>;
|
|
21
|
+
export type SyncOperationIds = MatchPrefix<"SyncOperations_", OperationIds>;
|
|
22
|
+
export type ThreadDetailOperationIds = MatchPrefix<"ThreadDetailOperations_", OperationIds>;
|
|
23
|
+
export type UnifiedThreadOperationIds = MatchPrefix<"UnifiedThreadOperations_", OperationIds>;
|
|
24
|
+
export type ThreadOperationIds = MatchPrefix<"ThreadOperations_", OperationIds>;
|
|
25
|
+
export type MessageOperationIds = MatchPrefix<"MessageOperations_", OperationIds>;
|
|
26
|
+
export type MessageBulkOperationIds = MatchPrefix<"MessageBulkOperations_", OperationIds>;
|
|
27
|
+
export type CalendarOperationIds = MatchPrefix<"CalendarOperations_", OperationIds>;
|
|
28
|
+
export type CalendarDetailOperationIds = MatchPrefix<"CalendarDetailOperations_", OperationIds>;
|
|
29
|
+
/**
|
|
30
|
+
* The feed sub-resource of `/calendars/{calendarId}`, handled beside the
|
|
31
|
+
* collection itself rather than inside it: minting a credential and serving one
|
|
32
|
+
* calendar's settings are different jobs on the same path.
|
|
33
|
+
*/
|
|
34
|
+
export type CalendarFeedDetailOperationIds = "CalendarDetailOperations_getCalendarFeed" | "CalendarDetailOperations_putCalendarFeed" | "CalendarDetailOperations_revokeCalendarFeed";
|
|
35
|
+
export type CalendarCollectionDetailOperationIds = Exclude<CalendarDetailOperationIds, CalendarFeedDetailOperationIds>;
|
|
36
|
+
export type CalendarFeedOperationIds = MatchPrefix<"CalendarFeedOperations_", OperationIds>;
|
|
37
|
+
export type CalendarEventOperationIds = MatchPrefix<"CalendarEventOperations_", OperationIds>;
|
|
38
|
+
export type CalendarEventDetailOperationIds = MatchPrefix<"CalendarEventDetailOperations_", OperationIds>;
|
|
39
|
+
export type CalendarFreeBusyOperationIds = MatchPrefix<"CalendarFreeBusyOperations_", OperationIds>;
|
|
40
|
+
export type TrashOperationIds = MatchPrefix<"TrashOperations_", OperationIds>;
|
|
41
|
+
export type OutboxOperationIds = MatchPrefix<"OutboxOperations_", OperationIds>;
|
|
42
|
+
export type OutboxDetailOperationIds = MatchPrefix<"OutboxDetailOperations_", OperationIds>;
|
|
43
|
+
export type OutboxAttachmentOperationIds = MatchPrefix<"OutboxAttachmentOperations_", OperationIds>;
|
|
44
|
+
export type AddressOperationIds = MatchPrefix<"AddressOperations_", OperationIds>;
|
|
45
|
+
export type AddressDetailOperationIds = MatchPrefix<"AddressDetailOperations_", OperationIds>;
|
|
46
|
+
export type MicrosoftOAuthOperationIds = MatchPrefix<"MicrosoftOAuthOperations_", OperationIds>;
|
|
47
|
+
export type CalendarSuggestionOperationIds = MatchPrefix<"CalendarSuggestionOperations_", OperationIds>;
|
|
48
|
+
export type MessageCalendarSuggestionOperationIds = MatchPrefix<"MessageCalendarSuggestionOperations_", OperationIds>;
|
|
49
|
+
export type CalendarSuggestionActionOperationIds = MatchPrefix<"CalendarSuggestionActionOperations_", OperationIds>;
|
|
50
|
+
type HandlerResponse = Record<string, any>;
|
|
51
|
+
export interface UserIdentity {
|
|
52
|
+
kind: "user";
|
|
53
|
+
accountConfigId: string;
|
|
54
|
+
sub: string;
|
|
55
|
+
email: string;
|
|
56
|
+
}
|
|
57
|
+
export type HandlerIdentity = {
|
|
58
|
+
kind: "anonymous";
|
|
59
|
+
} | UserIdentity;
|
|
60
|
+
export interface HandlerContext<K extends QueueName = never> {
|
|
61
|
+
client: RemitClient<K>;
|
|
62
|
+
identity: UserIdentity;
|
|
63
|
+
config: ApiConfig;
|
|
64
|
+
}
|
|
65
|
+
export interface PublicHandlerContext<K extends QueueName = never> {
|
|
66
|
+
client: RemitClient<K>;
|
|
67
|
+
config: ApiConfig;
|
|
68
|
+
}
|
|
69
|
+
export type PublicOperationIds = "MicrosoftOAuthOperations_microsoftOAuthCallback" | "CalendarFeedOperations_getCalendarFeedIcal";
|
|
70
|
+
export type OperationHandler<_T extends OperationIds = OperationIds, K extends QueueName = never> = (context: Context, ctx: HandlerContext<K>) => Promise<HandlerResponse>;
|
|
71
|
+
export type PublicOperationHandler<K extends QueueName = never> = (context: Context, ctx: PublicHandlerContext<K>) => Promise<HandlerResponse>;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,100 +1,127 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
2
|
+
"name": "@remit/backend",
|
|
3
|
+
"version": "0.0.145",
|
|
4
|
+
"description": "Remit Mail Inspector API backend",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/src/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/src/index.d.ts",
|
|
12
|
+
"default": "./dist/src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./api": {
|
|
15
|
+
"types": "./dist/src/api.d.ts",
|
|
16
|
+
"default": "./dist/src/api.js"
|
|
17
|
+
},
|
|
18
|
+
"./serve": {
|
|
19
|
+
"types": "./dist/src/serve.d.ts",
|
|
20
|
+
"default": "./dist/src/serve.js"
|
|
21
|
+
},
|
|
22
|
+
"./client": {
|
|
23
|
+
"types": "./dist/src/service/data-client.d.ts",
|
|
24
|
+
"default": "./dist/src/service/data-client.js"
|
|
25
|
+
},
|
|
26
|
+
"./create-remit-client": {
|
|
27
|
+
"types": "./dist/src/service/create-remit-client.d.ts",
|
|
28
|
+
"default": "./dist/src/service/create-remit-client.js"
|
|
29
|
+
},
|
|
30
|
+
"./shared-deps": {
|
|
31
|
+
"types": "./dist/src/service/shared-deps.d.ts",
|
|
32
|
+
"default": "./dist/src/service/shared-deps.js"
|
|
33
|
+
},
|
|
34
|
+
"./compose-sqlite": {
|
|
35
|
+
"types": "./dist/src/service/compose-sqlite.d.ts",
|
|
36
|
+
"default": "./dist/src/service/compose-sqlite.js"
|
|
37
|
+
},
|
|
38
|
+
"./folder-role-appointments": {
|
|
39
|
+
"types": "./dist/src/handlers/folder-role-appointments.d.ts",
|
|
40
|
+
"default": "./dist/src/handlers/folder-role-appointments.js"
|
|
41
|
+
},
|
|
42
|
+
"./folder-role-labels": {
|
|
43
|
+
"types": "./dist/src/handlers/folder-role-labels.d.ts",
|
|
44
|
+
"default": "./dist/src/handlers/folder-role-labels.js"
|
|
45
|
+
},
|
|
46
|
+
"./organize": {
|
|
47
|
+
"types": "./dist/src/service/organize.d.ts",
|
|
48
|
+
"default": "./dist/src/service/organize.js"
|
|
49
|
+
},
|
|
50
|
+
"./sender-category": {
|
|
51
|
+
"types": "./dist/src/service/sender-category.d.ts",
|
|
52
|
+
"default": "./dist/src/service/sender-category.js"
|
|
53
|
+
},
|
|
54
|
+
"./account-deletion": {
|
|
55
|
+
"types": "./dist/src/service/account-deletion.d.ts",
|
|
56
|
+
"default": "./dist/src/service/account-deletion.js"
|
|
57
|
+
},
|
|
58
|
+
"./trigger-sync": {
|
|
59
|
+
"types": "./dist/src/service/trigger-sync.d.ts",
|
|
60
|
+
"default": "./dist/src/service/trigger-sync.js"
|
|
61
|
+
},
|
|
62
|
+
"./content-handler": {
|
|
63
|
+
"types": "./dist/dev-server/content-handler.d.ts",
|
|
64
|
+
"default": "./dist/dev-server/content-handler.js"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"prepack": "node ../../npm-scripts/pack-dist.mjs prepack",
|
|
69
|
+
"postpack": "node ../../npm-scripts/pack-dist.mjs postpack",
|
|
70
|
+
"test:typecheck": "tsgo --noEmit",
|
|
71
|
+
"test:run": "node $NODE_TEST_FLAGS --env-file .env.test --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=76 --test 'src/**/*.test.ts' 'dev-server/**/*.test.ts'",
|
|
72
|
+
"test": "npm run test:typecheck && npm run test:run",
|
|
73
|
+
"bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
|
|
74
|
+
"dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
|
|
75
|
+
"dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts",
|
|
76
|
+
"serve": "node --import tsx dev-server/server.ts"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@remit/apisix": "*",
|
|
80
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
81
|
+
"@types/express": "^5.0.3",
|
|
82
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
83
|
+
"better-sqlite3": "^12.11.1",
|
|
84
|
+
"esbuild": "^0.28.1",
|
|
85
|
+
"express": "^5.1.0",
|
|
86
|
+
"swagger-ui-express": "^5.0.1",
|
|
87
|
+
"tsx": "*"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"@remit/config-format": "*",
|
|
91
|
+
"@remit/config-transfer": "*",
|
|
92
|
+
"@remit/calendar-service": "*",
|
|
93
|
+
"@remit/data-ports": "*",
|
|
94
|
+
"@remit/domain-enums": "*",
|
|
95
|
+
"@remit/api-openapi-types": "*",
|
|
96
|
+
"@remit/logger-lambda": "*",
|
|
97
|
+
"@remit/mail-oauth-service": "*",
|
|
98
|
+
"@remit/secrets-service": "*",
|
|
99
|
+
"@remit/mailbox-service": "*",
|
|
100
|
+
"@remit/storage-service": "*",
|
|
101
|
+
"@remit/auth-service": "*",
|
|
102
|
+
"@remit/drizzle-service": "*",
|
|
103
|
+
"@remit/sqs-client": "*",
|
|
104
|
+
"@aws-sdk/client-secrets-manager": "^3.1090.0",
|
|
105
|
+
"better-sqlite3": "^12.11.1",
|
|
106
|
+
"expect-env": "^1.0.1",
|
|
107
|
+
"openapi-backend": "^5.18.0",
|
|
108
|
+
"p-map": "^2.1.0",
|
|
109
|
+
"@types/aws-lambda": "^8.10.162",
|
|
110
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
111
|
+
"@remit/logger": "*",
|
|
112
|
+
"@remit/api-openapi-spec": "*",
|
|
113
|
+
"zod": "^3.25.0"
|
|
114
|
+
},
|
|
115
|
+
"publishConfig": {
|
|
116
|
+
"access": "public"
|
|
117
|
+
},
|
|
118
|
+
"repository": {
|
|
119
|
+
"type": "git",
|
|
120
|
+
"url": "git+https://github.com/remit-mail/reader.git",
|
|
121
|
+
"directory": "packages/backend"
|
|
122
|
+
},
|
|
123
|
+
"types": "dist/src/index.d.ts",
|
|
124
|
+
"files": [
|
|
125
|
+
"dist"
|
|
126
|
+
]
|
|
100
127
|
}
|
package/.env.test
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { createContentSigner } from "../src/derive/contentSignature.js";
|
|
4
|
-
import { authorizeContentRequest } from "./content-auth.js";
|
|
5
|
-
|
|
6
|
-
const SECRET = "test-master-secret-at-least-32-chars-long";
|
|
7
|
-
const PATH = "accounts/cfg-alice/acc-alice/messages/msg-1/parts/1.2";
|
|
8
|
-
const now = () => Math.floor(Date.now() / 1000);
|
|
9
|
-
|
|
10
|
-
describe("authorizeContentRequest", () => {
|
|
11
|
-
it("is a no-op on the AWS path (unsigned URLs)", () => {
|
|
12
|
-
const result = authorizeContentRequest({
|
|
13
|
-
dataBackend: undefined,
|
|
14
|
-
secret: undefined,
|
|
15
|
-
relativePath: PATH,
|
|
16
|
-
exp: undefined,
|
|
17
|
-
sig: undefined,
|
|
18
|
-
nowSeconds: now(),
|
|
19
|
-
});
|
|
20
|
-
assert.deepEqual(result, { authorized: true });
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("authorizes a validly signed request on the self-host stack", () => {
|
|
24
|
-
const { exp, sig } = createContentSigner(SECRET)(PATH);
|
|
25
|
-
const result = authorizeContentRequest({
|
|
26
|
-
dataBackend: "sqlite",
|
|
27
|
-
secret: SECRET,
|
|
28
|
-
relativePath: PATH,
|
|
29
|
-
exp: String(exp),
|
|
30
|
-
sig,
|
|
31
|
-
nowSeconds: now(),
|
|
32
|
-
});
|
|
33
|
-
assert.deepEqual(result, { authorized: true });
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("returns 401 when the signature is absent", () => {
|
|
37
|
-
const result = authorizeContentRequest({
|
|
38
|
-
dataBackend: "sqlite",
|
|
39
|
-
secret: SECRET,
|
|
40
|
-
relativePath: PATH,
|
|
41
|
-
exp: undefined,
|
|
42
|
-
sig: undefined,
|
|
43
|
-
nowSeconds: now(),
|
|
44
|
-
});
|
|
45
|
-
assert.equal(result.authorized, false);
|
|
46
|
-
assert.equal(result.authorized === false && result.status, 401);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("returns 403 for a signature minted for another account's path", () => {
|
|
50
|
-
const { exp, sig } = createContentSigner(SECRET)(PATH);
|
|
51
|
-
const result = authorizeContentRequest({
|
|
52
|
-
dataBackend: "sqlite",
|
|
53
|
-
secret: SECRET,
|
|
54
|
-
relativePath: "accounts/cfg-bob/acc-bob/messages/msg-9/parts/1.2",
|
|
55
|
-
exp: String(exp),
|
|
56
|
-
sig,
|
|
57
|
-
nowSeconds: now(),
|
|
58
|
-
});
|
|
59
|
-
assert.equal(result.authorized, false);
|
|
60
|
-
assert.equal(result.authorized === false && result.status, 403);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("returns 403 for an expired signature", () => {
|
|
64
|
-
const { exp, sig } = createContentSigner(SECRET, -20)(PATH);
|
|
65
|
-
const result = authorizeContentRequest({
|
|
66
|
-
dataBackend: "sqlite",
|
|
67
|
-
secret: SECRET,
|
|
68
|
-
relativePath: PATH,
|
|
69
|
-
exp: String(exp),
|
|
70
|
-
sig,
|
|
71
|
-
nowSeconds: now(),
|
|
72
|
-
});
|
|
73
|
-
assert.equal(result.authorized, false);
|
|
74
|
-
assert.equal(result.authorized === false && result.status, 403);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("fails closed with 500 when no signing secret is configured", () => {
|
|
78
|
-
const result = authorizeContentRequest({
|
|
79
|
-
dataBackend: "sqlite",
|
|
80
|
-
secret: undefined,
|
|
81
|
-
relativePath: PATH,
|
|
82
|
-
exp: "123",
|
|
83
|
-
sig: "whatever",
|
|
84
|
-
nowSeconds: now(),
|
|
85
|
-
});
|
|
86
|
-
assert.equal(result.authorized, false);
|
|
87
|
-
assert.equal(result.authorized === false && result.status, 500);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("enforces signatures on every self-host request", () => {
|
|
91
|
-
const { exp, sig } = createContentSigner(SECRET)(PATH);
|
|
92
|
-
const valid = authorizeContentRequest({
|
|
93
|
-
dataBackend: "sqlite",
|
|
94
|
-
secret: SECRET,
|
|
95
|
-
relativePath: PATH,
|
|
96
|
-
exp: String(exp),
|
|
97
|
-
sig,
|
|
98
|
-
nowSeconds: now(),
|
|
99
|
-
});
|
|
100
|
-
assert.deepEqual(valid, { authorized: true });
|
|
101
|
-
|
|
102
|
-
const unsigned = authorizeContentRequest({
|
|
103
|
-
dataBackend: "sqlite",
|
|
104
|
-
secret: SECRET,
|
|
105
|
-
relativePath: PATH,
|
|
106
|
-
exp: undefined,
|
|
107
|
-
sig: undefined,
|
|
108
|
-
nowSeconds: now(),
|
|
109
|
-
});
|
|
110
|
-
assert.equal(unsigned.authorized, false);
|
|
111
|
-
assert.equal(unsigned.authorized === false && unsigned.status, 401);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { verifyContentSignature } from "../src/derive/contentSignature.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Decide whether a `/content` request is authorized. Enforcement applies on the
|
|
5
|
-
* self-host SQL backend (sqlite), where this server is the
|
|
6
|
-
* deployed backend container and content URLs are signed. On AWS-local dev
|
|
7
|
-
* (`DATA_BACKEND` unset) `/content` is served straight from the filesystem
|
|
8
|
-
* stand-in for CloudFront and URLs are unsigned, so the check is a no-op.
|
|
9
|
-
*
|
|
10
|
-
* Pure so the decision can be unit-tested without a live server. `relativePath`
|
|
11
|
-
* is the decoded storage path (`accounts/{cfg}/{acc}/messages/{msg}/parts/{part}`)
|
|
12
|
-
* the signature was minted over — the caller strips the `/content/` prefix.
|
|
13
|
-
*/
|
|
14
|
-
export interface AuthorizeContentInput {
|
|
15
|
-
dataBackend: string | undefined;
|
|
16
|
-
secret: string | undefined;
|
|
17
|
-
relativePath: string;
|
|
18
|
-
exp: string | undefined;
|
|
19
|
-
sig: string | undefined;
|
|
20
|
-
nowSeconds: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const authorizeContentRequest = (
|
|
24
|
-
input: AuthorizeContentInput,
|
|
25
|
-
):
|
|
26
|
-
| { authorized: true }
|
|
27
|
-
| { authorized: false; status: number; reason: string } => {
|
|
28
|
-
if (input.dataBackend !== "sqlite") {
|
|
29
|
-
return { authorized: true };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!input.secret || input.secret.length === 0) {
|
|
33
|
-
// A self-host SQL backend with no signing secret means every content URL
|
|
34
|
-
// is unsigned and unverifiable. Fail closed rather than serve mail bytes
|
|
35
|
-
// without auth.
|
|
36
|
-
return {
|
|
37
|
-
authorized: false,
|
|
38
|
-
status: 500,
|
|
39
|
-
reason: "content-signing-not-configured",
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const result = verifyContentSignature(
|
|
44
|
-
input.relativePath,
|
|
45
|
-
input.exp,
|
|
46
|
-
input.sig,
|
|
47
|
-
input.secret,
|
|
48
|
-
input.nowSeconds,
|
|
49
|
-
);
|
|
50
|
-
if (result.valid) return { authorized: true };
|
|
51
|
-
|
|
52
|
-
const status = result.reason === "missing" ? 401 : 403;
|
|
53
|
-
return { authorized: false, status, reason: result.reason };
|
|
54
|
-
};
|