@hed-hog/core 0.0.398 → 0.0.400

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 (664) hide show
  1. package/dist/ai/ai-agent.sort.d.ts +6 -0
  2. package/dist/ai/ai-agent.sort.d.ts.map +1 -0
  3. package/dist/ai/ai-agent.sort.js +21 -0
  4. package/dist/ai/ai-agent.sort.js.map +1 -0
  5. package/dist/ai/ai.controller.d.ts +4 -4
  6. package/dist/ai/ai.service.d.ts +35 -4
  7. package/dist/ai/ai.service.d.ts.map +1 -1
  8. package/dist/ai/ai.service.js +51 -137
  9. package/dist/ai/ai.service.js.map +1 -1
  10. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  11. package/dist/ai-instruction/ai-instruction.service.js +26 -23
  12. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  13. package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
  14. package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
  15. package/dist/ai-instruction/ai-instruction.sort.js +32 -0
  16. package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
  17. package/dist/auth/auth.controller.d.ts +24 -8
  18. package/dist/auth/auth.controller.d.ts.map +1 -1
  19. package/dist/auth/auth.module.d.ts.map +1 -1
  20. package/dist/auth/auth.module.js +8 -1
  21. package/dist/auth/auth.module.js.map +1 -1
  22. package/dist/auth/auth.service.d.ts +72 -25
  23. package/dist/auth/auth.service.d.ts.map +1 -1
  24. package/dist/auth/auth.service.js +41 -4
  25. package/dist/auth/auth.service.js.map +1 -1
  26. package/dist/auth/guards/auth.guard.d.ts +3 -1
  27. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  28. package/dist/auth/guards/auth.guard.js +12 -3
  29. package/dist/auth/guards/auth.guard.js.map +1 -1
  30. package/dist/authorize/authorize.controller.d.ts +30 -6
  31. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  32. package/dist/authorize/authorize.controller.js +120 -13
  33. package/dist/authorize/authorize.controller.js.map +1 -1
  34. package/dist/authorize/authorize.errors.d.ts +34 -0
  35. package/dist/authorize/authorize.errors.d.ts.map +1 -0
  36. package/dist/authorize/authorize.errors.js +37 -0
  37. package/dist/authorize/authorize.errors.js.map +1 -0
  38. package/dist/authorize/authorize.module.d.ts.map +1 -1
  39. package/dist/authorize/authorize.module.js +6 -1
  40. package/dist/authorize/authorize.module.js.map +1 -1
  41. package/dist/authorize/authorize.service.d.ts +78 -4
  42. package/dist/authorize/authorize.service.d.ts.map +1 -1
  43. package/dist/authorize/authorize.service.js +212 -21
  44. package/dist/authorize/authorize.service.js.map +1 -1
  45. package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
  46. package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
  47. package/dist/authorize/dto/authorize-info.dto.js +16 -1
  48. package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
  49. package/dist/authorize/dto/consent.dto.d.ts +10 -1
  50. package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
  51. package/dist/authorize/dto/consent.dto.js +16 -1
  52. package/dist/authorize/dto/consent.dto.js.map +1 -1
  53. package/dist/authorize/dto/token.dto.d.ts +12 -0
  54. package/dist/authorize/dto/token.dto.d.ts.map +1 -1
  55. package/dist/authorize/dto/token.dto.js +21 -1
  56. package/dist/authorize/dto/token.dto.js.map +1 -1
  57. package/dist/authorize/oauth-exception.filter.d.ts +17 -0
  58. package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
  59. package/dist/authorize/oauth-exception.filter.js +54 -0
  60. package/dist/authorize/oauth-exception.filter.js.map +1 -0
  61. package/dist/authorize/oauth-metadata.service.d.ts +39 -0
  62. package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
  63. package/dist/authorize/oauth-metadata.service.js +81 -0
  64. package/dist/authorize/oauth-metadata.service.js.map +1 -0
  65. package/dist/authorize/well-known.controller.d.ts +67 -0
  66. package/dist/authorize/well-known.controller.d.ts.map +1 -0
  67. package/dist/authorize/well-known.controller.js +126 -0
  68. package/dist/authorize/well-known.controller.js.map +1 -0
  69. package/dist/captcha/captcha.service.d.ts +32 -2
  70. package/dist/captcha/captcha.service.d.ts.map +1 -1
  71. package/dist/captcha/captcha.service.js +85 -12
  72. package/dist/captcha/captcha.service.js.map +1 -1
  73. package/dist/challenge/challenge.service.d.ts +2 -2
  74. package/dist/core.module.d.ts.map +1 -1
  75. package/dist/core.module.js +6 -0
  76. package/dist/core.module.js.map +1 -1
  77. package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
  78. package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
  79. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
  80. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
  81. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
  82. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
  83. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  84. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
  85. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
  86. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
  87. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
  88. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
  89. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
  90. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  91. package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
  92. package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
  93. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
  94. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
  95. package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
  96. package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
  97. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
  98. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
  99. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
  100. package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
  101. package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
  102. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
  103. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
  104. package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
  105. package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
  106. package/dist/file/file-upload-ticket.service.d.ts +160 -0
  107. package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
  108. package/dist/file/file-upload-ticket.service.js +382 -0
  109. package/dist/file/file-upload-ticket.service.js.map +1 -0
  110. package/dist/file/file.controller.d.ts +24 -1
  111. package/dist/file/file.controller.d.ts.map +1 -1
  112. package/dist/file/file.controller.js +63 -2
  113. package/dist/file/file.controller.js.map +1 -1
  114. package/dist/file/file.module.d.ts.map +1 -1
  115. package/dist/file/file.module.js +14 -2
  116. package/dist/file/file.module.js.map +1 -1
  117. package/dist/file/file.service.d.ts +182 -6
  118. package/dist/file/file.service.d.ts.map +1 -1
  119. package/dist/file/file.service.js +385 -36
  120. package/dist/file/file.service.js.map +1 -1
  121. package/dist/file/filename-encoding.util.d.ts +24 -0
  122. package/dist/file/filename-encoding.util.d.ts.map +1 -0
  123. package/dist/file/filename-encoding.util.js +63 -0
  124. package/dist/file/filename-encoding.util.js.map +1 -0
  125. package/dist/file/image-optimization.service.d.ts +27 -0
  126. package/dist/file/image-optimization.service.d.ts.map +1 -1
  127. package/dist/file/image-optimization.service.js +63 -14
  128. package/dist/file/image-optimization.service.js.map +1 -1
  129. package/dist/file/mcp-file-source.schema.d.ts +33 -0
  130. package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
  131. package/dist/file/mcp-file-source.schema.js +75 -0
  132. package/dist/file/mcp-file-source.schema.js.map +1 -0
  133. package/dist/file/mcp-file-source.service.d.ts +124 -0
  134. package/dist/file/mcp-file-source.service.d.ts.map +1 -0
  135. package/dist/file/mcp-file-source.service.js +262 -0
  136. package/dist/file/mcp-file-source.service.js.map +1 -0
  137. package/dist/file/mimetype-sniff.util.d.ts +62 -0
  138. package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
  139. package/dist/file/mimetype-sniff.util.js +226 -0
  140. package/dist/file/mimetype-sniff.util.js.map +1 -0
  141. package/dist/file/remote-fetch.util.d.ts +35 -0
  142. package/dist/file/remote-fetch.util.d.ts.map +1 -0
  143. package/dist/file/remote-fetch.util.js +236 -0
  144. package/dist/file/remote-fetch.util.js.map +1 -0
  145. package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
  146. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
  147. package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
  148. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
  149. package/dist/impersonation/impersonation.service.d.ts +8 -3
  150. package/dist/impersonation/impersonation.service.d.ts.map +1 -1
  151. package/dist/impersonation/impersonation.service.js +14 -4
  152. package/dist/impersonation/impersonation.service.js.map +1 -1
  153. package/dist/incident/incident.controller.d.ts +2 -1
  154. package/dist/incident/incident.controller.d.ts.map +1 -1
  155. package/dist/incident/incident.controller.js +10 -6
  156. package/dist/incident/incident.controller.js.map +1 -1
  157. package/dist/incident/incident.service.d.ts +2 -0
  158. package/dist/incident/incident.service.d.ts.map +1 -1
  159. package/dist/incident/incident.service.js +4 -0
  160. package/dist/incident/incident.service.js.map +1 -1
  161. package/dist/index.d.ts +17 -0
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +17 -0
  164. package/dist/index.js.map +1 -1
  165. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  166. package/dist/integration-profile/integration-profile.secrets.js +7 -0
  167. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  168. package/dist/integration-profile/integration-profile.service.d.ts +0 -13
  169. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  170. package/dist/integration-profile/integration-profile.service.js +5 -40
  171. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  172. package/dist/integration-profile/integration-profile.utils.js +5 -5
  173. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  174. package/dist/integration-profile/testers/index.d.ts.map +1 -1
  175. package/dist/integration-profile/testers/index.js +2 -0
  176. package/dist/integration-profile/testers/index.js.map +1 -1
  177. package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
  178. package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
  179. package/dist/integration-profile/testers/shipping.testers.js +71 -0
  180. package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
  181. package/dist/locale/locale.controller.d.ts +1 -1
  182. package/dist/locale/locale.service.d.ts +1 -1
  183. package/dist/mail/dto/send.dto.d.ts +6 -1
  184. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  185. package/dist/mail/dto/send.dto.js.map +1 -1
  186. package/dist/mail/mail.service.d.ts +9 -0
  187. package/dist/mail/mail.service.d.ts.map +1 -1
  188. package/dist/mail/mail.service.js +34 -14
  189. package/dist/mail/mail.service.js.map +1 -1
  190. package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
  191. package/dist/mail-sent/mail-sent.service.d.ts +1 -1
  192. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  193. package/dist/mail-sent/mail-sent.service.js +2 -1
  194. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  195. package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
  196. package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
  197. package/dist/mail-sent/mail-sent.sort.js +19 -0
  198. package/dist/mail-sent/mail-sent.sort.js.map +1 -0
  199. package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
  200. package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
  201. package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
  202. package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
  203. package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
  204. package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
  205. package/dist/mcp/guards/mcp-auth.guard.js +19 -5
  206. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  207. package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
  208. package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
  209. package/dist/mcp/guards/mcp-permission.guard.js +46 -26
  210. package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
  211. package/dist/mcp/mcp-audit.service.d.ts +17 -0
  212. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  213. package/dist/mcp/mcp-audit.service.js +32 -3
  214. package/dist/mcp/mcp-audit.service.js.map +1 -1
  215. package/dist/mcp/mcp-permission.service.d.ts +64 -0
  216. package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
  217. package/dist/mcp/mcp-permission.service.js +178 -0
  218. package/dist/mcp/mcp-permission.service.js.map +1 -0
  219. package/dist/mcp/mcp-server.service.d.ts +3 -1
  220. package/dist/mcp/mcp-server.service.d.ts.map +1 -1
  221. package/dist/mcp/mcp-server.service.js +36 -15
  222. package/dist/mcp/mcp-server.service.js.map +1 -1
  223. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  224. package/dist/mcp/mcp.controller.js +10 -2
  225. package/dist/mcp/mcp.controller.js.map +1 -1
  226. package/dist/mcp/mcp.module.d.ts.map +1 -1
  227. package/dist/mcp/mcp.module.js +5 -1
  228. package/dist/mcp/mcp.module.js.map +1 -1
  229. package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
  230. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
  231. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
  232. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
  233. package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
  234. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
  235. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
  236. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
  237. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
  238. package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
  239. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
  240. package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
  241. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
  242. package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
  243. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
  244. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
  245. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
  246. package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
  247. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
  248. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
  249. package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
  250. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
  251. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
  252. package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
  253. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
  254. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
  255. package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
  256. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
  257. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
  258. package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
  259. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
  260. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
  261. package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
  262. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
  263. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
  264. package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
  265. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
  266. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  267. package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
  268. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  269. package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
  270. package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
  271. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
  272. package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
  273. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
  274. package/dist/mcp/types/mcp-context.type.d.ts +9 -0
  275. package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
  276. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  277. package/dist/mcp-chat/mcp-chat.service.js +8 -0
  278. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  279. package/dist/meeting/meeting-provider.registry.d.ts +115 -0
  280. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
  281. package/dist/meeting/meeting-provider.registry.js.map +1 -1
  282. package/dist/meeting/meeting.constants.d.ts +70 -0
  283. package/dist/meeting/meeting.constants.d.ts.map +1 -1
  284. package/dist/meeting/meeting.constants.js +9 -1
  285. package/dist/meeting/meeting.constants.js.map +1 -1
  286. package/dist/menu/menu.service.d.ts.map +1 -1
  287. package/dist/menu/menu.service.js +29 -9
  288. package/dist/menu/menu.service.js.map +1 -1
  289. package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
  290. package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
  291. package/dist/message-template/dto/update-message-template.dto.js +38 -0
  292. package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
  293. package/dist/message-template/message-template-admin.service.d.ts +70 -0
  294. package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
  295. package/dist/message-template/message-template-admin.service.js +226 -0
  296. package/dist/message-template/message-template-admin.service.js.map +1 -0
  297. package/dist/message-template/message-template.controller.d.ts +29 -0
  298. package/dist/message-template/message-template.controller.d.ts.map +1 -0
  299. package/dist/message-template/message-template.controller.js +101 -0
  300. package/dist/message-template/message-template.controller.js.map +1 -0
  301. package/dist/message-template/message-template.module.d.ts +10 -0
  302. package/dist/message-template/message-template.module.d.ts.map +1 -0
  303. package/dist/message-template/message-template.module.js +33 -0
  304. package/dist/message-template/message-template.module.js.map +1 -0
  305. package/dist/message-template/message-template.registry.d.ts +55 -0
  306. package/dist/message-template/message-template.registry.d.ts.map +1 -0
  307. package/dist/message-template/message-template.registry.js +64 -0
  308. package/dist/message-template/message-template.registry.js.map +1 -0
  309. package/dist/message-template/message-template.service.d.ts +59 -0
  310. package/dist/message-template/message-template.service.d.ts.map +1 -0
  311. package/dist/message-template/message-template.service.js +166 -0
  312. package/dist/message-template/message-template.service.js.map +1 -0
  313. package/dist/notification/notification.constants.d.ts +11 -0
  314. package/dist/notification/notification.constants.d.ts.map +1 -0
  315. package/dist/notification/notification.constants.js +14 -0
  316. package/dist/notification/notification.constants.js.map +1 -0
  317. package/dist/notification/notification.controller.d.ts +20 -20
  318. package/dist/notification/notification.module.d.ts.map +1 -1
  319. package/dist/notification/notification.module.js +3 -0
  320. package/dist/notification/notification.module.js.map +1 -1
  321. package/dist/notification/notification.service.d.ts +33 -21
  322. package/dist/notification/notification.service.d.ts.map +1 -1
  323. package/dist/notification/notification.service.js +36 -2
  324. package/dist/notification/notification.service.js.map +1 -1
  325. package/dist/oauth/oauth.controller.d.ts +4 -6
  326. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  327. package/dist/oauth/oauth.controller.js +0 -1
  328. package/dist/oauth/oauth.controller.js.map +1 -1
  329. package/dist/oauth/oauth.errors.d.ts +15 -1
  330. package/dist/oauth/oauth.errors.d.ts.map +1 -1
  331. package/dist/oauth/oauth.errors.js +9 -1
  332. package/dist/oauth/oauth.errors.js.map +1 -1
  333. package/dist/oauth/oauth.module.d.ts.map +1 -1
  334. package/dist/oauth/oauth.module.js +6 -1
  335. package/dist/oauth/oauth.module.js.map +1 -1
  336. package/dist/oauth/oauth.service.d.ts +25 -5
  337. package/dist/oauth/oauth.service.d.ts.map +1 -1
  338. package/dist/oauth/oauth.service.js +63 -27
  339. package/dist/oauth/oauth.service.js.map +1 -1
  340. package/dist/oauth/providers/abstract.provider.d.ts +10 -0
  341. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  342. package/dist/oauth/providers/abstract.provider.js +39 -12
  343. package/dist/oauth/providers/abstract.provider.js.map +1 -1
  344. package/dist/oauth/providers/github.provider.d.ts +1 -1
  345. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  346. package/dist/oauth/providers/github.provider.js +48 -22
  347. package/dist/oauth/providers/github.provider.js.map +1 -1
  348. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  349. package/dist/oauth/providers/microsoft-entra-id.provider.js +6 -0
  350. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  351. package/dist/observability/monitored-cron.d.ts +29 -12
  352. package/dist/observability/monitored-cron.d.ts.map +1 -1
  353. package/dist/observability/monitored-cron.js +131 -46
  354. package/dist/observability/monitored-cron.js.map +1 -1
  355. package/dist/pairing/pairing.controller.d.ts +9 -9
  356. package/dist/pairing/pairing.service.d.ts +15 -11
  357. package/dist/pairing/pairing.service.d.ts.map +1 -1
  358. package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
  359. package/dist/profile/dto/change-password.dto.js +4 -6
  360. package/dist/profile/dto/change-password.dto.js.map +1 -1
  361. package/dist/profile/profile.controller.d.ts +1 -1
  362. package/dist/profile/profile.service.d.ts +1 -1
  363. package/dist/profile/profile.service.d.ts.map +1 -1
  364. package/dist/profile/profile.service.js +9 -3
  365. package/dist/profile/profile.service.js.map +1 -1
  366. package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
  367. package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
  368. package/dist/realtime/realtime-tick.coalescer.js +60 -0
  369. package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
  370. package/dist/realtime/realtime.service.d.ts +2 -0
  371. package/dist/realtime/realtime.service.d.ts.map +1 -1
  372. package/dist/realtime/realtime.service.js +22 -2
  373. package/dist/realtime/realtime.service.js.map +1 -1
  374. package/dist/redis/redis-kv.service.d.ts +90 -0
  375. package/dist/redis/redis-kv.service.d.ts.map +1 -0
  376. package/dist/redis/redis-kv.service.js +247 -0
  377. package/dist/redis/redis-kv.service.js.map +1 -0
  378. package/dist/redis/redis-master.d.ts +29 -0
  379. package/dist/redis/redis-master.d.ts.map +1 -0
  380. package/dist/redis/redis-master.js +81 -0
  381. package/dist/redis/redis-master.js.map +1 -0
  382. package/dist/redis/redis-pubsub.d.ts +12 -0
  383. package/dist/redis/redis-pubsub.d.ts.map +1 -1
  384. package/dist/redis/redis-pubsub.js +25 -2
  385. package/dist/redis/redis-pubsub.js.map +1 -1
  386. package/dist/redis/redis.module.d.ts +11 -0
  387. package/dist/redis/redis.module.d.ts.map +1 -0
  388. package/dist/redis/redis.module.js +30 -0
  389. package/dist/redis/redis.module.js.map +1 -0
  390. package/dist/session/session.controller.d.ts +6 -2
  391. package/dist/session/session.controller.d.ts.map +1 -1
  392. package/dist/session/session.service.d.ts +118 -15
  393. package/dist/session/session.service.d.ts.map +1 -1
  394. package/dist/session/session.service.js +251 -77
  395. package/dist/session/session.service.js.map +1 -1
  396. package/dist/setting/setting.controller.d.ts +5 -0
  397. package/dist/setting/setting.controller.d.ts.map +1 -1
  398. package/dist/setting/setting.service.d.ts +7 -0
  399. package/dist/setting/setting.service.d.ts.map +1 -1
  400. package/dist/setting/setting.service.js +38 -3
  401. package/dist/setting/setting.service.js.map +1 -1
  402. package/dist/task/task.service.d.ts +17 -0
  403. package/dist/task/task.service.d.ts.map +1 -1
  404. package/dist/task/task.service.js +48 -13
  405. package/dist/task/task.service.js.map +1 -1
  406. package/dist/token/token.service.d.ts +19 -1
  407. package/dist/token/token.service.d.ts.map +1 -1
  408. package/dist/token/token.service.js +39 -2
  409. package/dist/token/token.service.js.map +1 -1
  410. package/dist/user/user-credential.util.d.ts +10 -0
  411. package/dist/user/user-credential.util.d.ts.map +1 -1
  412. package/dist/user/user-credential.util.js +14 -1
  413. package/dist/user/user-credential.util.js.map +1 -1
  414. package/dist/user/user.service.d.ts +12 -4
  415. package/dist/user/user.service.d.ts.map +1 -1
  416. package/dist/user/user.service.js +2 -11
  417. package/dist/user/user.service.js.map +1 -1
  418. package/dist/user/user.sort.d.ts +16 -0
  419. package/dist/user/user.sort.d.ts.map +1 -0
  420. package/dist/user/user.sort.js +25 -0
  421. package/dist/user/user.sort.js.map +1 -0
  422. package/dist/utils/abbreviate-name.d.ts +13 -0
  423. package/dist/utils/abbreviate-name.d.ts.map +1 -0
  424. package/dist/utils/abbreviate-name.js +42 -0
  425. package/dist/utils/abbreviate-name.js.map +1 -0
  426. package/dist/utils/date-range-query.dto.d.ts +11 -0
  427. package/dist/utils/date-range-query.dto.d.ts.map +1 -0
  428. package/dist/utils/date-range-query.dto.js +63 -0
  429. package/dist/utils/date-range-query.dto.js.map +1 -0
  430. package/dist/utils/date-range.d.ts +96 -0
  431. package/dist/utils/date-range.d.ts.map +1 -0
  432. package/dist/utils/date-range.js +172 -0
  433. package/dist/utils/date-range.js.map +1 -0
  434. package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
  435. package/dist/webhook-integration/webhook-utils.js +24 -0
  436. package/dist/webhook-integration/webhook-utils.js.map +1 -1
  437. package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
  438. package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
  439. package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
  440. package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
  441. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
  442. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
  443. package/dist/whatsapp-sender/whatsapp-sender.service.js +46 -5
  444. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
  445. package/hedhog/data/integration_provider.yaml +9 -0
  446. package/hedhog/data/integration_type.yaml +6 -0
  447. package/hedhog/data/locale.yaml +0 -1
  448. package/hedhog/data/mail.yaml +2 -2
  449. package/hedhog/data/mail_layout.yaml +6 -3
  450. package/hedhog/data/menu.yaml +25 -5
  451. package/hedhog/data/route.yaml +140 -0
  452. package/hedhog/data/setting_group.yaml +137 -3
  453. package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
  454. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
  455. package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
  456. package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
  457. package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
  458. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
  459. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
  460. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
  461. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
  462. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
  463. package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
  464. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
  465. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
  466. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
  467. package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
  468. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
  469. package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
  470. package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
  471. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
  472. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
  473. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
  474. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
  475. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
  476. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
  477. package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
  478. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
  479. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
  480. package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
  481. package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
  482. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
  483. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
  484. package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
  485. package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
  486. package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
  487. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
  488. package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
  489. package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
  490. package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
  491. package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
  492. package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
  493. package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
  494. package/hedhog/frontend/messages/en.json +183 -36
  495. package/hedhog/frontend/messages/pt.json +183 -36
  496. package/hedhog/table/cron_lock.yaml +17 -0
  497. package/hedhog/table/file.yaml +13 -1
  498. package/hedhog/table/file_upload_chunk.yaml +23 -0
  499. package/hedhog/table/file_upload_ticket.yaml +67 -0
  500. package/hedhog/table/mcp_tool_call_log.yaml +30 -22
  501. package/hedhog/table/message_template.yaml +33 -0
  502. package/hedhog/table/setting.yaml +8 -0
  503. package/hedhog/table/user_session.yaml +36 -1
  504. package/package.json +8 -6
  505. package/src/ai/ai-agent.sort.ts +21 -0
  506. package/src/ai/ai.service.spec.ts +173 -1
  507. package/src/ai/ai.service.ts +64 -193
  508. package/src/ai-instruction/ai-instruction.service.ts +32 -28
  509. package/src/ai-instruction/ai-instruction.sort.ts +32 -0
  510. package/src/auth/auth.module.ts +8 -1
  511. package/src/auth/auth.service.spec.ts +108 -1
  512. package/src/auth/auth.service.ts +59 -5
  513. package/src/auth/guards/auth.guard.ts +9 -1
  514. package/src/authorize/authorize.controller.ts +126 -11
  515. package/src/authorize/authorize.errors.ts +55 -0
  516. package/src/authorize/authorize.module.ts +6 -1
  517. package/src/authorize/authorize.service.spec.ts +379 -7
  518. package/src/authorize/authorize.service.ts +309 -23
  519. package/src/authorize/dto/authorize-info.dto.ts +21 -3
  520. package/src/authorize/dto/consent.dto.ts +21 -3
  521. package/src/authorize/dto/token.dto.ts +24 -2
  522. package/src/authorize/oauth-exception.filter.ts +58 -0
  523. package/src/authorize/oauth-metadata.service.ts +67 -0
  524. package/src/authorize/well-known.controller.spec.ts +84 -0
  525. package/src/authorize/well-known.controller.ts +96 -0
  526. package/src/captcha/captcha.service.spec.ts +211 -4
  527. package/src/captcha/captcha.service.ts +127 -10
  528. package/src/core.module.ts +6 -0
  529. package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
  530. package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
  531. package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
  532. package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
  533. package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
  534. package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
  535. package/src/file/file-upload-ticket.service.spec.ts +422 -0
  536. package/src/file/file-upload-ticket.service.ts +513 -0
  537. package/src/file/file.controller.ts +65 -0
  538. package/src/file/file.module.ts +14 -2
  539. package/src/file/file.service.spec.ts +94 -0
  540. package/src/file/file.service.ts +432 -30
  541. package/src/file/filename-encoding.util.spec.ts +54 -0
  542. package/src/file/filename-encoding.util.ts +67 -0
  543. package/src/file/image-optimization.service.spec.ts +156 -19
  544. package/src/file/image-optimization.service.ts +106 -21
  545. package/src/file/mcp-file-source.schema.ts +83 -0
  546. package/src/file/mcp-file-source.service.spec.ts +473 -0
  547. package/src/file/mcp-file-source.service.ts +445 -0
  548. package/src/file/mimetype-sniff.util.spec.ts +126 -0
  549. package/src/file/mimetype-sniff.util.ts +265 -0
  550. package/src/file/remote-fetch.util.spec.ts +117 -0
  551. package/src/file/remote-fetch.util.ts +330 -0
  552. package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
  553. package/src/impersonation/impersonation.service.spec.ts +24 -1
  554. package/src/impersonation/impersonation.service.ts +14 -4
  555. package/src/incident/incident.controller.ts +9 -1
  556. package/src/incident/incident.service.ts +7 -0
  557. package/src/index.ts +17 -0
  558. package/src/integration-profile/integration-profile.secrets.ts +7 -0
  559. package/src/integration-profile/integration-profile.service.ts +7 -64
  560. package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
  561. package/src/integration-profile/integration-profile.utils.ts +5 -5
  562. package/src/integration-profile/testers/index.ts +2 -0
  563. package/src/integration-profile/testers/remaining.testers.spec.ts +79 -0
  564. package/src/integration-profile/testers/shipping.testers.ts +94 -0
  565. package/src/mail/dto/send.dto.ts +6 -1
  566. package/src/mail/mail-template.spec.ts +58 -1
  567. package/src/mail/mail.service.spec.ts +112 -0
  568. package/src/mail/mail.service.ts +42 -4
  569. package/src/mail-sent/mail-sent.service.ts +3 -0
  570. package/src/mail-sent/mail-sent.sort.ts +19 -0
  571. package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
  572. package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
  573. package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
  574. package/src/mcp/guards/mcp-auth.guard.ts +24 -1
  575. package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
  576. package/src/mcp/guards/mcp-permission.guard.ts +76 -51
  577. package/src/mcp/mcp-audit.service.spec.ts +107 -0
  578. package/src/mcp/mcp-audit.service.ts +37 -1
  579. package/src/mcp/mcp-permission.service.spec.ts +197 -0
  580. package/src/mcp/mcp-permission.service.ts +184 -0
  581. package/src/mcp/mcp-server.service.spec.ts +84 -2
  582. package/src/mcp/mcp-server.service.ts +111 -87
  583. package/src/mcp/mcp.controller.ts +8 -1
  584. package/src/mcp/mcp.module.ts +5 -1
  585. package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
  586. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
  587. package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
  588. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
  589. package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
  590. package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
  591. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
  592. package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
  593. package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
  594. package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
  595. package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
  596. package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
  597. package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
  598. package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
  599. package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
  600. package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
  601. package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
  602. package/src/mcp/types/mcp-context.type.ts +15 -6
  603. package/src/mcp-chat/mcp-chat.service.ts +8 -0
  604. package/src/meeting/meeting-provider.registry.ts +121 -0
  605. package/src/meeting/meeting.constants.ts +74 -0
  606. package/src/menu/menu.service.spec.ts +89 -0
  607. package/src/menu/menu.service.ts +29 -11
  608. package/src/message-template/dto/update-message-template.dto.ts +22 -0
  609. package/src/message-template/message-template-admin.service.spec.ts +163 -0
  610. package/src/message-template/message-template-admin.service.ts +239 -0
  611. package/src/message-template/message-template.controller.ts +52 -0
  612. package/src/message-template/message-template.module.ts +21 -0
  613. package/src/message-template/message-template.registry.spec.ts +62 -0
  614. package/src/message-template/message-template.registry.ts +125 -0
  615. package/src/message-template/message-template.service.spec.ts +149 -0
  616. package/src/message-template/message-template.service.ts +149 -0
  617. package/src/notification/notification.constants.ts +10 -0
  618. package/src/notification/notification.module.ts +3 -0
  619. package/src/notification/notification.service.spec.ts +75 -1
  620. package/src/notification/notification.service.ts +39 -1
  621. package/src/oauth/oauth.controller.ts +0 -1
  622. package/src/oauth/oauth.errors.ts +7 -0
  623. package/src/oauth/oauth.module.ts +6 -1
  624. package/src/oauth/oauth.service.spec.ts +58 -7
  625. package/src/oauth/oauth.service.ts +71 -32
  626. package/src/oauth/providers/abstract.provider.spec.ts +192 -0
  627. package/src/oauth/providers/abstract.provider.ts +46 -14
  628. package/src/oauth/providers/github.provider.ts +57 -34
  629. package/src/oauth/providers/microsoft-entra-id.provider.ts +8 -1
  630. package/src/observability/monitored-cron.spec.ts +164 -31
  631. package/src/observability/monitored-cron.ts +154 -62
  632. package/src/profile/dto/change-password.dto.ts +25 -7
  633. package/src/profile/profile.service.spec.ts +97 -0
  634. package/src/profile/profile.service.ts +9 -3
  635. package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
  636. package/src/realtime/realtime-tick.coalescer.ts +60 -0
  637. package/src/realtime/realtime.service.spec.ts +33 -0
  638. package/src/realtime/realtime.service.ts +28 -2
  639. package/src/redis/redis-kv.service.ts +261 -0
  640. package/src/redis/redis-master.spec.ts +111 -0
  641. package/src/redis/redis-master.ts +86 -0
  642. package/src/redis/redis-pubsub.ts +27 -3
  643. package/src/redis/redis.module.ts +17 -0
  644. package/src/session/session.service.spec.ts +121 -42
  645. package/src/session/session.service.ts +279 -82
  646. package/src/setting/setting.service.ts +44 -3
  647. package/src/task/task.service.spec.ts +41 -0
  648. package/src/task/task.service.ts +53 -13
  649. package/src/token/token.service.spec.ts +90 -0
  650. package/src/token/token.service.ts +48 -2
  651. package/src/user/user-credential.util.ts +14 -5
  652. package/src/user/user.service.ts +2 -12
  653. package/src/user/user.sort.ts +25 -0
  654. package/src/utils/abbreviate-name.spec.ts +50 -0
  655. package/src/utils/abbreviate-name.ts +44 -0
  656. package/src/utils/date-range-query.dto.ts +56 -0
  657. package/src/utils/date-range.spec.ts +175 -0
  658. package/src/utils/date-range.ts +246 -0
  659. package/src/webhook-integration/webhook-utils.spec.ts +67 -0
  660. package/src/webhook-integration/webhook-utils.ts +26 -0
  661. package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
  662. package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
  663. package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +112 -0
  664. package/src/whatsapp-sender/whatsapp-sender.service.ts +58 -5
@@ -24,6 +24,7 @@ import { SecurityService } from '../security/security.service';
24
24
  import { SettingService } from '../setting/setting.service';
25
25
  import { TokenService } from '../token/token.service';
26
26
  import { UserService } from '../user/user.service';
27
+ import { isActivePasswordCredential } from '../user/user-credential.util';
27
28
  import { buildSecurityEventVars } from '../utils/security-event-vars';
28
29
  import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
29
30
  import { ChangeEmailDto } from './dto/change-email.dto';
@@ -101,7 +102,7 @@ export class ProfileService {
101
102
  throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found.'));
102
103
  }
103
104
 
104
- const credentials = (user as unknown as User).user_credential?.filter((c) => c.type === 'password') || [];
105
+ const credentials = (user as unknown as User).user_credential?.filter(isActivePasswordCredential) || [];
105
106
 
106
107
  if (!await this.security.validatePassword(locale, credentials, password)) {
107
108
  throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
@@ -325,7 +326,9 @@ export class ProfileService {
325
326
  async getProfile(userId: number) {
326
327
  const [user, passwordCredentials] = await Promise.all([
327
328
  this.prisma.user.findUnique({ where: { id: userId } }),
328
- this.prisma.user_credential.count({ where: { user_id: userId, type: 'password' } }),
329
+ this.prisma.user_credential.count({
330
+ where: { user_id: userId, type: 'password', revoked_at: null },
331
+ }),
329
332
  ]);
330
333
 
331
334
  if (!user) return user;
@@ -457,7 +460,10 @@ export class ProfileService {
457
460
 
458
461
  async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string, origin?: string) {
459
462
  const credential = await this.prisma.user_credential.findFirst({
460
- where: { user_id: userId, type: 'password' },
463
+ // Credencial revogada não é senha atual: sem este filtro, `changePassword`
464
+ // aceitava a senha antiga de uma credencial revogada e a reativava por via
465
+ // indireta, gravando um hash novo na mesma linha.
466
+ where: { user_id: userId, type: 'password', revoked_at: null },
461
467
  });
462
468
 
463
469
  if (!credential) {
@@ -0,0 +1,92 @@
1
+ /// <reference types="jest" />
2
+
3
+ import { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
4
+ import { RealtimeTickCoalescer } from './realtime-tick.coalescer';
5
+
6
+ describe('RealtimeTickCoalescer', () => {
7
+ beforeEach(() => {
8
+ jest.useFakeTimers();
9
+ });
10
+
11
+ afterEach(() => {
12
+ jest.useRealTimers();
13
+ });
14
+
15
+ it('junta uma rajada num único disparo', () => {
16
+ // O heartbeat bate a cada 15s por aluno: com 200 ao vivo seriam ~13
17
+ // eventos/s. É esta compressão que torna o push viável.
18
+ const onFlush = jest.fn();
19
+ const coalescer = new RealtimeTickCoalescer(2000, onFlush);
20
+
21
+ for (let i = 0; i < 200; i += 1) coalescer.signal();
22
+ expect(onFlush).not.toHaveBeenCalled();
23
+
24
+ jest.advanceTimersByTime(2000);
25
+
26
+ expect(onFlush).toHaveBeenCalledTimes(1);
27
+ expect(onFlush).toHaveBeenCalledWith({ seq: 1, coalesced: 200 });
28
+ });
29
+
30
+ it('não dispara sozinho quando não houve sinal', () => {
31
+ const onFlush = jest.fn();
32
+ new RealtimeTickCoalescer(2000, onFlush);
33
+
34
+ jest.advanceTimersByTime(10_000);
35
+
36
+ expect(onFlush).not.toHaveBeenCalled();
37
+ });
38
+
39
+ it('abre uma janela nova depois do disparo', () => {
40
+ const onFlush = jest.fn();
41
+ const coalescer = new RealtimeTickCoalescer(2000, onFlush);
42
+
43
+ coalescer.signal();
44
+ jest.advanceTimersByTime(2000);
45
+ expect(onFlush).toHaveBeenCalledTimes(1);
46
+
47
+ coalescer.signal();
48
+ jest.advanceTimersByTime(2000);
49
+
50
+ expect(onFlush).toHaveBeenCalledTimes(2);
51
+ expect(onFlush).toHaveBeenLastCalledWith({ seq: 2, coalesced: 1 });
52
+ });
53
+
54
+ it('não se auto-perpetua depois de esvaziar', () => {
55
+ const onFlush = jest.fn();
56
+ const coalescer = new RealtimeTickCoalescer(2000, onFlush);
57
+
58
+ coalescer.signal();
59
+ jest.advanceTimersByTime(2000);
60
+ jest.advanceTimersByTime(60_000);
61
+
62
+ expect(onFlush).toHaveBeenCalledTimes(1);
63
+ });
64
+
65
+ it('dispose cancela o disparo pendente', () => {
66
+ // Um timer vivo no encerramento segura o processo e atrasa o drain do pod.
67
+ const onFlush = jest.fn();
68
+ const coalescer = new RealtimeTickCoalescer(2000, onFlush);
69
+
70
+ coalescer.signal();
71
+ coalescer.dispose();
72
+ jest.advanceTimersByTime(10_000);
73
+
74
+ expect(onFlush).not.toHaveBeenCalled();
75
+ });
76
+
77
+ it('flush manual entrega o que estiver pendente', () => {
78
+ const onFlush = jest.fn();
79
+ const coalescer = new RealtimeTickCoalescer(2000, onFlush);
80
+
81
+ coalescer.signal();
82
+ coalescer.signal();
83
+ coalescer.flush();
84
+
85
+ expect(onFlush).toHaveBeenCalledWith({ seq: 1, coalesced: 2 });
86
+
87
+ // Depois do flush manual, o timer que já estava agendado não deve emitir de
88
+ // novo com a fila vazia.
89
+ jest.advanceTimersByTime(2000);
90
+ expect(onFlush).toHaveBeenCalledTimes(1);
91
+ });
92
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Junta uma rajada de sinais num único disparo.
3
+ *
4
+ * Nasceu no LMS e vive aqui porque o problema não é de lá: qualquer feature com
5
+ * uma tela de acompanhamento tem a mesma rajada. No LMS é o heartbeat do player,
6
+ * que bate a cada 15s por aluno — com 200 alunos ao vivo são ~13 sinais por
7
+ * segundo, e um evento de realtime por batida faria cada admin com a tela aberta
8
+ * refazer a consulta 13x/s. Na loja é o beacon de navegação dos visitantes. O
9
+ * coalescer transforma isso num disparo a cada `windowMs`.
10
+ *
11
+ * A borda escolhida é a de saída (trailing): o primeiro sinal agenda o disparo
12
+ * para daqui a `windowMs` e os que chegarem no meio são absorvidos. Isso limita
13
+ * o pior caso a um disparo por janela, ao custo de até `windowMs` de latência —
14
+ * aceitável para uma tela de acompanhamento, e o número de sinais absorvidos vai
15
+ * junto no disparo para quem quiser instrumentar.
16
+ */
17
+ export class RealtimeTickCoalescer {
18
+ private timer: ReturnType<typeof setTimeout> | null = null;
19
+ private pending = 0;
20
+ private sequence = 0;
21
+
22
+ constructor(
23
+ private readonly windowMs: number,
24
+ private readonly onFlush: (info: { seq: number; coalesced: number }) => void,
25
+ ) {}
26
+
27
+ /** Registra um sinal. Só o primeiro da janela agenda o disparo. */
28
+ signal(): void {
29
+ this.pending += 1;
30
+ if (this.timer !== null) return;
31
+
32
+ this.timer = setTimeout(() => {
33
+ this.timer = null;
34
+ this.flush();
35
+ }, this.windowMs);
36
+
37
+ // Um timer pendente não deve segurar o processo no shutdown do worker.
38
+ this.timer.unref?.();
39
+ }
40
+
41
+ /** Dispara agora o que estiver pendente, se houver. */
42
+ flush(): void {
43
+ if (this.pending === 0) return;
44
+
45
+ const coalesced = this.pending;
46
+ this.pending = 0;
47
+ this.sequence += 1;
48
+
49
+ this.onFlush({ seq: this.sequence, coalesced });
50
+ }
51
+
52
+ /** Cancela o disparo pendente. Usado no encerramento do módulo. */
53
+ dispose(): void {
54
+ if (this.timer !== null) {
55
+ clearTimeout(this.timer);
56
+ this.timer = null;
57
+ }
58
+ this.pending = 0;
59
+ }
60
+ }
@@ -37,6 +37,39 @@ describe('RealtimeService', () => {
37
37
  expect(() => bare.emitToUser(1, 'x', {})).not.toThrow();
38
38
  });
39
39
 
40
+ // Caminho que só existe com mais de um pod: quem cria a notificação quase
41
+ // nunca é o pod que segura o socket do usuário (4 réplicas, sem sticky
42
+ // session), então a entrega real depende deste re-emit a partir do Redis.
43
+ describe('handleRedisMessage', () => {
44
+ const message = (over: Record<string, unknown> = {}) =>
45
+ JSON.stringify({
46
+ target: { type: 'user', target: '7' },
47
+ event: 'notification:event',
48
+ payload: { type: 'notification.created' },
49
+ originInstanceId: 'outro-pod',
50
+ ...over,
51
+ });
52
+
53
+ it('re-emite localmente o que veio de outro pod', () => {
54
+ (service as any).handleRedisMessage(message());
55
+ expect(server.to).toHaveBeenCalledWith('user:7');
56
+ expect(server.emit).toHaveBeenCalledWith('notification:event', {
57
+ type: 'notification.created',
58
+ });
59
+ });
60
+
61
+ it('ignora a própria mensagem, já emitida localmente', () => {
62
+ const ownId = (service as any).instanceId;
63
+ (service as any).handleRedisMessage(message({ originInstanceId: ownId }));
64
+ expect(server.to).not.toHaveBeenCalled();
65
+ });
66
+
67
+ it('ignora payload malformado', () => {
68
+ expect(() => (service as any).handleRedisMessage('{')).not.toThrow();
69
+ expect(server.to).not.toHaveBeenCalled();
70
+ });
71
+ });
72
+
40
73
  it('tracks local connections per user', () => {
41
74
  expect(service.trackConnect(1)).toBe(1);
42
75
  expect(service.trackConnect(1)).toBe(2);
@@ -36,6 +36,15 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
36
36
  private server: Server | null = null;
37
37
  private redisPublisher: RedisClientType | null = null;
38
38
  private redisSubscriber: RedisClientType | null = null;
39
+ // A `reconnectStrategy` que nunca devolve `false` faz `client.connect()` ficar
40
+ // retentando pra sempre quando o Redis está indisponível: a Promise nunca
41
+ // resolve nem rejeita, e `onModuleInit` trava o boot inteiro da API esperando
42
+ // por ela. Este teto bound só a tentativa inicial; a reconexão de um cliente
43
+ // já conectado continua indefinida.
44
+ private readonly redisConnectTimeoutMs = Math.max(
45
+ 1000,
46
+ Number(process.env.REDIS_CONNECT_TIMEOUT_MS || 3000),
47
+ );
39
48
 
40
49
  /** Local (this-instance) socket count per user id, for presence/liveness. */
41
50
  private readonly localConnections = new Map<number, number>();
@@ -162,8 +171,8 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
162
171
  this.redisSubscriber = createClient(opts);
163
172
  this.redisPublisher.on('error', (e) => this.logger.warn(`Realtime Redis publisher error: ${e?.message ?? e}`));
164
173
  this.redisSubscriber.on('error', (e) => this.logger.warn(`Realtime Redis subscriber error: ${e?.message ?? e}`));
165
- await this.redisPublisher.connect();
166
- await this.redisSubscriber.connect();
174
+ await this.withConnectTimeout(this.redisPublisher.connect(), this.redisConnectTimeoutMs);
175
+ await this.withConnectTimeout(this.redisSubscriber.connect(), this.redisConnectTimeoutMs);
167
176
  await this.redisSubscriber.subscribe(this.channel, (msg) => this.handleRedisMessage(msg));
168
177
  this.logger.log(`Realtime Redis fan-out enabled on channel ${this.channel}`);
169
178
  } catch (error) {
@@ -184,4 +193,21 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
184
193
  /* ignore */
185
194
  }
186
195
  }
196
+
197
+ private async withConnectTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
198
+ let timer: NodeJS.Timeout;
199
+ try {
200
+ return await Promise.race([
201
+ promise,
202
+ new Promise<never>((_, reject) => {
203
+ timer = setTimeout(
204
+ () => reject(new Error(`Redis connection timed out after ${timeoutMs}ms`)),
205
+ timeoutMs,
206
+ );
207
+ }),
208
+ ]);
209
+ } finally {
210
+ clearTimeout(timer!);
211
+ }
212
+ }
187
213
  }
@@ -0,0 +1,261 @@
1
+ import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
2
+ import { createClient, type RedisClientType } from 'redis';
3
+ import { extractRedisErrorMessage, withConnectTimeout } from './redis-pubsub';
4
+ import { isReadOnlyReplicaError, resolveMasterUrl } from './redis-master';
5
+
6
+ /** Uma entrada do índice ordenado por tempo, já decodificada. */
7
+ export type RedisScoredMember = {
8
+ member: string;
9
+ score: number;
10
+ };
11
+
12
+ /**
13
+ * Chave/valor com TTL sobre o Redis, para estado efêmero compartilhado entre as
14
+ * réplicas da API.
15
+ *
16
+ * Existe ao lado do `redis-pubsub.ts` — que só publica e assina — porque
17
+ * algumas features precisam GUARDAR estado por um tempo curto, não só avisar
18
+ * que algo mudou. O caso que motivou: o painel de visitantes ao vivo da loja,
19
+ * onde gravar cada navegação no Postgres colocaria carga de escrita no banco
20
+ * que serve a própria loja, exatamente o que o recurso não pode fazer. Aqui o
21
+ * dado vive na memória do Redis e expira sozinho, sem job de limpeza.
22
+ *
23
+ * **Toda falha degrada para inerte.** Mesma postura do pub/sub: sem
24
+ * `REDIS_URL`, ou com o Redis fora, cada método vira no-op (ou devolve vazio) e
25
+ * quem chamou segue seu caminho. Um recurso de observação nunca pode derrubar o
26
+ * fluxo que ele observa — daí nenhum método propagar erro.
27
+ */
28
+ @Injectable()
29
+ export class RedisKvService implements OnModuleInit, OnModuleDestroy {
30
+ private readonly logger = new Logger(RedisKvService.name);
31
+ private readonly url = String(process.env.REDIS_URL || '').trim();
32
+ private readonly connectTimeoutMs = Math.max(
33
+ 1000,
34
+ Number(process.env.REDIS_CONNECT_TIMEOUT_MS || 3000),
35
+ );
36
+ /** Uma queda do Redis dispara `error` a cada tentativa de reconexão. */
37
+ private readonly errorLogCooldownMs = 30000;
38
+ private lastErrorLogAt = 0;
39
+
40
+ private client: RedisClientType | null = null;
41
+ /** Reconexão em andamento, para não abrir uma por escrita durante o failover. */
42
+ private reconnecting: Promise<void> | null = null;
43
+
44
+ /** `false` = sem Redis; todo método é no-op e quem chama não precisa saber. */
45
+ get enabled(): boolean {
46
+ return this.client !== null;
47
+ }
48
+
49
+ async onModuleInit(): Promise<void> {
50
+ if (!this.url) {
51
+ this.logger.log('Redis key-value disabled because REDIS_URL is not configured.');
52
+ return;
53
+ }
54
+
55
+ this.client = await this.connectToMaster();
56
+ if (this.client) this.logger.log('Redis key-value enabled.');
57
+ }
58
+
59
+ /**
60
+ * Conecta no master, e não em quem o Service devolver.
61
+ *
62
+ * `REDIS_URL` aponta para um Service que balanceia entre master e réplica
63
+ * (veja `resolveMasterUrl`), então conectar direto na URL deixa metade dos
64
+ * processos escrevendo numa réplica e falhando com `READONLY`.
65
+ */
66
+ private async connectToMaster(): Promise<RedisClientType | null> {
67
+ try {
68
+ const url = await resolveMasterUrl(
69
+ this.url,
70
+ this.logger,
71
+ this.connectTimeoutMs,
72
+ );
73
+
74
+ const client = createClient({
75
+ url,
76
+ socket: {
77
+ reconnectStrategy: (retries: number) => Math.min(retries * 250, 5000),
78
+ },
79
+ }) as RedisClientType;
80
+
81
+ // Registrado antes do connect: uma falha no handshake vira erro tratado
82
+ // em vez de um evento 'error' sem dono derrubando o processo.
83
+ client.on('error', (error: unknown) => this.logClientError(error));
84
+
85
+ await withConnectTimeout(client.connect(), this.connectTimeoutMs);
86
+ return client;
87
+ } catch (error) {
88
+ this.logger.warn(
89
+ `Redis key-value disabled: ${extractRedisErrorMessage(error)}`,
90
+ );
91
+ return null;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Refaz a conexão depois de um `READONLY`.
97
+ *
98
+ * Um failover troca quem é o master, e o client fica preso no nó que virou
99
+ * réplica — daí em diante toda escrita falha. Reconectar redescobre o master.
100
+ * O guard serializa as tentativas: sem ele, um pico de escritas em cima do
101
+ * failover abriria uma conexão nova por requisição.
102
+ */
103
+ private async reconnectToMaster(): Promise<void> {
104
+ if (this.reconnecting) return this.reconnecting;
105
+
106
+ this.reconnecting = (async () => {
107
+ const stale = this.client;
108
+ this.client = null;
109
+ if (stale) {
110
+ try {
111
+ await stale.quit();
112
+ } catch {
113
+ // O nó virou réplica ou caiu; encerrando de qualquer forma.
114
+ }
115
+ }
116
+ this.client = await this.connectToMaster();
117
+ })().finally(() => {
118
+ this.reconnecting = null;
119
+ });
120
+
121
+ return this.reconnecting;
122
+ }
123
+
124
+ async onModuleDestroy(): Promise<void> {
125
+ const client = this.client;
126
+ this.client = null;
127
+ if (!client) return;
128
+ try {
129
+ await client.quit();
130
+ } catch {
131
+ // Encerrando de qualquer forma.
132
+ }
133
+ }
134
+
135
+ /** Grava um objeto como JSON com expiração em segundos. */
136
+ async setJson(key: string, value: unknown, ttlSeconds: number): Promise<void> {
137
+ await this.run(async (client) => {
138
+ await client.set(key, JSON.stringify(value), { EX: Math.max(1, ttlSeconds) });
139
+ });
140
+ }
141
+
142
+ /** Lê e desserializa. Devolve `null` se não existe, expirou ou está corrompido. */
143
+ async getJson<T>(key: string): Promise<T | null> {
144
+ return (
145
+ (await this.run(async (client) => {
146
+ const raw = await client.get(key);
147
+ if (raw == null) return null;
148
+ try {
149
+ return JSON.parse(raw) as T;
150
+ } catch {
151
+ // Valor gravado por uma versão anterior com outro formato: tratar como
152
+ // ausente é melhor do que estourar na leitura do painel.
153
+ return null;
154
+ }
155
+ })) ?? null
156
+ );
157
+ }
158
+
159
+ /**
160
+ * Lê várias chaves numa ida só.
161
+ *
162
+ * `MGET` em vez de um `get` por chave: com dezenas de visitantes ao vivo, uma
163
+ * chamada por chave multiplicaria o round-trip pelo número de sessões a cada
164
+ * atualização do painel. Devolve na MESMA ordem das chaves pedidas, com `null`
165
+ * onde não havia valor.
166
+ */
167
+ async getJsonMany<T>(keys: string[]): Promise<Array<T | null>> {
168
+ if (!keys.length) return [];
169
+ const values = await this.run(async (client) => client.mGet(keys));
170
+ if (!values) return keys.map(() => null);
171
+
172
+ return values.map((raw) => {
173
+ if (raw == null) return null;
174
+ try {
175
+ return JSON.parse(raw) as T;
176
+ } catch {
177
+ return null;
178
+ }
179
+ });
180
+ }
181
+
182
+ async delete(key: string): Promise<void> {
183
+ await this.run(async (client) => {
184
+ await client.del(key);
185
+ });
186
+ }
187
+
188
+ /** Põe (ou repontua) um membro no índice ordenado. */
189
+ async zadd(key: string, member: string, score: number): Promise<void> {
190
+ await this.run(async (client) => {
191
+ await client.zAdd(key, [{ value: member, score }]);
192
+ });
193
+ }
194
+
195
+ /** Membros com score dentro do intervalo, do mais antigo para o mais recente. */
196
+ async zrangeByScore(
197
+ key: string,
198
+ min: number,
199
+ max: number,
200
+ ): Promise<RedisScoredMember[]> {
201
+ const rows = await this.run(async (client) =>
202
+ client.zRangeByScoreWithScores(key, min, max),
203
+ );
204
+ return (rows ?? []).map((row) => ({ member: row.value, score: row.score }));
205
+ }
206
+
207
+ /**
208
+ * Remove do índice o que caiu fora da janela.
209
+ *
210
+ * O TTL cuida das chaves individuais, mas não do índice: sem esta poda o
211
+ * sorted set acumularia para sempre todo visitante que já passou pela loja, e
212
+ * cada leitura do painel ficaria mais cara que a anterior.
213
+ */
214
+ async zremRangeByScore(key: string, min: number, max: number): Promise<void> {
215
+ await this.run(async (client) => {
216
+ await client.zRemRangeByScore(key, min, max);
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Executa contra o client, engolindo qualquer falha.
222
+ *
223
+ * É aqui que mora a promessa de degradar para inerte: quem chama recebe
224
+ * `undefined` e trata como "sem dado", em vez de precisar de try/catch em
225
+ * cada ponto de uso.
226
+ */
227
+ private async run<T>(fn: (client: RedisClientType) => Promise<T>): Promise<T | undefined> {
228
+ const client = this.client;
229
+ if (!client) return undefined;
230
+ try {
231
+ return await fn(client);
232
+ } catch (error) {
233
+ // `READONLY` quer dizer que este client está falando com uma réplica —
234
+ // um failover trocou o master debaixo dele. Reconectar redescobre quem é
235
+ // o master e a operação é refeita uma vez; qualquer outro erro segue a
236
+ // regra da casa e degrada para inerte.
237
+ if (isReadOnlyReplicaError(error)) {
238
+ await this.reconnectToMaster();
239
+ const reconnected = this.client;
240
+ if (reconnected) {
241
+ try {
242
+ return await fn(reconnected);
243
+ } catch (retryError) {
244
+ this.logClientError(retryError);
245
+ return undefined;
246
+ }
247
+ }
248
+ }
249
+
250
+ this.logClientError(error);
251
+ return undefined;
252
+ }
253
+ }
254
+
255
+ private logClientError(error: unknown): void {
256
+ const now = Date.now();
257
+ if (now - this.lastErrorLogAt < this.errorLogCooldownMs) return;
258
+ this.lastErrorLogAt = now;
259
+ this.logger.warn(`Redis key-value error: ${extractRedisErrorMessage(error)}`);
260
+ }
261
+ }
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect, beforeEach, jest } from '@jest/globals';
2
+ import { Logger } from '@nestjs/common';
3
+
4
+ const createClient = jest.fn();
5
+ jest.mock('redis', () => ({ createClient }));
6
+
7
+ // Importado depois do mock: `redis-master` resolve `createClient` no topo.
8
+ const { isReadOnlyReplicaError, resolveMasterUrl } = require('./redis-master');
9
+
10
+ const makeProbe = (info: string) => {
11
+ const probe = {
12
+ on: jest.fn(),
13
+ connect: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
14
+ info: jest.fn<() => Promise<string>>().mockResolvedValue(info),
15
+ quit: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
16
+ };
17
+ createClient.mockReturnValue(probe);
18
+ return probe;
19
+ };
20
+
21
+ const logger = new Logger('test');
22
+ const REPLICA_INFO = [
23
+ 'role:slave',
24
+ 'master_host:redis-hub-node-1.redis-hub-headless.hcode.svc.cluster.local',
25
+ 'master_port:6379',
26
+ 'master_link_status:up',
27
+ ].join('\r\n');
28
+
29
+ describe('resolveMasterUrl', () => {
30
+ beforeEach(() => {
31
+ jest.clearAllMocks();
32
+ jest.spyOn(logger, 'warn').mockImplementation(() => undefined);
33
+ jest.spyOn(logger, 'log').mockImplementation(() => undefined);
34
+ });
35
+
36
+ it('redireciona para o master quando a URL caiu numa replica', async () => {
37
+ // O Service redis-hub tem os dois pods como endpoint, entao a URL do env
38
+ // aponta para o master metade das vezes e para a replica na outra metade.
39
+ makeProbe(REPLICA_INFO);
40
+
41
+ const url = await resolveMasterUrl(
42
+ 'redis://:senha@redis-hub:6379/0',
43
+ logger,
44
+ 3000,
45
+ );
46
+
47
+ expect(url).toBe(
48
+ 'redis://:senha@redis-hub-node-1.redis-hub-headless.hcode.svc.cluster.local:6379/0',
49
+ );
50
+ });
51
+
52
+ it('mantem a URL quando ja caiu no master', async () => {
53
+ makeProbe('role:master\r\nconnected_slaves:1');
54
+
55
+ const url = await resolveMasterUrl('redis://redis-hub:6379/0', logger, 3000);
56
+
57
+ expect(url).toBe('redis://redis-hub:6379/0');
58
+ });
59
+
60
+ it('mantem a URL quando o probe falha, em vez de derrubar o boot', async () => {
61
+ createClient.mockReturnValue({
62
+ on: jest.fn(),
63
+ connect: jest
64
+ .fn<() => Promise<void>>()
65
+ .mockRejectedValue(new Error('ECONNREFUSED')),
66
+ info: jest.fn(),
67
+ quit: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
68
+ });
69
+
70
+ const url = await resolveMasterUrl('redis://redis-hub:6379/0', logger, 3000);
71
+
72
+ expect(url).toBe('redis://redis-hub:6379/0');
73
+ });
74
+
75
+ it('mantem a URL quando a replica nao informa o master', async () => {
76
+ makeProbe('role:slave\r\nmaster_link_status:down');
77
+
78
+ const url = await resolveMasterUrl('redis://redis-hub:6379/0', logger, 3000);
79
+
80
+ expect(url).toBe('redis://redis-hub:6379/0');
81
+ });
82
+
83
+ it('fecha o probe mesmo no caminho feliz', async () => {
84
+ const probe = makeProbe('role:master');
85
+
86
+ await resolveMasterUrl('redis://redis-hub:6379/0', logger, 3000);
87
+
88
+ expect(probe.quit).toHaveBeenCalled();
89
+ });
90
+
91
+ it('nao tenta descobrir nada sem URL', async () => {
92
+ const url = await resolveMasterUrl('', logger, 3000);
93
+
94
+ expect(url).toBe('');
95
+ expect(createClient).not.toHaveBeenCalled();
96
+ });
97
+ });
98
+
99
+ describe('isReadOnlyReplicaError', () => {
100
+ it('reconhece a recusa de escrita da replica', () => {
101
+ expect(
102
+ isReadOnlyReplicaError(
103
+ new Error("READONLY You can't write against a read only replica."),
104
+ ),
105
+ ).toBe(true);
106
+ });
107
+
108
+ it('nao confunde com outros erros', () => {
109
+ expect(isReadOnlyReplicaError(new Error('ECONNRESET'))).toBe(false);
110
+ });
111
+ });