@hed-hog/core 0.0.390 → 0.0.394

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/README.md +39 -0
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -1
  3. package/dist/access-log/access-log.controller.js +1 -1
  4. package/dist/access-log/access-log.controller.js.map +1 -1
  5. package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
  6. package/dist/access-log/access-log.interceptor.js +8 -5
  7. package/dist/access-log/access-log.interceptor.js.map +1 -1
  8. package/dist/access-log/access-log.service.d.ts +4 -0
  9. package/dist/access-log/access-log.service.d.ts.map +1 -1
  10. package/dist/access-log/access-log.service.js +31 -4
  11. package/dist/access-log/access-log.service.js.map +1 -1
  12. package/dist/access-log/dto/query-access-log.dto.d.ts +2 -0
  13. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -1
  14. package/dist/access-log/dto/query-access-log.dto.js +5 -0
  15. package/dist/access-log/dto/query-access-log.dto.js.map +1 -1
  16. package/dist/ai/ai.controller.d.ts +12 -8
  17. package/dist/ai/ai.controller.d.ts.map +1 -1
  18. package/dist/ai/ai.controller.js +17 -2
  19. package/dist/ai/ai.controller.js.map +1 -1
  20. package/dist/ai/ai.module.d.ts.map +1 -1
  21. package/dist/ai/ai.module.js +3 -2
  22. package/dist/ai/ai.module.js.map +1 -1
  23. package/dist/ai/ai.service.d.ts +30 -1
  24. package/dist/ai/ai.service.d.ts.map +1 -1
  25. package/dist/ai/ai.service.js +97 -3
  26. package/dist/ai/ai.service.js.map +1 -1
  27. package/dist/ai/dto/richtext-edit-html.dto.d.ts +5 -0
  28. package/dist/ai/dto/richtext-edit-html.dto.d.ts.map +1 -0
  29. package/dist/ai/dto/richtext-edit-html.dto.js +27 -0
  30. package/dist/ai/dto/richtext-edit-html.dto.js.map +1 -0
  31. package/dist/ai/richtext-ai.prompts.d.ts +5 -0
  32. package/dist/ai/richtext-ai.prompts.d.ts.map +1 -0
  33. package/dist/ai/richtext-ai.prompts.js +57 -0
  34. package/dist/ai/richtext-ai.prompts.js.map +1 -0
  35. package/dist/ai/richtext-ai.service.d.ts +34 -0
  36. package/dist/ai/richtext-ai.service.d.ts.map +1 -0
  37. package/dist/ai/richtext-ai.service.js +95 -0
  38. package/dist/ai/richtext-ai.service.js.map +1 -0
  39. package/dist/ai-instruction/ai-instruction.controller.d.ts +5 -5
  40. package/dist/auth/auth.controller.d.ts +66 -7
  41. package/dist/auth/auth.controller.d.ts.map +1 -1
  42. package/dist/auth/auth.controller.js +67 -23
  43. package/dist/auth/auth.controller.js.map +1 -1
  44. package/dist/auth/auth.module.d.ts.map +1 -1
  45. package/dist/auth/auth.module.js +2 -0
  46. package/dist/auth/auth.module.js.map +1 -1
  47. package/dist/auth/auth.service.d.ts +136 -4
  48. package/dist/auth/auth.service.d.ts.map +1 -1
  49. package/dist/auth/auth.service.js +246 -48
  50. package/dist/auth/auth.service.js.map +1 -1
  51. package/dist/auth/dto/confirm-email.dto.d.ts +4 -0
  52. package/dist/auth/dto/confirm-email.dto.d.ts.map +1 -0
  53. package/dist/auth/dto/confirm-email.dto.js +22 -0
  54. package/dist/auth/dto/confirm-email.dto.js.map +1 -0
  55. package/dist/auth/dto/resend-confirmation.dto.d.ts +8 -0
  56. package/dist/auth/dto/resend-confirmation.dto.d.ts.map +1 -0
  57. package/dist/auth/dto/resend-confirmation.dto.js +36 -0
  58. package/dist/auth/dto/resend-confirmation.dto.js.map +1 -0
  59. package/dist/authorize/authorize.controller.d.ts +1 -1
  60. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  61. package/dist/authorize/authorize.controller.js +4 -2
  62. package/dist/authorize/authorize.controller.js.map +1 -1
  63. package/dist/authorize/authorize.service.d.ts +7 -0
  64. package/dist/authorize/authorize.service.d.ts.map +1 -1
  65. package/dist/authorize/authorize.service.js +1 -1
  66. package/dist/authorize/authorize.service.js.map +1 -1
  67. package/dist/challenge/challenge.service.d.ts +18 -5
  68. package/dist/challenge/challenge.service.d.ts.map +1 -1
  69. package/dist/challenge/challenge.service.js +50 -3
  70. package/dist/challenge/challenge.service.js.map +1 -1
  71. package/dist/core.module.d.ts.map +1 -1
  72. package/dist/core.module.js +17 -0
  73. package/dist/core.module.js.map +1 -1
  74. package/dist/dashboard/dashboard/dashboard.controller.d.ts +6 -6
  75. package/dist/dashboard/dashboard/dashboard.service.d.ts +6 -6
  76. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +4 -4
  77. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +4 -4
  78. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -1
  79. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -1
  80. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +2 -2
  81. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
  82. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -2
  83. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -2
  84. package/dist/file/file.controller.d.ts +8 -1
  85. package/dist/file/file.controller.d.ts.map +1 -1
  86. package/dist/file/file.controller.js +45 -7
  87. package/dist/file/file.controller.js.map +1 -1
  88. package/dist/file/file.service.d.ts +128 -1
  89. package/dist/file/file.service.d.ts.map +1 -1
  90. package/dist/file/file.service.js +319 -8
  91. package/dist/file/file.service.js.map +1 -1
  92. package/dist/file/image-optimization.service.d.ts +43 -0
  93. package/dist/file/image-optimization.service.d.ts.map +1 -1
  94. package/dist/file/image-optimization.service.js +154 -0
  95. package/dist/file/image-optimization.service.js.map +1 -1
  96. package/dist/file/provider/abstract.provider.d.ts +16 -0
  97. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  98. package/dist/file/provider/abstract.provider.js +4 -0
  99. package/dist/file/provider/abstract.provider.js.map +1 -1
  100. package/dist/file/provider/gcs.provider.d.ts +2 -0
  101. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  102. package/dist/file/provider/gcs.provider.js +10 -0
  103. package/dist/file/provider/gcs.provider.js.map +1 -1
  104. package/dist/file/provider/local.provider.d.ts +1 -0
  105. package/dist/file/provider/local.provider.d.ts.map +1 -1
  106. package/dist/file/provider/local.provider.js +13 -0
  107. package/dist/file/provider/local.provider.js.map +1 -1
  108. package/dist/file/provider/s3.provider.d.ts +5 -0
  109. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  110. package/dist/file/provider/s3.provider.js +15 -0
  111. package/dist/file/provider/s3.provider.js.map +1 -1
  112. package/dist/file/provider/spaces.provider.d.ts +5 -0
  113. package/dist/file/provider/spaces.provider.d.ts.map +1 -1
  114. package/dist/file/provider/spaces.provider.js +16 -0
  115. package/dist/file/provider/spaces.provider.js.map +1 -1
  116. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts +4 -0
  117. package/dist/impersonation/dto/redeem-impersonation.dto.d.ts.map +1 -0
  118. package/dist/impersonation/dto/redeem-impersonation.dto.js +23 -0
  119. package/dist/impersonation/dto/redeem-impersonation.dto.js.map +1 -0
  120. package/dist/impersonation/dto/start-impersonation.dto.d.ts +7 -0
  121. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -0
  122. package/dist/impersonation/dto/start-impersonation.dto.js +27 -0
  123. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -0
  124. package/dist/impersonation/impersonation-restrictions.d.ts +26 -0
  125. package/dist/impersonation/impersonation-restrictions.d.ts.map +1 -0
  126. package/dist/impersonation/impersonation-restrictions.js +76 -0
  127. package/dist/impersonation/impersonation-restrictions.js.map +1 -0
  128. package/dist/impersonation/impersonation.controller.d.ts +90 -0
  129. package/dist/impersonation/impersonation.controller.d.ts.map +1 -0
  130. package/dist/impersonation/impersonation.controller.js +135 -0
  131. package/dist/impersonation/impersonation.controller.js.map +1 -0
  132. package/dist/impersonation/impersonation.module.d.ts +8 -0
  133. package/dist/impersonation/impersonation.module.d.ts.map +1 -0
  134. package/dist/impersonation/impersonation.module.js +44 -0
  135. package/dist/impersonation/impersonation.module.js.map +1 -0
  136. package/dist/impersonation/impersonation.service.d.ts +164 -0
  137. package/dist/impersonation/impersonation.service.d.ts.map +1 -0
  138. package/dist/impersonation/impersonation.service.js +408 -0
  139. package/dist/impersonation/impersonation.service.js.map +1 -0
  140. package/dist/index.d.ts +17 -0
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +19 -0
  143. package/dist/index.js.map +1 -1
  144. package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
  145. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  146. package/dist/integration/services/outbox-polling.coordinator.js +50 -30
  147. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  148. package/dist/integration/services/outbox.service.d.ts.map +1 -1
  149. package/dist/integration/services/outbox.service.js +5 -1
  150. package/dist/integration/services/outbox.service.js.map +1 -1
  151. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  152. package/dist/integration-profile/integration-profile.secrets.js +3 -0
  153. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  154. package/dist/integration-profile/integration-profile.utils.d.ts +14 -0
  155. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  156. package/dist/integration-profile/integration-profile.utils.js +26 -0
  157. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  158. package/dist/integration-profile/testers/email.testers.d.ts.map +1 -1
  159. package/dist/integration-profile/testers/email.testers.js +28 -0
  160. package/dist/integration-profile/testers/email.testers.js.map +1 -1
  161. package/dist/language/en.json +11 -0
  162. package/dist/language/pt.json +11 -0
  163. package/dist/mail/dto/send.dto.d.ts +38 -1
  164. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  165. package/dist/mail/dto/send.dto.js +10 -0
  166. package/dist/mail/dto/send.dto.js.map +1 -1
  167. package/dist/mail/mail.controller.d.ts +1 -0
  168. package/dist/mail/mail.controller.d.ts.map +1 -1
  169. package/dist/mail/mail.module.d.ts.map +1 -1
  170. package/dist/mail/mail.module.js +2 -0
  171. package/dist/mail/mail.module.js.map +1 -1
  172. package/dist/mail/mail.service.d.ts +48 -4
  173. package/dist/mail/mail.service.d.ts.map +1 -1
  174. package/dist/mail/mail.service.js +187 -35
  175. package/dist/mail/mail.service.js.map +1 -1
  176. package/dist/mail-delivery/mail-delivery-provision.service.d.ts +21 -0
  177. package/dist/mail-delivery/mail-delivery-provision.service.d.ts.map +1 -0
  178. package/dist/mail-delivery/mail-delivery-provision.service.js +64 -0
  179. package/dist/mail-delivery/mail-delivery-provision.service.js.map +1 -0
  180. package/dist/mail-delivery/mail-delivery.constants.d.ts +17 -0
  181. package/dist/mail-delivery/mail-delivery.constants.d.ts.map +1 -0
  182. package/dist/mail-delivery/mail-delivery.constants.js +31 -0
  183. package/dist/mail-delivery/mail-delivery.constants.js.map +1 -0
  184. package/dist/mail-delivery/mail-delivery.controller.d.ts +19 -0
  185. package/dist/mail-delivery/mail-delivery.controller.d.ts.map +1 -0
  186. package/dist/mail-delivery/mail-delivery.controller.js +53 -0
  187. package/dist/mail-delivery/mail-delivery.controller.js.map +1 -0
  188. package/dist/mail-delivery/mail-delivery.module.d.ts +7 -0
  189. package/dist/mail-delivery/mail-delivery.module.d.ts.map +1 -0
  190. package/dist/mail-delivery/mail-delivery.module.js +36 -0
  191. package/dist/mail-delivery/mail-delivery.module.js.map +1 -0
  192. package/dist/mail-delivery/mail-delivery.service.d.ts +38 -0
  193. package/dist/mail-delivery/mail-delivery.service.d.ts.map +1 -0
  194. package/dist/mail-delivery/mail-delivery.service.js +140 -0
  195. package/dist/mail-delivery/mail-delivery.service.js.map +1 -0
  196. package/dist/mail-delivery/ses-event.parser.d.ts +27 -0
  197. package/dist/mail-delivery/ses-event.parser.d.ts.map +1 -0
  198. package/dist/mail-delivery/ses-event.parser.js +131 -0
  199. package/dist/mail-delivery/ses-event.parser.js.map +1 -0
  200. package/dist/mail-delivery/ses-webhook.service.d.ts +58 -0
  201. package/dist/mail-delivery/ses-webhook.service.d.ts.map +1 -0
  202. package/dist/mail-delivery/ses-webhook.service.js +214 -0
  203. package/dist/mail-delivery/ses-webhook.service.js.map +1 -0
  204. package/dist/mail-delivery/sns-message.util.d.ts +38 -0
  205. package/dist/mail-delivery/sns-message.util.d.ts.map +1 -0
  206. package/dist/mail-delivery/sns-message.util.js +109 -0
  207. package/dist/mail-delivery/sns-message.util.js.map +1 -0
  208. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +5 -0
  209. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -1
  210. package/dist/mail-sent/dto/query-mail-sent-list.dto.js +6 -0
  211. package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -1
  212. package/dist/mail-sent/mail-sent.controller.d.ts +39 -0
  213. package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
  214. package/dist/mail-sent/mail-sent.service.d.ts +39 -0
  215. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  216. package/dist/mail-sent/mail-sent.service.js +45 -4
  217. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  218. package/dist/mailbox/mailbox-fetcher.registry.d.ts +100 -0
  219. package/dist/mailbox/mailbox-fetcher.registry.d.ts.map +1 -0
  220. package/dist/mailbox/mailbox-fetcher.registry.js +59 -0
  221. package/dist/mailbox/mailbox-fetcher.registry.js.map +1 -0
  222. package/dist/mailbox/mailbox.module.d.ts +8 -0
  223. package/dist/mailbox/mailbox.module.d.ts.map +1 -0
  224. package/dist/mailbox/mailbox.module.js +27 -0
  225. package/dist/mailbox/mailbox.module.js.map +1 -0
  226. package/dist/meeting/meeting-provider.registry.d.ts +155 -0
  227. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -0
  228. package/dist/meeting/meeting-provider.registry.js +53 -0
  229. package/dist/meeting/meeting-provider.registry.js.map +1 -0
  230. package/dist/meeting/meeting.constants.d.ts +47 -0
  231. package/dist/meeting/meeting.constants.d.ts.map +1 -0
  232. package/dist/meeting/meeting.constants.js +21 -0
  233. package/dist/meeting/meeting.constants.js.map +1 -0
  234. package/dist/meeting/meeting.module.d.ts +7 -0
  235. package/dist/meeting/meeting.module.d.ts.map +1 -0
  236. package/dist/meeting/meeting.module.js +26 -0
  237. package/dist/meeting/meeting.module.js.map +1 -0
  238. package/dist/middlewares/locale-injection.middleware.d.ts +12 -0
  239. package/dist/middlewares/locale-injection.middleware.d.ts.map +1 -1
  240. package/dist/middlewares/locale-injection.middleware.js +20 -4
  241. package/dist/middlewares/locale-injection.middleware.js.map +1 -1
  242. package/dist/oauth/providers/google.provider.d.ts +7 -0
  243. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  244. package/dist/oauth/providers/google.provider.js +43 -13
  245. package/dist/oauth/providers/google.provider.js.map +1 -1
  246. package/dist/observability/monitored-cron.d.ts +34 -0
  247. package/dist/observability/monitored-cron.d.ts.map +1 -0
  248. package/dist/observability/monitored-cron.js +118 -0
  249. package/dist/observability/monitored-cron.js.map +1 -0
  250. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts +12 -0
  251. package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts.map +1 -0
  252. package/dist/pairing/dto/approve-reverse-pairing.dto.js +30 -0
  253. package/dist/pairing/dto/approve-reverse-pairing.dto.js.map +1 -0
  254. package/dist/pairing/dto/claim-pairing.dto.d.ts +11 -0
  255. package/dist/pairing/dto/claim-pairing.dto.d.ts.map +1 -0
  256. package/dist/pairing/dto/claim-pairing.dto.js +35 -0
  257. package/dist/pairing/dto/claim-pairing.dto.js.map +1 -0
  258. package/dist/pairing/dto/confirm-pairing.dto.d.ts +10 -0
  259. package/dist/pairing/dto/confirm-pairing.dto.d.ts.map +1 -0
  260. package/dist/pairing/dto/confirm-pairing.dto.js +24 -0
  261. package/dist/pairing/dto/confirm-pairing.dto.js.map +1 -0
  262. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts +5 -0
  263. package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts.map +1 -0
  264. package/dist/pairing/dto/poll-reverse-pairing.dto.js +24 -0
  265. package/dist/pairing/dto/poll-reverse-pairing.dto.js.map +1 -0
  266. package/dist/pairing/dto/start-pairing.dto.d.ts +11 -0
  267. package/dist/pairing/dto/start-pairing.dto.d.ts.map +1 -0
  268. package/dist/pairing/dto/start-pairing.dto.js +30 -0
  269. package/dist/pairing/dto/start-pairing.dto.js.map +1 -0
  270. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts +24 -0
  271. package/dist/pairing/dto/start-reverse-pairing.dto.d.ts.map +1 -0
  272. package/dist/pairing/dto/start-reverse-pairing.dto.js +47 -0
  273. package/dist/pairing/dto/start-reverse-pairing.dto.js.map +1 -0
  274. package/dist/pairing/pairing.controller.d.ts +118 -0
  275. package/dist/pairing/pairing.controller.d.ts.map +1 -0
  276. package/dist/pairing/pairing.controller.js +296 -0
  277. package/dist/pairing/pairing.controller.js.map +1 -0
  278. package/dist/pairing/pairing.module.d.ts +10 -0
  279. package/dist/pairing/pairing.module.d.ts.map +1 -0
  280. package/dist/pairing/pairing.module.js +40 -0
  281. package/dist/pairing/pairing.module.js.map +1 -0
  282. package/dist/pairing/pairing.service.d.ts +238 -0
  283. package/dist/pairing/pairing.service.d.ts.map +1 -0
  284. package/dist/pairing/pairing.service.js +495 -0
  285. package/dist/pairing/pairing.service.js.map +1 -0
  286. package/dist/pdf/pdf.service.d.ts +3 -6
  287. package/dist/pdf/pdf.service.d.ts.map +1 -1
  288. package/dist/pdf/pdf.service.js +8 -20
  289. package/dist/pdf/pdf.service.js.map +1 -1
  290. package/dist/profile/profile.controller.d.ts +11 -0
  291. package/dist/profile/profile.controller.d.ts.map +1 -1
  292. package/dist/profile/profile.service.d.ts +11 -0
  293. package/dist/profile/profile.service.d.ts.map +1 -1
  294. package/dist/profile/profile.service.js +20 -11
  295. package/dist/profile/profile.service.js.map +1 -1
  296. package/dist/redis/redis-pubsub.d.ts.map +1 -1
  297. package/dist/redis/redis-pubsub.js +44 -0
  298. package/dist/redis/redis-pubsub.js.map +1 -1
  299. package/dist/role/guards/role.guard.d.ts +5 -0
  300. package/dist/role/guards/role.guard.d.ts.map +1 -1
  301. package/dist/role/guards/role.guard.js +41 -39
  302. package/dist/role/guards/role.guard.js.map +1 -1
  303. package/dist/session/session.controller.d.ts +2 -0
  304. package/dist/session/session.controller.d.ts.map +1 -1
  305. package/dist/session/session.service.d.ts +39 -0
  306. package/dist/session/session.service.d.ts.map +1 -1
  307. package/dist/session/session.service.js +91 -8
  308. package/dist/session/session.service.js.map +1 -1
  309. package/dist/setting/brand-asset.service.d.ts +48 -0
  310. package/dist/setting/brand-asset.service.d.ts.map +1 -0
  311. package/dist/setting/brand-asset.service.js +130 -0
  312. package/dist/setting/brand-asset.service.js.map +1 -0
  313. package/dist/setting/setting.module.d.ts.map +1 -1
  314. package/dist/setting/setting.module.js +3 -2
  315. package/dist/setting/setting.module.js.map +1 -1
  316. package/dist/setting/setting.service.d.ts +33 -0
  317. package/dist/setting/setting.service.d.ts.map +1 -1
  318. package/dist/setting/setting.service.js +88 -0
  319. package/dist/setting/setting.service.js.map +1 -1
  320. package/dist/token/token.service.d.ts +23 -2
  321. package/dist/token/token.service.d.ts.map +1 -1
  322. package/dist/token/token.service.js +39 -4
  323. package/dist/token/token.service.js.map +1 -1
  324. package/dist/user/user.controller.d.ts +8 -22
  325. package/dist/user/user.controller.d.ts.map +1 -1
  326. package/dist/user/user.service.d.ts +44 -24
  327. package/dist/user/user.service.d.ts.map +1 -1
  328. package/dist/user/user.service.js +89 -6
  329. package/dist/user/user.service.js.map +1 -1
  330. package/dist/user-device/user-device.controller.d.ts +54 -0
  331. package/dist/user-device/user-device.controller.d.ts.map +1 -0
  332. package/dist/user-device/user-device.controller.js +103 -0
  333. package/dist/user-device/user-device.controller.js.map +1 -0
  334. package/dist/user-device/user-device.module.d.ts +3 -0
  335. package/dist/user-device/user-device.module.d.ts.map +1 -0
  336. package/dist/user-device/user-device.module.js +26 -0
  337. package/dist/user-device/user-device.module.js.map +1 -0
  338. package/dist/user-device/user-device.service.d.ts +71 -0
  339. package/dist/user-device/user-device.service.d.ts.map +1 -0
  340. package/dist/user-device/user-device.service.js +158 -0
  341. package/dist/user-device/user-device.service.js.map +1 -0
  342. package/dist/utils/device-key.d.ts +23 -0
  343. package/dist/utils/device-key.d.ts.map +1 -0
  344. package/dist/utils/device-key.js +31 -0
  345. package/dist/utils/device-key.js.map +1 -0
  346. package/dist/utils/geo-ip.d.ts +38 -0
  347. package/dist/utils/geo-ip.d.ts.map +1 -0
  348. package/dist/utils/geo-ip.js +122 -0
  349. package/dist/utils/geo-ip.js.map +1 -0
  350. package/dist/utils/locale-context.d.ts +10 -7
  351. package/dist/utils/locale-context.d.ts.map +1 -1
  352. package/dist/utils/locale-context.js +14 -8
  353. package/dist/utils/locale-context.js.map +1 -1
  354. package/dist/utils/security-event-vars.d.ts +56 -0
  355. package/dist/utils/security-event-vars.d.ts.map +1 -0
  356. package/dist/utils/security-event-vars.js +109 -0
  357. package/dist/utils/security-event-vars.js.map +1 -0
  358. package/dist/utils/user-agent.d.ts +19 -0
  359. package/dist/utils/user-agent.d.ts.map +1 -0
  360. package/dist/utils/user-agent.js +36 -0
  361. package/dist/utils/user-agent.js.map +1 -0
  362. package/dist/webhook-integration/webhook-integration.service.d.ts +5 -0
  363. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  364. package/dist/webhook-integration/webhook-integration.service.js +50 -4
  365. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  366. package/dist/webhook-integration/webhook-protocol.registry.d.ts +6 -0
  367. package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -1
  368. package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -1
  369. package/hedhog/data/integration_provider.yaml +9 -0
  370. package/hedhog/data/mail.yaml +198 -62
  371. package/hedhog/data/mail_layout.yaml +43 -33
  372. package/hedhog/data/menu.yaml +2 -0
  373. package/hedhog/data/role.yaml +16 -0
  374. package/hedhog/data/route.yaml +172 -3
  375. package/hedhog/data/setting_group.yaml +178 -2
  376. package/hedhog/data/setting_subgroup.yaml +8 -0
  377. package/hedhog/frontend/app/_lib/hooks/use-core-access.test.tsx.ejs +125 -0
  378. package/hedhog/frontend/app/_lib/hooks/use-core-access.ts.ejs +29 -0
  379. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.test.ts.ejs +71 -0
  380. package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.ts.ejs +32 -0
  381. package/hedhog/frontend/app/access-log/page.tsx.ejs +96 -22
  382. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +17 -1
  383. package/hedhog/frontend/app/account/components/known-devices.tsx.ejs +89 -0
  384. package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +2 -1
  385. package/hedhog/frontend/app/account/components/user-devices-list.tsx.ejs +186 -0
  386. package/hedhog/frontend/app/account/devices/page.tsx.ejs +5 -0
  387. package/hedhog/frontend/app/account/layout.tsx.ejs +2 -0
  388. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +298 -0
  389. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +376 -1236
  390. package/hedhog/frontend/app/mail/log/page.tsx.ejs +210 -3
  391. package/hedhog/frontend/app/mail/template/page.tsx.ejs +190 -132
  392. package/hedhog/frontend/app/users/active-session.tsx.ejs +58 -11
  393. package/hedhog/frontend/app/users/page.tsx.ejs +69 -10
  394. package/hedhog/frontend/app/users/simulate-access-dialog.test.tsx.ejs +202 -0
  395. package/hedhog/frontend/app/users/simulate-access-dialog.tsx.ejs +170 -0
  396. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +19 -0
  397. package/hedhog/frontend/app/users/user-devices.tsx.ejs +89 -0
  398. package/hedhog/frontend/app/users/user-logs.tsx.ejs +13 -1
  399. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +46 -24
  400. package/hedhog/frontend/messages/en.json +146 -48
  401. package/hedhog/frontend/messages/pt.json +146 -48
  402. package/hedhog/table/access_log.yaml +7 -0
  403. package/hedhog/table/file.yaml +8 -0
  404. package/hedhog/table/mail_delivery_event.yaml +76 -0
  405. package/hedhog/table/mail_sent.yaml +23 -0
  406. package/hedhog/table/user_activity.yaml +9 -0
  407. package/hedhog/table/user_device.yaml +52 -0
  408. package/hedhog/table/user_session.yaml +20 -1
  409. package/package.json +6 -5
  410. package/src/access-log/access-log.controller.ts +1 -0
  411. package/src/access-log/access-log.interceptor.ts +5 -1
  412. package/src/access-log/access-log.service.spec.ts +33 -2
  413. package/src/access-log/access-log.service.ts +38 -0
  414. package/src/access-log/dto/query-access-log.dto.ts +5 -0
  415. package/src/ai/ai.controller.ts +9 -0
  416. package/src/ai/ai.module.ts +3 -2
  417. package/src/ai/ai.service.ts +150 -13
  418. package/src/ai/dto/richtext-edit-html.dto.ts +11 -0
  419. package/src/ai/richtext-ai.prompts.ts +57 -0
  420. package/src/ai/richtext-ai.service.ts +109 -0
  421. package/src/auth/auth.controller.ts +47 -8
  422. package/src/auth/auth.module.ts +2 -0
  423. package/src/auth/auth.service.spec.ts +383 -6
  424. package/src/auth/auth.service.ts +345 -56
  425. package/src/auth/dto/confirm-email.dto.ts +7 -0
  426. package/src/auth/dto/resend-confirmation.dto.ts +20 -0
  427. package/src/authorize/authorize.controller.ts +2 -0
  428. package/src/authorize/authorize.service.spec.ts +23 -0
  429. package/src/authorize/authorize.service.ts +8 -0
  430. package/src/challenge/challenge.service.spec.ts +73 -0
  431. package/src/challenge/challenge.service.ts +62 -3
  432. package/src/core.module.ts +19 -0
  433. package/src/file/file.controller.ts +54 -4
  434. package/src/file/file.service.spec.ts +275 -105
  435. package/src/file/file.service.ts +409 -14
  436. package/src/file/image-optimization.service.spec.ts +92 -0
  437. package/src/file/image-optimization.service.ts +202 -0
  438. package/src/file/provider/abstract.provider.ts +23 -0
  439. package/src/file/provider/gcs.provider.ts +17 -0
  440. package/src/file/provider/local.provider.ts +21 -0
  441. package/src/file/provider/s3.provider.ts +25 -0
  442. package/src/file/provider/spaces.provider.ts +26 -0
  443. package/src/impersonation/dto/redeem-impersonation.dto.ts +8 -0
  444. package/src/impersonation/dto/start-impersonation.dto.ts +13 -0
  445. package/src/impersonation/impersonation-restrictions.ts +84 -0
  446. package/src/impersonation/impersonation.controller.ts +109 -0
  447. package/src/impersonation/impersonation.module.ts +31 -0
  448. package/src/impersonation/impersonation.service.spec.ts +427 -0
  449. package/src/impersonation/impersonation.service.ts +577 -0
  450. package/src/index.ts +21 -0
  451. package/src/integration/services/outbox-polling.coordinator.spec.ts +90 -0
  452. package/src/integration/services/outbox-polling.coordinator.ts +62 -40
  453. package/src/integration/services/outbox.service.spec.ts +8 -0
  454. package/src/integration/services/outbox.service.ts +19 -13
  455. package/src/integration-profile/integration-profile.secrets.ts +3 -0
  456. package/src/integration-profile/integration-profile.utils.ts +38 -0
  457. package/src/integration-profile/testers/email.testers.ts +34 -1
  458. package/src/language/en.json +11 -0
  459. package/src/language/pt.json +11 -0
  460. package/src/mail/dto/send.dto.ts +46 -2
  461. package/src/mail/mail-template.spec.ts +79 -2
  462. package/src/mail/mail.module.ts +2 -0
  463. package/src/mail/mail.service.spec.ts +307 -6
  464. package/src/mail/mail.service.ts +227 -30
  465. package/src/mail-delivery/mail-delivery-provision.service.ts +66 -0
  466. package/src/mail-delivery/mail-delivery.constants.ts +31 -0
  467. package/src/mail-delivery/mail-delivery.controller.ts +38 -0
  468. package/src/mail-delivery/mail-delivery.module.ts +23 -0
  469. package/src/mail-delivery/mail-delivery.service.spec.ts +136 -0
  470. package/src/mail-delivery/mail-delivery.service.ts +152 -0
  471. package/src/mail-delivery/ses-event.parser.spec.ts +140 -0
  472. package/src/mail-delivery/ses-event.parser.ts +191 -0
  473. package/src/mail-delivery/ses-webhook.service.spec.ts +170 -0
  474. package/src/mail-delivery/ses-webhook.service.ts +240 -0
  475. package/src/mail-delivery/sns-message.util.spec.ts +163 -0
  476. package/src/mail-delivery/sns-message.util.ts +125 -0
  477. package/src/mail-sent/dto/query-mail-sent-list.dto.ts +15 -0
  478. package/src/mail-sent/mail-sent.service.ts +46 -2
  479. package/src/mailbox/mailbox-fetcher.registry.ts +132 -0
  480. package/src/mailbox/mailbox.module.ts +14 -0
  481. package/src/meeting/meeting-provider.registry.ts +193 -0
  482. package/src/meeting/meeting.constants.ts +53 -0
  483. package/src/meeting/meeting.module.ts +13 -0
  484. package/src/middlewares/locale-injection.middleware.ts +25 -6
  485. package/src/oauth/providers/google.provider.ts +49 -15
  486. package/src/observability/monitored-cron.spec.ts +102 -0
  487. package/src/observability/monitored-cron.ts +110 -0
  488. package/src/pairing/dto/approve-reverse-pairing.dto.ts +20 -0
  489. package/src/pairing/dto/claim-pairing.dto.ts +19 -0
  490. package/src/pairing/dto/confirm-pairing.dto.ts +14 -0
  491. package/src/pairing/dto/poll-reverse-pairing.dto.ts +9 -0
  492. package/src/pairing/dto/start-pairing.dto.ts +19 -0
  493. package/src/pairing/dto/start-reverse-pairing.dto.ts +45 -0
  494. package/src/pairing/pairing.controller.ts +246 -0
  495. package/src/pairing/pairing.module.ts +27 -0
  496. package/src/pairing/pairing.service.spec.ts +462 -0
  497. package/src/pairing/pairing.service.ts +614 -0
  498. package/src/pdf/pdf.service.ts +6 -24
  499. package/src/profile/profile.service.ts +26 -8
  500. package/src/redis/redis-pubsub.ts +11 -0
  501. package/src/role/guards/role.guard.spec.ts +75 -0
  502. package/src/role/guards/role.guard.ts +64 -46
  503. package/src/session/session.service.spec.ts +81 -0
  504. package/src/session/session.service.ts +93 -6
  505. package/src/setting/brand-asset.service.ts +137 -0
  506. package/src/setting/setting.module.ts +3 -2
  507. package/src/setting/setting.service.spec.ts +79 -1
  508. package/src/setting/setting.service.ts +109 -1
  509. package/src/token/token.service.spec.ts +62 -0
  510. package/src/token/token.service.ts +57 -3
  511. package/src/user/user.service.spec.ts +33 -2
  512. package/src/user/user.service.ts +108 -6
  513. package/src/user-device/user-device.controller.ts +77 -0
  514. package/src/user-device/user-device.module.ts +13 -0
  515. package/src/user-device/user-device.service.spec.ts +234 -0
  516. package/src/user-device/user-device.service.ts +168 -0
  517. package/src/utils/device-key.spec.ts +57 -0
  518. package/src/utils/device-key.ts +32 -0
  519. package/src/utils/geo-ip.spec.ts +134 -0
  520. package/src/utils/geo-ip.ts +93 -0
  521. package/src/utils/locale-context.spec.ts +27 -1
  522. package/src/utils/locale-context.ts +15 -12
  523. package/src/utils/security-event-vars.spec.ts +161 -0
  524. package/src/utils/security-event-vars.ts +153 -0
  525. package/src/utils/user-agent.spec.ts +37 -0
  526. package/src/utils/user-agent.ts +46 -0
  527. package/src/webhook-integration/webhook-integration.service.ts +57 -3
  528. package/src/webhook-integration/webhook-protocol.registry.ts +6 -0
@@ -0,0 +1,202 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { type ReactNode } from 'react';
5
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
6
+
7
+ const { appState, toastState } = vi.hoisted(() => ({
8
+ appState: { request: vi.fn() },
9
+ toastState: { error: vi.fn(), success: vi.fn() },
10
+ }));
11
+
12
+ vi.mock('@hed-hog/next-app-provider', async () => {
13
+ const actual =
14
+ await vi.importActual<typeof import('@tanstack/react-query')>(
15
+ '@tanstack/react-query'
16
+ );
17
+ return { useApp: () => appState, useQuery: actual.useQuery };
18
+ });
19
+
20
+ vi.mock('sonner', () => ({ toast: toastState }));
21
+
22
+ vi.mock('next-intl', () => ({
23
+ // Devolve a própria chave: o teste verifica comportamento, não tradução.
24
+ useTranslations: () => (key: string) => key,
25
+ }));
26
+
27
+ // O Select real é Radix e precisa de APIs de ponteiro que o jsdom não tem. O
28
+ // stub vira um <select> nativo, mesmo padrão de commerce-form-sheet.test.tsx.
29
+ vi.mock('@/components/ui/select', () => ({
30
+ Select: ({
31
+ children,
32
+ value,
33
+ onValueChange,
34
+ }: {
35
+ children: ReactNode;
36
+ value?: string;
37
+ onValueChange?: (value: string) => void;
38
+ }) => (
39
+ <select
40
+ aria-label="select-app"
41
+ value={value}
42
+ onChange={(event) => onValueChange?.(event.target.value)}
43
+ >
44
+ {children}
45
+ </select>
46
+ ),
47
+ SelectContent: ({ children }: { children: ReactNode }) => <>{children}</>,
48
+ SelectItem: ({ children, value }: { children: ReactNode; value: string }) => (
49
+ <option value={value}>{children}</option>
50
+ ),
51
+ SelectTrigger: ({ children }: { children: ReactNode }) => <>{children}</>,
52
+ SelectValue: ({ placeholder }: { placeholder?: string }) => (
53
+ <option value="" disabled>
54
+ {placeholder ?? 'Selecione'}
55
+ </option>
56
+ ),
57
+ }));
58
+
59
+ // O Dialog real também é Radix (portal + focus trap); aqui só interessa o
60
+ // conteúdo renderizado.
61
+ vi.mock('@/components/ui/dialog', () => ({
62
+ Dialog: ({ children, open }: { children: ReactNode; open?: boolean }) =>
63
+ open ? <div>{children}</div> : null,
64
+ DialogContent: ({ children }: { children: ReactNode }) => <div>{children}</div>,
65
+ DialogDescription: ({ children }: { children: ReactNode }) => <p>{children}</p>,
66
+ DialogFooter: ({ children }: { children: ReactNode }) => <div>{children}</div>,
67
+ DialogHeader: ({ children }: { children: ReactNode }) => <div>{children}</div>,
68
+ DialogTitle: ({ children }: { children: ReactNode }) => <h2>{children}</h2>,
69
+ }));
70
+
71
+ import { SimulateAccessDialog } from './simulate-access-dialog';
72
+
73
+ const TARGET = { id: 42, name: 'Aluna' } as never;
74
+
75
+ const renderDialog = () => {
76
+ const queryClient = new QueryClient({
77
+ defaultOptions: { queries: { retry: false } },
78
+ });
79
+ const wrapper = ({ children }: { children: ReactNode }) => (
80
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
81
+ );
82
+
83
+ return render(
84
+ <SimulateAccessDialog open onOpenChange={vi.fn()} user={TARGET} />,
85
+ { wrapper }
86
+ );
87
+ };
88
+
89
+ const chooseApp = async (value: string) => {
90
+ // Espera a lista de apps chegar: mudar o valor antes de a <option> existir é
91
+ // um no-op silencioso e o teste passaria a testar nada.
92
+ await screen.findByText(value);
93
+ fireEvent.change(screen.getByLabelText('select-app'), { target: { value } });
94
+ };
95
+
96
+ describe('SimulateAccessDialog', () => {
97
+ beforeEach(() => {
98
+ vi.clearAllMocks();
99
+ appState.request = vi.fn(async (config: { url: string }) => {
100
+ if (config.url === '/impersonation/apps') {
101
+ return {
102
+ data: {
103
+ apps: [
104
+ { key: 'admin', url: 'https://admin.example.com' },
105
+ { key: 'class', url: 'https://class.example.com' },
106
+ ],
107
+ },
108
+ };
109
+ }
110
+ return { data: { url: 'https://class.example.com/simulacao#code=abc' } };
111
+ });
112
+ });
113
+
114
+ it('lista apenas os apps que o backend devolve', async () => {
115
+ renderDialog();
116
+
117
+ expect(await screen.findByText('admin')).toBeInTheDocument();
118
+ expect(screen.getByText('class')).toBeInTheDocument();
119
+ // `training` tem autenticação própria e ficou fora desta fase.
120
+ expect(screen.queryByText('training')).not.toBeInTheDocument();
121
+ });
122
+
123
+ it('exige escolher o app antes de confirmar', async () => {
124
+ renderDialog();
125
+
126
+ await waitFor(() =>
127
+ expect(screen.getByRole('button', { name: 'confirm' })).toBeDisabled()
128
+ );
129
+ });
130
+
131
+ it('abre a aba no gesto do clique, antes do await, e só depois aponta a URL', async () => {
132
+ // Abrir a aba depois do await cai no bloqueador de pop-up de todo navegador.
133
+ const tab = { location: { href: '' }, close: vi.fn() };
134
+ const openSpy = vi
135
+ .spyOn(window, 'open')
136
+ .mockReturnValue(tab as unknown as Window);
137
+
138
+ renderDialog();
139
+ await chooseApp('class');
140
+ fireEvent.click(screen.getByRole('button', { name: 'confirm' }));
141
+
142
+ expect(openSpy).toHaveBeenCalledWith('', '_blank', 'noopener');
143
+ await waitFor(() =>
144
+ expect(tab.location.href).toBe(
145
+ 'https://class.example.com/simulacao#code=abc'
146
+ )
147
+ );
148
+ });
149
+
150
+ it('envia o alvo, o app e o motivo para o backend', async () => {
151
+ vi.spyOn(window, 'open').mockReturnValue({
152
+ location: { href: '' },
153
+ close: vi.fn(),
154
+ } as unknown as Window);
155
+
156
+ renderDialog();
157
+ await chooseApp('class');
158
+ fireEvent.change(screen.getByLabelText('reason'), {
159
+ target: { value: 'chamado 123' },
160
+ });
161
+ fireEvent.click(screen.getByRole('button', { name: 'confirm' }));
162
+
163
+ await waitFor(() =>
164
+ expect(appState.request).toHaveBeenCalledWith(
165
+ expect.objectContaining({
166
+ url: '/impersonation/start',
167
+ method: 'POST',
168
+ data: { targetUserId: 42, app: 'class', reason: 'chamado 123' },
169
+ })
170
+ )
171
+ );
172
+ });
173
+
174
+ it('mostra a mensagem específica do backend e fecha a aba em branco', async () => {
175
+ const tab = { location: { href: '' }, close: vi.fn() };
176
+ vi.spyOn(window, 'open').mockReturnValue(tab as unknown as Window);
177
+
178
+ appState.request = vi.fn(async (config: { url: string }) => {
179
+ if (config.url === '/impersonation/apps') {
180
+ return {
181
+ data: { apps: [{ key: 'class', url: 'https://class.example.com' }] },
182
+ };
183
+ }
184
+ throw {
185
+ response: {
186
+ data: { message: 'Administradores não podem ser simulados.' },
187
+ },
188
+ };
189
+ });
190
+
191
+ renderDialog();
192
+ await chooseApp('class');
193
+ fireEvent.click(screen.getByRole('button', { name: 'confirm' }));
194
+
195
+ await waitFor(() =>
196
+ expect(toastState.error).toHaveBeenCalledWith(
197
+ 'Administradores não podem ser simulados.'
198
+ )
199
+ );
200
+ expect(tab.close).toHaveBeenCalled();
201
+ });
202
+ });
@@ -0,0 +1,170 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ } from '@/components/ui/dialog';
12
+ import { Label } from '@/components/ui/label';
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue,
19
+ } from '@/components/ui/select';
20
+ import { Textarea } from '@/components/ui/textarea';
21
+ import { User } from '@hed-hog/api-types';
22
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
23
+ import { VenetianMask } from 'lucide-react';
24
+ import { useTranslations } from 'next-intl';
25
+ import { useEffect, useState } from 'react';
26
+ import { toast } from 'sonner';
27
+
28
+ type AppOption = { key: string; url: string };
29
+
30
+ export function SimulateAccessDialog({
31
+ open,
32
+ onOpenChange,
33
+ user,
34
+ }: {
35
+ open: boolean;
36
+ onOpenChange: (open: boolean) => void;
37
+ user: User | null;
38
+ }) {
39
+ const t = useTranslations('core.AccessSimulation');
40
+ const { request } = useApp();
41
+
42
+ const [app, setApp] = useState('');
43
+ const [reason, setReason] = useState('');
44
+ const [submitting, setSubmitting] = useState(false);
45
+
46
+ // Os apps de destino vêm do backend (chaves de `app-urls` que têm tela de
47
+ // resgate), e não de uma lista fixa aqui: o admin não sabe o que está
48
+ // configurado nesta instalação.
49
+ const { data: apps, isLoading: isLoadingApps } = useQuery<{ apps: AppOption[] }>({
50
+ queryKey: ['impersonation-apps'],
51
+ enabled: open,
52
+ queryFn: async () => {
53
+ const response = await request<{ apps: AppOption[] }>({
54
+ url: '/impersonation/apps',
55
+ });
56
+ return response.data;
57
+ },
58
+ });
59
+
60
+ const options = apps?.apps ?? [];
61
+
62
+ useEffect(() => {
63
+ if (!open) {
64
+ setApp('');
65
+ setReason('');
66
+ setSubmitting(false);
67
+ }
68
+ }, [open]);
69
+
70
+ const handleConfirm = async () => {
71
+ if (!user || !app) return;
72
+
73
+ // A aba precisa ser aberta AGORA, no gesto do clique. Abrir depois do await
74
+ // cai no bloqueador de pop-up de todo navegador.
75
+ const tab = window.open('', '_blank', 'noopener');
76
+
77
+ setSubmitting(true);
78
+ try {
79
+ const { data } = await request<{ url: string }>({
80
+ url: '/impersonation/start',
81
+ method: 'POST',
82
+ data: { targetUserId: Number(user.id), app, reason: reason.trim() || undefined },
83
+ showErrors: false,
84
+ });
85
+
86
+ if (tab) {
87
+ tab.location.href = data.url;
88
+ } else {
89
+ // Pop-up bloqueado apesar do gesto: o código vale 60s, então mandar o
90
+ // operador clicar num link é melhor do que perdê-lo.
91
+ window.location.href = data.url;
92
+ }
93
+
94
+ onOpenChange(false);
95
+ } catch (error: any) {
96
+ tab?.close();
97
+ // A mensagem do backend é específica (alvo administrador, conta
98
+ // desativada, app sem URL...) e vale mais que um texto genérico.
99
+ const message =
100
+ typeof error?.response?.data?.message === 'string'
101
+ ? error.response.data.message
102
+ : t('startFailure');
103
+ toast.error(message);
104
+ } finally {
105
+ setSubmitting(false);
106
+ }
107
+ };
108
+
109
+ return (
110
+ <Dialog open={open} onOpenChange={onOpenChange}>
111
+ <DialogContent>
112
+ <DialogHeader>
113
+ <DialogTitle className="flex items-center gap-2">
114
+ <VenetianMask className="h-4 w-4 text-amber-600" />
115
+ {t('dialogTitle')}
116
+ </DialogTitle>
117
+ <DialogDescription>
118
+ {t('dialogDescription', { name: user?.name ?? '' })}
119
+ </DialogDescription>
120
+ </DialogHeader>
121
+
122
+ <div className="space-y-4">
123
+ <div className="space-y-2">
124
+ <Label htmlFor="impersonation-app">{t('targetApp')}</Label>
125
+ <Select value={app} onValueChange={setApp} disabled={isLoadingApps}>
126
+ <SelectTrigger id="impersonation-app" className="w-full">
127
+ <SelectValue placeholder={t('targetAppPlaceholder')} />
128
+ </SelectTrigger>
129
+ <SelectContent>
130
+ {options.map((option) => (
131
+ <SelectItem key={option.key} value={option.key}>
132
+ {option.key}
133
+ </SelectItem>
134
+ ))}
135
+ </SelectContent>
136
+ </Select>
137
+ {!isLoadingApps && options.length === 0 && (
138
+ <p className="text-xs text-destructive">{t('noAppsConfigured')}</p>
139
+ )}
140
+ </div>
141
+
142
+ <div className="space-y-2">
143
+ <Label htmlFor="impersonation-reason">{t('reason')}</Label>
144
+ <Textarea
145
+ id="impersonation-reason"
146
+ value={reason}
147
+ onChange={(event) => setReason(event.target.value)}
148
+ placeholder={t('reasonPlaceholder')}
149
+ maxLength={255}
150
+ rows={3}
151
+ />
152
+ </div>
153
+
154
+ <div className="rounded-md border border-amber-200 bg-amber-50 p-3 text-xs text-amber-800">
155
+ {t('auditWarning')}
156
+ </div>
157
+ </div>
158
+
159
+ <DialogFooter>
160
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
161
+ {t('cancel')}
162
+ </Button>
163
+ <Button onClick={handleConfirm} disabled={!app || submitting}>
164
+ {submitting ? t('opening') : t('confirm')}
165
+ </Button>
166
+ </DialogFooter>
167
+ </DialogContent>
168
+ </Dialog>
169
+ );
170
+ }
@@ -17,6 +17,7 @@ import {
17
17
  ShieldOff,
18
18
  UserIcon,
19
19
  UserPlus,
20
+ VenetianMask,
20
21
  } from 'lucide-react';
21
22
  import { useTranslations } from 'next-intl';
22
23
  import { useMemo } from 'react';
@@ -30,6 +31,13 @@ type ActivityConfig = {
30
31
  dot: string;
31
32
  };
32
33
 
34
+ const IMPERSONATION_CONFIG: ActivityConfig = {
35
+ icon: VenetianMask,
36
+ color: 'text-amber-600 dark:text-amber-400',
37
+ bg: 'bg-amber-50 dark:bg-amber-950/40',
38
+ dot: 'ring-amber-200 dark:ring-amber-900',
39
+ };
40
+
33
41
  const ACTION_CONFIG: Record<string, ActivityConfig> = {
34
42
  login: {
35
43
  icon: UserIcon,
@@ -73,6 +81,12 @@ const ACTION_CONFIG: Record<string, ActivityConfig> = {
73
81
  bg: 'bg-rose-50 dark:bg-rose-950/40',
74
82
  dot: 'ring-rose-200 dark:ring-rose-900',
75
83
  },
84
+ // Simulação de acesso. As duas primeiras aparecem na linha do tempo do ALVO;
85
+ // as duas últimas, na do OPERADOR.
86
+ impersonationStarted: IMPERSONATION_CONFIG,
87
+ impersonationEnded: IMPERSONATION_CONFIG,
88
+ impersonationPerformed: IMPERSONATION_CONFIG,
89
+ impersonationPerformedEnded: IMPERSONATION_CONFIG,
76
90
  };
77
91
 
78
92
  const FALLBACK_CONFIG: ActivityConfig = {
@@ -206,6 +220,11 @@ export function UserActivityTimeline({
206
220
  </span>
207
221
  )}
208
222
  </div>
223
+ {activity.actor?.name && (
224
+ <span className="text-[11px] font-medium text-amber-700 dark:text-amber-400">
225
+ {t('activityByActor', { name: activity.actor.name })}
226
+ </span>
227
+ )}
209
228
  {activity.created_at && (
210
229
  <span className="text-[11px] text-muted-foreground">
211
230
  {formatDistanceToNow(
@@ -0,0 +1,89 @@
1
+ 'use client';
2
+
3
+ import { PaginationFooter } from '@/components/entity-list/pagination-footer';
4
+ import { usePagination } from '@/hooks/use-pagination';
5
+ import { User } from '@hed-hog/api-types';
6
+ import { useApp } from '@hed-hog/next-app-provider';
7
+ import { useTranslations } from 'next-intl';
8
+ import { toast } from 'sonner';
9
+ import {
10
+ DEVICE_PAGE_SIZES,
11
+ UserDevice,
12
+ UserDevicesList,
13
+ } from '../account/components/user-devices-list';
14
+
15
+ interface IProps {
16
+ editingUser: User;
17
+ }
18
+
19
+ /**
20
+ * Dispositivos conhecidos do usuario sendo editado.
21
+ *
22
+ * Busca propria e paginada em vez de vir embutido no detalhe do usuario: uma
23
+ * conta antiga acumula dezenas de dispositivos, e embutir a lista pesaria em
24
+ * todo GET /user/:id, inclusive nas abas que nem olham para isso.
25
+ */
26
+ export function UserDevices({ editingUser }: IProps) {
27
+ const { request } = useApp();
28
+ const t = useTranslations('core.KnownDevices');
29
+
30
+ const {
31
+ items: devices,
32
+ refetch,
33
+ page,
34
+ setPage,
35
+ totalItems,
36
+ pageSize,
37
+ setPageSize,
38
+ } = usePagination({
39
+ url: `/user-device/user/${editingUser?.id}`,
40
+ paginationOptions: { pageSizeOptions: [...DEVICE_PAGE_SIZES] },
41
+ });
42
+
43
+ const handleForget = async (deviceId: number) => {
44
+ try {
45
+ await request({
46
+ url: `/user-device/user/${editingUser.id}/${deviceId}`,
47
+ method: 'DELETE',
48
+ });
49
+ await refetch();
50
+ toast.success(t('forgetSuccess'));
51
+ } catch {
52
+ toast.error(t('forgetFailure'));
53
+ }
54
+ };
55
+
56
+ return (
57
+ <div className="space-y-3">
58
+ <div>
59
+ <h4 className="text-sm font-medium">{t('title')}</h4>
60
+ <p className="text-xs text-muted-foreground">{t('adminDescription')}</p>
61
+ {/* Um acesso simulado NÃO registra dispositivo de propósito: o
62
+ navegador do operador não pode virar dispositivo confiável do
63
+ usuário, senão o alerta de novo dispositivo dele perde o valor. */}
64
+ <p className="mt-1 text-xs text-muted-foreground">
65
+ {t('simulatedNotRecorded')}
66
+ </p>
67
+ </div>
68
+ <UserDevicesList
69
+ devices={devices as UserDevice[]}
70
+ onForget={handleForget}
71
+ emptyLabel={t('noDevices')}
72
+ t={t}
73
+ />
74
+ {totalItems > pageSize ? (
75
+ <PaginationFooter
76
+ currentPage={page}
77
+ pageSize={pageSize}
78
+ totalItems={totalItems}
79
+ onPageChange={setPage}
80
+ onPageSizeChange={(newSize) => {
81
+ setPageSize(newSize);
82
+ setPage(1);
83
+ }}
84
+ pageSizeOptions={[...DEVICE_PAGE_SIZES]}
85
+ />
86
+ ) : null}
87
+ </div>
88
+ );
89
+ }
@@ -12,6 +12,7 @@ import {
12
12
  ScrollText,
13
13
  ShieldMinus,
14
14
  ShieldPlus,
15
+ VenetianMask,
15
16
  } from 'lucide-react';
16
17
  import { useTranslations } from 'next-intl';
17
18
 
@@ -40,6 +41,10 @@ function getActionIcon(action: string) {
40
41
  return ShieldMinus;
41
42
  case 'avatar_changed':
42
43
  return ImageIcon;
44
+ case 'impersonation_requested':
45
+ case 'impersonation_started':
46
+ case 'impersonation_ended':
47
+ return VenetianMask;
43
48
  default:
44
49
  return ScrollText;
45
50
  }
@@ -104,8 +109,11 @@ export function UserLogs({ editingUser }: { editingUser: User }) {
104
109
  const { data: logs = [], isLoading } = useQuery<UserChangeLog[]>({
105
110
  queryKey: ['user-change-logs', userId, currentLocaleCode],
106
111
  queryFn: async () => {
112
+ // `sortOrder=desc` é obrigatório: o @Pagination() assume ordem crescente,
113
+ // então sem isto a primeira página traz as alterações mais ANTIGAS e o
114
+ // que acabou de acontecer some na última página.
107
115
  const response = await request<{ data: UserChangeLog[] }>({
108
- url: `/user/${userId}/log`,
116
+ url: `/user/${userId}/log?sortField=created_at&sortOrder=desc`,
109
117
  method: 'GET',
110
118
  });
111
119
  return response.data?.data || [];
@@ -119,6 +127,9 @@ export function UserLogs({ editingUser }: { editingUser: User }) {
119
127
  'role_assigned',
120
128
  'role_removed',
121
129
  'avatar_changed',
130
+ 'impersonation_requested',
131
+ 'impersonation_started',
132
+ 'impersonation_ended',
122
133
  ];
123
134
  const getActionLabel = (action: string) =>
124
135
  knownActions.includes(action) ? t(action as never) : action;
@@ -165,6 +176,7 @@ export function UserLogs({ editingUser }: { editingUser: User }) {
165
176
  </div>
166
177
  <p className="text-xs text-muted-foreground">
167
178
  {t('byActor')} {actor}
179
+ {log.ipAddress ? ` · ${log.ipAddress}` : ''}
168
180
  </p>
169
181
  {log.summary && (
170
182
  <p className="mt-1 text-xs text-muted-foreground">