@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.
Files changed (279) hide show
  1. package/dist/dev-server/content-handler.d.ts +38 -0
  2. package/dist/dev-server/content-handler.js +46 -0
  3. package/dist/src/api.d.ts +5 -0
  4. package/dist/src/api.js +73 -0
  5. package/dist/src/auth.d.ts +24 -0
  6. package/dist/src/auth.js +81 -0
  7. package/dist/src/config/api-config.d.ts +20 -0
  8. package/dist/src/config/api-config.js +1 -0
  9. package/dist/src/config/from-env.d.ts +2 -0
  10. package/dist/src/config/from-env.js +65 -0
  11. package/dist/src/config/msoauth-secrets-manager.d.ts +2 -0
  12. package/dist/src/config/msoauth-secrets-manager.js +28 -0
  13. package/dist/src/config/msoauth.d.ts +19 -0
  14. package/dist/src/config/msoauth.js +6 -0
  15. package/{src/derive/autoMoved.ts → dist/src/derive/autoMoved.d.ts} +1 -29
  16. package/dist/src/derive/autoMoved.js +49 -0
  17. package/dist/src/derive/contentSignature.d.ts +40 -0
  18. package/{src/derive/contentSignature.ts → dist/src/derive/contentSignature.js} +23 -77
  19. package/dist/src/derive/contentUrl.d.ts +33 -0
  20. package/dist/src/derive/contentUrl.js +27 -0
  21. package/{src/derive/deriveMuted.ts → dist/src/derive/deriveMuted.d.ts} +1 -3
  22. package/dist/src/derive/deriveMuted.js +7 -0
  23. package/dist/src/derive/enrichThreadRows.d.ts +118 -0
  24. package/dist/src/derive/enrichThreadRows.js +151 -0
  25. package/dist/src/derive/filterThreadCriteria.d.ts +23 -0
  26. package/dist/src/derive/filterThreadCriteria.js +24 -0
  27. package/{src/derive/pendingMoveCounts.ts → dist/src/derive/pendingMoveCounts.d.ts} +5 -53
  28. package/dist/src/derive/pendingMoveCounts.js +53 -0
  29. package/{src/derive/senderTrust.ts → dist/src/derive/senderTrust.d.ts} +1 -9
  30. package/dist/src/derive/senderTrust.js +19 -0
  31. package/dist/src/error.d.ts +21 -0
  32. package/dist/src/error.js +85 -0
  33. package/dist/src/handlers/account-guards.d.ts +36 -0
  34. package/dist/src/handlers/account-guards.js +107 -0
  35. package/dist/src/handlers/account-oauth.d.ts +15 -0
  36. package/dist/src/handlers/account-oauth.js +334 -0
  37. package/dist/src/handlers/account-overrides.d.ts +53 -0
  38. package/dist/src/handlers/account-overrides.js +233 -0
  39. package/dist/src/handlers/account-signature.d.ts +28 -0
  40. package/dist/src/handlers/account-signature.js +85 -0
  41. package/dist/src/handlers/account.d.ts +7 -0
  42. package/dist/src/handlers/account.js +337 -0
  43. package/dist/src/handlers/address.d.ts +35 -0
  44. package/dist/src/handlers/address.js +133 -0
  45. package/dist/src/handlers/calendar-event.d.ts +70 -0
  46. package/dist/src/handlers/calendar-event.js +417 -0
  47. package/dist/src/handlers/calendar-feed.d.ts +47 -0
  48. package/dist/src/handlers/calendar-feed.js +184 -0
  49. package/dist/src/handlers/calendar-suggestion.d.ts +47 -0
  50. package/dist/src/handlers/calendar-suggestion.js +204 -0
  51. package/dist/src/handlers/calendar.d.ts +116 -0
  52. package/dist/src/handlers/calendar.js +323 -0
  53. package/dist/src/handlers/config.d.ts +43 -0
  54. package/dist/src/handlers/config.js +164 -0
  55. package/dist/src/handlers/ensure-account-config.d.ts +9 -0
  56. package/dist/src/handlers/ensure-account-config.js +27 -0
  57. package/dist/src/handlers/filter.d.ts +3 -0
  58. package/dist/src/handlers/filter.js +88 -0
  59. package/dist/src/handlers/folder-role-appointments.d.ts +68 -0
  60. package/dist/src/handlers/folder-role-appointments.js +168 -0
  61. package/dist/src/handlers/folder-role-labels.d.ts +26 -0
  62. package/dist/src/handlers/folder-role-labels.js +40 -0
  63. package/dist/src/handlers/folder-role.d.ts +9 -0
  64. package/dist/src/handlers/folder-role.js +37 -0
  65. package/dist/src/handlers/index.d.ts +88 -0
  66. package/dist/src/handlers/index.js +62 -0
  67. package/dist/src/handlers/label.d.ts +43 -0
  68. package/dist/src/handlers/label.js +107 -0
  69. package/dist/src/handlers/mailbox-bindings.d.ts +41 -0
  70. package/dist/src/handlers/mailbox-bindings.js +61 -0
  71. package/dist/src/handlers/mailbox.d.ts +23 -0
  72. package/dist/src/handlers/mailbox.js +211 -0
  73. package/dist/src/handlers/me.d.ts +2 -0
  74. package/dist/src/handlers/me.js +117 -0
  75. package/dist/src/handlers/message.d.ts +82 -0
  76. package/dist/src/handlers/message.js +595 -0
  77. package/dist/src/handlers/organize.d.ts +10 -0
  78. package/dist/src/handlers/organize.js +103 -0
  79. package/dist/src/handlers/outbox-attachment.d.ts +25 -0
  80. package/dist/src/handlers/outbox-attachment.js +77 -0
  81. package/dist/src/handlers/outbox.d.ts +4 -0
  82. package/dist/src/handlers/outbox.js +136 -0
  83. package/dist/src/handlers/sync-progress.d.ts +46 -0
  84. package/dist/src/handlers/sync-progress.js +63 -0
  85. package/dist/src/handlers/sync.d.ts +28 -0
  86. package/dist/src/handlers/sync.js +73 -0
  87. package/dist/src/handlers/system-update.d.ts +2 -0
  88. package/dist/src/handlers/system-update.js +159 -0
  89. package/dist/src/handlers/thread.d.ts +148 -0
  90. package/dist/src/handlers/thread.js +228 -0
  91. package/dist/src/handlers/unified-threads.d.ts +247 -0
  92. package/dist/src/handlers/unified-threads.js +406 -0
  93. package/{src/handlers/vip-suggestions.ts → dist/src/handlers/vip-suggestions.d.ts} +1 -13
  94. package/dist/src/handlers/vip-suggestions.js +16 -0
  95. package/dist/src/index.d.ts +5 -0
  96. package/dist/src/index.js +53 -0
  97. package/{src/json.ts → dist/src/json.d.ts} +1 -4
  98. package/dist/src/json.js +10 -0
  99. package/dist/src/jwt-auth.d.ts +15 -0
  100. package/dist/src/jwt-auth.js +45 -0
  101. package/dist/src/lambda.d.ts +13 -0
  102. package/dist/src/lambda.js +33 -0
  103. package/dist/src/request.d.ts +9 -0
  104. package/dist/src/request.js +17 -0
  105. package/dist/src/response.d.ts +44 -0
  106. package/dist/src/response.js +80 -0
  107. package/dist/src/serve.d.ts +31 -0
  108. package/dist/src/serve.js +117 -0
  109. package/dist/src/service/account-deletion.d.ts +20 -0
  110. package/dist/src/service/account-deletion.js +33 -0
  111. package/dist/src/service/compose-sqlite.d.ts +3 -0
  112. package/dist/src/service/compose-sqlite.js +13 -0
  113. package/dist/src/service/create-remit-client.d.ts +109 -0
  114. package/dist/src/service/create-remit-client.js +188 -0
  115. package/dist/src/service/data-client.d.ts +12 -0
  116. package/dist/src/service/data-client.js +42 -0
  117. package/dist/src/service/export-identity.d.ts +4 -0
  118. package/dist/src/service/export-identity.js +33 -0
  119. package/dist/src/service/fire-and-forget.d.ts +54 -0
  120. package/dist/src/service/fire-and-forget.js +37 -0
  121. package/dist/src/service/organize.d.ts +181 -0
  122. package/dist/src/service/organize.js +367 -0
  123. package/dist/src/service/queues.d.ts +12 -0
  124. package/dist/src/service/queues.js +1 -0
  125. package/dist/src/service/refusal.d.ts +4 -0
  126. package/dist/src/service/refusal.js +20 -0
  127. package/dist/src/service/sender-category.d.ts +51 -0
  128. package/{src/service/sender-category.ts → dist/src/service/sender-category.js} +40 -81
  129. package/dist/src/service/shared-deps.d.ts +6 -0
  130. package/dist/src/service/shared-deps.js +24 -0
  131. package/dist/src/service/trigger-sync.d.ts +57 -0
  132. package/dist/src/service/trigger-sync.js +31 -0
  133. package/dist/src/types.d.ts +72 -0
  134. package/dist/src/types.js +1 -0
  135. package/package.json +125 -98
  136. package/.env.test +0 -4
  137. package/dev-server/content-auth.test.ts +0 -113
  138. package/dev-server/content-auth.ts +0 -54
  139. package/dev-server/content-handler.test.ts +0 -105
  140. package/dev-server/content-handler.ts +0 -79
  141. package/dev-server/content-path.test.ts +0 -37
  142. package/dev-server/content-path.ts +0 -27
  143. package/dev-server/cors.test.ts +0 -48
  144. package/dev-server/cors.ts +0 -25
  145. package/dev-server/keep-alive.test.ts +0 -24
  146. package/dev-server/lambda-helpers.ts +0 -69
  147. package/dev-server/log-output.test.ts +0 -173
  148. package/dev-server/oauth-callback-route.test.ts +0 -66
  149. package/dev-server/relational-health.test.ts +0 -83
  150. package/dev-server/relational-health.ts +0 -39
  151. package/dev-server/server.ts +0 -384
  152. package/dev-server/sync-age.test.ts +0 -91
  153. package/dev-server/sync-age.ts +0 -55
  154. package/dev-server/upload-handler.ts +0 -132
  155. package/dev-server/upload-route.test.ts +0 -196
  156. package/scripts/backfill-classification.ts +0 -91
  157. package/scripts/backfill-list-id.ts +0 -83
  158. package/scripts/config-save.ts +0 -128
  159. package/src/auth.test.ts +0 -12
  160. package/src/auth.ts +0 -92
  161. package/src/config/msoauth.test.ts +0 -48
  162. package/src/config/msoauth.ts +0 -48
  163. package/src/data-backend.test.ts +0 -23
  164. package/src/data-backend.ts +0 -14
  165. package/src/derive/autoMoved.test.ts +0 -104
  166. package/src/derive/contentSignature.test.ts +0 -161
  167. package/src/derive/contentUrl.test.ts +0 -172
  168. package/src/derive/contentUrl.ts +0 -86
  169. package/src/derive/deriveMuted.test.ts +0 -34
  170. package/src/derive/enrichThreadRows.test.ts +0 -360
  171. package/src/derive/enrichThreadRows.ts +0 -234
  172. package/src/derive/filterThreadCriteria.test.ts +0 -126
  173. package/src/derive/filterThreadCriteria.ts +0 -51
  174. package/src/derive/senderTrust.test.ts +0 -67
  175. package/src/error.test.ts +0 -201
  176. package/src/error.ts +0 -123
  177. package/src/handlers/account-guards.test.ts +0 -67
  178. package/src/handlers/account-guards.ts +0 -169
  179. package/src/handlers/account-oauth-callback.test.ts +0 -155
  180. package/src/handlers/account-oauth-consent.test.ts +0 -336
  181. package/src/handlers/account-oauth-start.test.ts +0 -191
  182. package/src/handlers/account-oauth.test.ts +0 -439
  183. package/src/handlers/account-oauth.ts +0 -635
  184. package/src/handlers/account-overrides.test.ts +0 -88
  185. package/src/handlers/account-overrides.ts +0 -323
  186. package/src/handlers/account-ownership.ts +0 -25
  187. package/src/handlers/account-signature.ts +0 -117
  188. package/src/handlers/account-synced-services.test.ts +0 -245
  189. package/src/handlers/account.ts +0 -552
  190. package/src/handlers/address.test.ts +0 -381
  191. package/src/handlers/address.ts +0 -200
  192. package/src/handlers/calendar-event.test.ts +0 -685
  193. package/src/handlers/calendar-event.ts +0 -738
  194. package/src/handlers/calendar-feed-validators.test.ts +0 -228
  195. package/src/handlers/calendar-feed.test.ts +0 -439
  196. package/src/handlers/calendar-feed.ts +0 -291
  197. package/src/handlers/calendar-sqlite-fixture.ts +0 -36
  198. package/src/handlers/calendar-suggestion.test.ts +0 -1269
  199. package/src/handlers/calendar-suggestion.ts +0 -425
  200. package/src/handlers/calendar.test.ts +0 -1080
  201. package/src/handlers/calendar.ts +0 -517
  202. package/src/handlers/config.test.ts +0 -245
  203. package/src/handlers/config.ts +0 -332
  204. package/src/handlers/ensure-account-config.ts +0 -30
  205. package/src/handlers/filter.test.ts +0 -644
  206. package/src/handlers/filter.ts +0 -518
  207. package/src/handlers/folder-role-appointments.test.ts +0 -438
  208. package/src/handlers/folder-role-appointments.ts +0 -249
  209. package/src/handlers/folder-role-labels.ts +0 -63
  210. package/src/handlers/folder-role.test.ts +0 -209
  211. package/src/handlers/folder-role.ts +0 -80
  212. package/src/handlers/index.ts +0 -83
  213. package/src/handlers/label.test.ts +0 -112
  214. package/src/handlers/label.ts +0 -210
  215. package/src/handlers/mailbox-bindings.ts +0 -111
  216. package/src/handlers/mailbox-rename-refusal.test.ts +0 -247
  217. package/src/handlers/mailbox-settled-target.test.ts +0 -251
  218. package/src/handlers/mailbox.test.ts +0 -280
  219. package/src/handlers/mailbox.ts +0 -581
  220. package/src/handlers/me.test.ts +0 -81
  221. package/src/handlers/me.ts +0 -203
  222. package/src/handlers/message-read-backfill.test.ts +0 -103
  223. package/src/handlers/message.test.ts +0 -131
  224. package/src/handlers/message.ts +0 -1131
  225. package/src/handlers/organize.test.ts +0 -277
  226. package/src/handlers/organize.ts +0 -197
  227. package/src/handlers/outbox-attachment.test.ts +0 -765
  228. package/src/handlers/outbox-attachment.ts +0 -126
  229. package/src/handlers/outbox-list.test.ts +0 -179
  230. package/src/handlers/outbox.test.ts +0 -772
  231. package/src/handlers/outbox.ts +0 -258
  232. package/src/handlers/search.test.ts +0 -161
  233. package/src/handlers/search.ts +0 -105
  234. package/src/handlers/sync-progress.test.ts +0 -205
  235. package/src/handlers/sync-progress.ts +0 -99
  236. package/src/handlers/sync.test.ts +0 -168
  237. package/src/handlers/sync.ts +0 -118
  238. package/src/handlers/system-update.test.ts +0 -497
  239. package/src/handlers/system-update.ts +0 -230
  240. package/src/handlers/thread.test.ts +0 -396
  241. package/src/handlers/thread.ts +0 -427
  242. package/src/handlers/unified-threads-mail-sources.sqlite.test.ts +0 -197
  243. package/src/handlers/unified-threads.test.ts +0 -785
  244. package/src/handlers/unified-threads.ts +0 -669
  245. package/src/index.test.ts +0 -83
  246. package/src/index.ts +0 -228
  247. package/src/jwt-auth.test.ts +0 -152
  248. package/src/jwt-auth.ts +0 -132
  249. package/src/log-redaction.test.ts +0 -144
  250. package/src/openapi-spec.test.ts +0 -118
  251. package/src/request-context.test.ts +0 -61
  252. package/src/request-context.ts +0 -33
  253. package/src/request-pipeline.test.ts +0 -88
  254. package/src/request.ts +0 -10
  255. package/src/response.test.ts +0 -130
  256. package/src/response.ts +0 -144
  257. package/src/service/compose-sqlite.ts +0 -98
  258. package/src/service/config-import.ts +0 -39
  259. package/src/service/create-remit-client.ts +0 -522
  260. package/src/service/data-client.test.ts +0 -97
  261. package/src/service/data-client.ts +0 -58
  262. package/src/service/export-identity.ts +0 -39
  263. package/src/service/filter.ts +0 -40
  264. package/src/service/fire-and-forget.test.ts +0 -126
  265. package/src/service/fire-and-forget.ts +0 -79
  266. package/src/service/localhost-env-config.test.ts +0 -36
  267. package/src/service/organize-queue.ts +0 -98
  268. package/src/service/organize.test.ts +0 -1451
  269. package/src/service/organize.ts +0 -1025
  270. package/src/service/read-path-filter-config.test.ts +0 -243
  271. package/src/service/semantic-capability.test.ts +0 -78
  272. package/src/service/semantic-capability.ts +0 -95
  273. package/src/service/sender-category.test.ts +0 -235
  274. package/src/service/sqs.ts +0 -4
  275. package/src/service/trigger-sync.test.ts +0 -205
  276. package/src/service/trigger-sync.ts +0 -100
  277. package/src/stdout-capture-fixture.ts +0 -40
  278. package/src/types.ts +0 -271
  279. 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
- "name": "@remit/backend",
3
- "version": "0.0.143",
4
- "description": "Remit Mail Inspector API backend",
5
- "license": "MIT",
6
- "author": "",
7
- "type": "module",
8
- "main": "src/index.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./src/index.ts",
12
- "default": "./src/index.ts"
13
- },
14
- "./client": {
15
- "types": "./src/service/data-client.ts",
16
- "default": "./src/service/data-client.ts"
17
- },
18
- "./create-remit-client": {
19
- "types": "./src/service/create-remit-client.ts",
20
- "default": "./src/service/create-remit-client.ts"
21
- },
22
- "./folder-role-appointments": {
23
- "types": "./src/handlers/folder-role-appointments.ts",
24
- "default": "./src/handlers/folder-role-appointments.ts"
25
- },
26
- "./folder-role-labels": {
27
- "types": "./src/handlers/folder-role-labels.ts",
28
- "default": "./src/handlers/folder-role-labels.ts"
29
- },
30
- "./organize": {
31
- "types": "./src/service/organize.ts",
32
- "default": "./src/service/organize.ts"
33
- },
34
- "./sender-category": {
35
- "types": "./src/service/sender-category.ts",
36
- "default": "./src/service/sender-category.ts"
37
- },
38
- "./trigger-sync": {
39
- "types": "./src/service/trigger-sync.ts",
40
- "default": "./src/service/trigger-sync.ts"
41
- },
42
- "./content-handler": {
43
- "types": "./dev-server/content-handler.ts",
44
- "default": "./dev-server/content-handler.ts"
45
- }
46
- },
47
- "scripts": {
48
- "test:typecheck": "tsgo --noEmit",
49
- "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'",
50
- "test": "npm run test:typecheck && npm run test:run",
51
- "bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
52
- "dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
53
- "dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts",
54
- "serve": "node --import tsx dev-server/server.ts"
55
- },
56
- "devDependencies": {
57
- "@remit/apisix": "*",
58
- "@types/better-sqlite3": "^7.6.13",
59
- "@types/express": "^5.0.3",
60
- "@types/swagger-ui-express": "^4.1.8",
61
- "better-sqlite3": "^12.11.1",
62
- "esbuild": "^0.28.1",
63
- "express": "^5.1.0",
64
- "swagger-ui-express": "^5.0.1",
65
- "tsx": "*"
66
- },
67
- "dependencies": {
68
- "@remit/config-format": "*",
69
- "@remit/config-transfer": "*",
70
- "@remit/calendar-service": "*",
71
- "@remit/data-ports": "*",
72
- "@remit/domain-enums": "*",
73
- "@remit/api-openapi-types": "*",
74
- "@remit/logger-lambda": "*",
75
- "@remit/mail-oauth-service": "*",
76
- "@remit/secrets-service": "*",
77
- "@remit/mailbox-service": "*",
78
- "@remit/search-service": "*",
79
- "@remit/storage-service": "*",
80
- "@remit/auth-service": "*",
81
- "@remit/drizzle-service": "*",
82
- "@remit/sqs-client": "*",
83
- "@aws-sdk/client-secrets-manager": "*",
84
- "@aws-sdk/client-sqs": "*",
85
- "better-sqlite3": "^12.11.1",
86
- "expect-env": "*",
87
- "openapi-backend": "*",
88
- "p-map": "*",
89
- "@types/aws-lambda": "*",
90
- "@types/better-sqlite3": "^7.6.13"
91
- },
92
- "publishConfig": {
93
- "access": "public"
94
- },
95
- "repository": {
96
- "type": "git",
97
- "url": "git+https://github.com/remit-mail/reader.git",
98
- "directory": "packages/backend"
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,4 +0,0 @@
1
- SERVER_PORT=5433
2
- SQS_QUEUE_URL=http://localhost:9324/queue/remit-local
3
- NODE_ENV=development
4
- LOG_LEVEL=debug
@@ -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
- };