@hed-hog/core 0.0.387 → 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 (535) 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 +24 -9
  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 +33 -8
  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 +26 -0
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +30 -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 +9 -1
  146. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  147. package/dist/integration-profile/integration-profile.service.d.ts +26 -7
  148. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  149. package/dist/integration-profile/integration-profile.service.js +94 -91
  150. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  151. package/dist/integration-profile/integration-profile.utils.d.ts +13 -1
  152. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  153. package/dist/integration-profile/integration-profile.utils.js +20 -5
  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 +21 -15
  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 +30 -18
  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/whatsapp-sender/whatsapp-sender.module.d.ts +3 -0
  361. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts.map +1 -0
  362. package/dist/whatsapp-sender/whatsapp-sender.module.js +23 -0
  363. package/dist/whatsapp-sender/whatsapp-sender.module.js.map +1 -0
  364. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +50 -0
  365. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -0
  366. package/dist/whatsapp-sender/whatsapp-sender.service.js +224 -0
  367. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -0
  368. package/hedhog/data/integration_provider.yaml +16 -0
  369. package/hedhog/data/mail.yaml +88 -61
  370. package/hedhog/data/mail_layout.yaml +188 -0
  371. package/hedhog/data/menu.yaml +67 -3
  372. package/hedhog/data/route.yaml +135 -0
  373. package/hedhog/data/setting_group.yaml +139 -1
  374. package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +36 -0
  375. package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +2 -0
  376. package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +10 -7
  377. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +7 -4
  378. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +580 -0
  379. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-preview.tsx.ejs +150 -0
  380. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +199 -0
  381. package/hedhog/frontend/app/ai_instruction/_components/prompt-diff.tsx.ejs +63 -0
  382. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.spec.ts.ejs +59 -0
  383. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.ts.ejs +72 -0
  384. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.spec.ts.ejs +170 -0
  385. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.ts.ejs +167 -0
  386. package/hedhog/frontend/app/ai_instruction/_lib/types.ts.ejs +99 -0
  387. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +422 -0
  388. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +51 -4
  389. package/hedhog/frontend/app/incidents/page.tsx.ejs +400 -0
  390. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +153 -84
  391. package/hedhog/frontend/app/mail/layout/page.tsx.ejs +344 -0
  392. package/hedhog/frontend/app/mail/template/page.tsx.ejs +155 -121
  393. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +129 -17
  394. package/hedhog/frontend/app/pages/_components/page-delete-dialog.tsx.ejs +343 -0
  395. package/hedhog/frontend/app/pages/_components/page-detail-sheet.tsx.ejs +438 -0
  396. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1009 -0
  397. package/hedhog/frontend/app/pages/_components/system-page-types.ts.ejs +56 -0
  398. package/hedhog/frontend/app/pages/_components/use-system-pages.ts.ejs +171 -0
  399. package/hedhog/frontend/app/pages/page.tsx.ejs +514 -0
  400. package/hedhog/frontend/app/users/page.tsx.ejs +119 -13
  401. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +231 -0
  402. package/hedhog/frontend/app/users/user-mails.tsx.ejs +399 -0
  403. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +60 -75
  404. package/hedhog/frontend/messages/en.json +499 -3
  405. package/hedhog/frontend/messages/pt.json +499 -3
  406. package/hedhog/table/ai_agent.yaml +1 -0
  407. package/hedhog/table/ai_instruction.yaml +15 -0
  408. package/hedhog/table/leaked_password.yaml +18 -0
  409. package/hedhog/table/mail_layout.yaml +35 -0
  410. package/hedhog/table/mail_var.yaml +4 -0
  411. package/hedhog/table/setting.yaml +1 -0
  412. package/hedhog/table/system_incident.yaml +59 -0
  413. package/hedhog/table/user_presence.yaml +36 -0
  414. package/package.json +8 -4
  415. package/src/access-log/access-log.interceptor.ts +4 -2
  416. package/src/ai/ai-attachment.fragments.spec.ts +319 -0
  417. package/src/ai/ai-attachment.fragments.ts +35 -0
  418. package/src/ai/ai.prompts.ts +38 -0
  419. package/src/ai/ai.service.spec.ts +280 -0
  420. package/src/ai/ai.service.ts +338 -82
  421. package/src/ai/dto/chat.dto.ts +33 -3
  422. package/src/ai/dto/create-agent.dto.ts +2 -2
  423. package/src/ai/dto/update-agent.dto.ts +2 -2
  424. package/src/ai-instruction/ai-instruction.controller.ts +40 -9
  425. package/src/ai-instruction/ai-instruction.module.ts +13 -2
  426. package/src/ai-instruction/ai-instruction.service.spec.ts +658 -0
  427. package/src/ai-instruction/ai-instruction.service.ts +661 -62
  428. package/src/ai-instruction/ai-prompt-builder.service.ts +10 -1
  429. package/src/ai-instruction/ai-prompt-registry.ts +87 -0
  430. package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +375 -0
  431. package/src/ai-instruction/ai-prompt-resolver.service.ts +295 -0
  432. package/src/ai-instruction/ai-prompt-template.util.spec.ts +162 -0
  433. package/src/ai-instruction/ai-prompt-template.util.ts +199 -0
  434. package/src/ai-instruction/dto/create-ai-instruction.dto.ts +39 -3
  435. package/src/ai-instruction/dto/preview-ai-instruction.dto.ts +26 -0
  436. package/src/ai-instruction/dto/update-ai-instruction.dto.ts +42 -1
  437. package/src/ai-instruction/types/ai-instruction.types.ts +24 -1
  438. package/src/auth/auth.controller.ts +31 -5
  439. package/src/auth/auth.service.spec.ts +3 -1
  440. package/src/auth/auth.service.ts +101 -36
  441. package/src/captcha/captcha.module.ts +10 -0
  442. package/src/captcha/captcha.service.spec.ts +210 -0
  443. package/src/captcha/captcha.service.ts +191 -0
  444. package/src/challenge/challenge.service.spec.ts +39 -4
  445. package/src/challenge/challenge.service.ts +72 -17
  446. package/src/core.module.ts +27 -1
  447. package/src/file/file.service.spec.ts +105 -0
  448. package/src/file/file.service.ts +60 -3
  449. package/src/incident/incident-notifier.service.spec.ts +205 -0
  450. package/src/incident/incident-notifier.service.ts +241 -0
  451. package/src/incident/incident.controller.ts +38 -0
  452. package/src/incident/incident.module.ts +19 -0
  453. package/src/incident/incident.service.spec.ts +150 -0
  454. package/src/incident/incident.service.ts +209 -0
  455. package/src/index.ts +37 -0
  456. package/src/integration-profile/dto/test.dto.ts +19 -0
  457. package/src/integration-profile/integration-profile.controller.ts +10 -1
  458. package/src/integration-profile/integration-profile.module.spec.ts +51 -0
  459. package/src/integration-profile/integration-profile.module.ts +23 -4
  460. package/src/integration-profile/integration-profile.secrets.ts +9 -1
  461. package/src/integration-profile/integration-profile.service.spec.ts +279 -0
  462. package/src/integration-profile/integration-profile.service.ts +120 -124
  463. package/src/integration-profile/integration-profile.utils.spec.ts +22 -0
  464. package/src/integration-profile/integration-profile.utils.ts +40 -3
  465. package/src/integration-profile/testers/ai.testers.ts +165 -0
  466. package/src/integration-profile/testers/captcha.testers.spec.ts +92 -0
  467. package/src/integration-profile/testers/captcha.testers.ts +139 -0
  468. package/src/integration-profile/testers/email.testers.ts +161 -0
  469. package/src/integration-profile/testers/http-probe.spec.ts +153 -0
  470. package/src/integration-profile/testers/http-probe.ts +171 -0
  471. package/src/integration-profile/testers/index.ts +30 -0
  472. package/src/integration-profile/testers/infrastructure.testers.ts +122 -0
  473. package/src/integration-profile/testers/integration-tester.registry.spec.ts +75 -0
  474. package/src/integration-profile/testers/integration-tester.registry.ts +43 -0
  475. package/src/integration-profile/testers/integration-tester.types.ts +61 -0
  476. package/src/integration-profile/testers/messaging.testers.ts +102 -0
  477. package/src/integration-profile/testers/oauth.testers.spec.ts +220 -0
  478. package/src/integration-profile/testers/oauth.testers.ts +305 -0
  479. package/src/integration-profile/testers/payment.testers.ts +185 -0
  480. package/src/integration-profile/testers/remaining.testers.spec.ts +401 -0
  481. package/src/integration-profile/testers/storage.testers.spec.ts +185 -0
  482. package/src/integration-profile/testers/storage.testers.ts +226 -0
  483. package/src/integration-profile/testers/tester-coverage.spec.ts +58 -0
  484. package/src/language/en.json +4 -2
  485. package/src/language/pt.json +4 -2
  486. package/src/leaked-password/README.md +83 -0
  487. package/src/leaked-password/leaked-password.controller.ts +52 -0
  488. package/src/leaked-password/leaked-password.module.ts +18 -0
  489. package/src/leaked-password/leaked-password.service.ts +242 -0
  490. package/src/leaked-password/seed/build-seed-sql.mjs +180 -0
  491. package/src/leaked-password/seed/common-passwords.txt +335 -0
  492. package/src/mail/dto/preview.dto.ts +50 -0
  493. package/src/mail/dto/send.dto.ts +14 -1
  494. package/src/mail/mail-template.spec.ts +197 -0
  495. package/src/mail/mail.controller.ts +9 -2
  496. package/src/mail/mail.service.spec.ts +116 -1
  497. package/src/mail/mail.service.ts +118 -12
  498. package/src/mail-layout/dto/update.dto.ts +19 -0
  499. package/src/mail-layout/mail-layout.controller.ts +47 -0
  500. package/src/mail-layout/mail-layout.module.ts +22 -0
  501. package/src/mail-layout/mail-layout.service.ts +94 -0
  502. package/src/mcp-chat/mcp-chat.service.spec.ts +236 -0
  503. package/src/mcp-chat/mcp-chat.service.ts +122 -26
  504. package/src/oauth/oauth-token-broker.service.spec.ts +172 -0
  505. package/src/oauth/oauth-token-broker.service.ts +363 -0
  506. package/src/oauth/oauth.module.ts +3 -1
  507. package/src/oauth/oauth.service.ts +8 -2
  508. package/src/pdf/pdf.service.ts +75 -0
  509. package/src/presence/presence.controller.ts +29 -0
  510. package/src/presence/presence.listener.ts +53 -0
  511. package/src/presence/presence.module.ts +19 -0
  512. package/src/presence/presence.service.spec.ts +97 -0
  513. package/src/presence/presence.service.ts +160 -0
  514. package/src/profile/dto/delete-account.dto.ts +11 -0
  515. package/src/profile/events/profile-account-deleted.event.ts +13 -0
  516. package/src/profile/profile.controller.ts +46 -34
  517. package/src/profile/profile.service.ts +138 -38
  518. package/src/realtime/realtime.gateway.ts +138 -0
  519. package/src/realtime/realtime.module.ts +16 -0
  520. package/src/realtime/realtime.service.spec.ts +50 -0
  521. package/src/realtime/realtime.service.ts +187 -0
  522. package/src/redis/redis-pubsub.ts +179 -0
  523. package/src/setting/dto/create.dto.ts +13 -2
  524. package/src/setting/setting.service.spec.ts +56 -0
  525. package/src/setting/setting.service.ts +29 -1
  526. package/src/user/dto/create-with-email-and-password.dto.ts +6 -1
  527. package/src/user/dto/query-user-mail-list.dto.ts +10 -0
  528. package/src/user/user-credential.util.ts +35 -0
  529. package/src/user/user.controller.ts +17 -2
  530. package/src/user/user.service.spec.ts +153 -0
  531. package/src/user/user.service.ts +255 -12
  532. package/src/validators/is-strong-password-with-settings.validator.ts +12 -0
  533. package/src/validators/service-locator.ts +15 -0
  534. package/src/whatsapp-sender/whatsapp-sender.module.ts +10 -0
  535. package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
@@ -1,5 +1,5 @@
1
1
  import { getLocaleText } from '@hed-hog/api-locale';
2
- import { PrismaService, user } from '@hed-hog/api-prisma';
2
+ import { PrismaService } from '@hed-hog/api-prisma';
3
3
  import { User } from '@hed-hog/api-types';
4
4
  import { BadRequestException, forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
5
5
  import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -27,10 +27,12 @@ import { UserService } from '../user/user.service';
27
27
  import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
28
28
  import { ChangeEmailDto } from './dto/change-email.dto';
29
29
  import { ChangePasswordDto } from './dto/change-password.dto';
30
+ import { DeleteAccountDto } from './dto/delete-account.dto';
30
31
  import { EmailVerificationDto } from './dto/email-verification-confirm.dto';
31
32
  import { SendEmailVerificationDto } from './dto/send-email-verification.dto';
32
33
  import { SetPreferencesDto } from './dto/set-preferences.dto';
33
34
  import { UpdateDto } from './dto/update.dto';
35
+ import { PROFILE_ACCOUNT_DELETED, ProfileAccountDeletedEvent } from './events/profile-account-deleted.event';
34
36
  import { PROFILE_AVATAR_UPDATED, ProfileAvatarUpdatedEvent } from './events/profile-avatar-updated.event';
35
37
  import { PROFILE_EMAIL_CHANGED, ProfileEmailChangedEvent } from './events/profile-email-changed.event';
36
38
  import { PROFILE_UPDATED, ProfileUpdatedEvent } from './events/profile-updated.event';
@@ -54,7 +56,7 @@ export class ProfileService {
54
56
  private readonly eventEmitter: EventEmitter2,
55
57
  ) {}
56
58
 
57
- async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto) {
59
+ async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto, origin?: string) {
58
60
 
59
61
  const checkAlreadyExists = await this.prisma.user_identifier.findFirst({
60
62
  where: { type: 'email', value: email, enabled: true, verified_at: { not: null } },
@@ -127,7 +129,7 @@ export class ProfileService {
127
129
  });
128
130
 
129
131
  // Create verification challenge for the new email
130
- await this.challenge.verifyEmail(locale, userId, email);
132
+ await this.challenge.verifyEmail(locale, userId, email, undefined, origin);
131
133
 
132
134
  });
133
135
 
@@ -150,7 +152,7 @@ export class ProfileService {
150
152
  return { success: true };
151
153
  }
152
154
 
153
- async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto) {
155
+ async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto, sessionId: number | null) {
154
156
 
155
157
  const user = await this.prisma.user.findUnique({
156
158
  where: { id: userId },
@@ -223,9 +225,8 @@ export class ProfileService {
223
225
 
224
226
  });
225
227
 
226
- const updatedUser = await this.prisma.user.findFirst({ where: { id: user.id }})
227
- const codes = await this.createMfaRecoveryCodes(user.id);
228
- const newToken = await this.getToken(updatedUser);
228
+ const codes = await this.createMfaRecoveryCodes(userId);
229
+ const newToken = await this.getToken(userId, sessionId);
229
230
  return { ...newToken, codes };
230
231
 
231
232
  }
@@ -302,16 +303,16 @@ export class ProfileService {
302
303
  return { success: true };
303
304
  }
304
305
 
305
- async sendEmailVerificationMfa(locale: string, userId: number, { email }: SendEmailVerificationDto) {
306
- return this.challenge.verifyMfaEmail(locale, userId, email);
306
+ async sendEmailVerificationMfa(locale: string, userId: number, { email }: SendEmailVerificationDto, origin?: string) {
307
+ return this.challenge.verifyMfaEmail(locale, userId, email, origin);
307
308
  }
308
309
 
309
- async sendEmailVerification(locale: string, userId: number, { email }: SendEmailVerificationDto) {
310
- return this.challenge.verifyEmail(locale, userId, email);
310
+ async sendEmailVerification(locale: string, userId: number, { email }: SendEmailVerificationDto, origin?: string) {
311
+ return this.challenge.verifyEmail(locale, userId, email, undefined, origin);
311
312
  }
312
313
 
313
- async sendCodeToRemove(locale, userId, email){
314
- return this.challenge.verifyEmail(locale, userId, email)
314
+ async sendCodeToRemove(locale, userId, email, origin?: string){
315
+ return this.challenge.verifyEmail(locale, userId, email, undefined, origin)
315
316
  }
316
317
 
317
318
  async getEmailIdentifier(userId: number) {
@@ -359,6 +360,84 @@ export class ProfileService {
359
360
  return result;
360
361
  }
361
362
 
363
+ /**
364
+ * Exclusão de conta pelo próprio usuário (exigida pelas lojas de aplicativos).
365
+ *
366
+ * Anonimiza em vez de apagar: os dados pessoais somem e o acesso é revogado
367
+ * em definitivo, mas as linhas continuam existindo para não quebrar registros
368
+ * fiscais e integridade referencial. Os identificadores (e-mail, CPF...) são
369
+ * embaralhados, o que libera o e-mail original para uma conta nova no futuro.
370
+ */
371
+ async deleteAccount(locale: string, userId: number, { password }: DeleteAccountDto) {
372
+ const user = await this.prisma.user.findUnique({ where: { id: userId } });
373
+
374
+ if (!user) {
375
+ throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found.'));
376
+ }
377
+
378
+ if (user.deactivated_at) {
379
+ throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
380
+ }
381
+
382
+ const credentials = await this.prisma.user_credential.findMany({
383
+ where: { user_id: userId, type: 'password' },
384
+ });
385
+
386
+ // Conta com senha exige a senha; conta só de login social não tem o que pedir.
387
+ if (credentials.length > 0) {
388
+ if (!password || !(await this.security.validatePassword(locale, credentials, password))) {
389
+ throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
390
+ }
391
+ }
392
+
393
+ const deletedAt = new Date();
394
+ const suffix = `${userId}-${deletedAt.getTime()}`;
395
+
396
+ await this.prisma.$transaction(async (tx) => {
397
+ // Identificadores primeiro: libera e-mail/CPF para reuso e impede o login,
398
+ // que resolve o usuário justamente por essa tabela.
399
+ const identifiers = await tx.user_identifier.findMany({
400
+ where: { user_id: userId },
401
+ select: { id: true, type: true },
402
+ });
403
+
404
+ for (const identifier of identifiers) {
405
+ await tx.user_identifier.update({
406
+ where: { id: identifier.id },
407
+ data: {
408
+ value: `deleted-${identifier.type}-${suffix}`,
409
+ enabled: false,
410
+ verified_at: null,
411
+ },
412
+ });
413
+ }
414
+
415
+ // Credenciais, sessões e segundos fatores: nada aqui pode sobreviver,
416
+ // ou restaria um caminho de acesso à conta anonimizada.
417
+ await tx.user_identifier_challenge.deleteMany({ where: { user_identifier: { user_id: userId } } });
418
+ await tx.user_credential.deleteMany({ where: { user_id: userId } });
419
+ await tx.user_session.deleteMany({ where: { user_id: userId } });
420
+ await tx.user_mfa_challenge.deleteMany({ where: { user_mfa: { user_id: userId } } });
421
+ await tx.user_mfa.deleteMany({ where: { user_id: userId } });
422
+ await tx.user_recovery_code.deleteMany({ where: { user_id: userId } });
423
+ await tx.user_account.deleteMany({ where: { user_id: userId } });
424
+
425
+ await tx.user.update({
426
+ where: { id: userId },
427
+ data: {
428
+ name: 'Usuário removido',
429
+ photo_id: null,
430
+ deactivated_at: deletedAt,
431
+ },
432
+ });
433
+ });
434
+
435
+ // Cada library cuida dos seus próprios dados (assinatura, progresso, mensagens).
436
+ this.eventEmitter.emit(PROFILE_ACCOUNT_DELETED, new ProfileAccountDeletedEvent(userId, deletedAt));
437
+
438
+ return { success: true };
439
+ }
440
+
362
441
  async updateAvatar(locale: string, userId: number, file: MulterFile) {
363
442
  const result = await this.user.changeAvatar(locale, userId, file);
364
443
  this.eventEmitter.emit(PROFILE_AVATAR_UPDATED, new ProfileAvatarUpdatedEvent(userId, result?.id ?? null));
@@ -366,7 +445,7 @@ export class ProfileService {
366
445
  return result;
367
446
  }
368
447
 
369
- async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string) {
448
+ async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string, origin?: string) {
370
449
  const credential = await this.prisma.user_credential.findFirst({
371
450
  where: { user_id: userId, type: 'password' },
372
451
  });
@@ -407,6 +486,7 @@ export class ProfileService {
407
486
  ipAddress: ipAddress || 'Unknown',
408
487
  location: 'Unknown',
409
488
  },
489
+ app: await this.challenge.resolveAppKeyFromOrigin(origin),
410
490
  }).catch(error => {
411
491
  console.error('Failed to send password change notification:', error);
412
492
  });
@@ -448,11 +528,11 @@ export class ProfileService {
448
528
  return codes;
449
529
  }
450
530
 
451
- async getToken(user: user) {
452
- return { token: this.jwt.sign(user) };
531
+ async getToken(userId: number, sessionId: number | null) {
532
+ return { token: await this.token.createAccessToken({ sub: userId, sessionId }) };
453
533
  }
454
534
 
455
- async verifyMfaTotp(locale: string, userId: number, token: string, secret: string, name?: string) {
535
+ async verifyMfaTotp(locale: string, userId: number, token: string, secret: string, name?: string, sessionId?: number | null) {
456
536
  const settings = await this.setting.getSettingValues([
457
537
  'mfa-issuer',
458
538
  'mfa-window',
@@ -514,9 +594,8 @@ export class ProfileService {
514
594
  });
515
595
  }
516
596
 
517
- const updatedUser = await this.prisma.user.findFirst({ where: { id: user.id }})
518
- const codes = await this.createMfaRecoveryCodes(updatedUser.id);
519
- const newToken = await this.getToken(updatedUser);
597
+ const codes = await this.createMfaRecoveryCodes(userId);
598
+ const newToken = await this.getToken(userId, sessionId ?? null);
520
599
  return { ...newToken, codes };
521
600
  }
522
601
 
@@ -569,7 +648,7 @@ export class ProfileService {
569
648
  return { otpauthUrl: secret.otpauth_url, qrCode, secret: encodedSecret };
570
649
  }
571
650
 
572
- async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string) {
651
+ async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string, sessionId: number | null) {
573
652
  try {
574
653
  const user = await this.prisma.user.findFirst({
575
654
  where: { id: userId },
@@ -606,26 +685,26 @@ export class ProfileService {
606
685
  });
607
686
  });
608
687
 
609
- return this.getUserToken(userId)
688
+ return this.getUserToken(userId, sessionId)
610
689
  } catch (error: any) {
611
690
  throw new BadRequestException(`Invalid code: ${error?.message}`);
612
691
  }
613
692
  }
614
693
 
615
- async removeMfaEmail(locale: string, userId: number, mfaId: number, token: string, providedHash: string) {
694
+ async removeMfaEmail(locale: string, userId: number, mfaId: number, token: string, providedHash: string, sessionId: number | null) {
616
695
  try {
617
696
  await this.challenge.verifyCode(locale, userId, token, providedHash)
618
697
  await this.prisma.user_mfa.deleteMany({
619
698
  where: { id: mfaId, user_id: userId, type: 'email' },
620
699
  });
621
700
 
622
- return this.getUserToken(userId)
701
+ return this.getUserToken(userId, sessionId)
623
702
  } catch (error: any) {
624
703
  throw new BadRequestException(`Invalid code: ${error?.message}`);
625
704
  }
626
705
  }
627
706
 
628
- async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string) {
707
+ async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string, sessionId: number | null) {
629
708
  try {
630
709
  const user = await this.prisma.user.findUnique({
631
710
  where: { id: userId },
@@ -659,13 +738,13 @@ export class ProfileService {
659
738
  where: { id: mfaId, user_id: userId },
660
739
  });
661
740
 
662
- return this.getUserToken(userId);
741
+ return this.getUserToken(userId, sessionId);
663
742
  } catch (error: any) {
664
743
  throw new BadRequestException(`Invalid code: ${error?.message}`);
665
744
  }
666
745
  }
667
746
 
668
- async removeMfa(locale: string, userId: number, mfaId: number, data: { token?: string; hash?: string; verificationType?: 'totp' | 'email' | 'recovery' | 'webauthn'; assertionResponse?: any }, requestOrigin?: string) {
747
+ async removeMfa(locale: string, userId: number, mfaId: number, data: { token?: string; hash?: string; verificationType?: 'totp' | 'email' | 'recovery' | 'webauthn'; assertionResponse?: any }, requestOrigin: string | undefined, sessionId: number | null) {
669
748
  try {
670
749
  const user = await this.prisma.user.findUnique({
671
750
  where: { id: userId },
@@ -786,13 +865,13 @@ export class ProfileService {
786
865
  where: { id: mfaId, user_id: userId },
787
866
  });
788
867
 
789
- return this.getUserToken(userId);
868
+ return this.getUserToken(userId, sessionId);
790
869
  } catch (error: any) {
791
870
  throw new BadRequestException(`Failed to remove MFA: ${error?.message}`);
792
871
  }
793
872
  }
794
873
 
795
- async checkMfaBeforeAdd(locale: string, userId: number) {
874
+ async checkMfaBeforeAdd(locale: string, userId: number, origin?: string) {
796
875
  try {
797
876
  const mfaMethods = await this.prisma.user_mfa.findMany({
798
877
  where: {
@@ -822,7 +901,7 @@ export class ProfileService {
822
901
 
823
902
  if (emailMfas.length > 0) {
824
903
  const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
825
- await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
904
+ await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
826
905
  availableMethods.push('email');
827
906
  }
828
907
 
@@ -859,7 +938,7 @@ export class ProfileService {
859
938
  }
860
939
  }
861
940
 
862
- async checkMfaBeforeRemove(locale: string, userId: number) {
941
+ async checkMfaBeforeRemove(locale: string, userId: number, origin?: string) {
863
942
  try {
864
943
  const mfaMethods = await this.prisma.user_mfa.findMany({
865
944
  where: {
@@ -890,7 +969,7 @@ export class ProfileService {
890
969
 
891
970
  if (emailMfas.length > 0) {
892
971
  const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
893
- await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
972
+ await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
894
973
  availableMethods.push('email');
895
974
  }
896
975
 
@@ -1031,7 +1110,7 @@ export class ProfileService {
1031
1110
  }
1032
1111
  }
1033
1112
 
1034
- async sendRecoveryCodesVerification(locale: string, userId: number) {
1113
+ async sendRecoveryCodesVerification(locale: string, userId: number, origin?: string) {
1035
1114
  try {
1036
1115
  const mfaMethods = await this.prisma.user_mfa.findMany({
1037
1116
  where: {
@@ -1060,7 +1139,7 @@ export class ProfileService {
1060
1139
 
1061
1140
  if (emailMfas.length > 0) {
1062
1141
  const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
1063
- await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
1142
+ await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
1064
1143
  availableMethods.push('email');
1065
1144
  }
1066
1145
 
@@ -1476,7 +1555,13 @@ export class ProfileService {
1476
1555
  }
1477
1556
  }
1478
1557
 
1479
- async removeWebAuthnMfa(locale: string, userId: number, mfaId: number) {
1558
+ async removeWebAuthnMfa(
1559
+ locale: string,
1560
+ userId: number,
1561
+ mfaId: number,
1562
+ assertionResponse: any,
1563
+ requestOrigin?: string,
1564
+ ) {
1480
1565
  try {
1481
1566
  const mfa = await this.prisma.user_mfa.findFirst({
1482
1567
  where: {
@@ -1492,6 +1577,22 @@ export class ProfileService {
1492
1577
  );
1493
1578
  }
1494
1579
 
1580
+ // Reautenticacao obrigatoria: sem isso, um token de acesso vazado bastava
1581
+ // para remover a chave de seguranca da conta sem provar posse dela.
1582
+ if (!assertionResponse) {
1583
+ throw new BadRequestException(
1584
+ getLocaleText('assertionResponseRequired', locale, 'Assertion response is required for WebAuthn.')
1585
+ );
1586
+ }
1587
+
1588
+ try {
1589
+ await this.verifyWebAuthnAuthentication(userId, assertionResponse, requestOrigin);
1590
+ } catch (error) {
1591
+ throw new BadRequestException(
1592
+ getLocaleText('invalidWebAuthn', locale, 'WebAuthn verification failed.')
1593
+ );
1594
+ }
1595
+
1495
1596
  await this.prisma.user_mfa.delete({
1496
1597
  where: { id: mfaId },
1497
1598
  });
@@ -1632,8 +1733,7 @@ export class ProfileService {
1632
1733
  }
1633
1734
  }
1634
1735
 
1635
- async getUserToken(userId: number){
1636
- const updatedUser = await this.prisma.user.findFirst({ where: { id: userId }})
1637
- return this.getToken(updatedUser);
1736
+ async getUserToken(userId: number, sessionId: number | null){
1737
+ return this.getToken(userId, sessionId);
1638
1738
  }
1639
1739
  }
@@ -0,0 +1,138 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { EventEmitter2 } from '@nestjs/event-emitter';
3
+ import {
4
+ ConnectedSocket,
5
+ MessageBody,
6
+ OnGatewayConnection,
7
+ OnGatewayDisconnect,
8
+ OnGatewayInit,
9
+ SubscribeMessage,
10
+ WebSocketGateway,
11
+ WebSocketServer,
12
+ } from '@nestjs/websockets';
13
+ import type { Server, Socket } from 'socket.io';
14
+ import { TokenService } from '../token/token.service';
15
+ import { RealtimeService } from './realtime.service';
16
+
17
+ /** Events other modules can listen to via `@OnEvent(...)`. */
18
+ export const REALTIME_EVENTS = {
19
+ userConnected: 'realtime.user.connected',
20
+ userDisconnected: 'realtime.user.disconnected',
21
+ } as const;
22
+
23
+ function corsOrigin(): string[] | boolean {
24
+ const raw = String(process.env.CORS_ALLOWED_ORIGINS || '').trim();
25
+ if (!raw) return true; // permissive handshake — the JWT is the real gate
26
+ return raw
27
+ .split(',')
28
+ .map((s) => s.trim())
29
+ .filter(Boolean);
30
+ }
31
+
32
+ /**
33
+ * Single Socket.IO gateway (`/realtime`) on the same HTTP server/port. It only
34
+ * handles transport + auth + room membership; the reusable emit API lives in
35
+ * {@link RealtimeService}. Auth reuses {@link TokenService.verify} (same JWT as
36
+ * the HTTP `AuthGuard`); connection lifecycle is broadcast via EventEmitter2 so
37
+ * features (e.g. presence) react without this layer knowing about them.
38
+ */
39
+ @WebSocketGateway({
40
+ namespace: '/realtime',
41
+ cors: { origin: corsOrigin(), credentials: true },
42
+ })
43
+ export class RealtimeGateway
44
+ implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
45
+ {
46
+ private readonly logger = new Logger(RealtimeGateway.name);
47
+
48
+ @WebSocketServer()
49
+ server!: Server;
50
+
51
+ constructor(
52
+ private readonly realtime: RealtimeService,
53
+ private readonly token: TokenService,
54
+ private readonly events: EventEmitter2,
55
+ ) {}
56
+
57
+ afterInit(server: Server) {
58
+ this.realtime.setServer(server);
59
+ this.logger.log('Realtime gateway initialized on /realtime');
60
+ }
61
+
62
+ private extractToken(socket: Socket): string | undefined {
63
+ const authToken = (socket.handshake.auth as { token?: string } | undefined)?.token;
64
+ if (authToken) return authToken;
65
+ const header = socket.handshake.headers.authorization;
66
+ const [type, value] = String(header ?? '').split(' ');
67
+ return type === 'Bearer' ? value : undefined;
68
+ }
69
+
70
+ async handleConnection(socket: Socket) {
71
+ const token = this.extractToken(socket);
72
+ if (!token) {
73
+ socket.disconnect(true);
74
+ return;
75
+ }
76
+ try {
77
+ const payload = await this.token.verify('en', token);
78
+ const userId = Number(payload?.sub);
79
+ if (!Number.isInteger(userId) || userId <= 0) {
80
+ socket.disconnect(true);
81
+ return;
82
+ }
83
+ socket.data.userId = userId;
84
+ socket.join(`user:${userId}`);
85
+ const localCount = this.realtime.trackConnect(userId);
86
+ this.events.emit(REALTIME_EVENTS.userConnected, {
87
+ userId,
88
+ firstConnection: localCount === 1,
89
+ });
90
+ } catch {
91
+ socket.disconnect(true);
92
+ }
93
+ }
94
+
95
+ handleDisconnect(socket: Socket) {
96
+ const userId = socket.data?.userId as number | undefined;
97
+ if (!userId) return;
98
+ const remaining = this.realtime.trackDisconnect(userId);
99
+ this.events.emit(REALTIME_EVENTS.userDisconnected, {
100
+ userId,
101
+ remainingLocalSockets: remaining,
102
+ });
103
+ }
104
+
105
+ /** Joins the socket to the given channels (rooms `channel:{name}`). */
106
+ @SubscribeMessage('subscribe')
107
+ onSubscribe(
108
+ @ConnectedSocket() socket: Socket,
109
+ @MessageBody() body: { channels?: string[] },
110
+ ) {
111
+ const channels = Array.isArray(body?.channels) ? body.channels : [];
112
+ for (const ch of channels) {
113
+ if (this.isAllowed(socket, String(ch))) socket.join(`channel:${ch}`);
114
+ }
115
+ return { ok: true, subscribed: channels };
116
+ }
117
+
118
+ @SubscribeMessage('unsubscribe')
119
+ onUnsubscribe(
120
+ @ConnectedSocket() socket: Socket,
121
+ @MessageBody() body: { channels?: string[] },
122
+ ) {
123
+ const channels = Array.isArray(body?.channels) ? body.channels : [];
124
+ for (const ch of channels) socket.leave(`channel:${ch}`);
125
+ return { ok: true };
126
+ }
127
+
128
+ /**
129
+ * Channel-subscription authorization. `presence:{id}` is readable by any
130
+ * authenticated user (presence is coarse/non-sensitive). Modules adding
131
+ * sensitive channels should extend this with per-channel checks.
132
+ */
133
+ private isAllowed(_socket: Socket, channel: string): boolean {
134
+ if (channel.startsWith('presence:')) return true;
135
+ // Default-allow other channels for now; tighten per feature as needed.
136
+ return true;
137
+ }
138
+ }
@@ -0,0 +1,16 @@
1
+ import { forwardRef, Module } from '@nestjs/common';
2
+ import { TokenModule } from '../token/token.module';
3
+ import { RealtimeGateway } from './realtime.gateway';
4
+ import { RealtimeService } from './realtime.service';
5
+
6
+ /**
7
+ * Generic real-time (WebSocket) layer for the whole app. Any module can inject
8
+ * {@link RealtimeService} to push events to users/channels, fanned out across
9
+ * instances via Redis. EventEmitter2 (global) delivers connection lifecycle.
10
+ */
11
+ @Module({
12
+ imports: [forwardRef(() => TokenModule)],
13
+ providers: [RealtimeGateway, RealtimeService],
14
+ exports: [RealtimeService],
15
+ })
16
+ export class RealtimeModule {}
@@ -0,0 +1,50 @@
1
+ import { beforeEach, describe, expect, it, jest } from '@jest/globals';
2
+ import { RealtimeService } from './realtime.service';
3
+
4
+ const makeServer = () => {
5
+ const emit = jest.fn();
6
+ const to = jest.fn(() => ({ emit }));
7
+ return { to, emit: emit as any, rootEmit: jest.fn(), server: { to, emit: jest.fn() } };
8
+ };
9
+
10
+ describe('RealtimeService', () => {
11
+ let service: RealtimeService;
12
+ let server: ReturnType<typeof makeServer>;
13
+
14
+ beforeEach(() => {
15
+ service = new RealtimeService();
16
+ server = makeServer();
17
+ service.setServer(server.server as any);
18
+ });
19
+
20
+ it('emits to the user room', () => {
21
+ service.emitToUser(7, 'presence:update', { online: true });
22
+ expect(server.to).toHaveBeenCalledWith('user:7');
23
+ });
24
+
25
+ it('emits to the channel room', () => {
26
+ service.emitToChannel('presence:7', 'presence:update', { online: true });
27
+ expect(server.to).toHaveBeenCalledWith('channel:presence:7');
28
+ });
29
+
30
+ it('broadcasts to everyone', () => {
31
+ service.broadcast('ping', {});
32
+ expect((server.server as any).emit).toHaveBeenCalledWith('ping', {});
33
+ });
34
+
35
+ it('does not throw when no server is bound yet', () => {
36
+ const bare = new RealtimeService();
37
+ expect(() => bare.emitToUser(1, 'x', {})).not.toThrow();
38
+ });
39
+
40
+ it('tracks local connections per user', () => {
41
+ expect(service.trackConnect(1)).toBe(1);
42
+ expect(service.trackConnect(1)).toBe(2);
43
+ expect(service.isLocallyConnected(1)).toBe(true);
44
+ expect(service.getLocallyConnectedUserIds()).toEqual([1]);
45
+ expect(service.trackDisconnect(1)).toBe(1);
46
+ expect(service.trackDisconnect(1)).toBe(0);
47
+ expect(service.isLocallyConnected(1)).toBe(false);
48
+ expect(service.getLocallyConnectedUserIds()).toEqual([]);
49
+ });
50
+ });