@rapidmx/restapi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +50 -0
  3. package/dist/lib/blob/BlobStore.js +6 -0
  4. package/dist/lib/blob/BlobStore.js.map +1 -0
  5. package/dist/lib/blob/LocalFsBlobStore.js +101 -0
  6. package/dist/lib/blob/LocalFsBlobStore.js.map +1 -0
  7. package/dist/lib/blob/index.js +7 -0
  8. package/dist/lib/blob/index.js.map +1 -0
  9. package/dist/lib/index.js +12 -0
  10. package/dist/lib/index.js.map +1 -0
  11. package/dist/lib/jobs/AttachmentExtractionJob.js +124 -0
  12. package/dist/lib/jobs/AttachmentExtractionJob.js.map +1 -0
  13. package/dist/lib/jobs/CalendarReminderJob.js +132 -0
  14. package/dist/lib/jobs/CalendarReminderJob.js.map +1 -0
  15. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +101 -0
  16. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -0
  17. package/dist/lib/jobs/ExternalShareExpirationJob.js +117 -0
  18. package/dist/lib/jobs/ExternalShareExpirationJob.js.map +1 -0
  19. package/dist/lib/jobs/MailboxQuotaRecalcJob.js +163 -0
  20. package/dist/lib/jobs/MailboxQuotaRecalcJob.js.map +1 -0
  21. package/dist/lib/jobs/MeetingSchedulingJob.js +106 -0
  22. package/dist/lib/jobs/MeetingSchedulingJob.js.map +1 -0
  23. package/dist/lib/jobs/QuarantineRetentionJob.js +97 -0
  24. package/dist/lib/jobs/QuarantineRetentionJob.js.map +1 -0
  25. package/dist/lib/jobs/ScanQueueJob.js +223 -0
  26. package/dist/lib/jobs/ScanQueueJob.js.map +1 -0
  27. package/dist/lib/jobs/SearchIndexJob.js +146 -0
  28. package/dist/lib/jobs/SearchIndexJob.js.map +1 -0
  29. package/dist/lib/jobs/index.js +10 -0
  30. package/dist/lib/jobs/index.js.map +1 -0
  31. package/dist/lib/jobs/mongo/AttachmentExtractionJobMongo.js +14 -0
  32. package/dist/lib/jobs/mongo/AttachmentExtractionJobMongo.js.map +1 -0
  33. package/dist/lib/jobs/mongo/CalendarReminderJobMongo.js +13 -0
  34. package/dist/lib/jobs/mongo/CalendarReminderJobMongo.js.map +1 -0
  35. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +13 -0
  36. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -0
  37. package/dist/lib/jobs/mongo/ExternalShareExpirationJobMongo.js +13 -0
  38. package/dist/lib/jobs/mongo/ExternalShareExpirationJobMongo.js.map +1 -0
  39. package/dist/lib/jobs/mongo/MailboxQuotaRecalcJobMongo.js +15 -0
  40. package/dist/lib/jobs/mongo/MailboxQuotaRecalcJobMongo.js.map +1 -0
  41. package/dist/lib/jobs/mongo/MeetingSchedulingJobMongo.js +13 -0
  42. package/dist/lib/jobs/mongo/MeetingSchedulingJobMongo.js.map +1 -0
  43. package/dist/lib/jobs/mongo/QuarantineRetentionJobMongo.js +13 -0
  44. package/dist/lib/jobs/mongo/QuarantineRetentionJobMongo.js.map +1 -0
  45. package/dist/lib/jobs/mongo/ScanQueueJobMongo.js +18 -0
  46. package/dist/lib/jobs/mongo/ScanQueueJobMongo.js.map +1 -0
  47. package/dist/lib/jobs/mongo/SearchIndexJobMongo.js +14 -0
  48. package/dist/lib/jobs/mongo/SearchIndexJobMongo.js.map +1 -0
  49. package/dist/lib/jobs/mongo/index.js +10 -0
  50. package/dist/lib/jobs/mongo/index.js.map +1 -0
  51. package/dist/lib/jobs/mongo.js +2 -0
  52. package/dist/lib/jobs/mongo.js.map +1 -0
  53. package/dist/lib/jobs/sql/AttachmentExtractionJobSQL.js +14 -0
  54. package/dist/lib/jobs/sql/AttachmentExtractionJobSQL.js.map +1 -0
  55. package/dist/lib/jobs/sql/CalendarReminderJobSQL.js +13 -0
  56. package/dist/lib/jobs/sql/CalendarReminderJobSQL.js.map +1 -0
  57. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +13 -0
  58. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -0
  59. package/dist/lib/jobs/sql/ExternalShareExpirationJobSQL.js +13 -0
  60. package/dist/lib/jobs/sql/ExternalShareExpirationJobSQL.js.map +1 -0
  61. package/dist/lib/jobs/sql/MailboxQuotaRecalcJobSQL.js +15 -0
  62. package/dist/lib/jobs/sql/MailboxQuotaRecalcJobSQL.js.map +1 -0
  63. package/dist/lib/jobs/sql/MeetingSchedulingJobSQL.js +13 -0
  64. package/dist/lib/jobs/sql/MeetingSchedulingJobSQL.js.map +1 -0
  65. package/dist/lib/jobs/sql/QuarantineRetentionJobSQL.js +13 -0
  66. package/dist/lib/jobs/sql/QuarantineRetentionJobSQL.js.map +1 -0
  67. package/dist/lib/jobs/sql/ScanQueueJobSQL.js +18 -0
  68. package/dist/lib/jobs/sql/ScanQueueJobSQL.js.map +1 -0
  69. package/dist/lib/jobs/sql/SearchIndexJobSQL.js +14 -0
  70. package/dist/lib/jobs/sql/SearchIndexJobSQL.js.map +1 -0
  71. package/dist/lib/jobs/sql/index.js +10 -0
  72. package/dist/lib/jobs/sql/index.js.map +1 -0
  73. package/dist/lib/jobs/sql.js +2 -0
  74. package/dist/lib/jobs/sql.js.map +1 -0
  75. package/dist/lib/models/index.js +2 -0
  76. package/dist/lib/models/index.js.map +1 -0
  77. package/dist/lib/models/mongo/AttachmentMongo.js +129 -0
  78. package/dist/lib/models/mongo/AttachmentMongo.js.map +1 -0
  79. package/dist/lib/models/mongo/CalendarEventMongo.js +172 -0
  80. package/dist/lib/models/mongo/CalendarEventMongo.js.map +1 -0
  81. package/dist/lib/models/mongo/CalendarShareLinkMongo.js +85 -0
  82. package/dist/lib/models/mongo/CalendarShareLinkMongo.js.map +1 -0
  83. package/dist/lib/models/mongo/ContactListMongo.js +60 -0
  84. package/dist/lib/models/mongo/ContactListMongo.js.map +1 -0
  85. package/dist/lib/models/mongo/ContactMongo.js +148 -0
  86. package/dist/lib/models/mongo/ContactMongo.js.map +1 -0
  87. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +97 -0
  88. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -0
  89. package/dist/lib/models/mongo/FolderMongo.js +102 -0
  90. package/dist/lib/models/mongo/FolderMongo.js.map +1 -0
  91. package/dist/lib/models/mongo/IngestQueueEntryMongo.js +93 -0
  92. package/dist/lib/models/mongo/IngestQueueEntryMongo.js.map +1 -0
  93. package/dist/lib/models/mongo/MailboxMongo.js +105 -0
  94. package/dist/lib/models/mongo/MailboxMongo.js.map +1 -0
  95. package/dist/lib/models/mongo/MessageMongo.js +180 -0
  96. package/dist/lib/models/mongo/MessageMongo.js.map +1 -0
  97. package/dist/lib/models/mongo/NoteMongo.js +83 -0
  98. package/dist/lib/models/mongo/NoteMongo.js.map +1 -0
  99. package/dist/lib/models/mongo/QuarantineEntryMongo.js +99 -0
  100. package/dist/lib/models/mongo/QuarantineEntryMongo.js.map +1 -0
  101. package/dist/lib/models/mongo/ScanResultMongo.js +112 -0
  102. package/dist/lib/models/mongo/ScanResultMongo.js.map +1 -0
  103. package/dist/lib/models/mongo/SearchIndexStateMongo.js +83 -0
  104. package/dist/lib/models/mongo/SearchIndexStateMongo.js.map +1 -0
  105. package/dist/lib/models/mongo/TaskMongo.js +105 -0
  106. package/dist/lib/models/mongo/TaskMongo.js.map +1 -0
  107. package/dist/lib/models/mongo/index.js +16 -0
  108. package/dist/lib/models/mongo/index.js.map +1 -0
  109. package/dist/lib/models/mongo.js +2 -0
  110. package/dist/lib/models/mongo.js.map +1 -0
  111. package/dist/lib/models/sql/AttachmentSQL.js +129 -0
  112. package/dist/lib/models/sql/AttachmentSQL.js.map +1 -0
  113. package/dist/lib/models/sql/CalendarEventSQL.js +176 -0
  114. package/dist/lib/models/sql/CalendarEventSQL.js.map +1 -0
  115. package/dist/lib/models/sql/CalendarShareLinkSQL.js +85 -0
  116. package/dist/lib/models/sql/CalendarShareLinkSQL.js.map +1 -0
  117. package/dist/lib/models/sql/ContactListSQL.js +60 -0
  118. package/dist/lib/models/sql/ContactListSQL.js.map +1 -0
  119. package/dist/lib/models/sql/ContactSQL.js +148 -0
  120. package/dist/lib/models/sql/ContactSQL.js.map +1 -0
  121. package/dist/lib/models/sql/DeviceSyncStateSQL.js +97 -0
  122. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -0
  123. package/dist/lib/models/sql/FolderSQL.js +106 -0
  124. package/dist/lib/models/sql/FolderSQL.js.map +1 -0
  125. package/dist/lib/models/sql/IngestQueueEntrySQL.js +97 -0
  126. package/dist/lib/models/sql/IngestQueueEntrySQL.js.map +1 -0
  127. package/dist/lib/models/sql/MailboxSQL.js +105 -0
  128. package/dist/lib/models/sql/MailboxSQL.js.map +1 -0
  129. package/dist/lib/models/sql/MessageSQL.js +184 -0
  130. package/dist/lib/models/sql/MessageSQL.js.map +1 -0
  131. package/dist/lib/models/sql/NoteSQL.js +83 -0
  132. package/dist/lib/models/sql/NoteSQL.js.map +1 -0
  133. package/dist/lib/models/sql/QuarantineEntrySQL.js +103 -0
  134. package/dist/lib/models/sql/QuarantineEntrySQL.js.map +1 -0
  135. package/dist/lib/models/sql/ScanResultSQL.js +116 -0
  136. package/dist/lib/models/sql/ScanResultSQL.js.map +1 -0
  137. package/dist/lib/models/sql/SearchIndexStateSQL.js +83 -0
  138. package/dist/lib/models/sql/SearchIndexStateSQL.js.map +1 -0
  139. package/dist/lib/models/sql/TaskSQL.js +109 -0
  140. package/dist/lib/models/sql/TaskSQL.js.map +1 -0
  141. package/dist/lib/models/sql/index.js +16 -0
  142. package/dist/lib/models/sql/index.js.map +1 -0
  143. package/dist/lib/models/sql.js +2 -0
  144. package/dist/lib/models/sql.js.map +1 -0
  145. package/dist/lib/models/types.js +108 -0
  146. package/dist/lib/models/types.js.map +1 -0
  147. package/dist/lib/mongo.js +8 -0
  148. package/dist/lib/mongo.js.map +1 -0
  149. package/dist/lib/push/MailPushRoute.js +42 -0
  150. package/dist/lib/push/MailPushRoute.js.map +1 -0
  151. package/dist/lib/push/index.js +6 -0
  152. package/dist/lib/push/index.js.map +1 -0
  153. package/dist/lib/routes/BaseAttachmentRoute.js +142 -0
  154. package/dist/lib/routes/BaseAttachmentRoute.js.map +1 -0
  155. package/dist/lib/routes/BaseCalendarShareLinkRoute.js +123 -0
  156. package/dist/lib/routes/BaseCalendarShareLinkRoute.js.map +1 -0
  157. package/dist/lib/routes/BaseFolderRoute.js +192 -0
  158. package/dist/lib/routes/BaseFolderRoute.js.map +1 -0
  159. package/dist/lib/routes/BaseMailIngestRoute.js +179 -0
  160. package/dist/lib/routes/BaseMailIngestRoute.js.map +1 -0
  161. package/dist/lib/routes/BaseMailboxRoute.js +187 -0
  162. package/dist/lib/routes/BaseMailboxRoute.js.map +1 -0
  163. package/dist/lib/routes/BaseMessageRoute.js +142 -0
  164. package/dist/lib/routes/BaseMessageRoute.js.map +1 -0
  165. package/dist/lib/routes/BaseScopedChildRoute.js +348 -0
  166. package/dist/lib/routes/BaseScopedChildRoute.js.map +1 -0
  167. package/dist/lib/routes/BaseSearchRoute.js +91 -0
  168. package/dist/lib/routes/BaseSearchRoute.js.map +1 -0
  169. package/dist/lib/routes/index.js +13 -0
  170. package/dist/lib/routes/index.js.map +1 -0
  171. package/dist/lib/routes/mongo/AttachmentRouteMongo.js +26 -0
  172. package/dist/lib/routes/mongo/AttachmentRouteMongo.js.map +1 -0
  173. package/dist/lib/routes/mongo/CalendarEventRouteMongo.js +27 -0
  174. package/dist/lib/routes/mongo/CalendarEventRouteMongo.js.map +1 -0
  175. package/dist/lib/routes/mongo/CalendarShareLinkRouteMongo.js +26 -0
  176. package/dist/lib/routes/mongo/CalendarShareLinkRouteMongo.js.map +1 -0
  177. package/dist/lib/routes/mongo/ContactListRouteMongo.js +26 -0
  178. package/dist/lib/routes/mongo/ContactListRouteMongo.js.map +1 -0
  179. package/dist/lib/routes/mongo/ContactRouteMongo.js +27 -0
  180. package/dist/lib/routes/mongo/ContactRouteMongo.js.map +1 -0
  181. package/dist/lib/routes/mongo/FolderRouteMongo.js +26 -0
  182. package/dist/lib/routes/mongo/FolderRouteMongo.js.map +1 -0
  183. package/dist/lib/routes/mongo/IngestQueueRouteMongo.js +33 -0
  184. package/dist/lib/routes/mongo/IngestQueueRouteMongo.js.map +1 -0
  185. package/dist/lib/routes/mongo/MailIngestRouteMongo.js +14 -0
  186. package/dist/lib/routes/mongo/MailIngestRouteMongo.js.map +1 -0
  187. package/dist/lib/routes/mongo/MailboxRouteMongo.js +39 -0
  188. package/dist/lib/routes/mongo/MailboxRouteMongo.js.map +1 -0
  189. package/dist/lib/routes/mongo/MessageRouteMongo.js +27 -0
  190. package/dist/lib/routes/mongo/MessageRouteMongo.js.map +1 -0
  191. package/dist/lib/routes/mongo/NoteRouteMongo.js +26 -0
  192. package/dist/lib/routes/mongo/NoteRouteMongo.js.map +1 -0
  193. package/dist/lib/routes/mongo/QuarantineRouteMongo.js +35 -0
  194. package/dist/lib/routes/mongo/QuarantineRouteMongo.js.map +1 -0
  195. package/dist/lib/routes/mongo/SearchRouteMongo.js +13 -0
  196. package/dist/lib/routes/mongo/SearchRouteMongo.js.map +1 -0
  197. package/dist/lib/routes/mongo/TaskRouteMongo.js +27 -0
  198. package/dist/lib/routes/mongo/TaskRouteMongo.js.map +1 -0
  199. package/dist/lib/routes/mongo/index.js +15 -0
  200. package/dist/lib/routes/mongo/index.js.map +1 -0
  201. package/dist/lib/routes/mongo.js +2 -0
  202. package/dist/lib/routes/mongo.js.map +1 -0
  203. package/dist/lib/routes/sql/AttachmentRouteSQL.js +26 -0
  204. package/dist/lib/routes/sql/AttachmentRouteSQL.js.map +1 -0
  205. package/dist/lib/routes/sql/CalendarEventRouteSQL.js +27 -0
  206. package/dist/lib/routes/sql/CalendarEventRouteSQL.js.map +1 -0
  207. package/dist/lib/routes/sql/CalendarShareLinkRouteSQL.js +26 -0
  208. package/dist/lib/routes/sql/CalendarShareLinkRouteSQL.js.map +1 -0
  209. package/dist/lib/routes/sql/ContactListRouteSQL.js +26 -0
  210. package/dist/lib/routes/sql/ContactListRouteSQL.js.map +1 -0
  211. package/dist/lib/routes/sql/ContactRouteSQL.js +27 -0
  212. package/dist/lib/routes/sql/ContactRouteSQL.js.map +1 -0
  213. package/dist/lib/routes/sql/FolderRouteSQL.js +26 -0
  214. package/dist/lib/routes/sql/FolderRouteSQL.js.map +1 -0
  215. package/dist/lib/routes/sql/IngestQueueRouteSQL.js +29 -0
  216. package/dist/lib/routes/sql/IngestQueueRouteSQL.js.map +1 -0
  217. package/dist/lib/routes/sql/MailIngestRouteSQL.js +35 -0
  218. package/dist/lib/routes/sql/MailIngestRouteSQL.js.map +1 -0
  219. package/dist/lib/routes/sql/MailboxRouteSQL.js +64 -0
  220. package/dist/lib/routes/sql/MailboxRouteSQL.js.map +1 -0
  221. package/dist/lib/routes/sql/MessageRouteSQL.js +27 -0
  222. package/dist/lib/routes/sql/MessageRouteSQL.js.map +1 -0
  223. package/dist/lib/routes/sql/NoteRouteSQL.js +26 -0
  224. package/dist/lib/routes/sql/NoteRouteSQL.js.map +1 -0
  225. package/dist/lib/routes/sql/QuarantineRouteSQL.js +29 -0
  226. package/dist/lib/routes/sql/QuarantineRouteSQL.js.map +1 -0
  227. package/dist/lib/routes/sql/SearchRouteSQL.js +13 -0
  228. package/dist/lib/routes/sql/SearchRouteSQL.js.map +1 -0
  229. package/dist/lib/routes/sql/TaskRouteSQL.js +27 -0
  230. package/dist/lib/routes/sql/TaskRouteSQL.js.map +1 -0
  231. package/dist/lib/routes/sql/index.js +15 -0
  232. package/dist/lib/routes/sql/index.js.map +1 -0
  233. package/dist/lib/routes/sql.js +2 -0
  234. package/dist/lib/routes/sql.js.map +1 -0
  235. package/dist/lib/scan/AvScanProvider.js +2 -0
  236. package/dist/lib/scan/AvScanProvider.js.map +1 -0
  237. package/dist/lib/scan/ClamAvScanProvider.js +112 -0
  238. package/dist/lib/scan/ClamAvScanProvider.js.map +1 -0
  239. package/dist/lib/scan/RspamdSpamScanProvider.js +100 -0
  240. package/dist/lib/scan/RspamdSpamScanProvider.js.map +1 -0
  241. package/dist/lib/scan/ScanPipeline.js +118 -0
  242. package/dist/lib/scan/ScanPipeline.js.map +1 -0
  243. package/dist/lib/scan/SpamScanProvider.js +2 -0
  244. package/dist/lib/scan/SpamScanProvider.js.map +1 -0
  245. package/dist/lib/scan/index.js +10 -0
  246. package/dist/lib/scan/index.js.map +1 -0
  247. package/dist/lib/search/MongoTextSearchProvider.js +116 -0
  248. package/dist/lib/search/MongoTextSearchProvider.js.map +1 -0
  249. package/dist/lib/search/OpenSearchProvider.js +156 -0
  250. package/dist/lib/search/OpenSearchProvider.js.map +1 -0
  251. package/dist/lib/search/PostgresFullTextSearchProvider.js +139 -0
  252. package/dist/lib/search/PostgresFullTextSearchProvider.js.map +1 -0
  253. package/dist/lib/search/SearchProvider.js +6 -0
  254. package/dist/lib/search/SearchProvider.js.map +1 -0
  255. package/dist/lib/search/extraction/DocxTextExtractor.js +27 -0
  256. package/dist/lib/search/extraction/DocxTextExtractor.js.map +1 -0
  257. package/dist/lib/search/extraction/ExtractorRegistry.js +82 -0
  258. package/dist/lib/search/extraction/ExtractorRegistry.js.map +1 -0
  259. package/dist/lib/search/extraction/HtmlTextExtractor.js +20 -0
  260. package/dist/lib/search/extraction/HtmlTextExtractor.js.map +1 -0
  261. package/dist/lib/search/extraction/PdfTextExtractor.js +22 -0
  262. package/dist/lib/search/extraction/PdfTextExtractor.js.map +1 -0
  263. package/dist/lib/search/extraction/PlainTextExtractor.js +14 -0
  264. package/dist/lib/search/extraction/PlainTextExtractor.js.map +1 -0
  265. package/dist/lib/search/extraction/TextExtractor.js +6 -0
  266. package/dist/lib/search/extraction/TextExtractor.js.map +1 -0
  267. package/dist/lib/search/index.js +15 -0
  268. package/dist/lib/search/index.js.map +1 -0
  269. package/dist/lib/sql.js +8 -0
  270. package/dist/lib/sql.js.map +1 -0
  271. package/dist/lib/transport/MTAIngestAdapter.js +30 -0
  272. package/dist/lib/transport/MTAIngestAdapter.js.map +1 -0
  273. package/dist/lib/transport/MailTransport.js +6 -0
  274. package/dist/lib/transport/MailTransport.js.map +1 -0
  275. package/dist/lib/transport/PostfixSendmailTransport.js +61 -0
  276. package/dist/lib/transport/PostfixSendmailTransport.js.map +1 -0
  277. package/dist/lib/transport/index.js +8 -0
  278. package/dist/lib/transport/index.js.map +1 -0
  279. package/dist/lib/util/FolderUtils.js +50 -0
  280. package/dist/lib/util/FolderUtils.js.map +1 -0
  281. package/dist/lib/util/MailSendUtils.js +36 -0
  282. package/dist/lib/util/MailSendUtils.js.map +1 -0
  283. package/dist/lib/util/MailboxScopeUtils.js +23 -0
  284. package/dist/lib/util/MailboxScopeUtils.js.map +1 -0
  285. package/dist/lib/util/OptionalDeps.js +23 -0
  286. package/dist/lib/util/OptionalDeps.js.map +1 -0
  287. package/dist/lib/util/RecoverableRepoUtils.js +42 -0
  288. package/dist/lib/util/RecoverableRepoUtils.js.map +1 -0
  289. package/dist/lib/util/index.js +17 -0
  290. package/dist/lib/util/index.js.map +1 -0
  291. package/dist/types/blob/BlobStore.d.ts +54 -0
  292. package/dist/types/blob/LocalFsBlobStore.d.ts +23 -0
  293. package/dist/types/blob/index.d.ts +2 -0
  294. package/dist/types/index.d.ts +7 -0
  295. package/dist/types/jobs/AttachmentExtractionJob.d.ts +34 -0
  296. package/dist/types/jobs/CalendarReminderJob.d.ts +37 -0
  297. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +26 -0
  298. package/dist/types/jobs/ExternalShareExpirationJob.d.ts +34 -0
  299. package/dist/types/jobs/MailboxQuotaRecalcJob.d.ts +44 -0
  300. package/dist/types/jobs/MeetingSchedulingJob.d.ts +32 -0
  301. package/dist/types/jobs/QuarantineRetentionJob.d.ts +27 -0
  302. package/dist/types/jobs/ScanQueueJob.d.ts +41 -0
  303. package/dist/types/jobs/SearchIndexJob.d.ts +37 -0
  304. package/dist/types/jobs/index.d.ts +9 -0
  305. package/dist/types/jobs/mongo/AttachmentExtractionJobMongo.d.ts +6 -0
  306. package/dist/types/jobs/mongo/CalendarReminderJobMongo.d.ts +5 -0
  307. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +5 -0
  308. package/dist/types/jobs/mongo/ExternalShareExpirationJobMongo.d.ts +5 -0
  309. package/dist/types/jobs/mongo/MailboxQuotaRecalcJobMongo.d.ts +7 -0
  310. package/dist/types/jobs/mongo/MeetingSchedulingJobMongo.d.ts +5 -0
  311. package/dist/types/jobs/mongo/QuarantineRetentionJobMongo.d.ts +5 -0
  312. package/dist/types/jobs/mongo/ScanQueueJobMongo.d.ts +10 -0
  313. package/dist/types/jobs/mongo/SearchIndexJobMongo.d.ts +6 -0
  314. package/dist/types/jobs/mongo/index.d.ts +9 -0
  315. package/dist/types/jobs/mongo.d.ts +1 -0
  316. package/dist/types/jobs/sql/AttachmentExtractionJobSQL.d.ts +6 -0
  317. package/dist/types/jobs/sql/CalendarReminderJobSQL.d.ts +5 -0
  318. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +5 -0
  319. package/dist/types/jobs/sql/ExternalShareExpirationJobSQL.d.ts +5 -0
  320. package/dist/types/jobs/sql/MailboxQuotaRecalcJobSQL.d.ts +7 -0
  321. package/dist/types/jobs/sql/MeetingSchedulingJobSQL.d.ts +5 -0
  322. package/dist/types/jobs/sql/QuarantineRetentionJobSQL.d.ts +5 -0
  323. package/dist/types/jobs/sql/ScanQueueJobSQL.d.ts +10 -0
  324. package/dist/types/jobs/sql/SearchIndexJobSQL.d.ts +6 -0
  325. package/dist/types/jobs/sql/index.d.ts +9 -0
  326. package/dist/types/jobs/sql.d.ts +1 -0
  327. package/dist/types/models/index.d.ts +1 -0
  328. package/dist/types/models/mongo/AttachmentMongo.d.ts +22 -0
  329. package/dist/types/models/mongo/CalendarEventMongo.d.ts +28 -0
  330. package/dist/types/models/mongo/CalendarShareLinkMongo.d.ts +16 -0
  331. package/dist/types/models/mongo/ContactListMongo.d.ts +13 -0
  332. package/dist/types/models/mongo/ContactMongo.d.ts +25 -0
  333. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +18 -0
  334. package/dist/types/models/mongo/FolderMongo.d.ts +18 -0
  335. package/dist/types/models/mongo/IngestQueueEntryMongo.d.ts +17 -0
  336. package/dist/types/models/mongo/MailboxMongo.d.ts +18 -0
  337. package/dist/types/models/mongo/MessageMongo.d.ts +29 -0
  338. package/dist/types/models/mongo/NoteMongo.d.ts +16 -0
  339. package/dist/types/models/mongo/QuarantineEntryMongo.d.ts +18 -0
  340. package/dist/types/models/mongo/ScanResultMongo.d.ts +23 -0
  341. package/dist/types/models/mongo/SearchIndexStateMongo.d.ts +16 -0
  342. package/dist/types/models/mongo/TaskMongo.d.ts +19 -0
  343. package/dist/types/models/mongo/index.d.ts +15 -0
  344. package/dist/types/models/mongo.d.ts +1 -0
  345. package/dist/types/models/sql/AttachmentSQL.d.ts +22 -0
  346. package/dist/types/models/sql/CalendarEventSQL.d.ts +28 -0
  347. package/dist/types/models/sql/CalendarShareLinkSQL.d.ts +16 -0
  348. package/dist/types/models/sql/ContactListSQL.d.ts +13 -0
  349. package/dist/types/models/sql/ContactSQL.d.ts +25 -0
  350. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +18 -0
  351. package/dist/types/models/sql/FolderSQL.d.ts +18 -0
  352. package/dist/types/models/sql/IngestQueueEntrySQL.d.ts +17 -0
  353. package/dist/types/models/sql/MailboxSQL.d.ts +18 -0
  354. package/dist/types/models/sql/MessageSQL.d.ts +29 -0
  355. package/dist/types/models/sql/NoteSQL.d.ts +16 -0
  356. package/dist/types/models/sql/QuarantineEntrySQL.d.ts +18 -0
  357. package/dist/types/models/sql/ScanResultSQL.d.ts +23 -0
  358. package/dist/types/models/sql/SearchIndexStateSQL.d.ts +16 -0
  359. package/dist/types/models/sql/TaskSQL.d.ts +19 -0
  360. package/dist/types/models/sql/index.d.ts +15 -0
  361. package/dist/types/models/sql.d.ts +1 -0
  362. package/dist/types/models/types.d.ts +541 -0
  363. package/dist/types/mongo.d.ts +3 -0
  364. package/dist/types/push/MailPushRoute.d.ts +37 -0
  365. package/dist/types/push/index.d.ts +1 -0
  366. package/dist/types/routes/BaseAttachmentRoute.d.ts +24 -0
  367. package/dist/types/routes/BaseCalendarShareLinkRoute.d.ts +35 -0
  368. package/dist/types/routes/BaseFolderRoute.d.ts +58 -0
  369. package/dist/types/routes/BaseMailIngestRoute.d.ts +47 -0
  370. package/dist/types/routes/BaseMailboxRoute.d.ts +60 -0
  371. package/dist/types/routes/BaseMessageRoute.d.ts +27 -0
  372. package/dist/types/routes/BaseScopedChildRoute.d.ts +61 -0
  373. package/dist/types/routes/BaseSearchRoute.d.ts +23 -0
  374. package/dist/types/routes/index.d.ts +8 -0
  375. package/dist/types/routes/mongo/AttachmentRouteMongo.d.ts +6 -0
  376. package/dist/types/routes/mongo/CalendarEventRouteMongo.d.ts +6 -0
  377. package/dist/types/routes/mongo/CalendarShareLinkRouteMongo.d.ts +6 -0
  378. package/dist/types/routes/mongo/ContactListRouteMongo.d.ts +6 -0
  379. package/dist/types/routes/mongo/ContactRouteMongo.d.ts +6 -0
  380. package/dist/types/routes/mongo/FolderRouteMongo.d.ts +5 -0
  381. package/dist/types/routes/mongo/IngestQueueRouteMongo.d.ts +13 -0
  382. package/dist/types/routes/mongo/MailIngestRouteMongo.d.ts +6 -0
  383. package/dist/types/routes/mongo/MailboxRouteMongo.d.ts +8 -0
  384. package/dist/types/routes/mongo/MessageRouteMongo.d.ts +6 -0
  385. package/dist/types/routes/mongo/NoteRouteMongo.d.ts +6 -0
  386. package/dist/types/routes/mongo/QuarantineRouteMongo.d.ts +15 -0
  387. package/dist/types/routes/mongo/SearchRouteMongo.d.ts +5 -0
  388. package/dist/types/routes/mongo/TaskRouteMongo.d.ts +6 -0
  389. package/dist/types/routes/mongo/index.d.ts +14 -0
  390. package/dist/types/routes/mongo.d.ts +1 -0
  391. package/dist/types/routes/sql/AttachmentRouteSQL.d.ts +6 -0
  392. package/dist/types/routes/sql/CalendarEventRouteSQL.d.ts +6 -0
  393. package/dist/types/routes/sql/CalendarShareLinkRouteSQL.d.ts +6 -0
  394. package/dist/types/routes/sql/ContactListRouteSQL.d.ts +6 -0
  395. package/dist/types/routes/sql/ContactRouteSQL.d.ts +6 -0
  396. package/dist/types/routes/sql/FolderRouteSQL.d.ts +5 -0
  397. package/dist/types/routes/sql/IngestQueueRouteSQL.d.ts +9 -0
  398. package/dist/types/routes/sql/MailIngestRouteSQL.d.ts +23 -0
  399. package/dist/types/routes/sql/MailboxRouteSQL.d.ts +22 -0
  400. package/dist/types/routes/sql/MessageRouteSQL.d.ts +6 -0
  401. package/dist/types/routes/sql/NoteRouteSQL.d.ts +6 -0
  402. package/dist/types/routes/sql/QuarantineRouteSQL.d.ts +9 -0
  403. package/dist/types/routes/sql/SearchRouteSQL.d.ts +5 -0
  404. package/dist/types/routes/sql/TaskRouteSQL.d.ts +6 -0
  405. package/dist/types/routes/sql/index.d.ts +14 -0
  406. package/dist/types/routes/sql.d.ts +1 -0
  407. package/dist/types/scan/AvScanProvider.d.ts +22 -0
  408. package/dist/types/scan/ClamAvScanProvider.d.ts +17 -0
  409. package/dist/types/scan/RspamdSpamScanProvider.d.ts +16 -0
  410. package/dist/types/scan/ScanPipeline.d.ts +53 -0
  411. package/dist/types/scan/SpamScanProvider.d.ts +29 -0
  412. package/dist/types/scan/index.d.ts +5 -0
  413. package/dist/types/search/MongoTextSearchProvider.d.ts +22 -0
  414. package/dist/types/search/OpenSearchProvider.d.ts +24 -0
  415. package/dist/types/search/PostgresFullTextSearchProvider.d.ts +21 -0
  416. package/dist/types/search/SearchProvider.d.ts +62 -0
  417. package/dist/types/search/extraction/DocxTextExtractor.d.ts +14 -0
  418. package/dist/types/search/extraction/ExtractorRegistry.d.ts +19 -0
  419. package/dist/types/search/extraction/HtmlTextExtractor.d.ts +11 -0
  420. package/dist/types/search/extraction/PdfTextExtractor.d.ts +10 -0
  421. package/dist/types/search/extraction/PlainTextExtractor.d.ts +10 -0
  422. package/dist/types/search/extraction/TextExtractor.d.ts +17 -0
  423. package/dist/types/search/index.d.ts +10 -0
  424. package/dist/types/sql.d.ts +3 -0
  425. package/dist/types/transport/MTAIngestAdapter.d.ts +25 -0
  426. package/dist/types/transport/MailTransport.d.ts +29 -0
  427. package/dist/types/transport/PostfixSendmailTransport.d.ts +15 -0
  428. package/dist/types/transport/index.d.ts +3 -0
  429. package/dist/types/util/FolderUtils.d.ts +15 -0
  430. package/dist/types/util/MailSendUtils.d.ts +19 -0
  431. package/dist/types/util/MailboxScopeUtils.d.ts +19 -0
  432. package/dist/types/util/OptionalDeps.d.ts +9 -0
  433. package/dist/types/util/RecoverableRepoUtils.d.ts +24 -0
  434. package/dist/types/util/index.d.ts +12 -0
  435. package/package.json +122 -0
@@ -0,0 +1,163 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import { ObjectDecorators } from "@rapidrest/core";
15
+ import { BackgroundService, RepoUtils } from "@rapidrest/service-core";
16
+ import { RecoverableRepoUtils } from "../util/RecoverableRepoUtils.js";
17
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
18
+ /**
19
+ * Periodically recomputes `Mailbox.usedBytes` from the mailbox's actual stored content (message body blobs plus
20
+ * attachment content) and corrects any drift from the incremental updates other code paths (ingest, delete,
21
+ * etc.) are expected to apply on their own. This job is a safety net against drift, not the primary mechanism
22
+ * for keeping `usedBytes` accurate — it runs cheaply and idempotently, and only writes when the recomputed value
23
+ * actually differs from what's stored.
24
+ *
25
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`MailboxQuotaRecalcJobMongo`/
26
+ * `MailboxQuotaRecalcJobSQL`), following the same multi-entity-type generic pattern `ScanQueueJob` uses.
27
+ *
28
+ * @author Jean-Philippe Steinmetz
29
+ */
30
+ export class MailboxQuotaRecalcJob extends BackgroundService {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.scheduleExpr = "0 0 * * * *";
34
+ this.batchSize = 100;
35
+ }
36
+ get schedule() {
37
+ return this.scheduleExpr;
38
+ }
39
+ async init() {
40
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
41
+ name: this.mailboxClass.name,
42
+ args: [this.mailboxClass],
43
+ });
44
+ this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
45
+ name: this.messageClass.name,
46
+ args: [this.messageClass],
47
+ });
48
+ this.attachmentRepo = await this._objectFactory.newInstance(RepoUtils, {
49
+ name: this.attachmentClass.name,
50
+ args: [this.attachmentClass],
51
+ });
52
+ }
53
+ async start() {
54
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
55
+ }
56
+ stop() {
57
+ // Do nothing
58
+ }
59
+ async run() {
60
+ if (!this.mailboxRepo || !this.messageRepo || !this.attachmentRepo || !this.blobStore) {
61
+ return;
62
+ }
63
+ // No obvious "least-recently-recalculated" field exists on `Mailbox` to sort by, so this simply
64
+ // processes the first page each run - acceptable since this is cheap, idempotent drift-correction, not
65
+ // correctness-critical delivery.
66
+ //
67
+ // `limit` is passed both via `options` (which is all the Mongo backend of `RepoUtils.find()` actually
68
+ // reads) *and* baked into the query object itself (which is all `ModelUtils.buildSearchQuerySQL` reads
69
+ // - it ignores `options.limit` entirely and falls back to its own default of 100 otherwise). Confirmed
70
+ // by real-database testing: on the SQL backend, `options.limit` alone silently caps at 100 regardless
71
+ // of the configured batch size, rather than the requested value.
72
+ const mailboxes = await this.mailboxRepo.find({ limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
73
+ for (const mailbox of mailboxes) {
74
+ try {
75
+ await this.recalcMailbox(mailbox);
76
+ }
77
+ catch (err) {
78
+ this.logger?.error(`MailboxQuotaRecalcJob: failed to recalculate usedBytes for mailbox ${mailbox.uid}: ${err.message}`);
79
+ }
80
+ }
81
+ }
82
+ /**
83
+ * Fetches every page of `repo.find(criteria, ...)` results, not just the first. `RepoUtils.find()` caps at
84
+ * 100 rows per call by default (and SQL ignores `options.limit`/`options.page` entirely unless `limit`/
85
+ * `page` are *also* baked into the criteria object itself - see the comments on `run()`'s own `find()` call
86
+ * above for the full explanation) - a bare, unpaginated `find()` call here would silently recompute
87
+ * `usedBytes` from only the first ~100 messages/attachments, permanently understating usage for any mailbox
88
+ * larger than that. Confirmed as a real bug (not just a SQL-only quirk) by real-database testing: the
89
+ * previous unpaginated call truncated identically on both backends, since 100 is `RepoUtils.find()`'s own
90
+ * unconditional default, independent of which backend is in use.
91
+ */
92
+ async findAllPages(repo, criteria, pageSize = 500) {
93
+ const all = [];
94
+ for (let page = 0;; page++) {
95
+ const batch = await repo.find({ ...criteria, limit: pageSize, page }, { ignoreACL: true, limit: pageSize, page });
96
+ all.push(...batch);
97
+ if (batch.length < pageSize) {
98
+ break;
99
+ }
100
+ }
101
+ return all;
102
+ }
103
+ async recalcMailbox(mailbox) {
104
+ const messages = await this.findAllPages(this.messageRepo, { mailboxUid: mailbox.uid });
105
+ let usedBytes = 0;
106
+ for (const message of messages) {
107
+ try {
108
+ usedBytes += await this.blobStore.size(message.bodyBlobKey);
109
+ }
110
+ catch (err) {
111
+ this.logger?.warn(`MailboxQuotaRecalcJob: failed to size body blob ${message.bodyBlobKey} for message ${message.uid}, treating as 0 bytes: ${err.message}`);
112
+ }
113
+ if (message.hasAttachments) {
114
+ const attachments = await this.findAllPages(this.attachmentRepo, { messageUid: message.uid });
115
+ for (const attachment of attachments) {
116
+ usedBytes += attachment.sizeBytes;
117
+ }
118
+ }
119
+ }
120
+ // Skip the write entirely when nothing has drifted - this job runs frequently and most mailboxes won't
121
+ // have drifted, so avoiding a no-op update() call (and the version bump/push it would trigger) matters.
122
+ if (usedBytes !== mailbox.usedBytes) {
123
+ // `mailbox` came from `run()`'s own `find()` call, which - unlike `findOne()` - returns a plain,
124
+ // un-hydrated document on the Mongo backend rather than a real model instance. `RepoUtils.update()`
125
+ // branches its optimistic-lock check, version bump, and `dateModified` refresh entirely on an
126
+ // `instanceof BaseEntity` check, and silently skips all three (a confirmed real cross-backend bug,
127
+ // caught by real-database testing: the write still applies `usedBytes`, but leaves `version` and
128
+ // `dateModified` untouched) when that check comes back false. Re-fetching via `findOne()` right
129
+ // before the write - the same defensive pattern `ScanQueueJob` already uses for its own updates -
130
+ // sidesteps the bug and also guards against `mailbox`'s version having gone stale during this
131
+ // method's own async work above.
132
+ // The `?? mailbox` fallback only matters if the mailbox was deleted out from under this run between
133
+ // the initial `find()` and this `findOne()` a few lines later - unreachable in any realistic single
134
+ // -process test without artificially deleting the row mid-`run()` to win that race, so it's
135
+ // intentionally left uncovered rather than contrived.
136
+ const current = (await this.mailboxRepo.findOne(mailbox.uid, { ignoreACL: true })) ?? mailbox;
137
+ await this.mailboxRepo.update({ uid: current.uid, version: current.version, usedBytes }, current, { ignoreACL: true, skipPush: true });
138
+ }
139
+ }
140
+ }
141
+ __decorate([
142
+ Inject("BlobStore"),
143
+ __metadata("design:type", Object)
144
+ ], MailboxQuotaRecalcJob.prototype, "blobStore", void 0);
145
+ __decorate([
146
+ Config("mail:jobs:mailbox_quota_recalc:schedule", "0 0 * * * *"),
147
+ __metadata("design:type", String)
148
+ ], MailboxQuotaRecalcJob.prototype, "scheduleExpr", void 0);
149
+ __decorate([
150
+ Config("mail:jobs:mailbox_quota_recalc:batch_size", 100),
151
+ __metadata("design:type", Number)
152
+ ], MailboxQuotaRecalcJob.prototype, "batchSize", void 0);
153
+ __decorate([
154
+ Logger,
155
+ __metadata("design:type", Object)
156
+ ], MailboxQuotaRecalcJob.prototype, "logger", void 0);
157
+ __decorate([
158
+ Init,
159
+ __metadata("design:type", Function),
160
+ __metadata("design:paramtypes", []),
161
+ __metadata("design:returntype", Promise)
162
+ ], MailboxQuotaRecalcJob.prototype, "init", null);
163
+ //# sourceMappingURL=MailboxQuotaRecalcJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailboxQuotaRecalcJob.js","sourceRoot":"","sources":["../../../src/jobs/MailboxQuotaRecalcJob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import { ObjectDecorators } from "@rapidrest/core";
15
+ import { BackgroundService } from "@rapidrest/service-core";
16
+ import { RecoverableRepoUtils } from "../util/RecoverableRepoUtils.js";
17
+ const { Config, Init, Logger } = ObjectDecorators;
18
+ /**
19
+ * Scheduled placeholder for outbound iTIP meeting-request processing: an organizer creates/updates a
20
+ * `CalendarEvent` with attendees, and an iTIP `REQUEST` email needs to go out to each attendee.
21
+ *
22
+ * TODO: Full iTIP send/response processing is deferred pending (1) an `IcsUtils` helper for composing RFC 5546
23
+ * iTIP messages/ICS payloads, which does not exist in this codebase yet, and (2) a persisted `CalendarEvent`
24
+ * invite-tracking field (there is currently no way to tell "attendees were already invited" apart from
25
+ * "attendees were just added and haven't been invited yet"). Inventing either of those here would just have to
26
+ * be redone once the real implementations land. This job currently exists purely as a scheduled placeholder so
27
+ * the surrounding plumbing (config, DI, `jobs/index.ts` registration) is already in place when that follow-up
28
+ * work is ready - `run()` only finds and logs candidate events, it does not compose or send anything.
29
+ *
30
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`MeetingSchedulingJobMongo`/
31
+ * `MeetingSchedulingJobSQL`), following the same generic pattern `ScanQueueJob` uses.
32
+ *
33
+ * @author Jean-Philippe Steinmetz
34
+ */
35
+ export class MeetingSchedulingJob extends BackgroundService {
36
+ constructor() {
37
+ super(...arguments);
38
+ this.scheduleExpr = "0 */5 * * * *";
39
+ this.batchSize = 100;
40
+ }
41
+ get schedule() {
42
+ return this.scheduleExpr;
43
+ }
44
+ async init() {
45
+ this.calendarEventRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
46
+ name: this.calendarEventClass.name,
47
+ args: [this.calendarEventClass],
48
+ });
49
+ }
50
+ async start() {
51
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
52
+ }
53
+ stop() {
54
+ // Do nothing
55
+ }
56
+ async run() {
57
+ if (!this.calendarEventRepo) {
58
+ return;
59
+ }
60
+ // `attendees.length > 0` can't be pushed into the shared `find()` query DSL (array-length queries are
61
+ // backend-specific), so this fetches a batch and filters in-process, same as the other simplicity
62
+ // trade-offs this job's siblings make.
63
+ //
64
+ // `limit` must be passed both via `options` (used by the Mongo backend) *and* baked into the query
65
+ // object itself (all `ModelUtils.buildSearchQuerySQL` reads - it ignores `options.limit` entirely and
66
+ // falls back to its own default of 100 otherwise). Confirmed by real-database testing: on the SQL
67
+ // backend, `options.limit` alone silently caps at 100 regardless of the configured batch size.
68
+ const candidates = await this.calendarEventRepo.find({ limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
69
+ let needingInvite = 0;
70
+ for (const event of candidates) {
71
+ try {
72
+ if (event.attendees && event.attendees.length > 0) {
73
+ needingInvite++;
74
+ }
75
+ }
76
+ catch (err) {
77
+ /* v8 ignore next -- unreachable via real data: `event.attendees` round-trips through Mongo
78
+ BSON or a SQL `simple-json` column as a plain array or is absent entirely; no real write
79
+ path can produce a value that survives `find()` yet throws on `&&`/`.length` here. */
80
+ this.logger?.warn(`MeetingSchedulingJob: failed to inspect event ${event.uid}: ${err.message}`);
81
+ }
82
+ }
83
+ // TODO: Once `IcsUtils` and an invite-tracking field exist, replace this log line with actual iTIP
84
+ // REQUEST composition/send per candidate event (and mark each as invited so it isn't re-selected).
85
+ this.logger?.debug(`MeetingSchedulingJob: found ${needingInvite} candidate event(s) with attendees needing outbound iTIP processing (not yet implemented).`);
86
+ }
87
+ }
88
+ __decorate([
89
+ Config("mail:jobs:meeting_scheduling:schedule", "0 */5 * * * *"),
90
+ __metadata("design:type", String)
91
+ ], MeetingSchedulingJob.prototype, "scheduleExpr", void 0);
92
+ __decorate([
93
+ Config("mail:jobs:meeting_scheduling:batch_size", 100),
94
+ __metadata("design:type", Number)
95
+ ], MeetingSchedulingJob.prototype, "batchSize", void 0);
96
+ __decorate([
97
+ Logger,
98
+ __metadata("design:type", Object)
99
+ ], MeetingSchedulingJob.prototype, "logger", void 0);
100
+ __decorate([
101
+ Init,
102
+ __metadata("design:type", Function),
103
+ __metadata("design:paramtypes", []),
104
+ __metadata("design:returntype", Promise)
105
+ ], MeetingSchedulingJob.prototype, "init", null);
106
+ //# sourceMappingURL=MeetingSchedulingJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeetingSchedulingJob.js","sourceRoot":"","sources":["../../../src/jobs/MeetingSchedulingJob.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAiB,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAgB,oBAA+C,SAAQ,iBAAiB;IAA9F;;QASY,iBAAY,GAAW,eAAe,CAAC;QAGvC,cAAS,GAAW,GAAG,CAAC;IA+DpC,CAAC;IA1DG,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAGY,AAAN,KAAK,CAAC,IAAI;QACb,IAAI,CAAC,iBAAiB,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,oBAAoB,EAAE;YAClF,IAAI,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI;YAClC,IAAI,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC;SAClC,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,KAAK;QACd,0FAA0F;IAC9F,CAAC;IAEM,IAAI;QACP,aAAa;IACjB,CAAC;IAEM,KAAK,CAAC,GAAG;QACZ,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC1B,OAAO;QACX,CAAC;QAED,sGAAsG;QACtG,kGAAkG;QAClG,uCAAuC;QACvC,EAAE;QACF,mGAAmG;QACnG,sGAAsG;QACtG,kGAAkG;QAClG,+FAA+F;QAC/F,MAAM,UAAU,GAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CACtD,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAS,EAChC,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAC7C,CAAC;QAEF,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChD,aAAa,EAAE,CAAC;gBACpB,CAAC;YACL,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAChB;;wGAEwF;gBACxF,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,iDAAiD,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACpG,CAAC;QACL,CAAC;QAED,mGAAmG;QACnG,mGAAmG;QACnG,IAAI,CAAC,MAAM,EAAE,KAAK,CACd,+BAA+B,aAAa,4FAA4F,CAC3I,CAAC;IACN,CAAC;CACJ;AAlEW;IADP,MAAM,CAAC,uCAAuC,EAAE,eAAe,CAAC;;0DAClB;AAGvC;IADP,MAAM,CAAC,yCAAyC,EAAE,GAAG,CAAC;;uDACvB;AAGxB;IADP,MAAM;;oDACa;AAOP;IADZ,IAAI;;;;gDAMJ"}
@@ -0,0 +1,97 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import { ObjectDecorators } from "@rapidrest/core";
15
+ import { BackgroundService, RepoUtils } from "@rapidrest/service-core";
16
+ const { Config, Init, Logger } = ObjectDecorators;
17
+ /**
18
+ * Purges `QuarantineEntry` rows (and, once `BlobStore` deletion is wired in here, their `rawBlobKey` payload)
19
+ * once they've sat in quarantine longer than `retention_days`, whether or not they were ever released. A
20
+ * released entry is retained for the same window as an unreleased one — this job doesn't distinguish between
21
+ * them, since both represent quarantine history an operator may want to review for that same window.
22
+ *
23
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`QuarantineRetentionJobMongo`/
24
+ * `QuarantineRetentionJobSQL`), following the same generic pattern `ScanQueueJob` uses.
25
+ *
26
+ * @author Jean-Philippe Steinmetz
27
+ */
28
+ export class QuarantineRetentionJob extends BackgroundService {
29
+ constructor() {
30
+ super(...arguments);
31
+ this.scheduleExpr = "0 0 6 * * *";
32
+ this.batchSize = 500;
33
+ this.retentionDays = 30;
34
+ }
35
+ get schedule() {
36
+ return this.scheduleExpr;
37
+ }
38
+ async init() {
39
+ this.quarantineEntryRepo = await this._objectFactory.newInstance(RepoUtils, {
40
+ name: this.quarantineEntryClass.name,
41
+ args: [this.quarantineEntryClass],
42
+ });
43
+ }
44
+ async start() {
45
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
46
+ }
47
+ stop() {
48
+ // Do nothing
49
+ }
50
+ async run() {
51
+ if (!this.quarantineEntryRepo) {
52
+ return;
53
+ }
54
+ const cutoff = new Date(Date.now() - this.retentionDays * 24 * 60 * 60 * 1000);
55
+ // `ModelUtils.buildSearchQuery` supports single-sided `lt(...)` comparisons with correct Date
56
+ // coercion on both backends, so retention age is pushed directly into the query rather than fetched
57
+ // and filtered in-process.
58
+ //
59
+ // `limit` is passed both via `options` (all the Mongo backend of `RepoUtils.find()` actually reads)
60
+ // *and* baked into the query object itself (all `ModelUtils.buildSearchQuerySQL` reads - it ignores
61
+ // `options.limit` entirely and falls back to its own default of 100 otherwise). Confirmed by
62
+ // real-database testing: on the SQL backend, `options.limit` alone silently caps at 100 regardless of
63
+ // the configured batch size.
64
+ const expired = await this.quarantineEntryRepo.find({ dateCreated: `lt(${cutoff.toISOString()})`, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
65
+ for (const entry of expired) {
66
+ try {
67
+ await this.quarantineEntryRepo.delete(entry.uid, { ignoreACL: true, purge: true });
68
+ }
69
+ catch (err) {
70
+ this.logger?.warn(`QuarantineRetentionJob: failed to purge quarantine entry ${entry.uid}: ${err.message}`);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ __decorate([
76
+ Config("mail:jobs:quarantine_retention:schedule", "0 0 6 * * *"),
77
+ __metadata("design:type", String)
78
+ ], QuarantineRetentionJob.prototype, "scheduleExpr", void 0);
79
+ __decorate([
80
+ Config("mail:jobs:quarantine_retention:batch_size", 500),
81
+ __metadata("design:type", Number)
82
+ ], QuarantineRetentionJob.prototype, "batchSize", void 0);
83
+ __decorate([
84
+ Config("mail:jobs:quarantine_retention:retention_days", 30),
85
+ __metadata("design:type", Number)
86
+ ], QuarantineRetentionJob.prototype, "retentionDays", void 0);
87
+ __decorate([
88
+ Logger,
89
+ __metadata("design:type", Object)
90
+ ], QuarantineRetentionJob.prototype, "logger", void 0);
91
+ __decorate([
92
+ Init,
93
+ __metadata("design:type", Function),
94
+ __metadata("design:paramtypes", []),
95
+ __metadata("design:returntype", Promise)
96
+ ], QuarantineRetentionJob.prototype, "init", null);
97
+ //# sourceMappingURL=QuarantineRetentionJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuarantineRetentionJob.js","sourceRoot":"","sources":["../../../src/jobs/QuarantineRetentionJob.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAiB,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEtF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,OAAgB,sBAAkD,SAAQ,iBAAiB;IAAjG;;QASY,iBAAY,GAAW,aAAa,CAAC;QAGrC,cAAS,GAAW,GAAG,CAAC;QAGxB,kBAAa,GAAW,EAAE,CAAC;IAsDvC,CAAC;IAjDG,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAGY,AAAN,KAAK,CAAC,IAAI;QACb,IAAI,CAAC,mBAAmB,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,SAAS,EAAE;YACzE,IAAI,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI;YACpC,IAAI,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC;SACpC,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,KAAK;QACd,0FAA0F;IAC9F,CAAC;IAEM,IAAI;QACP,aAAa;IACjB,CAAC;IAEM,KAAK,CAAC,GAAG;QACZ,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC5B,OAAO;QACX,CAAC;QAED,MAAM,MAAM,GAAS,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAErF,8FAA8F;QAC9F,oGAAoG;QACpG,2BAA2B;QAC3B,EAAE;QACF,oGAAoG;QACpG,oGAAoG;QACpG,6FAA6F;QAC7F,sGAAsG;QACtG,6BAA6B;QAC7B,MAAM,OAAO,GAAQ,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CACpD,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAS,EAC5E,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAC7C,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACvF,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,4DAA4D,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/G,CAAC;QACL,CAAC;IACL,CAAC;CACJ;AA5DW;IADP,MAAM,CAAC,yCAAyC,EAAE,aAAa,CAAC;;4DACpB;AAGrC;IADP,MAAM,CAAC,2CAA2C,EAAE,GAAG,CAAC;;yDACzB;AAGxB;IADP,MAAM,CAAC,+CAA+C,EAAE,EAAE,CAAC;;6DACzB;AAG3B;IADP,MAAM;;sDACa;AAOP;IADZ,IAAI;;;;kDAMJ"}
@@ -0,0 +1,223 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import * as crypto from "crypto";
15
+ import { ObjectDecorators } from "@rapidrest/core";
16
+ import { BackgroundService, NotificationUtils, RepoUtils } from "@rapidrest/service-core";
17
+ import { resolveDeliveryVerdict, ScanPipeline } from "../scan/ScanPipeline.js";
18
+ import { findOrCreateWellKnownFolder } from "../util/FolderUtils.js";
19
+ import { RecoverableRepoUtils } from "../util/RecoverableRepoUtils.js";
20
+ import { AvVerdict, FolderType, IngestStatus, MessageImportance, QuarantineReason, RecipientType, ScanTargetType, } from "../models/types.js";
21
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
22
+ /**
23
+ * Drains `IngestQueueEntry` rows staged by `BaseMailIngestRoute`: runs the `ScanPipeline` against each one's
24
+ * raw message, then either delivers it to the mailbox's Inbox, files it in Junk, or holds it in
25
+ * `QuarantineEntry` — depending on `resolveDeliveryVerdict()`. No client protocol (webmail/EAS/MAPI) ever sees
26
+ * a message before this job has processed it.
27
+ *
28
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`ScanQueueJobMongo`/`ScanQueueJobSQL`),
29
+ * following the same multi-entity-type generic pattern `DefaultAccounts` uses.
30
+ *
31
+ * @author Jean-Philippe Steinmetz
32
+ */
33
+ export class ScanQueueJob extends BackgroundService {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.scheduleExpr = "*/10 * * * * *";
37
+ this.batchSize = 25;
38
+ }
39
+ get schedule() {
40
+ return this.scheduleExpr;
41
+ }
42
+ async init() {
43
+ this.ingestQueueRepo = await this._objectFactory.newInstance(RepoUtils, {
44
+ name: this.ingestQueueClass.name,
45
+ args: [this.ingestQueueClass],
46
+ });
47
+ this.folderRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
48
+ name: this.folderClass.name,
49
+ args: [this.folderClass],
50
+ });
51
+ this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
52
+ name: this.messageClass.name,
53
+ args: [this.messageClass],
54
+ });
55
+ this.attachmentRepo = await this._objectFactory.newInstance(RepoUtils, {
56
+ name: this.attachmentClass.name,
57
+ args: [this.attachmentClass],
58
+ });
59
+ this.quarantineEntryRepo = await this._objectFactory.newInstance(RepoUtils, {
60
+ name: this.quarantineEntryClass.name,
61
+ args: [this.quarantineEntryClass],
62
+ });
63
+ this.scanResultRepo = await this._objectFactory.newInstance(RepoUtils, {
64
+ name: this.scanResultClass.name,
65
+ args: [this.scanResultClass],
66
+ });
67
+ }
68
+ async start() {
69
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
70
+ }
71
+ stop() {
72
+ // Do nothing
73
+ }
74
+ async run() {
75
+ // `limit` must be passed both via `options` (used by the Mongo backend) *and* baked into the query
76
+ // object itself (all `ModelUtils.buildSearchQuerySQL` reads - it ignores `options.limit` entirely and
77
+ // falls back to its own default of 100 otherwise). Confirmed by real-database testing: on the SQL
78
+ // backend, `options.limit` alone silently caps at 100 regardless of the configured batch size.
79
+ const pending = await this.ingestQueueRepo.find({ status: IngestStatus.PENDING, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
80
+ for (const entry of pending) {
81
+ try {
82
+ await this.processEntry(entry);
83
+ }
84
+ catch (err) {
85
+ this.logger?.error(`ScanQueueJob: failed to process ingest entry ${entry.uid}: ${err.message}`);
86
+ // `entry` may be stale: `processEntry()` may have already bumped this row to SCANNING (and thus
87
+ // its persisted version) before failing partway through. Updating against that stale version
88
+ // would optimistically-lock-mismatch and silently affect zero rows on some backends, leaving
89
+ // the entry stuck at SCANNING forever instead of FAILED - re-fetch the current row first.
90
+ const current = (await this.ingestQueueRepo.findOne(entry.uid, { ignoreACL: true })) ?? entry;
91
+ await this.ingestQueueRepo.update({ uid: entry.uid, version: current.version, status: IngestStatus.FAILED, errorMessage: err.message }, current, { ignoreACL: true });
92
+ }
93
+ }
94
+ }
95
+ async processEntry(entry) {
96
+ await this.ingestQueueRepo.update({ uid: entry.uid, version: entry.version, status: IngestStatus.SCANNING }, entry, { ignoreACL: true });
97
+ // `entry` is now stale (its `version` no longer matches the persisted row) — re-fetch before the next
98
+ // optimistic-locked update rather than reusing the pre-update snapshot.
99
+ const scanning = (await this.ingestQueueRepo.findOne(entry.uid, { ignoreACL: true }));
100
+ const raw = await this.blobStore.get(entry.rawBlobKey);
101
+ const result = await this.scanPipeline.run(raw, {
102
+ from: entry.envelopeFrom,
103
+ to: entry.envelopeTo,
104
+ });
105
+ const verdict = resolveDeliveryVerdict(result);
106
+ // The `Message`/`QuarantineEntry` this scan is *for* doesn't exist yet, and `ScanResult.targetUid`
107
+ // needs to reference it - pre-generating the target's uid here (rather than letting `create()` mint
108
+ // one) breaks that chicken-and-egg ordering: the target entity is then created *with* this exact uid
109
+ // (BaseEntity's constructor honors an explicitly supplied `uid`), so both records can reference each
110
+ // other correctly regardless of which is actually persisted first.
111
+ const targetUid = crypto.randomUUID();
112
+ const scanResult = await this.scanResultRepo.create(new this.scanResultClass({
113
+ targetType: ScanTargetType.MESSAGE,
114
+ targetUid,
115
+ spamScore: result.spam.score,
116
+ spamVerdict: result.spam.verdict,
117
+ spamSymbols: result.spam.symbols,
118
+ avVerdict: result.av.verdict,
119
+ avSignatureName: result.av.signatureName,
120
+ scannedAt: new Date(),
121
+ providerVersions: {},
122
+ }), { ignoreACL: true });
123
+ if (verdict === "quarantine") {
124
+ await this.quarantineEntryRepo.create(new this.quarantineEntryClass({
125
+ uid: targetUid,
126
+ mailboxUid: entry.mailboxUid,
127
+ reason: result.av.verdict === AvVerdict.INFECTED ? QuarantineReason.INFECTED : QuarantineReason.OTHER,
128
+ scanResultUid: scanResult.uid,
129
+ rawBlobKey: entry.rawBlobKey,
130
+ }), { ignoreACL: true });
131
+ }
132
+ else {
133
+ const folder = await findOrCreateWellKnownFolder(this.folderRepo, this.folderClass, entry.mailboxUid, verdict === "junk" ? FolderType.JUNK : FolderType.INBOX);
134
+ // `ScanPipeline.run()`'s sanitized HTML (script/active-content stripped) is stored under its own
135
+ // blob key, separate from `bodyBlobKey`'s raw MIME - `bodyBlobKey` must stay exactly what was
136
+ // ingested/sent (the send path and any future "view original" feature need the untouched bytes),
137
+ // so the sanitization pass would otherwise be computed and then silently discarded with no
138
+ // consumer ever able to read it, leaving the only body representation this library persists
139
+ // completely unsanitized.
140
+ let sanitizedHtmlBlobKey;
141
+ if (result.sanitizedHtml !== undefined) {
142
+ sanitizedHtmlBlobKey = `sanitized/${crypto.randomUUID()}`;
143
+ await this.blobStore.put(sanitizedHtmlBlobKey, Buffer.from(result.sanitizedHtml, "utf-8"), {
144
+ contentType: "text/html",
145
+ });
146
+ }
147
+ const message = await this.messageRepo.create(new this.messageClass({
148
+ uid: targetUid,
149
+ folderUid: folder.uid,
150
+ mailboxUid: entry.mailboxUid,
151
+ messageId: crypto.randomUUID(),
152
+ subject: "",
153
+ from: { address: entry.envelopeFrom, type: RecipientType.TO },
154
+ recipients: entry.envelopeTo.map((address) => ({ address, type: RecipientType.TO })),
155
+ sentDate: new Date(),
156
+ receivedDate: new Date(),
157
+ bodyBlobKey: entry.rawBlobKey,
158
+ sanitizedHtmlBlobKey,
159
+ bodyPreview: "",
160
+ flags: { read: false, flagged: false, answered: false, forwarded: false },
161
+ importance: MessageImportance.NORMAL,
162
+ references: [],
163
+ hasAttachments: result.attachments.length > 0,
164
+ scanResultUid: scanResult.uid,
165
+ }), { ignoreACL: true });
166
+ this.notificationUtils?.sendMessage(folder.uid, this.messageClass.name, "create", message);
167
+ for (const attachment of result.attachments) {
168
+ const blobKey = `attachments/${crypto.randomUUID()}`;
169
+ await this.blobStore.put(blobKey, attachment.content, { contentType: attachment.contentType });
170
+ await this.attachmentRepo.create(new this.attachmentClass({
171
+ messageUid: message.uid,
172
+ folderUid: folder.uid,
173
+ mailboxUid: entry.mailboxUid,
174
+ filename: attachment.filename ?? "attachment",
175
+ mimeType: attachment.contentType,
176
+ sizeBytes: attachment.content.length,
177
+ blobKey,
178
+ contentId: attachment.contentId,
179
+ isInline: attachment.isInline,
180
+ }), { ignoreACL: true });
181
+ }
182
+ await this.folderRepo.update({
183
+ uid: folder.uid,
184
+ version: folder.version,
185
+ unreadCount: folder.unreadCount + 1,
186
+ totalCount: folder.totalCount + 1,
187
+ syncKeyVersion: folder.syncKeyVersion + 1,
188
+ }, folder, { ignoreACL: true });
189
+ }
190
+ await this.ingestQueueRepo.update({ uid: scanning.uid, version: scanning.version, status: IngestStatus.DELIVERED }, scanning, { ignoreACL: true });
191
+ }
192
+ }
193
+ __decorate([
194
+ Inject("BlobStore"),
195
+ __metadata("design:type", Object)
196
+ ], ScanQueueJob.prototype, "blobStore", void 0);
197
+ __decorate([
198
+ Inject(ScanPipeline),
199
+ __metadata("design:type", ScanPipeline)
200
+ ], ScanQueueJob.prototype, "scanPipeline", void 0);
201
+ __decorate([
202
+ Inject(NotificationUtils),
203
+ __metadata("design:type", NotificationUtils)
204
+ ], ScanQueueJob.prototype, "notificationUtils", void 0);
205
+ __decorate([
206
+ Config("mail:jobs:scan_queue:schedule", "*/10 * * * * *"),
207
+ __metadata("design:type", String)
208
+ ], ScanQueueJob.prototype, "scheduleExpr", void 0);
209
+ __decorate([
210
+ Config("mail:jobs:scan_queue:batch_size", 25),
211
+ __metadata("design:type", Number)
212
+ ], ScanQueueJob.prototype, "batchSize", void 0);
213
+ __decorate([
214
+ Logger,
215
+ __metadata("design:type", Object)
216
+ ], ScanQueueJob.prototype, "logger", void 0);
217
+ __decorate([
218
+ Init,
219
+ __metadata("design:type", Function),
220
+ __metadata("design:paramtypes", []),
221
+ __metadata("design:returntype", Promise)
222
+ ], ScanQueueJob.prototype, "init", null);
223
+ //# sourceMappingURL=ScanQueueJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScanQueueJob.js","sourceRoot":"","sources":["../../../src/jobs/ScanQueueJob.ts"],"names":[],"mappings":"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"}