@hed-hog/core 0.0.386 → 0.0.390

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 (556) hide show
  1. package/README.md +12 -11
  2. package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
  3. package/dist/access-log/access-log.interceptor.js +4 -2
  4. package/dist/access-log/access-log.interceptor.js.map +1 -1
  5. package/dist/ai/ai-attachment.fragments.d.ts +23 -0
  6. package/dist/ai/ai-attachment.fragments.d.ts.map +1 -0
  7. package/dist/ai/ai-attachment.fragments.js +34 -0
  8. package/dist/ai/ai-attachment.fragments.js.map +1 -0
  9. package/dist/ai/ai.controller.d.ts +7 -16
  10. package/dist/ai/ai.controller.d.ts.map +1 -1
  11. package/dist/ai/ai.prompts.d.ts +16 -0
  12. package/dist/ai/ai.prompts.d.ts.map +1 -0
  13. package/dist/ai/ai.prompts.js +37 -0
  14. package/dist/ai/ai.prompts.js.map +1 -0
  15. package/dist/ai/ai.service.d.ts +63 -12
  16. package/dist/ai/ai.service.d.ts.map +1 -1
  17. package/dist/ai/ai.service.js +215 -88
  18. package/dist/ai/ai.service.js.map +1 -1
  19. package/dist/ai/dto/chat.dto.d.ts +4 -1
  20. package/dist/ai/dto/chat.dto.d.ts.map +1 -1
  21. package/dist/ai/dto/chat.dto.js +23 -1
  22. package/dist/ai/dto/chat.dto.js.map +1 -1
  23. package/dist/ai/dto/create-agent.dto.d.ts +1 -1
  24. package/dist/ai/dto/create-agent.dto.d.ts.map +1 -1
  25. package/dist/ai/dto/create-agent.dto.js +1 -1
  26. package/dist/ai/dto/create-agent.dto.js.map +1 -1
  27. package/dist/ai/dto/update-agent.dto.d.ts +1 -1
  28. package/dist/ai/dto/update-agent.dto.d.ts.map +1 -1
  29. package/dist/ai/dto/update-agent.dto.js +1 -1
  30. package/dist/ai/dto/update-agent.dto.js.map +1 -1
  31. package/dist/ai-instruction/ai-instruction.controller.d.ts +61 -7
  32. package/dist/ai-instruction/ai-instruction.controller.d.ts.map +1 -1
  33. package/dist/ai-instruction/ai-instruction.controller.js +63 -20
  34. package/dist/ai-instruction/ai-instruction.controller.js.map +1 -1
  35. package/dist/ai-instruction/ai-instruction.module.d.ts.map +1 -1
  36. package/dist/ai-instruction/ai-instruction.module.js +13 -2
  37. package/dist/ai-instruction/ai-instruction.module.js.map +1 -1
  38. package/dist/ai-instruction/ai-instruction.service.d.ts +131 -6
  39. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  40. package/dist/ai-instruction/ai-instruction.service.js +533 -60
  41. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  42. package/dist/ai-instruction/ai-prompt-builder.service.d.ts +8 -0
  43. package/dist/ai-instruction/ai-prompt-builder.service.d.ts.map +1 -1
  44. package/dist/ai-instruction/ai-prompt-builder.service.js +10 -1
  45. package/dist/ai-instruction/ai-prompt-builder.service.js.map +1 -1
  46. package/dist/ai-instruction/ai-prompt-registry.d.ts +45 -0
  47. package/dist/ai-instruction/ai-prompt-registry.d.ts.map +1 -0
  48. package/dist/ai-instruction/ai-prompt-registry.js +49 -0
  49. package/dist/ai-instruction/ai-prompt-registry.js.map +1 -0
  50. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +89 -0
  51. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -0
  52. package/dist/ai-instruction/ai-prompt-resolver.service.js +222 -0
  53. package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -0
  54. package/dist/ai-instruction/ai-prompt-template.util.d.ts +70 -0
  55. package/dist/ai-instruction/ai-prompt-template.util.d.ts.map +1 -0
  56. package/dist/ai-instruction/ai-prompt-template.util.js +154 -0
  57. package/dist/ai-instruction/ai-prompt-template.util.js.map +1 -0
  58. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts +5 -0
  59. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts.map +1 -1
  60. package/dist/ai-instruction/dto/create-ai-instruction.dto.js +27 -1
  61. package/dist/ai-instruction/dto/create-ai-instruction.dto.js.map +1 -1
  62. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts +14 -0
  63. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts.map +1 -0
  64. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js +42 -0
  65. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js.map +1 -0
  66. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts +11 -0
  67. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts.map +1 -1
  68. package/dist/ai-instruction/dto/update-ai-instruction.dto.js +34 -0
  69. package/dist/ai-instruction/dto/update-ai-instruction.dto.js.map +1 -1
  70. package/dist/ai-instruction/types/ai-instruction.types.d.ts +13 -1
  71. package/dist/ai-instruction/types/ai-instruction.types.d.ts.map +1 -1
  72. package/dist/ai-instruction/types/ai-instruction.types.js +10 -0
  73. package/dist/ai-instruction/types/ai-instruction.types.js.map +1 -1
  74. package/dist/auth/auth.controller.d.ts +26 -10
  75. package/dist/auth/auth.controller.d.ts.map +1 -1
  76. package/dist/auth/auth.controller.js +49 -21
  77. package/dist/auth/auth.controller.js.map +1 -1
  78. package/dist/auth/auth.service.d.ts +35 -9
  79. package/dist/auth/auth.service.d.ts.map +1 -1
  80. package/dist/auth/auth.service.js +78 -29
  81. package/dist/auth/auth.service.js.map +1 -1
  82. package/dist/captcha/captcha.module.d.ts +3 -0
  83. package/dist/captcha/captcha.module.d.ts.map +1 -0
  84. package/dist/captcha/captcha.module.js +23 -0
  85. package/dist/captcha/captcha.module.js.map +1 -0
  86. package/dist/captcha/captcha.service.d.ts +38 -0
  87. package/dist/captcha/captcha.service.d.ts.map +1 -0
  88. package/dist/captcha/captcha.service.js +144 -0
  89. package/dist/captcha/captcha.service.js.map +1 -0
  90. package/dist/challenge/challenge.service.d.ts +16 -7
  91. package/dist/challenge/challenge.service.d.ts.map +1 -1
  92. package/dist/challenge/challenge.service.js +64 -16
  93. package/dist/challenge/challenge.service.js.map +1 -1
  94. package/dist/core.module.d.ts +3 -1
  95. package/dist/core.module.d.ts.map +1 -1
  96. package/dist/core.module.js +29 -2
  97. package/dist/core.module.js.map +1 -1
  98. package/dist/dashboard/dashboard/dashboard.controller.d.ts +15 -15
  99. package/dist/dashboard/dashboard/dashboard.service.d.ts +15 -15
  100. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +6 -6
  101. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +6 -6
  102. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +20 -20
  103. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +20 -20
  104. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +1 -1
  105. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +1 -1
  106. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +13 -13
  107. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +13 -13
  108. package/dist/file/file.service.d.ts +6 -0
  109. package/dist/file/file.service.d.ts.map +1 -1
  110. package/dist/file/file.service.js +44 -4
  111. package/dist/file/file.service.js.map +1 -1
  112. package/dist/incident/incident-notifier.service.d.ts +36 -0
  113. package/dist/incident/incident-notifier.service.d.ts.map +1 -0
  114. package/dist/incident/incident-notifier.service.js +208 -0
  115. package/dist/incident/incident-notifier.service.js.map +1 -0
  116. package/dist/incident/incident.controller.d.ts +23 -0
  117. package/dist/incident/incident.controller.d.ts.map +1 -0
  118. package/dist/incident/incident.controller.js +75 -0
  119. package/dist/incident/incident.controller.js.map +1 -0
  120. package/dist/incident/incident.module.d.ts +7 -0
  121. package/dist/incident/incident.module.d.ts.map +1 -0
  122. package/dist/incident/incident.module.js +32 -0
  123. package/dist/incident/incident.module.js.map +1 -0
  124. package/dist/incident/incident.service.d.ts +72 -0
  125. package/dist/incident/incident.service.d.ts.map +1 -0
  126. package/dist/incident/incident.service.js +184 -0
  127. package/dist/incident/incident.service.js.map +1 -0
  128. package/dist/index.d.ts +27 -0
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +31 -0
  131. package/dist/index.js.map +1 -1
  132. package/dist/integration-profile/dto/test.dto.d.ts +13 -0
  133. package/dist/integration-profile/dto/test.dto.d.ts.map +1 -0
  134. package/dist/integration-profile/dto/test.dto.js +26 -0
  135. package/dist/integration-profile/dto/test.dto.js.map +1 -0
  136. package/dist/integration-profile/integration-profile.controller.d.ts +12 -1
  137. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
  138. package/dist/integration-profile/integration-profile.controller.js +9 -1
  139. package/dist/integration-profile/integration-profile.controller.js.map +1 -1
  140. package/dist/integration-profile/integration-profile.module.d.ts +6 -1
  141. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
  142. package/dist/integration-profile/integration-profile.module.js +26 -3
  143. package/dist/integration-profile/integration-profile.module.js.map +1 -1
  144. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  145. package/dist/integration-profile/integration-profile.secrets.js +12 -2
  146. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  147. package/dist/integration-profile/integration-profile.service.d.ts +28 -2
  148. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  149. package/dist/integration-profile/integration-profile.service.js +105 -31
  150. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  151. package/dist/integration-profile/integration-profile.utils.d.ts +31 -1
  152. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  153. package/dist/integration-profile/integration-profile.utils.js +35 -4
  154. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  155. package/dist/integration-profile/testers/ai.testers.d.ts +3 -0
  156. package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -0
  157. package/dist/integration-profile/testers/ai.testers.js +125 -0
  158. package/dist/integration-profile/testers/ai.testers.js.map +1 -0
  159. package/dist/integration-profile/testers/captcha.testers.d.ts +3 -0
  160. package/dist/integration-profile/testers/captcha.testers.d.ts.map +1 -0
  161. package/dist/integration-profile/testers/captcha.testers.js +121 -0
  162. package/dist/integration-profile/testers/captcha.testers.js.map +1 -0
  163. package/dist/integration-profile/testers/email.testers.d.ts +3 -0
  164. package/dist/integration-profile/testers/email.testers.d.ts.map +1 -0
  165. package/dist/integration-profile/testers/email.testers.js +128 -0
  166. package/dist/integration-profile/testers/email.testers.js.map +1 -0
  167. package/dist/integration-profile/testers/http-probe.d.ts +57 -0
  168. package/dist/integration-profile/testers/http-probe.d.ts.map +1 -0
  169. package/dist/integration-profile/testers/http-probe.js +140 -0
  170. package/dist/integration-profile/testers/http-probe.js.map +1 -0
  171. package/dist/integration-profile/testers/index.d.ts +12 -0
  172. package/dist/integration-profile/testers/index.d.ts.map +1 -0
  173. package/dist/integration-profile/testers/index.js +45 -0
  174. package/dist/integration-profile/testers/index.js.map +1 -0
  175. package/dist/integration-profile/testers/infrastructure.testers.d.ts +3 -0
  176. package/dist/integration-profile/testers/infrastructure.testers.d.ts.map +1 -0
  177. package/dist/integration-profile/testers/infrastructure.testers.js +105 -0
  178. package/dist/integration-profile/testers/infrastructure.testers.js.map +1 -0
  179. package/dist/integration-profile/testers/integration-tester.registry.d.ts +17 -0
  180. package/dist/integration-profile/testers/integration-tester.registry.d.ts.map +1 -0
  181. package/dist/integration-profile/testers/integration-tester.registry.js +52 -0
  182. package/dist/integration-profile/testers/integration-tester.registry.js.map +1 -0
  183. package/dist/integration-profile/testers/integration-tester.types.d.ts +55 -0
  184. package/dist/integration-profile/testers/integration-tester.types.d.ts.map +1 -0
  185. package/dist/integration-profile/testers/integration-tester.types.js +16 -0
  186. package/dist/integration-profile/testers/integration-tester.types.js.map +1 -0
  187. package/dist/integration-profile/testers/messaging.testers.d.ts +3 -0
  188. package/dist/integration-profile/testers/messaging.testers.d.ts.map +1 -0
  189. package/dist/integration-profile/testers/messaging.testers.js +70 -0
  190. package/dist/integration-profile/testers/messaging.testers.js.map +1 -0
  191. package/dist/integration-profile/testers/oauth.testers.d.ts +3 -0
  192. package/dist/integration-profile/testers/oauth.testers.d.ts.map +1 -0
  193. package/dist/integration-profile/testers/oauth.testers.js +285 -0
  194. package/dist/integration-profile/testers/oauth.testers.js.map +1 -0
  195. package/dist/integration-profile/testers/payment.testers.d.ts +3 -0
  196. package/dist/integration-profile/testers/payment.testers.d.ts.map +1 -0
  197. package/dist/integration-profile/testers/payment.testers.js +169 -0
  198. package/dist/integration-profile/testers/payment.testers.js.map +1 -0
  199. package/dist/integration-profile/testers/storage.testers.d.ts +3 -0
  200. package/dist/integration-profile/testers/storage.testers.d.ts.map +1 -0
  201. package/dist/integration-profile/testers/storage.testers.js +208 -0
  202. package/dist/integration-profile/testers/storage.testers.js.map +1 -0
  203. package/dist/language/en.json +4 -2
  204. package/dist/language/pt.json +4 -2
  205. package/dist/leaked-password/leaked-password.controller.d.ts +24 -0
  206. package/dist/leaked-password/leaked-password.controller.d.ts.map +1 -0
  207. package/dist/leaked-password/leaked-password.controller.js +78 -0
  208. package/dist/leaked-password/leaked-password.controller.js.map +1 -0
  209. package/dist/leaked-password/leaked-password.module.d.ts +8 -0
  210. package/dist/leaked-password/leaked-password.module.d.ts.map +1 -0
  211. package/dist/leaked-password/leaked-password.module.js +31 -0
  212. package/dist/leaked-password/leaked-password.module.js.map +1 -0
  213. package/dist/leaked-password/leaked-password.service.d.ts +64 -0
  214. package/dist/leaked-password/leaked-password.service.d.ts.map +1 -0
  215. package/dist/leaked-password/leaked-password.service.js +218 -0
  216. package/dist/leaked-password/leaked-password.service.js.map +1 -0
  217. package/dist/locale/locale.controller.d.ts +1 -1
  218. package/dist/locale/locale.service.d.ts +1 -1
  219. package/dist/mail/dto/preview.dto.d.ts +18 -0
  220. package/dist/mail/dto/preview.dto.d.ts.map +1 -0
  221. package/dist/mail/dto/preview.dto.js +66 -0
  222. package/dist/mail/dto/preview.dto.js.map +1 -0
  223. package/dist/mail/dto/send.dto.d.ts +7 -0
  224. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  225. package/dist/mail/dto/send.dto.js +10 -0
  226. package/dist/mail/dto/send.dto.js.map +1 -1
  227. package/dist/mail/mail.controller.d.ts +4 -0
  228. package/dist/mail/mail.controller.d.ts.map +1 -1
  229. package/dist/mail/mail.controller.js +16 -2
  230. package/dist/mail/mail.controller.js.map +1 -1
  231. package/dist/mail/mail.service.d.ts +23 -2
  232. package/dist/mail/mail.service.d.ts.map +1 -1
  233. package/dist/mail/mail.service.js +102 -13
  234. package/dist/mail/mail.service.js.map +1 -1
  235. package/dist/mail-layout/dto/update.dto.d.ts +17 -0
  236. package/dist/mail-layout/dto/update.dto.d.ts.map +1 -0
  237. package/dist/mail-layout/dto/update.dto.js +27 -0
  238. package/dist/mail-layout/dto/update.dto.js.map +1 -0
  239. package/dist/mail-layout/mail-layout.controller.d.ts +15 -0
  240. package/dist/mail-layout/mail-layout.controller.d.ts.map +1 -0
  241. package/dist/mail-layout/mail-layout.controller.js +71 -0
  242. package/dist/mail-layout/mail-layout.controller.js.map +1 -0
  243. package/dist/mail-layout/mail-layout.module.d.ts +3 -0
  244. package/dist/mail-layout/mail-layout.module.d.ts.map +1 -0
  245. package/dist/mail-layout/mail-layout.module.js +35 -0
  246. package/dist/mail-layout/mail-layout.module.js.map +1 -0
  247. package/dist/mail-layout/mail-layout.service.d.ts +27 -0
  248. package/dist/mail-layout/mail-layout.service.d.ts.map +1 -0
  249. package/dist/mail-layout/mail-layout.service.js +80 -0
  250. package/dist/mail-layout/mail-layout.service.js.map +1 -0
  251. package/dist/mcp-chat/mcp-chat.controller.d.ts +2 -2
  252. package/dist/mcp-chat/mcp-chat.service.d.ts +24 -2
  253. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  254. package/dist/mcp-chat/mcp-chat.service.js +80 -24
  255. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  256. package/dist/menu/menu.controller.d.ts +3 -3
  257. package/dist/menu/menu.service.d.ts +3 -3
  258. package/dist/notification/notification.controller.d.ts +12 -12
  259. package/dist/notification/notification.service.d.ts +12 -12
  260. package/dist/oauth/oauth-token-broker.service.d.ts +72 -0
  261. package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -0
  262. package/dist/oauth/oauth-token-broker.service.js +258 -0
  263. package/dist/oauth/oauth-token-broker.service.js.map +1 -0
  264. package/dist/oauth/oauth.module.d.ts.map +1 -1
  265. package/dist/oauth/oauth.module.js +3 -1
  266. package/dist/oauth/oauth.module.js.map +1 -1
  267. package/dist/oauth/oauth.service.d.ts +2 -0
  268. package/dist/oauth/oauth.service.d.ts.map +1 -1
  269. package/dist/oauth/oauth.service.js +6 -3
  270. package/dist/oauth/oauth.service.js.map +1 -1
  271. package/dist/pdf/pdf.service.d.ts +8 -0
  272. package/dist/pdf/pdf.service.d.ts.map +1 -1
  273. package/dist/pdf/pdf.service.js +52 -0
  274. package/dist/pdf/pdf.service.js.map +1 -1
  275. package/dist/presence/presence.controller.d.ts +16 -0
  276. package/dist/presence/presence.controller.d.ts.map +1 -0
  277. package/dist/presence/presence.controller.js +60 -0
  278. package/dist/presence/presence.controller.js.map +1 -0
  279. package/dist/presence/presence.listener.d.ts +29 -0
  280. package/dist/presence/presence.listener.d.ts.map +1 -0
  281. package/dist/presence/presence.listener.js +82 -0
  282. package/dist/presence/presence.listener.js.map +1 -0
  283. package/dist/presence/presence.module.d.ts +3 -0
  284. package/dist/presence/presence.module.d.ts.map +1 -0
  285. package/dist/presence/presence.module.js +32 -0
  286. package/dist/presence/presence.module.js.map +1 -0
  287. package/dist/presence/presence.service.d.ts +42 -0
  288. package/dist/presence/presence.service.d.ts.map +1 -0
  289. package/dist/presence/presence.service.js +147 -0
  290. package/dist/presence/presence.service.js.map +1 -0
  291. package/dist/profile/dto/delete-account.dto.d.ts +8 -0
  292. package/dist/profile/dto/delete-account.dto.d.ts.map +1 -0
  293. package/dist/profile/dto/delete-account.dto.js +22 -0
  294. package/dist/profile/dto/delete-account.dto.js.map +1 -0
  295. package/dist/profile/events/profile-account-deleted.event.d.ts +12 -0
  296. package/dist/profile/events/profile-account-deleted.event.d.ts.map +1 -0
  297. package/dist/profile/events/profile-account-deleted.event.js +17 -0
  298. package/dist/profile/events/profile-account-deleted.event.js.map +1 -0
  299. package/dist/profile/profile.controller.d.ts +24 -17
  300. package/dist/profile/profile.controller.d.ts.map +1 -1
  301. package/dist/profile/profile.controller.js +75 -46
  302. package/dist/profile/profile.controller.js.map +1 -1
  303. package/dist/profile/profile.service.d.ts +34 -21
  304. package/dist/profile/profile.service.d.ts.map +1 -1
  305. package/dist/profile/profile.service.js +113 -37
  306. package/dist/profile/profile.service.js.map +1 -1
  307. package/dist/realtime/realtime.gateway.d.ts +48 -0
  308. package/dist/realtime/realtime.gateway.d.ts.map +1 -0
  309. package/dist/realtime/realtime.gateway.js +156 -0
  310. package/dist/realtime/realtime.gateway.js.map +1 -0
  311. package/dist/realtime/realtime.module.d.ts +8 -0
  312. package/dist/realtime/realtime.module.d.ts.map +1 -0
  313. package/dist/realtime/realtime.module.js +29 -0
  314. package/dist/realtime/realtime.module.js.map +1 -0
  315. package/dist/realtime/realtime.service.d.ts +51 -0
  316. package/dist/realtime/realtime.service.d.ts.map +1 -0
  317. package/dist/realtime/realtime.service.js +177 -0
  318. package/dist/realtime/realtime.service.js.map +1 -0
  319. package/dist/redis/redis-pubsub.d.ts +27 -0
  320. package/dist/redis/redis-pubsub.d.ts.map +1 -0
  321. package/dist/redis/redis-pubsub.js +140 -0
  322. package/dist/redis/redis-pubsub.js.map +1 -0
  323. package/dist/role/role.controller.d.ts +3 -3
  324. package/dist/role/role.service.d.ts +3 -3
  325. package/dist/setting/dto/create.dto.d.ts.map +1 -1
  326. package/dist/setting/dto/create.dto.js +9 -2
  327. package/dist/setting/dto/create.dto.js.map +1 -1
  328. package/dist/setting/setting.controller.d.ts +5 -5
  329. package/dist/setting/setting.service.d.ts +8 -5
  330. package/dist/setting/setting.service.d.ts.map +1 -1
  331. package/dist/setting/setting.service.js +25 -1
  332. package/dist/setting/setting.service.js.map +1 -1
  333. package/dist/user/dto/create-with-email-and-password.dto.d.ts +2 -0
  334. package/dist/user/dto/create-with-email-and-password.dto.d.ts.map +1 -1
  335. package/dist/user/dto/create-with-email-and-password.dto.js +5 -0
  336. package/dist/user/dto/create-with-email-and-password.dto.js.map +1 -1
  337. package/dist/user/dto/query-user-mail-list.dto.d.ts +5 -0
  338. package/dist/user/dto/query-user-mail-list.dto.d.ts.map +1 -0
  339. package/dist/user/dto/query-user-mail-list.dto.js +25 -0
  340. package/dist/user/dto/query-user-mail-list.dto.js.map +1 -0
  341. package/dist/user/user-credential.util.d.ts +19 -0
  342. package/dist/user/user-credential.util.d.ts.map +1 -0
  343. package/dist/user/user-credential.util.js +32 -0
  344. package/dist/user/user-credential.util.js.map +1 -0
  345. package/dist/user/user.controller.d.ts +17 -5
  346. package/dist/user/user.controller.d.ts.map +1 -1
  347. package/dist/user/user.controller.js +18 -3
  348. package/dist/user/user.controller.js.map +1 -1
  349. package/dist/user/user.service.d.ts +28 -6
  350. package/dist/user/user.service.d.ts.map +1 -1
  351. package/dist/user/user.service.js +204 -13
  352. package/dist/user/user.service.js.map +1 -1
  353. package/dist/validators/is-strong-password-with-settings.validator.d.ts.map +1 -1
  354. package/dist/validators/is-strong-password-with-settings.validator.js +11 -0
  355. package/dist/validators/is-strong-password-with-settings.validator.js.map +1 -1
  356. package/dist/validators/service-locator.d.ts +9 -0
  357. package/dist/validators/service-locator.d.ts.map +1 -1
  358. package/dist/validators/service-locator.js +12 -0
  359. package/dist/validators/service-locator.js.map +1 -1
  360. package/dist/webhook-integration/webhook-integration.controller.d.ts +5 -8
  361. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  362. package/dist/webhook-integration/webhook-integration.controller.js +31 -8
  363. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  364. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  365. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  366. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  367. package/dist/webhook-integration/webhook-integration.service.d.ts +33 -1
  368. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  369. package/dist/webhook-integration/webhook-integration.service.js +109 -4
  370. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  371. package/dist/webhook-integration/webhook-protocol.registry.d.ts +63 -0
  372. package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -0
  373. package/dist/webhook-integration/webhook-protocol.registry.js +51 -0
  374. package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -0
  375. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts +3 -0
  376. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts.map +1 -0
  377. package/dist/whatsapp-sender/whatsapp-sender.module.js +23 -0
  378. package/dist/whatsapp-sender/whatsapp-sender.module.js.map +1 -0
  379. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +50 -0
  380. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -0
  381. package/dist/whatsapp-sender/whatsapp-sender.service.js +224 -0
  382. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -0
  383. package/hedhog/data/integration_provider.yaml +16 -0
  384. package/hedhog/data/mail.yaml +88 -61
  385. package/hedhog/data/mail_layout.yaml +188 -0
  386. package/hedhog/data/menu.yaml +67 -3
  387. package/hedhog/data/route.yaml +135 -0
  388. package/hedhog/data/setting_group.yaml +139 -1
  389. package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +36 -0
  390. package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +2 -0
  391. package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +10 -7
  392. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +7 -4
  393. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +580 -0
  394. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-preview.tsx.ejs +150 -0
  395. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +199 -0
  396. package/hedhog/frontend/app/ai_instruction/_components/prompt-diff.tsx.ejs +63 -0
  397. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.spec.ts.ejs +59 -0
  398. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.ts.ejs +72 -0
  399. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.spec.ts.ejs +170 -0
  400. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.ts.ejs +167 -0
  401. package/hedhog/frontend/app/ai_instruction/_lib/types.ts.ejs +99 -0
  402. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +422 -0
  403. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +51 -4
  404. package/hedhog/frontend/app/incidents/page.tsx.ejs +400 -0
  405. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +169 -92
  406. package/hedhog/frontend/app/mail/layout/page.tsx.ejs +344 -0
  407. package/hedhog/frontend/app/mail/template/page.tsx.ejs +155 -121
  408. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +129 -17
  409. package/hedhog/frontend/app/pages/_components/page-delete-dialog.tsx.ejs +343 -0
  410. package/hedhog/frontend/app/pages/_components/page-detail-sheet.tsx.ejs +438 -0
  411. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1009 -0
  412. package/hedhog/frontend/app/pages/_components/system-page-types.ts.ejs +56 -0
  413. package/hedhog/frontend/app/pages/_components/use-system-pages.ts.ejs +171 -0
  414. package/hedhog/frontend/app/pages/page.tsx.ejs +514 -0
  415. package/hedhog/frontend/app/users/page.tsx.ejs +119 -13
  416. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +231 -0
  417. package/hedhog/frontend/app/users/user-mails.tsx.ejs +399 -0
  418. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +60 -75
  419. package/hedhog/frontend/messages/en.json +521 -3
  420. package/hedhog/frontend/messages/pt.json +521 -3
  421. package/hedhog/table/ai_agent.yaml +1 -0
  422. package/hedhog/table/ai_instruction.yaml +15 -0
  423. package/hedhog/table/leaked_password.yaml +18 -0
  424. package/hedhog/table/mail_layout.yaml +35 -0
  425. package/hedhog/table/mail_var.yaml +4 -0
  426. package/hedhog/table/setting.yaml +1 -0
  427. package/hedhog/table/system_incident.yaml +59 -0
  428. package/hedhog/table/user_presence.yaml +36 -0
  429. package/hedhog/table/webhook_integration.yaml +64 -43
  430. package/package.json +8 -4
  431. package/src/access-log/access-log.interceptor.ts +4 -2
  432. package/src/ai/ai-attachment.fragments.spec.ts +319 -0
  433. package/src/ai/ai-attachment.fragments.ts +35 -0
  434. package/src/ai/ai.prompts.ts +38 -0
  435. package/src/ai/ai.service.spec.ts +280 -0
  436. package/src/ai/ai.service.ts +338 -82
  437. package/src/ai/dto/chat.dto.ts +33 -3
  438. package/src/ai/dto/create-agent.dto.ts +2 -2
  439. package/src/ai/dto/update-agent.dto.ts +2 -2
  440. package/src/ai-instruction/ai-instruction.controller.ts +40 -9
  441. package/src/ai-instruction/ai-instruction.module.ts +13 -2
  442. package/src/ai-instruction/ai-instruction.service.spec.ts +658 -0
  443. package/src/ai-instruction/ai-instruction.service.ts +661 -62
  444. package/src/ai-instruction/ai-prompt-builder.service.ts +10 -1
  445. package/src/ai-instruction/ai-prompt-registry.ts +87 -0
  446. package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +375 -0
  447. package/src/ai-instruction/ai-prompt-resolver.service.ts +295 -0
  448. package/src/ai-instruction/ai-prompt-template.util.spec.ts +162 -0
  449. package/src/ai-instruction/ai-prompt-template.util.ts +199 -0
  450. package/src/ai-instruction/dto/create-ai-instruction.dto.ts +39 -3
  451. package/src/ai-instruction/dto/preview-ai-instruction.dto.ts +26 -0
  452. package/src/ai-instruction/dto/update-ai-instruction.dto.ts +42 -1
  453. package/src/ai-instruction/types/ai-instruction.types.ts +24 -1
  454. package/src/auth/auth.controller.ts +33 -6
  455. package/src/auth/auth.service.spec.ts +3 -1
  456. package/src/auth/auth.service.ts +103 -37
  457. package/src/captcha/captcha.module.ts +10 -0
  458. package/src/captcha/captcha.service.spec.ts +210 -0
  459. package/src/captcha/captcha.service.ts +191 -0
  460. package/src/challenge/challenge.service.spec.ts +39 -4
  461. package/src/challenge/challenge.service.ts +72 -17
  462. package/src/core.module.ts +27 -1
  463. package/src/file/file.service.spec.ts +105 -0
  464. package/src/file/file.service.ts +60 -3
  465. package/src/incident/incident-notifier.service.spec.ts +205 -0
  466. package/src/incident/incident-notifier.service.ts +241 -0
  467. package/src/incident/incident.controller.ts +38 -0
  468. package/src/incident/incident.module.ts +19 -0
  469. package/src/incident/incident.service.spec.ts +150 -0
  470. package/src/incident/incident.service.ts +209 -0
  471. package/src/index.ts +38 -0
  472. package/src/integration-profile/dto/test.dto.ts +19 -0
  473. package/src/integration-profile/integration-profile.controller.ts +10 -1
  474. package/src/integration-profile/integration-profile.module.spec.ts +51 -0
  475. package/src/integration-profile/integration-profile.module.ts +23 -4
  476. package/src/integration-profile/integration-profile.secrets.ts +12 -2
  477. package/src/integration-profile/integration-profile.service.spec.ts +279 -0
  478. package/src/integration-profile/integration-profile.service.ts +132 -29
  479. package/src/integration-profile/integration-profile.utils.spec.ts +97 -0
  480. package/src/integration-profile/integration-profile.utils.ts +79 -3
  481. package/src/integration-profile/testers/ai.testers.ts +165 -0
  482. package/src/integration-profile/testers/captcha.testers.spec.ts +92 -0
  483. package/src/integration-profile/testers/captcha.testers.ts +139 -0
  484. package/src/integration-profile/testers/email.testers.ts +161 -0
  485. package/src/integration-profile/testers/http-probe.spec.ts +153 -0
  486. package/src/integration-profile/testers/http-probe.ts +171 -0
  487. package/src/integration-profile/testers/index.ts +30 -0
  488. package/src/integration-profile/testers/infrastructure.testers.ts +122 -0
  489. package/src/integration-profile/testers/integration-tester.registry.spec.ts +75 -0
  490. package/src/integration-profile/testers/integration-tester.registry.ts +43 -0
  491. package/src/integration-profile/testers/integration-tester.types.ts +61 -0
  492. package/src/integration-profile/testers/messaging.testers.ts +102 -0
  493. package/src/integration-profile/testers/oauth.testers.spec.ts +220 -0
  494. package/src/integration-profile/testers/oauth.testers.ts +305 -0
  495. package/src/integration-profile/testers/payment.testers.ts +185 -0
  496. package/src/integration-profile/testers/remaining.testers.spec.ts +401 -0
  497. package/src/integration-profile/testers/storage.testers.spec.ts +185 -0
  498. package/src/integration-profile/testers/storage.testers.ts +226 -0
  499. package/src/integration-profile/testers/tester-coverage.spec.ts +58 -0
  500. package/src/language/en.json +4 -2
  501. package/src/language/pt.json +4 -2
  502. package/src/leaked-password/README.md +83 -0
  503. package/src/leaked-password/leaked-password.controller.ts +52 -0
  504. package/src/leaked-password/leaked-password.module.ts +18 -0
  505. package/src/leaked-password/leaked-password.service.ts +242 -0
  506. package/src/leaked-password/seed/build-seed-sql.mjs +180 -0
  507. package/src/leaked-password/seed/common-passwords.txt +335 -0
  508. package/src/mail/dto/preview.dto.ts +50 -0
  509. package/src/mail/dto/send.dto.ts +14 -1
  510. package/src/mail/mail-template.spec.ts +197 -0
  511. package/src/mail/mail.controller.ts +9 -2
  512. package/src/mail/mail.service.spec.ts +116 -1
  513. package/src/mail/mail.service.ts +118 -12
  514. package/src/mail-layout/dto/update.dto.ts +19 -0
  515. package/src/mail-layout/mail-layout.controller.ts +47 -0
  516. package/src/mail-layout/mail-layout.module.ts +22 -0
  517. package/src/mail-layout/mail-layout.service.ts +94 -0
  518. package/src/mcp-chat/mcp-chat.service.spec.ts +236 -0
  519. package/src/mcp-chat/mcp-chat.service.ts +122 -26
  520. package/src/oauth/oauth-token-broker.service.spec.ts +172 -0
  521. package/src/oauth/oauth-token-broker.service.ts +363 -0
  522. package/src/oauth/oauth.module.ts +3 -1
  523. package/src/oauth/oauth.service.ts +8 -2
  524. package/src/pdf/pdf.service.ts +75 -0
  525. package/src/presence/presence.controller.ts +29 -0
  526. package/src/presence/presence.listener.ts +53 -0
  527. package/src/presence/presence.module.ts +19 -0
  528. package/src/presence/presence.service.spec.ts +97 -0
  529. package/src/presence/presence.service.ts +160 -0
  530. package/src/profile/dto/delete-account.dto.ts +11 -0
  531. package/src/profile/events/profile-account-deleted.event.ts +13 -0
  532. package/src/profile/profile.controller.ts +52 -36
  533. package/src/profile/profile.service.ts +143 -41
  534. package/src/realtime/realtime.gateway.ts +138 -0
  535. package/src/realtime/realtime.module.ts +16 -0
  536. package/src/realtime/realtime.service.spec.ts +50 -0
  537. package/src/realtime/realtime.service.ts +187 -0
  538. package/src/redis/redis-pubsub.ts +179 -0
  539. package/src/setting/dto/create.dto.ts +13 -2
  540. package/src/setting/setting.service.spec.ts +56 -0
  541. package/src/setting/setting.service.ts +29 -1
  542. package/src/user/dto/create-with-email-and-password.dto.ts +6 -1
  543. package/src/user/dto/query-user-mail-list.dto.ts +10 -0
  544. package/src/user/user-credential.util.ts +35 -0
  545. package/src/user/user.controller.ts +17 -2
  546. package/src/user/user.service.spec.ts +153 -0
  547. package/src/user/user.service.ts +255 -12
  548. package/src/validators/is-strong-password-with-settings.validator.ts +12 -0
  549. package/src/validators/service-locator.ts +15 -0
  550. package/src/webhook-integration/webhook-integration.controller.ts +40 -2
  551. package/src/webhook-integration/webhook-integration.module.ts +5 -0
  552. package/src/webhook-integration/webhook-integration.service.ts +133 -0
  553. package/src/webhook-integration/webhook-protocol.registry.spec.ts +70 -0
  554. package/src/webhook-integration/webhook-protocol.registry.ts +93 -0
  555. package/src/whatsapp-sender/whatsapp-sender.module.ts +10 -0
  556. package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
@@ -7,6 +7,7 @@ import {
7
7
  BadRequestException,
8
8
  HttpException,
9
9
  Injectable,
10
+ Logger,
10
11
  NotFoundException,
11
12
  } from '@nestjs/common';
12
13
  import { EventEmitter } from 'events';
@@ -25,6 +26,11 @@ import {
25
26
  UpdateWebhookActionDTO,
26
27
  } from './dto/webhook-action.dto';
27
28
  import { AppCommandRegistry } from '../app-command/app-command.registry';
29
+ import {
30
+ WebhookProtocolRegistry,
31
+ WebhookProtocolRequest,
32
+ WebhookProtocolResponse,
33
+ } from './webhook-protocol.registry';
28
34
  import {
29
35
  createPlainToken,
30
36
  createPublicUuid,
@@ -58,6 +64,7 @@ type WebhookActionPayload =
58
64
 
59
65
  @Injectable()
60
66
  export class WebhookIntegrationService {
67
+ private readonly logger = new Logger(WebhookIntegrationService.name);
61
68
  private readonly logEmitter = new EventEmitter();
62
69
  private readonly webhookActionSelect = {
63
70
  id: true,
@@ -105,6 +112,7 @@ export class WebhookIntegrationService {
105
112
  private readonly settingService: SettingService,
106
113
  private readonly commandRegistry: AppCommandRegistry,
107
114
  private readonly mcpApiKeyService: McpApiKeyService,
115
+ private readonly protocolRegistry: WebhookProtocolRegistry,
108
116
  ) {}
109
117
 
110
118
  private get integrations() {
@@ -551,6 +559,131 @@ export class WebhookIntegrationService {
551
559
  });
552
560
  }
553
561
 
562
+ /**
563
+ * Cria (ou devolve) o webhook de um perfil de integração que fala um protocolo
564
+ * próprio. Idempotente: a chave é (protocol, integration_profile_id), então
565
+ * chamar de novo devolve o mesmo UUID — e a URL já registrada no painel do
566
+ * provedor continua válida.
567
+ *
568
+ * Existe para que módulos não precisem montar a URL pública por conta própria:
569
+ * `/webhook/:uuid` e a base vinda dos settings ficam sob responsabilidade do core.
570
+ */
571
+ async provisionProtocolWebhook(input: {
572
+ slug: string;
573
+ name: string;
574
+ protocol: string;
575
+ integrationProfileId: number;
576
+ description?: string;
577
+ }) {
578
+ const existing = await this.integrations.findFirst({
579
+ where: {
580
+ protocol: input.protocol,
581
+ integration_profile_id: input.integrationProfileId,
582
+ },
583
+ });
584
+
585
+ if (existing) return this.withPublicUrl(existing);
586
+
587
+ const created = await this.integrations.create({
588
+ data: {
589
+ slug: await this.ensureProvisionSlug(input.slug),
590
+ name: input.name,
591
+ description: input.description ?? null,
592
+ protocol: input.protocol,
593
+ integration_profile_id: input.integrationProfileId,
594
+ public_uuid: createPublicUuid(),
595
+ status: 'active',
596
+ // Webhooks de protocolo não usam o token estático do core: o adaptador
597
+ // faz a própria autenticação (HMAC, no caso da Meta).
598
+ require_token: false,
599
+ },
600
+ });
601
+
602
+ this.logger.log(
603
+ `Provisioned "${input.protocol}" webhook ${created.id} for integration profile ${input.integrationProfileId}`,
604
+ );
605
+
606
+ return this.withPublicUrl(created);
607
+ }
608
+
609
+ /** Sufixa o slug até achar um livre (o slug é unique na tabela). */
610
+ private async ensureProvisionSlug(base: string): Promise<string> {
611
+ const slug = base.trim();
612
+ if (!(await this.integrations.findUnique({ where: { slug } }))) return slug;
613
+
614
+ for (let i = 2; ; i++) {
615
+ const candidate = `${slug}-${i}`;
616
+ if (!(await this.integrations.findUnique({ where: { slug: candidate } }))) {
617
+ return candidate;
618
+ }
619
+ }
620
+ }
621
+
622
+ /**
623
+ * Entrega a chamada ao adaptador de protocolo do webhook, se houver um.
624
+ *
625
+ * Devolve `null` quando o webhook é clássico (protocol nulo) ou quando nenhum
626
+ * módulo registrou o protocolo — nesse caso o chamador segue pelo fluxo normal
627
+ * de token + actions.
628
+ *
629
+ * Webhooks com protocolo NÃO passam por validateToken/validateIp/validateBodySchema:
630
+ * o adaptador faz a própria autenticação (a Meta, por exemplo, assina com HMAC).
631
+ */
632
+ async executeProtocol(
633
+ uuid: string,
634
+ method: 'GET' | 'POST',
635
+ body: any,
636
+ query: any,
637
+ headers: any,
638
+ req: Request,
639
+ ): Promise<WebhookProtocolResponse | null> {
640
+ const integration = await this.integrations.findUnique({
641
+ where: { public_uuid: uuid },
642
+ select: {
643
+ id: true,
644
+ slug: true,
645
+ status: true,
646
+ protocol: true,
647
+ integration_profile_id: true,
648
+ },
649
+ });
650
+
651
+ if (!integration?.protocol) return null;
652
+
653
+ const adapter = this.protocolRegistry.get(integration.protocol);
654
+ if (!adapter) {
655
+ // Protocolo gravado mas nenhum módulo o registrou (library desabilitada,
656
+ // por exemplo). Não cai no fluxo de actions: seria autenticar com o token
657
+ // clássico um webhook que deveria exigir a auth do provedor.
658
+ this.logger.error(
659
+ `Webhook ${integration.id} declares protocol "${integration.protocol}", but no adapter is registered.`,
660
+ );
661
+ throw new NotFoundException('Webhook not found.');
662
+ }
663
+
664
+ if (integration.status !== 'active') {
665
+ throw new NotFoundException('Webhook not found.');
666
+ }
667
+
668
+ const request: WebhookProtocolRequest = {
669
+ integration: {
670
+ id: integration.id,
671
+ slug: integration.slug,
672
+ integration_profile_id: integration.integration_profile_id ?? null,
673
+ },
674
+ body,
675
+ query: (query ?? {}) as Record<string, string>,
676
+ headers: (headers ?? {}) as Record<string, string | string[] | undefined>,
677
+ rawBody: (req as Request & { rawBody?: Buffer }).rawBody,
678
+ remoteIp: this.getRemoteIp(req),
679
+ req,
680
+ };
681
+
682
+ return method === 'GET'
683
+ ? adapter.verify(request)
684
+ : adapter.handle(request);
685
+ }
686
+
554
687
  async executePublic(uuid: string, body: any, headers: any, req: Request) {
555
688
  const startedAt = Date.now();
556
689
  const remoteIp = this.getRemoteIp(req);
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it, jest } from '@jest/globals';
2
+ import { WebhookProtocolRegistry } from './webhook-protocol.registry';
3
+
4
+ const makeAdapter = () =>
5
+ ({
6
+ verify: jest.fn(),
7
+ handle: jest.fn(),
8
+ }) as any;
9
+
10
+ describe('WebhookProtocolRegistry', () => {
11
+ it('devolve o adaptador registrado', () => {
12
+ const registry = new WebhookProtocolRegistry();
13
+ const adapter = makeAdapter();
14
+
15
+ registry.register('meta-whatsapp', adapter);
16
+
17
+ expect(registry.get('meta-whatsapp')).toBe(adapter);
18
+ expect(registry.has('meta-whatsapp')).toBe(true);
19
+ });
20
+
21
+ it('normaliza a chave (case e espaços)', () => {
22
+ const registry = new WebhookProtocolRegistry();
23
+ const adapter = makeAdapter();
24
+
25
+ registry.register(' Meta-WhatsApp ', adapter);
26
+
27
+ expect(registry.get('meta-whatsapp')).toBe(adapter);
28
+ expect(registry.get('META-WHATSAPP')).toBe(adapter);
29
+ });
30
+
31
+ it('devolve null para protocolo desconhecido, nulo ou vazio', () => {
32
+ const registry = new WebhookProtocolRegistry();
33
+
34
+ expect(registry.get('nao-existe')).toBeNull();
35
+ expect(registry.get(null)).toBeNull();
36
+ expect(registry.get(undefined)).toBeNull();
37
+ expect(registry.get('')).toBeNull();
38
+ expect(registry.has(null)).toBe(false);
39
+ });
40
+
41
+ it('recusa registrar com chave vazia', () => {
42
+ const registry = new WebhookProtocolRegistry();
43
+
44
+ expect(() => registry.register(' ', makeAdapter())).toThrow();
45
+ });
46
+
47
+ it('avisa ao sobrescrever um protocolo já registrado', () => {
48
+ const registry = new WebhookProtocolRegistry();
49
+ const warn = jest
50
+ .spyOn((registry as any).logger, 'warn')
51
+ .mockImplementation(() => undefined);
52
+ const first = makeAdapter();
53
+ const second = makeAdapter();
54
+
55
+ registry.register('dup', first);
56
+ registry.register('dup', second);
57
+
58
+ // Sobrescrever silenciosamente esconderia dois módulos brigando pelo protocolo.
59
+ expect(warn).toHaveBeenCalled();
60
+ expect(registry.get('dup')).toBe(second);
61
+ });
62
+
63
+ it('lista os protocolos registrados em ordem', () => {
64
+ const registry = new WebhookProtocolRegistry();
65
+ registry.register('zeta', makeAdapter());
66
+ registry.register('alpha', makeAdapter());
67
+
68
+ expect(registry.list()).toEqual(['alpha', 'zeta']);
69
+ });
70
+ });
@@ -0,0 +1,93 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { Request } from 'express';
3
+
4
+ /**
5
+ * Resposta que um adaptador devolve ao controller público.
6
+ *
7
+ * `contentType` existe porque nem todo provedor aceita JSON: o handshake da Meta,
8
+ * por exemplo, compara o corpo com o challenge literalmente — um envelope JSON
9
+ * reprova a verificação.
10
+ */
11
+ export type WebhookProtocolResponse = {
12
+ status: number;
13
+ contentType?: string;
14
+ body: string | object;
15
+ };
16
+
17
+ export type WebhookProtocolRequest = {
18
+ /** O registro de webhook_integration que casou com o uuid da URL. */
19
+ integration: {
20
+ id: number;
21
+ slug: string;
22
+ integration_profile_id: number | null;
23
+ };
24
+ /** Body já parseado (POST) ou query params (GET). */
25
+ body: unknown;
26
+ query: Record<string, string>;
27
+ headers: Record<string, string | string[] | undefined>;
28
+ /**
29
+ * Bytes originais da requisição. Obrigatório para validar assinaturas HMAC:
30
+ * reserializar o JSON reordena chaves e invalida a assinatura.
31
+ */
32
+ rawBody?: Buffer;
33
+ remoteIp: string;
34
+ req: Request;
35
+ };
36
+
37
+ /**
38
+ * Adaptador de um protocolo de webhook de entrada.
39
+ *
40
+ * O framework de webhooks do core é um disparador de ações genérico, autenticado
41
+ * por token estático. Provedores como a Meta falam um protocolo próprio (handshake
42
+ * de verificação + assinatura HMAC) que não cabe nesse modelo.
43
+ *
44
+ * Em vez de espalhar `if (provider === 'meta')` pelo core, um módulo registra aqui
45
+ * um adaptador e passa a responder pela mesma URL pública (`/webhook/:uuid`). O core
46
+ * continua sem conhecer nenhum provedor específico.
47
+ */
48
+ export interface IWebhookProtocolAdapter {
49
+ /** GET na URL pública (tipicamente o handshake de verificação). */
50
+ verify(request: WebhookProtocolRequest): Promise<WebhookProtocolResponse>;
51
+ /** POST na URL pública (o evento em si). */
52
+ handle(request: WebhookProtocolRequest): Promise<WebhookProtocolResponse>;
53
+ }
54
+
55
+ /**
56
+ * Registro dos adaptadores de protocolo, no mesmo padrão do QueueHandlerRegistry
57
+ * e do FiscalProviderRegistry: o módulo dono do protocolo se registra no boot.
58
+ */
59
+ @Injectable()
60
+ export class WebhookProtocolRegistry {
61
+ private readonly logger = new Logger(WebhookProtocolRegistry.name);
62
+ private readonly adapters = new Map<string, IWebhookProtocolAdapter>();
63
+
64
+ register(protocol: string, adapter: IWebhookProtocolAdapter): void {
65
+ const key = normalizeProtocol(protocol);
66
+ if (!key) throw new Error('Webhook protocol key is required.');
67
+
68
+ if (this.adapters.has(key)) {
69
+ this.logger.warn(`Webhook protocol "${key}" was overwritten.`);
70
+ }
71
+
72
+ this.adapters.set(key, adapter);
73
+ this.logger.log(`Registered webhook protocol "${key}"`);
74
+ }
75
+
76
+ get(protocol: string | null | undefined): IWebhookProtocolAdapter | null {
77
+ const key = normalizeProtocol(protocol);
78
+ return key ? (this.adapters.get(key) ?? null) : null;
79
+ }
80
+
81
+ has(protocol: string | null | undefined): boolean {
82
+ return this.get(protocol) !== null;
83
+ }
84
+
85
+ /** Protocolos registrados (para a UI listar o que existe). */
86
+ list(): string[] {
87
+ return [...this.adapters.keys()].sort();
88
+ }
89
+ }
90
+
91
+ function normalizeProtocol(protocol: string | null | undefined): string {
92
+ return String(protocol ?? '').trim().toLowerCase();
93
+ }
@@ -0,0 +1,10 @@
1
+ import { PrismaModule } from '@hed-hog/api-prisma';
2
+ import { forwardRef, Module } from '@nestjs/common';
3
+ import { WhatsappSenderService } from './whatsapp-sender.service';
4
+
5
+ @Module({
6
+ imports: [forwardRef(() => PrismaModule)],
7
+ providers: [WhatsappSenderService],
8
+ exports: [WhatsappSenderService],
9
+ })
10
+ export class WhatsappSenderModule {}
@@ -0,0 +1,313 @@
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import {
3
+ BadRequestException,
4
+ forwardRef,
5
+ Inject,
6
+ Injectable,
7
+ Logger,
8
+ NotFoundException,
9
+ } from '@nestjs/common';
10
+ import {
11
+ buildWhatsappOfficialConfigFromIntegration,
12
+ } from '../integration-profile/integration-profile.utils';
13
+
14
+ export type WhatsappTargetType = 'phone' | 'group';
15
+
16
+ export type WhatsappMedia = {
17
+ /** Bare base64 (sem prefixo data:) ou data URL — ambos aceitos. */
18
+ base64: string;
19
+ mimetype: string;
20
+ fileName: string;
21
+ caption?: string;
22
+ };
23
+
24
+ export type WhatsappSendInput = {
25
+ /** Slug do perfil de integração. Vazio => primeiro perfil ativo do tipo whatsapp. */
26
+ profileSlug?: string;
27
+ to: string;
28
+ targetType?: WhatsappTargetType;
29
+ text: string;
30
+ media?: WhatsappMedia;
31
+ /** Exigido pelo provider `whatsapp-official` (id do número no WABA). */
32
+ phoneNumberId?: string;
33
+ /** Template aprovado pela Meta — sem ele o envio só chega na janela de 24h. */
34
+ template?: { name: string; language?: string; params?: string[] };
35
+ };
36
+
37
+ type ResolvedProfile = {
38
+ slug: string;
39
+ providerSlug: string;
40
+ config: Record<string, unknown>;
41
+ };
42
+
43
+ /**
44
+ * Envio de WhatsApp usando um perfil de integração escolhido pelo usuário.
45
+ *
46
+ * O provider não é fixado em código: ele vem do próprio perfil, então quem
47
+ * configura o sistema decide entre Evolution API e a Cloud API oficial da Meta
48
+ * sem que o chamador precise saber a diferença.
49
+ */
50
+ @Injectable()
51
+ export class WhatsappSenderService {
52
+ private readonly logger = new Logger(WhatsappSenderService.name);
53
+
54
+ constructor(
55
+ @Inject(forwardRef(() => PrismaService))
56
+ private readonly prisma: PrismaService,
57
+ ) {}
58
+
59
+ async send(input: WhatsappSendInput): Promise<void> {
60
+ const profile = await this.resolveProfile(input.profileSlug);
61
+
62
+ switch (profile.providerSlug) {
63
+ case 'evolution-api':
64
+ return this.sendWithEvolution(profile, input);
65
+ case 'whatsapp-official':
66
+ return this.sendWithOfficial(profile, input);
67
+ default:
68
+ throw new BadRequestException(
69
+ `Perfil de integração "${profile.slug}" usa o provider "${profile.providerSlug}", que não envia WhatsApp. Use um perfil evolution-api ou whatsapp-official.`,
70
+ );
71
+ }
72
+ }
73
+
74
+ /** Diz se há perfil utilizável, para o chamador decidir por um fallback sem tentar o envio. */
75
+ async isConfigured(profileSlug?: string): Promise<boolean> {
76
+ try {
77
+ const profile = await this.resolveProfile(profileSlug);
78
+
79
+ if (profile.providerSlug === 'evolution-api') {
80
+ const { baseUrl, token, instance } = this.readEvolutionConfig(profile);
81
+ return Boolean(baseUrl && token && instance);
82
+ }
83
+
84
+ if (profile.providerSlug === 'whatsapp-official') {
85
+ const cfg = buildWhatsappOfficialConfigFromIntegration(profile.config);
86
+ return Boolean(cfg.accessToken);
87
+ }
88
+
89
+ return false;
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Normaliza o destino: número vira só dígitos, id de grupo passa intacto
97
+ * (grupos do WhatsApp têm sufixo `@g.us`, que seria destruído por um strip).
98
+ */
99
+ normalizeTarget(target: string, targetType: WhatsappTargetType = 'phone'): string {
100
+ const raw = String(target ?? '').trim();
101
+
102
+ if (!raw) {
103
+ throw new BadRequestException('Destino de WhatsApp vazio.');
104
+ }
105
+
106
+ if (targetType === 'group') {
107
+ return raw;
108
+ }
109
+
110
+ const normalized = raw.replace(/\D+/g, '');
111
+
112
+ if (!normalized) {
113
+ throw new BadRequestException(
114
+ 'O número de WhatsApp precisa conter ao menos um dígito.',
115
+ );
116
+ }
117
+
118
+ return normalized;
119
+ }
120
+
121
+ private async resolveProfile(profileSlug?: string): Promise<ResolvedProfile> {
122
+ const slug = String(profileSlug ?? '').trim();
123
+
124
+ const profile = slug
125
+ ? await this.prisma.integration_profile.findUnique({
126
+ where: { slug },
127
+ include: {
128
+ integration_provider: { select: { slug: true } },
129
+ integration_type: { select: { slug: true } },
130
+ },
131
+ })
132
+ : await this.prisma.integration_profile.findFirst({
133
+ where: { is_active: true, integration_type: { slug: 'whatsapp' } },
134
+ orderBy: { updated_at: 'desc' },
135
+ include: {
136
+ integration_provider: { select: { slug: true } },
137
+ integration_type: { select: { slug: true } },
138
+ },
139
+ });
140
+
141
+ if (!profile) {
142
+ throw new NotFoundException(
143
+ slug
144
+ ? `Perfil de integração "${slug}" não encontrado.`
145
+ : 'Nenhum perfil de integração de WhatsApp ativo encontrado.',
146
+ );
147
+ }
148
+
149
+ if ((profile as any).integration_type?.slug !== 'whatsapp') {
150
+ throw new BadRequestException(
151
+ `Perfil de integração "${profile.slug}" não é do tipo whatsapp.`,
152
+ );
153
+ }
154
+
155
+ return {
156
+ slug: profile.slug,
157
+ providerSlug: String((profile as any).integration_provider?.slug ?? ''),
158
+ config: ((profile.config ?? {}) as Record<string, unknown>),
159
+ };
160
+ }
161
+
162
+ private readEvolutionConfig(profile: ResolvedProfile) {
163
+ const cfg = profile.config;
164
+
165
+ return {
166
+ baseUrl: String(cfg.host ?? cfg.base_url ?? '').trim().replace(/\/+$/, ''),
167
+ token: String(cfg.token ?? cfg.api_key ?? '').trim(),
168
+ instance: String(cfg.instance_name ?? cfg.instance ?? '').trim(),
169
+ };
170
+ }
171
+
172
+ private async sendWithEvolution(
173
+ profile: ResolvedProfile,
174
+ input: WhatsappSendInput,
175
+ ): Promise<void> {
176
+ const { baseUrl, token, instance } = this.readEvolutionConfig(profile);
177
+
178
+ if (!baseUrl || !token || !instance) {
179
+ throw new BadRequestException(
180
+ `Perfil de WhatsApp "${profile.slug}" incompleto. Obrigatórios: host, instance_name e token.`,
181
+ );
182
+ }
183
+
184
+ const target = this.normalizeTarget(input.to, input.targetType);
185
+ const encodedInstance = encodeURIComponent(instance);
186
+
187
+ const url = input.media
188
+ ? `${baseUrl}/message/sendMedia/${encodedInstance}`
189
+ : `${baseUrl}/message/sendText/${encodedInstance}`;
190
+
191
+ const body = input.media
192
+ ? {
193
+ number: target,
194
+ mediatype: 'image',
195
+ mimetype: input.media.mimetype,
196
+ fileName: input.media.fileName,
197
+ media: stripDataUrlPrefix(input.media.base64),
198
+ caption: input.media.caption ?? input.text,
199
+ }
200
+ : { number: target, text: input.text };
201
+
202
+ const response = await fetch(url, {
203
+ method: 'POST',
204
+ headers: { apikey: token, 'Content-Type': 'application/json' },
205
+ body: JSON.stringify(body),
206
+ });
207
+
208
+ if (!response.ok) {
209
+ const detail = await response.text().catch(() => '');
210
+
211
+ if (response.status === 401) {
212
+ throw new BadRequestException(
213
+ `Credenciais do WhatsApp inválidas (401). Verifique o token do perfil "${profile.slug}".`,
214
+ );
215
+ }
216
+
217
+ throw new BadRequestException(
218
+ `Evolution API recusou o envio (HTTP ${response.status}): ${detail.slice(0, 300)}`,
219
+ );
220
+ }
221
+ }
222
+
223
+ private async sendWithOfficial(
224
+ profile: ResolvedProfile,
225
+ input: WhatsappSendInput,
226
+ ): Promise<void> {
227
+ const cfg = buildWhatsappOfficialConfigFromIntegration(profile.config);
228
+
229
+ if (!cfg.accessToken) {
230
+ throw new BadRequestException(
231
+ `Perfil de WhatsApp "${profile.slug}" está sem access_token.`,
232
+ );
233
+ }
234
+
235
+ // O phone_number_id não vive no perfil: um WABA pode ter vários números,
236
+ // então quem envia precisa dizer por qual deles a mensagem sai.
237
+ const phoneNumberId = String(
238
+ input.phoneNumberId ?? profile.config.phone_number_id ?? '',
239
+ ).trim();
240
+
241
+ if (!phoneNumberId) {
242
+ throw new BadRequestException(
243
+ 'O provider whatsapp-official exige o phone_number_id do número remetente.',
244
+ );
245
+ }
246
+
247
+ const to = this.normalizeTarget(input.to, 'phone');
248
+
249
+ // A Meta só aceita mensagem iniciada pela empresa através de template
250
+ // aprovado. Sem template, o texto livre só chega se o destinatário tiver
251
+ // falado com o número nas últimas 24h.
252
+ const body = input.template?.name
253
+ ? {
254
+ messaging_product: 'whatsapp',
255
+ to,
256
+ type: 'template',
257
+ template: {
258
+ name: input.template.name,
259
+ language: { code: input.template.language || 'pt_BR' },
260
+ components: [
261
+ {
262
+ type: 'body',
263
+ parameters: (input.template.params ?? [input.text]).map((text) => ({
264
+ type: 'text',
265
+ text,
266
+ })),
267
+ },
268
+ ],
269
+ },
270
+ }
271
+ : {
272
+ messaging_product: 'whatsapp',
273
+ to,
274
+ type: 'text',
275
+ text: { body: input.text.slice(0, 4000) },
276
+ };
277
+
278
+ const url = `https://graph.facebook.com/${cfg.graphApiVersion}/${encodeURIComponent(
279
+ phoneNumberId,
280
+ )}/messages`;
281
+
282
+ const response = await fetch(url, {
283
+ method: 'POST',
284
+ headers: {
285
+ Authorization: `Bearer ${cfg.accessToken}`,
286
+ 'Content-Type': 'application/json',
287
+ },
288
+ body: JSON.stringify(body),
289
+ });
290
+
291
+ if (!response.ok) {
292
+ const payload = (await response.json().catch(() => ({}))) as {
293
+ error?: { message?: string };
294
+ };
295
+
296
+ throw new BadRequestException(
297
+ `WhatsApp recusou o envio (HTTP ${response.status}): ${
298
+ payload?.error?.message ?? 'sem detalhe'
299
+ }${
300
+ input.template?.name
301
+ ? ''
302
+ : ' — sem template configurado, a entrega só funciona na janela de 24h'
303
+ }`,
304
+ );
305
+ }
306
+ }
307
+ }
308
+
309
+ /** Evolution espera base64 puro; um data URL vindo do chamador quebraria o envio. */
310
+ function stripDataUrlPrefix(image: string): string {
311
+ const match = /^data:[^;]+;base64,(.*)$/i.exec(image);
312
+ return match ? match[1] : image;
313
+ }