@hed-hog/core 0.0.390 → 0.0.393

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 (528) hide show
  1. package/README.md +39 -0
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -1
  3. package/dist/access-log/access-log.controller.js +1 -1
  4. package/dist/access-log/access-log.controller.js.map +1 -1
  5. package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
  6. package/dist/access-log/access-log.interceptor.js +8 -5
  7. package/dist/access-log/access-log.interceptor.js.map +1 -1
  8. package/dist/access-log/access-log.service.d.ts +4 -0
  9. package/dist/access-log/access-log.service.d.ts.map +1 -1
  10. package/dist/access-log/access-log.service.js +31 -4
  11. package/dist/access-log/access-log.service.js.map +1 -1
  12. package/dist/access-log/dto/query-access-log.dto.d.ts +2 -0
  13. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -1
  14. package/dist/access-log/dto/query-access-log.dto.js +5 -0
  15. package/dist/access-log/dto/query-access-log.dto.js.map +1 -1
  16. package/dist/ai/ai.controller.d.ts +12 -8
  17. package/dist/ai/ai.controller.d.ts.map +1 -1
  18. package/dist/ai/ai.controller.js +17 -2
  19. package/dist/ai/ai.controller.js.map +1 -1
  20. package/dist/ai/ai.module.d.ts.map +1 -1
  21. package/dist/ai/ai.module.js +3 -2
  22. package/dist/ai/ai.module.js.map +1 -1
  23. package/dist/ai/ai.service.d.ts +30 -1
  24. package/dist/ai/ai.service.d.ts.map +1 -1
  25. package/dist/ai/ai.service.js +97 -3
  26. package/dist/ai/ai.service.js.map +1 -1
  27. package/dist/ai/dto/richtext-edit-html.dto.d.ts +5 -0
  28. package/dist/ai/dto/richtext-edit-html.dto.d.ts.map +1 -0
  29. package/dist/ai/dto/richtext-edit-html.dto.js +27 -0
  30. package/dist/ai/dto/richtext-edit-html.dto.js.map +1 -0
  31. package/dist/ai/richtext-ai.prompts.d.ts +5 -0
  32. package/dist/ai/richtext-ai.prompts.d.ts.map +1 -0
  33. package/dist/ai/richtext-ai.prompts.js +57 -0
  34. package/dist/ai/richtext-ai.prompts.js.map +1 -0
  35. package/dist/ai/richtext-ai.service.d.ts +34 -0
  36. package/dist/ai/richtext-ai.service.d.ts.map +1 -0
  37. package/dist/ai/richtext-ai.service.js +95 -0
  38. package/dist/ai/richtext-ai.service.js.map +1 -0
  39. package/dist/ai-instruction/ai-instruction.controller.d.ts +5 -5
  40. package/dist/auth/auth.controller.d.ts +66 -7
  41. package/dist/auth/auth.controller.d.ts.map +1 -1
  42. package/dist/auth/auth.controller.js +67 -23
  43. package/dist/auth/auth.controller.js.map +1 -1
  44. package/dist/auth/auth.module.d.ts.map +1 -1
  45. package/dist/auth/auth.module.js +2 -0
  46. package/dist/auth/auth.module.js.map +1 -1
  47. package/dist/auth/auth.service.d.ts +136 -4
  48. package/dist/auth/auth.service.d.ts.map +1 -1
  49. package/dist/auth/auth.service.js +246 -48
  50. package/dist/auth/auth.service.js.map +1 -1
  51. package/dist/auth/dto/confirm-email.dto.d.ts +4 -0
  52. package/dist/auth/dto/confirm-email.dto.d.ts.map +1 -0
  53. package/dist/auth/dto/confirm-email.dto.js +22 -0
  54. package/dist/auth/dto/confirm-email.dto.js.map +1 -0
  55. package/dist/auth/dto/resend-confirmation.dto.d.ts +8 -0
  56. package/dist/auth/dto/resend-confirmation.dto.d.ts.map +1 -0
  57. package/dist/auth/dto/resend-confirmation.dto.js +36 -0
  58. package/dist/auth/dto/resend-confirmation.dto.js.map +1 -0
  59. package/dist/authorize/authorize.controller.d.ts +1 -1
  60. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  61. package/dist/authorize/authorize.controller.js +4 -2
  62. package/dist/authorize/authorize.controller.js.map +1 -1
  63. package/dist/authorize/authorize.service.d.ts +7 -0
  64. package/dist/authorize/authorize.service.d.ts.map +1 -1
  65. package/dist/authorize/authorize.service.js +1 -1
  66. package/dist/authorize/authorize.service.js.map +1 -1
  67. package/dist/challenge/challenge.service.d.ts +18 -5
  68. package/dist/challenge/challenge.service.d.ts.map +1 -1
  69. package/dist/challenge/challenge.service.js +50 -3
  70. package/dist/challenge/challenge.service.js.map +1 -1
  71. package/dist/core.module.d.ts.map +1 -1
  72. package/dist/core.module.js +17 -0
  73. package/dist/core.module.js.map +1 -1
  74. package/dist/dashboard/dashboard/dashboard.controller.d.ts +6 -6
  75. package/dist/dashboard/dashboard/dashboard.service.d.ts +6 -6
  76. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +4 -4
  77. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +4 -4
  78. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -1
  79. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -1
  80. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +2 -2
  81. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
  82. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -2
  83. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -2
  84. package/dist/file/file.controller.d.ts +8 -1
  85. package/dist/file/file.controller.d.ts.map +1 -1
  86. package/dist/file/file.controller.js +45 -7
  87. package/dist/file/file.controller.js.map +1 -1
  88. package/dist/file/file.service.d.ts +128 -1
  89. package/dist/file/file.service.d.ts.map +1 -1
  90. package/dist/file/file.service.js +319 -8
  91. package/dist/file/file.service.js.map +1 -1
  92. package/dist/file/image-optimization.service.d.ts +43 -0
  93. package/dist/file/image-optimization.service.d.ts.map +1 -1
  94. package/dist/file/image-optimization.service.js +154 -0
  95. package/dist/file/image-optimization.service.js.map +1 -1
  96. package/dist/file/provider/abstract.provider.d.ts +16 -0
  97. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  98. package/dist/file/provider/abstract.provider.js +4 -0
  99. package/dist/file/provider/abstract.provider.js.map +1 -1
  100. package/dist/file/provider/gcs.provider.d.ts +2 -0
  101. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  102. package/dist/file/provider/gcs.provider.js +10 -0
  103. package/dist/file/provider/gcs.provider.js.map +1 -1
  104. package/dist/file/provider/local.provider.d.ts +1 -0
  105. package/dist/file/provider/local.provider.d.ts.map +1 -1
  106. package/dist/file/provider/local.provider.js +13 -0
  107. package/dist/file/provider/local.provider.js.map +1 -1
  108. package/dist/file/provider/s3.provider.d.ts +5 -0
  109. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  110. package/dist/file/provider/s3.provider.js +15 -0
  111. package/dist/file/provider/s3.provider.js.map +1 -1
  112. package/dist/file/provider/spaces.provider.d.ts +5 -0
  113. package/dist/file/provider/spaces.provider.d.ts.map +1 -1
  114. package/dist/file/provider/spaces.provider.js +16 -0
  115. package/dist/file/provider/spaces.provider.js.map +1 -1
  116. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts +4 -0
  117. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts.map +1 -0
  118. package/dist/impersonation/dto/redeem-impersonation.dto.js +23 -0
  119. package/dist/impersonation/dto/redeem-impersonation.dto.js.map +1 -0
  120. package/dist/impersonation/dto/start-impersonation.dto.d.ts +7 -0
  121. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -0
  122. package/dist/impersonation/dto/start-impersonation.dto.js +27 -0
  123. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -0
  124. package/dist/impersonation/impersonation-restrictions.d.ts +26 -0
  125. package/dist/impersonation/impersonation-restrictions.d.ts.map +1 -0
  126. package/dist/impersonation/impersonation-restrictions.js +76 -0
  127. package/dist/impersonation/impersonation-restrictions.js.map +1 -0
  128. package/dist/impersonation/impersonation.controller.d.ts +90 -0
  129. package/dist/impersonation/impersonation.controller.d.ts.map +1 -0
  130. package/dist/impersonation/impersonation.controller.js +135 -0
  131. package/dist/impersonation/impersonation.controller.js.map +1 -0
  132. package/dist/impersonation/impersonation.module.d.ts +8 -0
  133. package/dist/impersonation/impersonation.module.d.ts.map +1 -0
  134. package/dist/impersonation/impersonation.module.js +44 -0
  135. package/dist/impersonation/impersonation.module.js.map +1 -0
  136. package/dist/impersonation/impersonation.service.d.ts +164 -0
  137. package/dist/impersonation/impersonation.service.d.ts.map +1 -0
  138. package/dist/impersonation/impersonation.service.js +408 -0
  139. package/dist/impersonation/impersonation.service.js.map +1 -0
  140. package/dist/index.d.ts +17 -0
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +19 -0
  143. package/dist/index.js.map +1 -1
  144. package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
  145. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  146. package/dist/integration/services/outbox-polling.coordinator.js +50 -30
  147. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  148. package/dist/integration/services/outbox.service.d.ts.map +1 -1
  149. package/dist/integration/services/outbox.service.js +5 -1
  150. package/dist/integration/services/outbox.service.js.map +1 -1
  151. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  152. package/dist/integration-profile/integration-profile.secrets.js +3 -0
  153. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  154. package/dist/integration-profile/integration-profile.utils.d.ts +14 -0
  155. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  156. package/dist/integration-profile/integration-profile.utils.js +26 -0
  157. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  158. package/dist/integration-profile/testers/email.testers.d.ts.map +1 -1
  159. package/dist/integration-profile/testers/email.testers.js +28 -0
  160. package/dist/integration-profile/testers/email.testers.js.map +1 -1
  161. package/dist/language/en.json +11 -0
  162. package/dist/language/pt.json +11 -0
  163. package/dist/mail/dto/send.dto.d.ts +38 -1
  164. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  165. package/dist/mail/dto/send.dto.js +10 -0
  166. package/dist/mail/dto/send.dto.js.map +1 -1
  167. package/dist/mail/mail.controller.d.ts +1 -0
  168. package/dist/mail/mail.controller.d.ts.map +1 -1
  169. package/dist/mail/mail.module.d.ts.map +1 -1
  170. package/dist/mail/mail.module.js +2 -0
  171. package/dist/mail/mail.module.js.map +1 -1
  172. package/dist/mail/mail.service.d.ts +48 -4
  173. package/dist/mail/mail.service.d.ts.map +1 -1
  174. package/dist/mail/mail.service.js +187 -35
  175. package/dist/mail/mail.service.js.map +1 -1
  176. package/dist/mail-delivery/mail-delivery-provision.service.d.ts +21 -0
  177. package/dist/mail-delivery/mail-delivery-provision.service.d.ts.map +1 -0
  178. package/dist/mail-delivery/mail-delivery-provision.service.js +64 -0
  179. package/dist/mail-delivery/mail-delivery-provision.service.js.map +1 -0
  180. package/dist/mail-delivery/mail-delivery.constants.d.ts +17 -0
  181. package/dist/mail-delivery/mail-delivery.constants.d.ts.map +1 -0
  182. package/dist/mail-delivery/mail-delivery.constants.js +31 -0
  183. package/dist/mail-delivery/mail-delivery.constants.js.map +1 -0
  184. package/dist/mail-delivery/mail-delivery.controller.d.ts +19 -0
  185. package/dist/mail-delivery/mail-delivery.controller.d.ts.map +1 -0
  186. package/dist/mail-delivery/mail-delivery.controller.js +53 -0
  187. package/dist/mail-delivery/mail-delivery.controller.js.map +1 -0
  188. package/dist/mail-delivery/mail-delivery.module.d.ts +7 -0
  189. package/dist/mail-delivery/mail-delivery.module.d.ts.map +1 -0
  190. package/dist/mail-delivery/mail-delivery.module.js +36 -0
  191. package/dist/mail-delivery/mail-delivery.module.js.map +1 -0
  192. package/dist/mail-delivery/mail-delivery.service.d.ts +38 -0
  193. package/dist/mail-delivery/mail-delivery.service.d.ts.map +1 -0
  194. package/dist/mail-delivery/mail-delivery.service.js +140 -0
  195. package/dist/mail-delivery/mail-delivery.service.js.map +1 -0
  196. package/dist/mail-delivery/ses-event.parser.d.ts +27 -0
  197. package/dist/mail-delivery/ses-event.parser.d.ts.map +1 -0
  198. package/dist/mail-delivery/ses-event.parser.js +131 -0
  199. package/dist/mail-delivery/ses-event.parser.js.map +1 -0
  200. package/dist/mail-delivery/ses-webhook.service.d.ts +58 -0
  201. package/dist/mail-delivery/ses-webhook.service.d.ts.map +1 -0
  202. package/dist/mail-delivery/ses-webhook.service.js +214 -0
  203. package/dist/mail-delivery/ses-webhook.service.js.map +1 -0
  204. package/dist/mail-delivery/sns-message.util.d.ts +38 -0
  205. package/dist/mail-delivery/sns-message.util.d.ts.map +1 -0
  206. package/dist/mail-delivery/sns-message.util.js +109 -0
  207. package/dist/mail-delivery/sns-message.util.js.map +1 -0
  208. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +5 -0
  209. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -1
  210. package/dist/mail-sent/dto/query-mail-sent-list.dto.js +6 -0
  211. package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -1
  212. package/dist/mail-sent/mail-sent.controller.d.ts +39 -0
  213. package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
  214. package/dist/mail-sent/mail-sent.service.d.ts +39 -0
  215. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  216. package/dist/mail-sent/mail-sent.service.js +45 -4
  217. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  218. package/dist/mailbox/mailbox-fetcher.registry.d.ts +100 -0
  219. package/dist/mailbox/mailbox-fetcher.registry.d.ts.map +1 -0
  220. package/dist/mailbox/mailbox-fetcher.registry.js +59 -0
  221. package/dist/mailbox/mailbox-fetcher.registry.js.map +1 -0
  222. package/dist/mailbox/mailbox.module.d.ts +8 -0
  223. package/dist/mailbox/mailbox.module.d.ts.map +1 -0
  224. package/dist/mailbox/mailbox.module.js +27 -0
  225. package/dist/mailbox/mailbox.module.js.map +1 -0
  226. package/dist/meeting/meeting-provider.registry.d.ts +155 -0
  227. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -0
  228. package/dist/meeting/meeting-provider.registry.js +53 -0
  229. package/dist/meeting/meeting-provider.registry.js.map +1 -0
  230. package/dist/meeting/meeting.constants.d.ts +47 -0
  231. package/dist/meeting/meeting.constants.d.ts.map +1 -0
  232. package/dist/meeting/meeting.constants.js +21 -0
  233. package/dist/meeting/meeting.constants.js.map +1 -0
  234. package/dist/meeting/meeting.module.d.ts +7 -0
  235. package/dist/meeting/meeting.module.d.ts.map +1 -0
  236. package/dist/meeting/meeting.module.js +26 -0
  237. package/dist/meeting/meeting.module.js.map +1 -0
  238. package/dist/middlewares/locale-injection.middleware.d.ts +12 -0
  239. package/dist/middlewares/locale-injection.middleware.d.ts.map +1 -1
  240. package/dist/middlewares/locale-injection.middleware.js +20 -4
  241. package/dist/middlewares/locale-injection.middleware.js.map +1 -1
  242. package/dist/oauth/providers/google.provider.d.ts +7 -0
  243. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  244. package/dist/oauth/providers/google.provider.js +43 -13
  245. package/dist/oauth/providers/google.provider.js.map +1 -1
  246. package/dist/observability/monitored-cron.d.ts +34 -0
  247. package/dist/observability/monitored-cron.d.ts.map +1 -0
  248. package/dist/observability/monitored-cron.js +118 -0
  249. package/dist/observability/monitored-cron.js.map +1 -0
  250. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts +12 -0
  251. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts.map +1 -0
  252. package/dist/pairing/dto/approve-reverse-pairing.dto.js +30 -0
  253. package/dist/pairing/dto/approve-reverse-pairing.dto.js.map +1 -0
  254. package/dist/pairing/dto/claim-pairing.dto.d.ts +11 -0
  255. package/dist/pairing/dto/claim-pairing.dto.d.ts.map +1 -0
  256. package/dist/pairing/dto/claim-pairing.dto.js +35 -0
  257. package/dist/pairing/dto/claim-pairing.dto.js.map +1 -0
  258. package/dist/pairing/dto/confirm-pairing.dto.d.ts +10 -0
  259. package/dist/pairing/dto/confirm-pairing.dto.d.ts.map +1 -0
  260. package/dist/pairing/dto/confirm-pairing.dto.js +24 -0
  261. package/dist/pairing/dto/confirm-pairing.dto.js.map +1 -0
  262. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts +5 -0
  263. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts.map +1 -0
  264. package/dist/pairing/dto/poll-reverse-pairing.dto.js +24 -0
  265. package/dist/pairing/dto/poll-reverse-pairing.dto.js.map +1 -0
  266. package/dist/pairing/dto/start-pairing.dto.d.ts +11 -0
  267. package/dist/pairing/dto/start-pairing.dto.d.ts.map +1 -0
  268. package/dist/pairing/dto/start-pairing.dto.js +30 -0
  269. package/dist/pairing/dto/start-pairing.dto.js.map +1 -0
  270. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts +24 -0
  271. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts.map +1 -0
  272. package/dist/pairing/dto/start-reverse-pairing.dto.js +47 -0
  273. package/dist/pairing/dto/start-reverse-pairing.dto.js.map +1 -0
  274. package/dist/pairing/pairing.controller.d.ts +118 -0
  275. package/dist/pairing/pairing.controller.d.ts.map +1 -0
  276. package/dist/pairing/pairing.controller.js +296 -0
  277. package/dist/pairing/pairing.controller.js.map +1 -0
  278. package/dist/pairing/pairing.module.d.ts +10 -0
  279. package/dist/pairing/pairing.module.d.ts.map +1 -0
  280. package/dist/pairing/pairing.module.js +40 -0
  281. package/dist/pairing/pairing.module.js.map +1 -0
  282. package/dist/pairing/pairing.service.d.ts +238 -0
  283. package/dist/pairing/pairing.service.d.ts.map +1 -0
  284. package/dist/pairing/pairing.service.js +495 -0
  285. package/dist/pairing/pairing.service.js.map +1 -0
  286. package/dist/pdf/pdf.service.d.ts +3 -6
  287. package/dist/pdf/pdf.service.d.ts.map +1 -1
  288. package/dist/pdf/pdf.service.js +8 -20
  289. package/dist/pdf/pdf.service.js.map +1 -1
  290. package/dist/profile/profile.controller.d.ts +11 -0
  291. package/dist/profile/profile.controller.d.ts.map +1 -1
  292. package/dist/profile/profile.service.d.ts +11 -0
  293. package/dist/profile/profile.service.d.ts.map +1 -1
  294. package/dist/profile/profile.service.js +20 -11
  295. package/dist/profile/profile.service.js.map +1 -1
  296. package/dist/redis/redis-pubsub.d.ts.map +1 -1
  297. package/dist/redis/redis-pubsub.js +44 -0
  298. package/dist/redis/redis-pubsub.js.map +1 -1
  299. package/dist/role/guards/role.guard.d.ts +5 -0
  300. package/dist/role/guards/role.guard.d.ts.map +1 -1
  301. package/dist/role/guards/role.guard.js +41 -39
  302. package/dist/role/guards/role.guard.js.map +1 -1
  303. package/dist/session/session.controller.d.ts +2 -0
  304. package/dist/session/session.controller.d.ts.map +1 -1
  305. package/dist/session/session.service.d.ts +39 -0
  306. package/dist/session/session.service.d.ts.map +1 -1
  307. package/dist/session/session.service.js +91 -8
  308. package/dist/session/session.service.js.map +1 -1
  309. package/dist/setting/brand-asset.service.d.ts +48 -0
  310. package/dist/setting/brand-asset.service.d.ts.map +1 -0
  311. package/dist/setting/brand-asset.service.js +130 -0
  312. package/dist/setting/brand-asset.service.js.map +1 -0
  313. package/dist/setting/setting.module.d.ts.map +1 -1
  314. package/dist/setting/setting.module.js +3 -2
  315. package/dist/setting/setting.module.js.map +1 -1
  316. package/dist/setting/setting.service.d.ts +33 -0
  317. package/dist/setting/setting.service.d.ts.map +1 -1
  318. package/dist/setting/setting.service.js +88 -0
  319. package/dist/setting/setting.service.js.map +1 -1
  320. package/dist/token/token.service.d.ts +23 -2
  321. package/dist/token/token.service.d.ts.map +1 -1
  322. package/dist/token/token.service.js +39 -4
  323. package/dist/token/token.service.js.map +1 -1
  324. package/dist/user/user.controller.d.ts +8 -22
  325. package/dist/user/user.controller.d.ts.map +1 -1
  326. package/dist/user/user.service.d.ts +44 -24
  327. package/dist/user/user.service.d.ts.map +1 -1
  328. package/dist/user/user.service.js +89 -6
  329. package/dist/user/user.service.js.map +1 -1
  330. package/dist/user-device/user-device.controller.d.ts +54 -0
  331. package/dist/user-device/user-device.controller.d.ts.map +1 -0
  332. package/dist/user-device/user-device.controller.js +103 -0
  333. package/dist/user-device/user-device.controller.js.map +1 -0
  334. package/dist/user-device/user-device.module.d.ts +3 -0
  335. package/dist/user-device/user-device.module.d.ts.map +1 -0
  336. package/dist/user-device/user-device.module.js +26 -0
  337. package/dist/user-device/user-device.module.js.map +1 -0
  338. package/dist/user-device/user-device.service.d.ts +71 -0
  339. package/dist/user-device/user-device.service.d.ts.map +1 -0
  340. package/dist/user-device/user-device.service.js +158 -0
  341. package/dist/user-device/user-device.service.js.map +1 -0
  342. package/dist/utils/device-key.d.ts +23 -0
  343. package/dist/utils/device-key.d.ts.map +1 -0
  344. package/dist/utils/device-key.js +31 -0
  345. package/dist/utils/device-key.js.map +1 -0
  346. package/dist/utils/geo-ip.d.ts +38 -0
  347. package/dist/utils/geo-ip.d.ts.map +1 -0
  348. package/dist/utils/geo-ip.js +122 -0
  349. package/dist/utils/geo-ip.js.map +1 -0
  350. package/dist/utils/locale-context.d.ts +10 -7
  351. package/dist/utils/locale-context.d.ts.map +1 -1
  352. package/dist/utils/locale-context.js +14 -8
  353. package/dist/utils/locale-context.js.map +1 -1
  354. package/dist/utils/security-event-vars.d.ts +56 -0
  355. package/dist/utils/security-event-vars.d.ts.map +1 -0
  356. package/dist/utils/security-event-vars.js +109 -0
  357. package/dist/utils/security-event-vars.js.map +1 -0
  358. package/dist/utils/user-agent.d.ts +19 -0
  359. package/dist/utils/user-agent.d.ts.map +1 -0
  360. package/dist/utils/user-agent.js +36 -0
  361. package/dist/utils/user-agent.js.map +1 -0
  362. package/dist/webhook-integration/webhook-integration.service.d.ts +5 -0
  363. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  364. package/dist/webhook-integration/webhook-integration.service.js +50 -4
  365. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  366. package/dist/webhook-integration/webhook-protocol.registry.d.ts +6 -0
  367. package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -1
  368. package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -1
  369. package/hedhog/data/integration_provider.yaml +9 -0
  370. package/hedhog/data/mail.yaml +198 -62
  371. package/hedhog/data/mail_layout.yaml +43 -33
  372. package/hedhog/data/menu.yaml +2 -0
  373. package/hedhog/data/role.yaml +16 -0
  374. package/hedhog/data/route.yaml +172 -3
  375. package/hedhog/data/setting_group.yaml +178 -2
  376. package/hedhog/data/setting_subgroup.yaml +8 -0
  377. package/hedhog/frontend/app/_lib/hooks/use-core-access.test.tsx.ejs +125 -0
  378. package/hedhog/frontend/app/_lib/hooks/use-core-access.ts.ejs +29 -0
  379. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.test.ts.ejs +71 -0
  380. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.ts.ejs +32 -0
  381. package/hedhog/frontend/app/access-log/page.tsx.ejs +96 -22
  382. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +17 -1
  383. package/hedhog/frontend/app/account/components/known-devices.tsx.ejs +89 -0
  384. package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +2 -1
  385. package/hedhog/frontend/app/account/components/user-devices-list.tsx.ejs +186 -0
  386. package/hedhog/frontend/app/account/devices/page.tsx.ejs +5 -0
  387. package/hedhog/frontend/app/account/layout.tsx.ejs +2 -0
  388. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +298 -0
  389. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +376 -1236
  390. package/hedhog/frontend/app/mail/log/page.tsx.ejs +210 -3
  391. package/hedhog/frontend/app/mail/template/page.tsx.ejs +190 -132
  392. package/hedhog/frontend/app/users/active-session.tsx.ejs +58 -11
  393. package/hedhog/frontend/app/users/page.tsx.ejs +69 -10
  394. package/hedhog/frontend/app/users/simulate-access-dialog.test.tsx.ejs +202 -0
  395. package/hedhog/frontend/app/users/simulate-access-dialog.tsx.ejs +170 -0
  396. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +19 -0
  397. package/hedhog/frontend/app/users/user-devices.tsx.ejs +89 -0
  398. package/hedhog/frontend/app/users/user-logs.tsx.ejs +13 -1
  399. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +46 -24
  400. package/hedhog/frontend/messages/en.json +146 -48
  401. package/hedhog/frontend/messages/pt.json +146 -48
  402. package/hedhog/table/access_log.yaml +7 -0
  403. package/hedhog/table/file.yaml +8 -0
  404. package/hedhog/table/mail_delivery_event.yaml +76 -0
  405. package/hedhog/table/mail_sent.yaml +23 -0
  406. package/hedhog/table/user_activity.yaml +9 -0
  407. package/hedhog/table/user_device.yaml +52 -0
  408. package/hedhog/table/user_session.yaml +20 -1
  409. package/package.json +6 -5
  410. package/src/access-log/access-log.controller.ts +1 -0
  411. package/src/access-log/access-log.interceptor.ts +5 -1
  412. package/src/access-log/access-log.service.spec.ts +33 -2
  413. package/src/access-log/access-log.service.ts +38 -0
  414. package/src/access-log/dto/query-access-log.dto.ts +5 -0
  415. package/src/ai/ai.controller.ts +9 -0
  416. package/src/ai/ai.module.ts +3 -2
  417. package/src/ai/ai.service.ts +150 -13
  418. package/src/ai/dto/richtext-edit-html.dto.ts +11 -0
  419. package/src/ai/richtext-ai.prompts.ts +57 -0
  420. package/src/ai/richtext-ai.service.ts +109 -0
  421. package/src/auth/auth.controller.ts +47 -8
  422. package/src/auth/auth.module.ts +2 -0
  423. package/src/auth/auth.service.spec.ts +383 -6
  424. package/src/auth/auth.service.ts +345 -56
  425. package/src/auth/dto/confirm-email.dto.ts +7 -0
  426. package/src/auth/dto/resend-confirmation.dto.ts +20 -0
  427. package/src/authorize/authorize.controller.ts +2 -0
  428. package/src/authorize/authorize.service.spec.ts +23 -0
  429. package/src/authorize/authorize.service.ts +8 -0
  430. package/src/challenge/challenge.service.spec.ts +73 -0
  431. package/src/challenge/challenge.service.ts +62 -3
  432. package/src/core.module.ts +19 -0
  433. package/src/file/file.controller.ts +54 -4
  434. package/src/file/file.service.spec.ts +275 -105
  435. package/src/file/file.service.ts +409 -14
  436. package/src/file/image-optimization.service.spec.ts +92 -0
  437. package/src/file/image-optimization.service.ts +202 -0
  438. package/src/file/provider/abstract.provider.ts +23 -0
  439. package/src/file/provider/gcs.provider.ts +17 -0
  440. package/src/file/provider/local.provider.ts +21 -0
  441. package/src/file/provider/s3.provider.ts +25 -0
  442. package/src/file/provider/spaces.provider.ts +26 -0
  443. package/src/impersonation/dto/redeem-impersonation.dto.ts +8 -0
  444. package/src/impersonation/dto/start-impersonation.dto.ts +13 -0
  445. package/src/impersonation/impersonation-restrictions.ts +84 -0
  446. package/src/impersonation/impersonation.controller.ts +109 -0
  447. package/src/impersonation/impersonation.module.ts +31 -0
  448. package/src/impersonation/impersonation.service.spec.ts +427 -0
  449. package/src/impersonation/impersonation.service.ts +577 -0
  450. package/src/index.ts +21 -0
  451. package/src/integration/services/outbox-polling.coordinator.spec.ts +90 -0
  452. package/src/integration/services/outbox-polling.coordinator.ts +62 -40
  453. package/src/integration/services/outbox.service.spec.ts +8 -0
  454. package/src/integration/services/outbox.service.ts +19 -13
  455. package/src/integration-profile/integration-profile.secrets.ts +3 -0
  456. package/src/integration-profile/integration-profile.utils.ts +38 -0
  457. package/src/integration-profile/testers/email.testers.ts +34 -1
  458. package/src/language/en.json +11 -0
  459. package/src/language/pt.json +11 -0
  460. package/src/mail/dto/send.dto.ts +46 -2
  461. package/src/mail/mail-template.spec.ts +79 -2
  462. package/src/mail/mail.module.ts +2 -0
  463. package/src/mail/mail.service.spec.ts +307 -6
  464. package/src/mail/mail.service.ts +227 -30
  465. package/src/mail-delivery/mail-delivery-provision.service.ts +66 -0
  466. package/src/mail-delivery/mail-delivery.constants.ts +31 -0
  467. package/src/mail-delivery/mail-delivery.controller.ts +38 -0
  468. package/src/mail-delivery/mail-delivery.module.ts +23 -0
  469. package/src/mail-delivery/mail-delivery.service.spec.ts +136 -0
  470. package/src/mail-delivery/mail-delivery.service.ts +152 -0
  471. package/src/mail-delivery/ses-event.parser.spec.ts +140 -0
  472. package/src/mail-delivery/ses-event.parser.ts +191 -0
  473. package/src/mail-delivery/ses-webhook.service.spec.ts +170 -0
  474. package/src/mail-delivery/ses-webhook.service.ts +240 -0
  475. package/src/mail-delivery/sns-message.util.spec.ts +163 -0
  476. package/src/mail-delivery/sns-message.util.ts +125 -0
  477. package/src/mail-sent/dto/query-mail-sent-list.dto.ts +15 -0
  478. package/src/mail-sent/mail-sent.service.ts +46 -2
  479. package/src/mailbox/mailbox-fetcher.registry.ts +132 -0
  480. package/src/mailbox/mailbox.module.ts +14 -0
  481. package/src/meeting/meeting-provider.registry.ts +193 -0
  482. package/src/meeting/meeting.constants.ts +53 -0
  483. package/src/meeting/meeting.module.ts +13 -0
  484. package/src/middlewares/locale-injection.middleware.ts +25 -6
  485. package/src/oauth/providers/google.provider.ts +49 -15
  486. package/src/observability/monitored-cron.spec.ts +102 -0
  487. package/src/observability/monitored-cron.ts +110 -0
  488. package/src/pairing/dto/approve-reverse-pairing.dto.ts +20 -0
  489. package/src/pairing/dto/claim-pairing.dto.ts +19 -0
  490. package/src/pairing/dto/confirm-pairing.dto.ts +14 -0
  491. package/src/pairing/dto/poll-reverse-pairing.dto.ts +9 -0
  492. package/src/pairing/dto/start-pairing.dto.ts +19 -0
  493. package/src/pairing/dto/start-reverse-pairing.dto.ts +45 -0
  494. package/src/pairing/pairing.controller.ts +246 -0
  495. package/src/pairing/pairing.module.ts +27 -0
  496. package/src/pairing/pairing.service.spec.ts +462 -0
  497. package/src/pairing/pairing.service.ts +614 -0
  498. package/src/pdf/pdf.service.ts +6 -24
  499. package/src/profile/profile.service.ts +26 -8
  500. package/src/redis/redis-pubsub.ts +11 -0
  501. package/src/role/guards/role.guard.spec.ts +75 -0
  502. package/src/role/guards/role.guard.ts +64 -46
  503. package/src/session/session.service.spec.ts +81 -0
  504. package/src/session/session.service.ts +93 -6
  505. package/src/setting/brand-asset.service.ts +137 -0
  506. package/src/setting/setting.module.ts +3 -2
  507. package/src/setting/setting.service.spec.ts +79 -1
  508. package/src/setting/setting.service.ts +109 -1
  509. package/src/token/token.service.spec.ts +62 -0
  510. package/src/token/token.service.ts +57 -3
  511. package/src/user/user.service.spec.ts +33 -2
  512. package/src/user/user.service.ts +108 -6
  513. package/src/user-device/user-device.controller.ts +77 -0
  514. package/src/user-device/user-device.module.ts +13 -0
  515. package/src/user-device/user-device.service.spec.ts +234 -0
  516. package/src/user-device/user-device.service.ts +168 -0
  517. package/src/utils/device-key.spec.ts +57 -0
  518. package/src/utils/device-key.ts +32 -0
  519. package/src/utils/geo-ip.spec.ts +134 -0
  520. package/src/utils/geo-ip.ts +93 -0
  521. package/src/utils/locale-context.spec.ts +27 -1
  522. package/src/utils/locale-context.ts +15 -12
  523. package/src/utils/security-event-vars.spec.ts +161 -0
  524. package/src/utils/security-event-vars.ts +153 -0
  525. package/src/utils/user-agent.spec.ts +37 -0
  526. package/src/utils/user-agent.ts +46 -0
  527. package/src/webhook-integration/webhook-integration.service.ts +57 -3
  528. package/src/webhook-integration/webhook-protocol.registry.ts +6 -0
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
2
+ import { OutboxPollingCoordinator } from './outbox-polling.coordinator';
3
+
4
+ const makeSettings = (over: Partial<Record<string, any>> = {}) => ({
5
+ outboxEnabled: true,
6
+ outboxProcessorEnabled: true,
7
+ pollingIntervalMs: 1000,
8
+ idlePollingIntervalMs: 2000,
9
+ batchSize: 10,
10
+ startupDrainBatchSize: 50,
11
+ maxAttempts: 3,
12
+ processingLeaseMs: 30000,
13
+ retryBaseDelayMs: 1000,
14
+ deadLetterEnabled: true,
15
+ ...over,
16
+ });
17
+
18
+ const makeDeps = () => {
19
+ const processor = { processBatch: jest.fn<any>() };
20
+ const notifier = { subscribe: jest.fn(), unsubscribe: jest.fn() };
21
+ const integrationSettingsService = { getRuntimeSettings: jest.fn<any>() };
22
+ return { processor, notifier, integrationSettingsService };
23
+ };
24
+
25
+ describe('OutboxPollingCoordinator', () => {
26
+ beforeEach(() => {
27
+ jest.useFakeTimers();
28
+ });
29
+
30
+ afterEach(async () => {
31
+ jest.clearAllMocks();
32
+ jest.useRealTimers();
33
+ });
34
+
35
+ it('não deixa uma falha de uma iteração derrubar o loop (sem unhandled rejection)', async () => {
36
+ const deps = makeDeps();
37
+ deps.integrationSettingsService.getRuntimeSettings.mockResolvedValue(makeSettings());
38
+ deps.processor.processBatch
39
+ .mockRejectedValueOnce(
40
+ new Error('Transaction API error: Unable to start a transaction in the given time.'),
41
+ )
42
+ .mockResolvedValue(0);
43
+
44
+ const coordinator = new OutboxPollingCoordinator(
45
+ deps.processor as any,
46
+ deps.notifier as any,
47
+ deps.integrationSettingsService as any,
48
+ );
49
+
50
+ await coordinator.startProcessingLoop();
51
+
52
+ // 1st iteration throws -> caught, backs off ERROR_BACKOFF_MS (5000ms) instead of crashing
53
+ await jest.advanceTimersByTimeAsync(5000);
54
+
55
+ expect(deps.processor.processBatch).toHaveBeenCalledTimes(2);
56
+ // The loop must still be running — a caught iteration error must not stop/crash it
57
+ expect(coordinator.isRunning()).toBe(true);
58
+
59
+ // Ask it to stop (flips shouldStopLoop synchronously), then let the in-flight idle delay
60
+ // from the 2nd (successful) iteration resolve so the loop can observe the flag and exit
61
+ // cleanly, without running a 3rd iteration.
62
+ const stopPromise = coordinator.stopProcessingLoop();
63
+ await jest.advanceTimersByTimeAsync(2000);
64
+ await stopPromise;
65
+
66
+ expect(coordinator.isRunning()).toBe(false);
67
+ expect(deps.processor.processBatch).toHaveBeenCalledTimes(2);
68
+ });
69
+
70
+ it('não fica preso em "running" para sempre se o loop rejeitar de forma inesperada', async () => {
71
+ const deps = makeDeps();
72
+ // Simulate a failure before the inner try/catch can even run (e.g. notifier.subscribe throwing)
73
+ deps.notifier.subscribe.mockImplementation(() => {
74
+ throw new Error('boom');
75
+ });
76
+
77
+ const coordinator = new OutboxPollingCoordinator(
78
+ deps.processor as any,
79
+ deps.notifier as any,
80
+ deps.integrationSettingsService as any,
81
+ );
82
+
83
+ await coordinator.startProcessingLoop();
84
+ // Let the rejected promise's .catch() handler run
85
+ await Promise.resolve();
86
+ await Promise.resolve();
87
+
88
+ expect(coordinator.isRunning()).toBe(false);
89
+ });
90
+ });
@@ -11,6 +11,8 @@ import { OutboxProcessor } from './outbox.processor';
11
11
  @Injectable()
12
12
  export class OutboxPollingCoordinator {
13
13
  private readonly logger = new Logger(OutboxPollingCoordinator.name);
14
+ /** Fallback backoff used when a loop iteration throws before settings can be trusted */
15
+ private static readonly ERROR_BACKOFF_MS = 5000;
14
16
  private processingLoopRunning = false;
15
17
  private processingLoopPromise: Promise<void> | null = null;
16
18
  private immediateWakeupTriggered = false;
@@ -38,7 +40,14 @@ export class OutboxPollingCoordinator {
38
40
  this.shouldStopLoop = false;
39
41
  this.logger.log('Starting outbox hybrid processing loop');
40
42
 
41
- this.processingLoopPromise = this.runProcessingLoop();
43
+ // Defense-in-depth: runProcessingLoop() catches errors internally and should never reject,
44
+ // but if it somehow does, don't let it become an unhandled rejection (which kills the pod —
45
+ // see Sentry.onUnhandledRejectionIntegration({ mode: 'strict' }) in apps/api/src/instrument.ts)
46
+ // and don't leave the flag stuck as "running" with no way to recover.
47
+ this.processingLoopPromise = this.runProcessingLoop().catch((error) => {
48
+ this.logger.error('[outbox:loop] processing loop crashed unexpectedly', error);
49
+ this.processingLoopRunning = false;
50
+ });
42
51
  }
43
52
 
44
53
  /**
@@ -94,47 +103,60 @@ export class OutboxPollingCoordinator {
94
103
 
95
104
  try {
96
105
  while (!this.shouldStopLoop) {
97
- const settings = await this.integrationSettingsService.getRuntimeSettings();
98
- const isEnabled = settings.outboxEnabled;
99
- const processorEnabled = settings.outboxProcessorEnabled;
100
-
101
- if (!isEnabled || !processorEnabled) {
102
- this.logger.debug(
103
- `[outbox:loop] processor disabled (outboxEnabled=${isEnabled} processorEnabled=${processorEnabled}), sleeping ${settings.idlePollingIntervalMs}ms`,
104
- );
105
- await this.delay(settings.idlePollingIntervalMs);
106
- continue;
107
- }
108
-
109
- // Check if we should process immediately (woken by notification)
110
- const shouldProcessImmediately = this.immediateWakeupTriggered;
111
- this.immediateWakeupTriggered = false;
112
-
113
- // Process batch
114
- const processedCount = await this.processor.processBatch();
115
-
116
- // Determine next polling interval
117
- let nextDelayMs: number;
118
-
119
- if (shouldProcessImmediately && processedCount > 0) {
120
- // We processed items from immediate wakeup - use active polling interval
121
- nextDelayMs = settings.pollingIntervalMs;
122
- this.logger.debug(
123
- `Processed ${processedCount} events from immediate wakeup, next poll in ${nextDelayMs}ms`,
106
+ try {
107
+ const settings = await this.integrationSettingsService.getRuntimeSettings();
108
+ const isEnabled = settings.outboxEnabled;
109
+ const processorEnabled = settings.outboxProcessorEnabled;
110
+
111
+ if (!isEnabled || !processorEnabled) {
112
+ this.logger.debug(
113
+ `[outbox:loop] processor disabled (outboxEnabled=${isEnabled} processorEnabled=${processorEnabled}), sleeping ${settings.idlePollingIntervalMs}ms`,
114
+ );
115
+ await this.delay(settings.idlePollingIntervalMs);
116
+ continue;
117
+ }
118
+
119
+ // Check if we should process immediately (woken by notification)
120
+ const shouldProcessImmediately = this.immediateWakeupTriggered;
121
+ this.immediateWakeupTriggered = false;
122
+
123
+ // Process batch
124
+ const processedCount = await this.processor.processBatch();
125
+
126
+ // Determine next polling interval
127
+ let nextDelayMs: number;
128
+
129
+ if (shouldProcessImmediately && processedCount > 0) {
130
+ // We processed items from immediate wakeup - use active polling interval
131
+ nextDelayMs = settings.pollingIntervalMs;
132
+ this.logger.debug(
133
+ `Processed ${processedCount} events from immediate wakeup, next poll in ${nextDelayMs}ms`,
134
+ );
135
+ } else if (processedCount > 0) {
136
+ // Processed items from periodic polling - use active interval
137
+ nextDelayMs = settings.pollingIntervalMs;
138
+ this.logger.debug(
139
+ `Processed ${processedCount} events, next poll in ${nextDelayMs}ms`,
140
+ );
141
+ } else {
142
+ // No items processed - use idle interval
143
+ nextDelayMs = settings.idlePollingIntervalMs;
144
+ }
145
+
146
+ // Wait for next poll, but be ready for immediate wakeup
147
+ await this.delay(nextDelayMs);
148
+ } catch (error) {
149
+ // A single iteration failing (e.g. a transient DB/transaction error) must never escape
150
+ // the loop — that would reject processingLoopPromise as an unhandled rejection and, per
151
+ // Sentry's strict unhandled-rejection mode, take down the whole pod. Log, back off, and
152
+ // keep polling instead. Don't trust `settings` here — the error may have come from
153
+ // fetching it — so use a fixed fallback delay.
154
+ this.logger.error(
155
+ '[outbox:loop] iteration failed, backing off before retrying',
156
+ error,
124
157
  );
125
- } else if (processedCount > 0) {
126
- // Processed items from periodic polling - use active interval
127
- nextDelayMs = settings.pollingIntervalMs;
128
- this.logger.debug(
129
- `Processed ${processedCount} events, next poll in ${nextDelayMs}ms`,
130
- );
131
- } else {
132
- // No items processed - use idle interval
133
- nextDelayMs = settings.idlePollingIntervalMs;
158
+ await this.delay(OutboxPollingCoordinator.ERROR_BACKOFF_MS);
134
159
  }
135
-
136
- // Wait for next poll, but be ready for immediate wakeup
137
- await this.delay(nextDelayMs);
138
160
  }
139
161
  } finally {
140
162
  // Cleanup: unsubscribe from notifications
@@ -156,6 +156,10 @@ describe('OutboxService', () => {
156
156
 
157
157
  expect(result).toEqual([]);
158
158
  expect(tx.$executeRaw).not.toHaveBeenCalled();
159
+ expect(deps.prisma.$transaction).toHaveBeenCalledWith(
160
+ expect.any(Function),
161
+ { maxWait: 5000, timeout: 5000 },
162
+ );
159
163
  });
160
164
 
161
165
  it('bloqueia linhas encontradas e marca como PROCESSING', async () => {
@@ -173,6 +177,10 @@ describe('OutboxService', () => {
173
177
  expect(result).toHaveLength(2);
174
178
  expect(result[0].status).toBe(OutboxEventStatus.PROCESSING);
175
179
  expect(result[1].status).toBe(OutboxEventStatus.PROCESSING);
180
+ expect(deps.prisma.$transaction).toHaveBeenCalledWith(
181
+ expect.any(Function),
182
+ { maxWait: 5000, timeout: 5000 },
183
+ );
176
184
  });
177
185
  });
178
186
 
@@ -114,8 +114,9 @@ export class OutboxService {
114
114
  const leaseThreshold = new Date(now.getTime() - leaseMs);
115
115
  const leaseUntil = new Date(now.getTime() + leaseMs);
116
116
 
117
- return this.prisma.$transaction(async (tx) => {
118
- const rows = await tx.$queryRaw<OutboxEventRecord[]>`
117
+ return this.prisma.$transaction(
118
+ async (tx) => {
119
+ const rows = await tx.$queryRaw<OutboxEventRecord[]>`
119
120
  SELECT * FROM outbox_event
120
121
  WHERE status = 'pending'::"outbox_event_status_e5d55ba10d_enum"
121
122
  OR (status = 'processing'::"outbox_event_status_e5d55ba10d_enum"
@@ -125,10 +126,10 @@ export class OutboxService {
125
126
  FOR UPDATE SKIP LOCKED
126
127
  `;
127
128
 
128
- if (rows.length === 0) return [];
129
+ if (rows.length === 0) return [];
129
130
 
130
- const ids = rows.map((r) => r.id);
131
- await tx.$executeRaw`
131
+ const ids = rows.map((r) => r.id);
132
+ await tx.$executeRaw`
132
133
  UPDATE outbox_event
133
134
  SET status = 'processing'::"outbox_event_status_e5d55ba10d_enum",
134
135
  available_at = ${leaseUntil},
@@ -136,14 +137,19 @@ export class OutboxService {
136
137
  WHERE id = ANY(${ids}::int[])
137
138
  `;
138
139
 
139
- return rows.map((r) =>
140
- this.toDomainEvent({
141
- ...r,
142
- status: OutboxEventStatus.PROCESSING,
143
- available_at: leaseUntil,
144
- }),
145
- );
146
- });
140
+ return rows.map((r) =>
141
+ this.toDomainEvent({
142
+ ...r,
143
+ status: OutboxEventStatus.PROCESSING,
144
+ available_at: leaseUntil,
145
+ }),
146
+ );
147
+ },
148
+ // Default maxWait (2s) is too tight when the pool is briefly under contention from other
149
+ // long-lived transactions elsewhere in the app; give this short, index-backed query a bit
150
+ // more slack to acquire a connection before giving up.
151
+ { maxWait: 5000, timeout: 5000 },
152
+ );
147
153
  }
148
154
 
149
155
  /**
@@ -20,6 +20,9 @@ export const INTEGRATION_SECRET_KEYS: Record<string, string[]> = {
20
20
  gmail: ['client_secret', 'refresh_token'],
21
21
  ses: ['secret_access_key'],
22
22
  resend: ['api_token', 'webhook_secret'],
23
+ // IMAP e um perfil de RECEBIMENTO: a senha (ou senha de app, no Google
24
+ // Workspace e no M365) da acesso de leitura a caixa inteira.
25
+ imap: ['password'],
23
26
  // Mensageria
24
27
  'evolution-api': ['token'],
25
28
  // app_secret assina o HMAC do webhook (X-Hub-Signature-256) e verify_token é
@@ -50,11 +50,49 @@ export function buildMailConfigFromIntegration(
50
50
  region: String(cfg.region ?? ''),
51
51
  from: String(cfg.from_email ?? ''),
52
52
  };
53
+ case 'imap':
54
+ // Sem este caso o operador que apontasse o perfil de envio para uma caixa
55
+ // IMAP receberia "Unsupported mail provider: imap" e ficaria procurando um
56
+ // provedor que na verdade existe — so nao serve para enviar.
57
+ throw new BadRequestException(
58
+ 'IMAP profiles are inbound-only and cannot be used to send mail. ' +
59
+ 'Choose an SMTP, Gmail, SES or Resend profile instead.',
60
+ );
53
61
  default:
54
62
  throw new BadRequestException(`Unsupported mail provider: ${providerSlug}`);
55
63
  }
56
64
  }
57
65
 
66
+ export type ImapIntegrationConfig = {
67
+ host: string;
68
+ port: number;
69
+ secure: boolean;
70
+ username: string;
71
+ password: string;
72
+ };
73
+
74
+ /**
75
+ * Mapeia o config de um perfil `imap` para as credenciais de leitura da caixa.
76
+ *
77
+ * `secure: true` e IMAPS direto na 993 (o normal); `false` e conexao limpa na
78
+ * 143 com STARTTLS. O padrao 993/true cobre Gmail, Microsoft 365, Zoho e cPanel.
79
+ */
80
+ export function buildImapConfigFromIntegration(
81
+ config: unknown,
82
+ ): ImapIntegrationConfig {
83
+ const cfg = (config as Cfg) ?? {};
84
+ const port = Number(cfg.port ?? 993);
85
+
86
+ return {
87
+ host: String(cfg.host ?? '').trim(),
88
+ port: Number.isFinite(port) && port > 0 ? port : 993,
89
+ // Só quem explicitamente marcou 143/STARTTLS sai do TLS direto.
90
+ secure: cfg.secure === undefined ? true : Boolean(cfg.secure),
91
+ username: String(cfg.username ?? '').trim(),
92
+ password: String(cfg.password ?? ''),
93
+ };
94
+ }
95
+
58
96
  export type ResendIntegrationConfig = {
59
97
  apiToken: string;
60
98
  domain: string;
@@ -1,4 +1,8 @@
1
- import { verifySesCredentials, verifySmtpConnection } from '@hed-hog/api-mail';
1
+ import {
2
+ verifyImapConnection,
3
+ verifySesCredentials,
4
+ verifySmtpConnection,
5
+ } from '@hed-hog/api-mail';
2
6
  import {
3
7
  describeHttpError,
4
8
  fail,
@@ -153,9 +157,38 @@ const resendTester: IntegrationTester = {
153
157
  },
154
158
  };
155
159
 
160
+ /**
161
+ * IMAP é o único provider de e-mail que não envia — ele lê uma caixa. Por isso
162
+ * é o único que não exige `from_email`.
163
+ */
164
+ const imapTester: IntegrationTester = {
165
+ providerSlug: 'imap',
166
+ test: async (config) => {
167
+ const missing = requireFields(config, [
168
+ ['host', 'IMAP host'],
169
+ ['username', 'Username'],
170
+ ['password', 'Password'],
171
+ ]);
172
+ if (missing) return missing;
173
+
174
+ const result = await verifyImapConnection({
175
+ host: str(config, 'host'),
176
+ port: Number(config.port ?? 993),
177
+ secure: config.secure === undefined ? true : Boolean(config.secure),
178
+ username: str(config, 'username'),
179
+ password: String(config.password ?? ''),
180
+ folder: str(config, 'folder') || 'INBOX',
181
+ timeoutMs: 10_000,
182
+ });
183
+
184
+ return result.ok ? pass(result.info) : fail(result.error!);
185
+ },
186
+ };
187
+
156
188
  export const EMAIL_TESTERS: IntegrationTester[] = [
157
189
  smtpTester,
158
190
  sesTester,
159
191
  gmailTester,
160
192
  resendTester,
193
+ imapTester,
161
194
  ];
@@ -171,6 +171,17 @@
171
171
  "not_found": "Mail template with id {id} not found."
172
172
  }
173
173
  },
174
+ "oauth": {
175
+ "callback": {
176
+ "codeRequired": "Authorization code is required to complete OAuth authentication.",
177
+ "invalidState": "OAuth callback state is invalid or expired. Start the sign-in flow again.",
178
+ "invalidType": "Unsupported OAuth callback type.",
179
+ "invalid": "OAuth callback is invalid or expired. Start the sign-in flow again.",
180
+ "alreadyProcessed": "This OAuth callback was already processed. Start the sign-in flow again.",
181
+ "inProgress": "This OAuth callback is already being processed. Wait a moment and try again.",
182
+ "unavailable": "Unable to complete OAuth authentication right now. Please try again."
183
+ }
184
+ },
174
185
  "mail_sent_not_found": "Mail sent record not found.",
175
186
  "validationSslugMustBeString": "Slug must be a string.",
176
187
  "sessionRevoked": "Session has been revoked or expired.",
@@ -171,6 +171,17 @@
171
171
  "not_found": "Modelo de e-mail com id {id} não encontrado."
172
172
  }
173
173
  },
174
+ "oauth": {
175
+ "callback": {
176
+ "codeRequired": "O código de autorização é obrigatório para concluir a autenticação.",
177
+ "invalidState": "O retorno da autenticação é inválido ou expirou. Inicie o login novamente.",
178
+ "invalidType": "Tipo de retorno de autenticação não suportado.",
179
+ "invalid": "O retorno da autenticação é inválido ou expirou. Inicie o login novamente.",
180
+ "alreadyProcessed": "Este retorno de autenticação já foi processado. Inicie o login novamente.",
181
+ "inProgress": "Este retorno de autenticação já está sendo processado. Aguarde um instante e tente novamente.",
182
+ "unavailable": "Não foi possível concluir a autenticação agora. Tente novamente."
183
+ }
184
+ },
174
185
  "mail_sent_not_found": "Registro de e-mail enviado não encontrado.",
175
186
  "validationSslugMustBeString": "O slug deve ser uma string.",
176
187
  "sessionRevoked": "Sessão foi revogada ou expirou.",
@@ -1,5 +1,5 @@
1
1
  import { getLocaleText } from '@hed-hog/api-locale';
2
- import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator';
2
+ import { IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from 'class-validator';
3
3
 
4
4
  export class SendTemplatedMailDTO {
5
5
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
@@ -21,8 +21,52 @@ export class SendTemplatedMailDTO {
21
21
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
22
22
  profileSlug?: string;
23
23
 
24
- /** Chave do app de origem (ex.: "class"), usada para resolver o nome de marca via a setting `app-names`. */
24
+ /**
25
+ * Endereco para onde a resposta do destinatario deve voltar.
26
+ *
27
+ * Sem isto a resposta cai no `from` do perfil, que nao carrega nenhuma
28
+ * identificacao do assunto tratado. E o que permite ao SAC endereçar cada
29
+ * chamado (`<protocolo>@dominio-de-resposta`) e reconhecer a resposta na
30
+ * entrada.
31
+ */
32
+ @IsOptional()
33
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
34
+ replyTo?: string;
35
+
36
+ /**
37
+ * Chave do app de origem (ex.: "class"). Nao afeta mais a marca —
38
+ * `{{brandName}}` vem sempre de `system-name`.
39
+ */
25
40
  @IsOptional()
26
41
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
27
42
  app?: string;
43
+
44
+ /**
45
+ * Destinatario, quando o chamador ja o conhece.
46
+ *
47
+ * Serve so para pular a busca do usuario pelo e-mail na hora de descobrir o
48
+ * idioma de preferencia; omitir nao muda o resultado.
49
+ */
50
+ @IsOptional()
51
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
52
+ userId?: number;
28
53
  }
54
+
55
+ /**
56
+ * Resultado de `MailService.sendTemplatedMail`.
57
+ *
58
+ * `sent: false` cobre tanto o aborto por configuração ausente quanto qualquer
59
+ * falha do provedor: os dois são tratados internamente e não lançam, então o
60
+ * retorno é a única forma de o chamador saber que nada saiu.
61
+ *
62
+ * Deliberadamente um objeto único em vez de união discriminada: as libraries
63
+ * compilam com `strictNullChecks: false`, e nessa configuração o `if (r.sent)`
64
+ * não estreita a união no ponto de uso.
65
+ */
66
+ export type SendTemplatedMailResult = {
67
+ sent: boolean;
68
+ /** Id devolvido pelo provedor quando `sent` é `true`. */
69
+ providerMessageId?: string | null;
70
+ reason?: 'mail-not-configured' | 'send-failed' | 'template-locale-missing';
71
+ error?: string;
72
+ };
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
- import { DEFAULT_MAIL_LAYOUT, renderMailTemplate } from '@hed-hog/api-mail';
2
+ import { DEFAULT_MAIL_LAYOUT, renderMailTemplate, resolveMailAssetUrl } from '@hed-hog/api-mail';
3
3
 
4
4
  // Testes do renderizador de e-mail moram aqui (e nao em packages/api-mail)
5
5
  // porque o api-mail nao tem jest configurado; o jest do core resolve o pacote.
@@ -192,6 +192,83 @@ describe('renderMailTemplate', () => {
192
192
  expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{content}}}');
193
193
  expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{header}}}');
194
194
  expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{footer}}}');
195
- expect(DEFAULT_MAIL_LAYOUT.header).toContain('{{brandName}}');
195
+ expect(DEFAULT_MAIL_LAYOUT.header).toContain('{{{brandMark}}}');
196
+ });
197
+
198
+ it('usa o selo de texto com a cor de marca quando nao ha logo', () => {
199
+ const html = renderMailTemplate({
200
+ subject: 'Bem-vindo',
201
+ body: '<p>Ola</p>',
202
+ brandName: 'Acme',
203
+ primaryColor: '#ff760c',
204
+ primaryForeground: '#ffffff',
205
+ });
206
+
207
+ expect(html).toContain('class="brand"');
208
+ expect(html).toContain('background:#ff760c');
209
+ expect(html).toContain('color:#ffffff');
210
+ expect(html).toContain('>Acme<');
211
+ // A cor tambem tempera o acento do painel e os links do corpo.
212
+ expect(html).toContain('background:#ff760c;"></div>');
213
+ });
214
+
215
+ it('usa a imagem do logo quando configurado, no lugar do selo de texto', () => {
216
+ const html = renderMailTemplate({
217
+ subject: 'Bem-vindo',
218
+ body: '<p>Ola</p>',
219
+ brandName: 'Acme',
220
+ logoUrl: 'https://cdn.example.com/logo.svg',
221
+ });
222
+
223
+ expect(html).toContain('<img src="https://cdn.example.com/logo.svg"');
224
+ expect(html).toContain('alt="Acme"');
225
+ expect(html).not.toContain('class="brand"');
226
+ });
227
+
228
+ it('ignora o logo quando a URL nao e publica, caindo no selo de texto', () => {
229
+ // Regressao do bug: com API_URL ausente, `image-url` = `/logo.svg` virava
230
+ // `http://localhost:3100/logo.svg` e o destinatario recebia um <img>
231
+ // quebrado, mostrando so o alt.
232
+ const html = renderMailTemplate({
233
+ subject: 'Bem-vindo',
234
+ body: '<p>Ola</p>',
235
+ brandName: 'Acme',
236
+ logoUrl: 'http://localhost:3100/logo.svg',
237
+ });
238
+
239
+ expect(html).not.toContain('<img');
240
+ expect(html).toContain('class="brand"');
241
+ expect(html).toContain('>Acme<');
242
+ });
243
+
244
+ it('cai para as cores padrao quando nenhuma e informada', () => {
245
+ const html = renderMailTemplate({ subject: 'x', body: 'corpo' });
246
+
247
+ expect(html).toContain('#111827');
248
+ expect(html).toContain('#ffffff');
249
+ });
250
+ });
251
+
252
+ describe('resolveMailAssetUrl', () => {
253
+ it('mantem URLs ja absolutas ou data: intactas', () => {
254
+ expect(resolveMailAssetUrl('https://cdn.example.com/logo.svg', 'http://localhost:3100')).toBe(
255
+ 'https://cdn.example.com/logo.svg',
256
+ );
257
+ expect(resolveMailAssetUrl('data:image/png;base64,abc', 'http://localhost:3100')).toBe(
258
+ 'data:image/png;base64,abc',
259
+ );
260
+ });
261
+
262
+ it('resolve caminhos relativos contra a base informada', () => {
263
+ expect(resolveMailAssetUrl('/logo.svg', 'http://localhost:3100')).toBe(
264
+ 'http://localhost:3100/logo.svg',
265
+ );
266
+ expect(resolveMailAssetUrl('/logo.svg', 'http://localhost:3100/')).toBe(
267
+ 'http://localhost:3100/logo.svg',
268
+ );
269
+ });
270
+
271
+ it('devolve string vazia quando a URL esta vazia', () => {
272
+ expect(resolveMailAssetUrl('', 'http://localhost:3100')).toBe('');
196
273
  });
197
274
  });
@@ -3,6 +3,7 @@ import { MailModule as MailMainModule, MailModuleOptions } from '@hed-hog/api-ma
3
3
  import { PaginationModule } from '@hed-hog/api-pagination';
4
4
  import { PrismaModule, PrismaService } from '@hed-hog/api-prisma';
5
5
  import { forwardRef, Module } from '@nestjs/common';
6
+ import { ConfigModule } from '@nestjs/config';
6
7
  import { MailSentModule } from '../mail-sent/mail-sent.module';
7
8
  import { SettingModule } from '../setting/setting.module';
8
9
  import { MailController } from './mail.controller';
@@ -95,6 +96,7 @@ import { MailService } from './mail.service';
95
96
  forwardRef(() => PaginationModule),
96
97
  forwardRef(() => LocaleModule),
97
98
  forwardRef(() => SettingModule),
99
+ ConfigModule,
98
100
  ],
99
101
  controllers: [MailController],
100
102
  providers: [MailService],