@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,124 @@
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, RepoUtils } from "@rapidrest/service-core";
17
+ import { ExtractorRegistry } from "../search/extraction/ExtractorRegistry.js";
18
+ import { RecoverableRepoUtils } from "../util/RecoverableRepoUtils.js";
19
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
20
+ /**
21
+ * Runs `ExtractorRegistry` (PDF/DOCX/plain-text/HTML text extraction) over `Attachment` records that haven't
22
+ * been processed yet (`extractedTextBlobKey` unset), as a background job — never inline on ingestion, since
23
+ * extraction can be slow and must not block SMTP-ACK or webmail send/save-draft latency (see the architecture
24
+ * plan's rationale for this).
25
+ *
26
+ * Every eligible attachment gets `extractedTextBlobKey` set exactly once, even when nothing extractable was
27
+ * found (an empty-content blob) — this is what marks an attachment as "already attempted" so an unsupported
28
+ * MIME type (e.g. an image) isn't re-selected by this job forever. When extraction *does* produce non-empty
29
+ * text for an attachment whose parent `Message` was already search-indexed, this job clears that message's
30
+ * `searchIndexedAt` back to `undefined` so `SearchIndexJob` re-indexes it with the newly available text.
31
+ *
32
+ * @author Jean-Philippe Steinmetz
33
+ */
34
+ export class AttachmentExtractionJob extends BackgroundService {
35
+ constructor() {
36
+ super(...arguments);
37
+ this.extractorRegistry = new ExtractorRegistry();
38
+ this.scheduleExpr = "*/20 * * * * *";
39
+ this.batchSize = 25;
40
+ }
41
+ get schedule() {
42
+ return this.scheduleExpr;
43
+ }
44
+ async init() {
45
+ this.attachmentRepo = await this._objectFactory.newInstance(RepoUtils, {
46
+ name: this.attachmentClass.name,
47
+ args: [this.attachmentClass],
48
+ });
49
+ this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
50
+ name: this.messageClass.name,
51
+ args: [this.messageClass],
52
+ });
53
+ }
54
+ async start() {
55
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
56
+ }
57
+ stop() {
58
+ // Do nothing
59
+ }
60
+ async run() {
61
+ if (!this.attachmentRepo || !this.blobStore) {
62
+ return;
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 pending = await this.attachmentRepo.find({ extractedTextBlobKey: null, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
69
+ for (const attachment of pending) {
70
+ try {
71
+ await this.processAttachment(attachment);
72
+ }
73
+ catch (err) {
74
+ this.logger?.warn(`AttachmentExtractionJob: failed to process attachment ${attachment.uid}: ${err.message}`);
75
+ }
76
+ }
77
+ }
78
+ async processAttachment(attachment) {
79
+ const content = await this.blobStore.get(attachment.blobKey);
80
+ const text = await this.extractorRegistry.extract(attachment.mimeType, content);
81
+ const extractedTextBlobKey = `attachment-text/${crypto.randomUUID()}`;
82
+ await this.blobStore.put(extractedTextBlobKey, Buffer.from(text ?? "", "utf-8"), {
83
+ contentType: "text/plain",
84
+ });
85
+ await this.attachmentRepo.update({ uid: attachment.uid, version: attachment.version, extractedTextBlobKey }, attachment, { ignoreACL: true });
86
+ if (text && text.length > 0) {
87
+ const message = await this.messageRepo.findOne(attachment.messageUid, {
88
+ ignoreACL: true,
89
+ });
90
+ if (message?.searchIndexedAt) {
91
+ // Explicit `null`, not `undefined`: TypeORM's `Repository.update()` silently drops any
92
+ // property whose value is `undefined` from its generated `SET` clause, so on the SQL backend
93
+ // an `undefined` here would leave the persisted `searchIndexedAt` completely untouched (still
94
+ // reporting "already indexed") - a real, confirmed cross-backend bug caught by real-database
95
+ // testing. MongoDB's own `updateOne($set: ...)` happens to coerce either value to `null`
96
+ // equivalently, so `null` is correct there too.
97
+ await this.messageRepo.update({ uid: message.uid, version: message.version, searchIndexedAt: null }, message, { ignoreACL: true, skipPush: true });
98
+ }
99
+ }
100
+ }
101
+ }
102
+ __decorate([
103
+ Inject("BlobStore"),
104
+ __metadata("design:type", Object)
105
+ ], AttachmentExtractionJob.prototype, "blobStore", void 0);
106
+ __decorate([
107
+ Config("mail:jobs:attachment_extraction:schedule", "*/20 * * * * *"),
108
+ __metadata("design:type", String)
109
+ ], AttachmentExtractionJob.prototype, "scheduleExpr", void 0);
110
+ __decorate([
111
+ Config("mail:jobs:attachment_extraction:batch_size", 25),
112
+ __metadata("design:type", Number)
113
+ ], AttachmentExtractionJob.prototype, "batchSize", void 0);
114
+ __decorate([
115
+ Logger,
116
+ __metadata("design:type", Object)
117
+ ], AttachmentExtractionJob.prototype, "logger", void 0);
118
+ __decorate([
119
+ Init,
120
+ __metadata("design:type", Function),
121
+ __metadata("design:paramtypes", []),
122
+ __metadata("design:returntype", Promise)
123
+ ], AttachmentExtractionJob.prototype, "init", null);
124
+ //# sourceMappingURL=AttachmentExtractionJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttachmentExtractionJob.js","sourceRoot":"","sources":["../../../src/jobs/AttachmentExtractionJob.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAiB,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAgB,uBAAiE,SAAQ,iBAAiB;IAAhH;;QAaY,sBAAiB,GAAsB,IAAI,iBAAiB,EAAE,CAAC;QAG/D,iBAAY,GAAW,gBAAgB,CAAC;QAGxC,cAAS,GAAW,EAAE,CAAC;IAsFnC,CAAC;IAjFG,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAGY,AAAN,KAAK,CAAC,IAAI;QACb,IAAI,CAAC,cAAc,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,SAAS,EAAE;YACpE,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI;YAC/B,IAAI,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC;SAC/B,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,oBAAoB,EAAE;YAC5E,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI;YAC5B,IAAI,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;SAC5B,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,cAAc,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1C,OAAO;QACX,CAAC;QAED,mGAAmG;QACnG,sGAAsG;QACtG,kGAAkG;QAClG,+FAA+F;QAC/F,MAAM,OAAO,GAAQ,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAC/C,EAAE,oBAAoB,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAS,EAC5D,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAC7C,CAAC;QAEF,KAAK,MAAM,UAAU,IAAI,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,yDAAyD,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACjH,CAAC;QACL,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,UAAa;QACzC,MAAM,OAAO,GAAW,MAAM,IAAI,CAAC,SAAU,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACtE,MAAM,IAAI,GAAuB,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpG,MAAM,oBAAoB,GAAG,mBAAmB,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;QACtE,MAAM,IAAI,CAAC,SAAU,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE;YAC9E,WAAW,EAAE,YAAY;SAC5B,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,cAAe,CAAC,MAAM,CAC7B,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,OAAO,EAAG,UAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAS,EAC1F,UAAU,EACV,EAAE,SAAS,EAAE,IAAI,EAAE,CACtB,CAAC;QAEF,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAkB,MAAM,IAAI,CAAC,WAAY,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE;gBAClF,SAAS,EAAE,IAAI;aAClB,CAAC,CAAC;YACH,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC3B,uFAAuF;gBACvF,6FAA6F;gBAC7F,8FAA8F;gBAC9F,6FAA6F;gBAC7F,yFAAyF;gBACzF,gDAAgD;gBAChD,MAAM,IAAI,CAAC,WAAY,CAAC,MAAM,CAC1B,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAG,OAAe,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAS,EACrF,OAAO,EACP,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CACtC,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;CACJ;AA9FW;IADP,MAAM,CAAC,WAAW,CAAC;;0DACU;AAKtB;IADP,MAAM,CAAC,0CAA0C,EAAE,gBAAgB,CAAC;;6DACrB;AAGxC;IADP,MAAM,CAAC,4CAA4C,EAAE,EAAE,CAAC;;0DAC1B;AAGvB;IADP,MAAM;;uDACa;AAOP;IADZ,IAAI;;;;mDAUJ"}
@@ -0,0 +1,132 @@
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, NotificationUtils } from "@rapidrest/service-core";
16
+ import { RecoverableRepoUtils } from "../util/RecoverableRepoUtils.js";
17
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
18
+ /**
19
+ * Polls upcoming `CalendarEvent` records and dispatches a `"reminder"` push notification once each event's
20
+ * configured `reminderMinutesBeforeStart` fire time falls within this job's own polling window.
21
+ *
22
+ * `CalendarEvent` has no persisted "reminder already sent" flag, and adding one is out of scope here, so this
23
+ * job takes the simplest correct-enough approach: the query only prunes past events (`startDate` lower-bounded
24
+ * at `now`, via `ModelUtils.buildSearchQuery`'s single-sided `gte(...)` operator, which - unlike its two-sided
25
+ * `range(...)` operator - has confirmed correct Date coercion on both backends), and everything else (the
26
+ * upper lookahead bound, and the actual reminder-fire-time arithmetic) is computed/filtered in-process.
27
+ *
28
+ * KNOWN LIMITATION: without a persisted "reminder already sent" flag (e.g. a future `CalendarEvent.
29
+ * reminderSentAt`), a reminder could in principle fire more than once if this job's own polling window ever
30
+ * overlaps across runs (e.g. after a restart, or a run that took longer than `windowSeconds`). This is a
31
+ * deliberate simplification, not an oversight - flagged here for a future follow-up.
32
+ *
33
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`CalendarReminderJobMongo`/
34
+ * `CalendarReminderJobSQL`), following the same generic pattern `ScanQueueJob` uses.
35
+ *
36
+ * @author Jean-Philippe Steinmetz
37
+ */
38
+ export class CalendarReminderJob extends BackgroundService {
39
+ constructor() {
40
+ super(...arguments);
41
+ this.scheduleExpr = "0 * * * * *";
42
+ this.batchSize = 200;
43
+ this.windowSeconds = 60;
44
+ }
45
+ get schedule() {
46
+ return this.scheduleExpr;
47
+ }
48
+ async init() {
49
+ this.calendarEventRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
50
+ name: this.calendarEventClass.name,
51
+ args: [this.calendarEventClass],
52
+ });
53
+ }
54
+ async start() {
55
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
56
+ }
57
+ stop() {
58
+ // Do nothing
59
+ }
60
+ async run() {
61
+ if (!this.calendarEventRepo) {
62
+ return;
63
+ }
64
+ const now = new Date();
65
+ const windowEnd = new Date(now.getTime() + this.windowSeconds * 1000);
66
+ // A generous upper bound (24 hours) on candidates fetched per run - actual eligibility is decided below
67
+ // by comparing each candidate's computed reminder fire time against [now, windowEnd].
68
+ const lookaheadEnd = new Date(now.getTime() + 24 * 60 * 60 * 1000);
69
+ // `limit` must be passed both via `options` (used by the Mongo backend) *and* baked into the query
70
+ // object itself (all `ModelUtils.buildSearchQuerySQL` reads - it ignores `options.limit` entirely and
71
+ // falls back to its own default of 100 otherwise). Confirmed by real-database testing: on the SQL
72
+ // backend, `options.limit` alone silently caps at 100 regardless of the configured batch size - and
73
+ // since this job's own default `batch_size` (200) already exceeds that fallback, this was a live bug
74
+ // on every SQL deployment out of the box, not just a high-`batch_size` edge case.
75
+ //
76
+ // `sort: "startDate"` (ascending) ensures that when the candidate set IS truncated at `batchSize`, the
77
+ // soonest-due events are the ones kept - without it, truncation could arbitrarily drop an event whose
78
+ // reminder is due imminently in favor of one due much later, silently skipping (and, per this job's own
79
+ // documented "no persisted already-sent flag" limitation, never retrying) that reminder.
80
+ const candidates = await this.calendarEventRepo.find({ startDate: `gte(${now.toISOString()})`, limit: this.batchSize, sort: "startDate" }, { ignoreACL: true, limit: this.batchSize });
81
+ for (const event of candidates) {
82
+ try {
83
+ if (event.reminderMinutesBeforeStart === undefined || event.reminderMinutesBeforeStart === null) {
84
+ continue;
85
+ }
86
+ // The query already lower-bounds `startDate` at `now`; only the upper lookahead bound needs
87
+ // checking here.
88
+ if (event.startDate > lookaheadEnd) {
89
+ continue;
90
+ }
91
+ const fireAt = new Date(event.startDate.getTime() - event.reminderMinutesBeforeStart * 60 * 1000);
92
+ if (fireAt >= now && fireAt <= windowEnd) {
93
+ this.notificationUtils?.sendMessage([event.folderUid, event.mailboxUid], "CalendarEvent", "reminder", {
94
+ eventUid: event.uid,
95
+ title: event.title,
96
+ startDate: event.startDate,
97
+ });
98
+ }
99
+ }
100
+ catch (err) {
101
+ this.logger?.warn(`CalendarReminderJob: failed to process reminder for event ${event.uid}: ${err.message}`);
102
+ }
103
+ }
104
+ }
105
+ }
106
+ __decorate([
107
+ Inject(NotificationUtils),
108
+ __metadata("design:type", NotificationUtils)
109
+ ], CalendarReminderJob.prototype, "notificationUtils", void 0);
110
+ __decorate([
111
+ Config("mail:jobs:calendar_reminder:schedule", "0 * * * * *"),
112
+ __metadata("design:type", String)
113
+ ], CalendarReminderJob.prototype, "scheduleExpr", void 0);
114
+ __decorate([
115
+ Config("mail:jobs:calendar_reminder:batch_size", 200),
116
+ __metadata("design:type", Number)
117
+ ], CalendarReminderJob.prototype, "batchSize", void 0);
118
+ __decorate([
119
+ Config("mail:jobs:calendar_reminder:window_seconds", 60),
120
+ __metadata("design:type", Number)
121
+ ], CalendarReminderJob.prototype, "windowSeconds", void 0);
122
+ __decorate([
123
+ Logger,
124
+ __metadata("design:type", Object)
125
+ ], CalendarReminderJob.prototype, "logger", void 0);
126
+ __decorate([
127
+ Init,
128
+ __metadata("design:type", Function),
129
+ __metadata("design:paramtypes", []),
130
+ __metadata("design:returntype", Promise)
131
+ ], CalendarReminderJob.prototype, "init", null);
132
+ //# sourceMappingURL=CalendarReminderJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CalendarReminderJob.js","sourceRoot":"","sources":["../../../src/jobs/CalendarReminderJob.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,EAAE,iBAAiB,EAAiB,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAgB,mBAA8C,SAAQ,iBAAiB;IAA7F;;QAYY,iBAAY,GAAW,aAAa,CAAC;QAGrC,cAAS,GAAW,GAAG,CAAC;QAGxB,kBAAa,GAAW,EAAE,CAAC;IA4EvC,CAAC;IAvEG,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,MAAM,GAAG,GAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QAC5E,wGAAwG;QACxG,sFAAsF;QACtF,MAAM,YAAY,GAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAEzE,mGAAmG;QACnG,sGAAsG;QACtG,kGAAkG;QAClG,oGAAoG;QACpG,qGAAqG;QACrG,kFAAkF;QAClF,EAAE;QACF,uGAAuG;QACvG,sGAAsG;QACtG,wGAAwG;QACxG,yFAAyF;QACzF,MAAM,UAAU,GAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CACtD,EAAE,SAAS,EAAE,OAAO,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,EAAS,EAC3F,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAC7C,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,IAAI,KAAK,CAAC,0BAA0B,KAAK,SAAS,IAAI,KAAK,CAAC,0BAA0B,KAAK,IAAI,EAAE,CAAC;oBAC9F,SAAS;gBACb,CAAC;gBACD,4FAA4F;gBAC5F,iBAAiB;gBACjB,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;oBACjC,SAAS;gBACb,CAAC;gBAED,MAAM,MAAM,GAAS,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,0BAA0B,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBACxG,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;oBACvC,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,eAAe,EAAE,UAAU,EAAE;wBAClG,QAAQ,EAAE,KAAK,CAAC,GAAG;wBACnB,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC7B,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,6DAA6D,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;IACL,CAAC;CACJ;AArFW;IADP,MAAM,CAAC,iBAAiB,CAAC;8BACE,iBAAiB;8DAAC;AAGtC;IADP,MAAM,CAAC,sCAAsC,EAAE,aAAa,CAAC;;yDACjB;AAGrC;IADP,MAAM,CAAC,wCAAwC,EAAE,GAAG,CAAC;;sDACtB;AAGxB;IADP,MAAM,CAAC,4CAA4C,EAAE,EAAE,CAAC;;0DACtB;AAG3B;IADP,MAAM;;mDACa;AAOP;IADZ,IAAI;;;;+CAMJ"}
@@ -0,0 +1,101 @@
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
+ * Prunes `DeviceSyncState` rows for EAS devices that haven't synced in more than `device_ttl_days` days (or
19
+ * that have never successfully synced at all), keeping the collection from growing unbounded with abandoned
20
+ * device pairings.
21
+ *
22
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`EasDeviceStateCleanupJobMongo`/
23
+ * `EasDeviceStateCleanupJobSQL`), following the same generic pattern `ScanQueueJob` uses.
24
+ *
25
+ * @author Jean-Philippe Steinmetz
26
+ */
27
+ export class EasDeviceStateCleanupJob extends BackgroundService {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.scheduleExpr = "0 0 4 * * *";
31
+ this.batchSize = 500;
32
+ this.deviceTtlDays = 90;
33
+ }
34
+ get schedule() {
35
+ return this.scheduleExpr;
36
+ }
37
+ async init() {
38
+ this.deviceSyncStateRepo = await this._objectFactory.newInstance(RepoUtils, {
39
+ name: this.deviceSyncStateClass.name,
40
+ args: [this.deviceSyncStateClass],
41
+ });
42
+ }
43
+ async start() {
44
+ // Nothing to do at startup beyond `init()` above; processing happens entirely in `run()`.
45
+ }
46
+ stop() {
47
+ // Do nothing
48
+ }
49
+ async run() {
50
+ if (!this.deviceSyncStateRepo) {
51
+ return;
52
+ }
53
+ const cutoff = new Date(Date.now() - this.deviceTtlDays * 24 * 60 * 60 * 1000);
54
+ // `ModelUtils.buildSearchQuery` supports single-sided `lt(...)` comparisons with correct Date
55
+ // coercion on both backends, so the "stale" half of this job's criteria is pushed into the query. The
56
+ // "never synced at all" half (`lastSyncAt` unset) is a separate, plain-equality query rather than an
57
+ // `$or` of the two — `$or` support isn't confirmed identical across both backends' query builders, and
58
+ // a second bounded query is just as cheap for a low-frequency daily cleanup job.
59
+ //
60
+ // `limit` is passed both via `options` (all the Mongo backend of `RepoUtils.find()` actually reads)
61
+ // *and* baked into each query object (all `ModelUtils.buildSearchQuerySQL` reads - it ignores
62
+ // `options.limit` entirely and falls back to its own default of 100 otherwise). Confirmed by
63
+ // real-database testing: on the SQL backend, `options.limit` alone silently caps at 100 regardless of
64
+ // the configured batch size.
65
+ const [stale, neverSynced] = await Promise.all([
66
+ this.deviceSyncStateRepo.find({ lastSyncAt: `lt(${cutoff.toISOString()})`, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize }),
67
+ this.deviceSyncStateRepo.find({ lastSyncAt: null, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize }),
68
+ ]);
69
+ for (const row of [...stale, ...neverSynced]) {
70
+ try {
71
+ await this.deviceSyncStateRepo.delete(row.uid, { ignoreACL: true, purge: true });
72
+ }
73
+ catch (err) {
74
+ this.logger?.warn(`EasDeviceStateCleanupJob: failed to delete stale device sync state ${row.uid}: ${err.message}`);
75
+ }
76
+ }
77
+ }
78
+ }
79
+ __decorate([
80
+ Config("mail:jobs:eas_device_cleanup:schedule", "0 0 4 * * *"),
81
+ __metadata("design:type", String)
82
+ ], EasDeviceStateCleanupJob.prototype, "scheduleExpr", void 0);
83
+ __decorate([
84
+ Config("mail:jobs:eas_device_cleanup:batch_size", 500),
85
+ __metadata("design:type", Number)
86
+ ], EasDeviceStateCleanupJob.prototype, "batchSize", void 0);
87
+ __decorate([
88
+ Config("mail:jobs:eas_device_cleanup:device_ttl_days", 90),
89
+ __metadata("design:type", Number)
90
+ ], EasDeviceStateCleanupJob.prototype, "deviceTtlDays", void 0);
91
+ __decorate([
92
+ Logger,
93
+ __metadata("design:type", Object)
94
+ ], EasDeviceStateCleanupJob.prototype, "logger", void 0);
95
+ __decorate([
96
+ Init,
97
+ __metadata("design:type", Function),
98
+ __metadata("design:paramtypes", []),
99
+ __metadata("design:returntype", Promise)
100
+ ], EasDeviceStateCleanupJob.prototype, "init", null);
101
+ //# sourceMappingURL=EasDeviceStateCleanupJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EasDeviceStateCleanupJob.js","sourceRoot":"","sources":["../../../src/jobs/EasDeviceStateCleanupJob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
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, ACLUtils, RepoUtils } from "@rapidrest/service-core";
16
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
17
+ /**
18
+ * Garbage-collects expired `CalendarShareLink` rows (anonymous external calendar-sharing links whose
19
+ * `expiresAt` has passed), also revoking the `ACLRecord` each link's `token` was granted on its shared
20
+ * folder's `AccessControlList` (see `BaseCalendarShareLinkRoute`, which grants/upserts that same record on
21
+ * create/update — this job is the other half of keeping it in sync, for links that expire rather than being
22
+ * explicitly deleted via the API).
23
+ *
24
+ * Concrete entity classes are supplied by the Mongo/SQL subclasses (`ExternalShareExpirationJobMongo`/
25
+ * `ExternalShareExpirationJobSQL`), following the same generic pattern `ScanQueueJob` uses.
26
+ *
27
+ * @author Jean-Philippe Steinmetz
28
+ */
29
+ export class ExternalShareExpirationJob extends BackgroundService {
30
+ constructor() {
31
+ super(...arguments);
32
+ this.scheduleExpr = "0 0 5 * * *";
33
+ this.batchSize = 500;
34
+ }
35
+ get schedule() {
36
+ return this.scheduleExpr;
37
+ }
38
+ async init() {
39
+ this.calendarShareLinkRepo = await this._objectFactory.newInstance(RepoUtils, {
40
+ name: this.calendarShareLinkClass.name,
41
+ args: [this.calendarShareLinkClass],
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.calendarShareLinkRepo) {
52
+ return;
53
+ }
54
+ const now = new Date();
55
+ // `ModelUtils.buildSearchQuery` supports single-sided `lt(...)`/`gt(...)`/`gte(...)`/`lte(...)`
56
+ // comparisons with correct Date coercion on both backends (only its two-sided `range(...)` operator
57
+ // has a documented Date-coercion gap) - a plain `lt(now)` on `expiresAt` also naturally excludes rows
58
+ // where it's unset, on both Mongo (`$lt` against a missing field never matches) and SQL (comparing
59
+ // NULL is never true), so this needs no additional in-process filtering for that case.
60
+ //
61
+ // `limit` is passed both via `options` (all the Mongo backend of `RepoUtils.find()` actually reads)
62
+ // *and* baked into the query object itself (all `ModelUtils.buildSearchQuerySQL` reads - it ignores
63
+ // `options.limit` entirely and falls back to its own default of 100 otherwise). Confirmed by
64
+ // real-database testing: on the SQL backend, `options.limit` alone silently caps at 100 regardless of
65
+ // the configured batch size.
66
+ const links = await this.calendarShareLinkRepo.find({ expiresAt: `lt(${now.toISOString()})`, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
67
+ for (const link of links) {
68
+ try {
69
+ await this.calendarShareLinkRepo.delete(link.uid, { ignoreACL: true, purge: true });
70
+ await this.revokeShareTokenAccess(link.folderUid, link.token);
71
+ }
72
+ catch (err) {
73
+ this.logger?.warn(`ExternalShareExpirationJob: failed to delete expired share link ${link.uid}: ${err.message}`);
74
+ }
75
+ }
76
+ }
77
+ /** Removes any ACL record for `token` from `folderUid`'s ACL - see `BaseCalendarShareLinkRoute`'s identical
78
+ * helper for the full rationale (kept as a separate copy here rather than a shared import, since this job
79
+ * has no other dependency on the routes layer and the logic is a few lines). A no-op if the folder has no
80
+ * ACL document or no matching record - failing open here keeps a missing/corrupt folder ACL from turning a
81
+ * routine expiration sweep into a failed job run for every OTHER link in the same batch. */
82
+ async revokeShareTokenAccess(folderUid, token) {
83
+ const acl = await this.aclUtils?.findACL(folderUid);
84
+ if (!acl) {
85
+ return;
86
+ }
87
+ const records = acl.records.filter((record) => record.userOrRoleId !== token);
88
+ if (records.length === acl.records.length) {
89
+ return;
90
+ }
91
+ acl.records = records;
92
+ await this.aclUtils.saveACL(acl);
93
+ }
94
+ }
95
+ __decorate([
96
+ Inject(ACLUtils),
97
+ __metadata("design:type", ACLUtils)
98
+ ], ExternalShareExpirationJob.prototype, "aclUtils", void 0);
99
+ __decorate([
100
+ Config("mail:jobs:external_share_expiration:schedule", "0 0 5 * * *"),
101
+ __metadata("design:type", String)
102
+ ], ExternalShareExpirationJob.prototype, "scheduleExpr", void 0);
103
+ __decorate([
104
+ Config("mail:jobs:external_share_expiration:batch_size", 500),
105
+ __metadata("design:type", Number)
106
+ ], ExternalShareExpirationJob.prototype, "batchSize", void 0);
107
+ __decorate([
108
+ Logger,
109
+ __metadata("design:type", Object)
110
+ ], ExternalShareExpirationJob.prototype, "logger", void 0);
111
+ __decorate([
112
+ Init,
113
+ __metadata("design:type", Function),
114
+ __metadata("design:paramtypes", []),
115
+ __metadata("design:returntype", Promise)
116
+ ], ExternalShareExpirationJob.prototype, "init", null);
117
+ //# sourceMappingURL=ExternalShareExpirationJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExternalShareExpirationJob.js","sourceRoot":"","sources":["../../../src/jobs/ExternalShareExpirationJob.ts"],"names":[],"mappings":"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"}