@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
@@ -15,6 +15,25 @@ export interface OptimizeOptions {
15
15
  maxHeight?: number;
16
16
  }
17
17
 
18
+ export type ImageVariantFormat = 'webp' | 'jpeg' | 'png' | 'avif';
19
+
20
+ export interface ResizeVariantOptions {
21
+ /** Target width in px. The height follows the aspect ratio. */
22
+ width: number;
23
+ /** Output encoding. Defaults to keeping the source format. */
24
+ format?: ImageVariantFormat;
25
+ quality?: number;
26
+ }
27
+
28
+ export interface ShareCardOptions {
29
+ /** Defaults to the 1200x630 expected by Open Graph consumers. */
30
+ width?: number;
31
+ height?: number;
32
+ /** Canvas color behind the composition (hex, e.g. `#ff760c`). */
33
+ background?: string | null;
34
+ quality?: number;
35
+ }
36
+
18
37
  @Injectable()
19
38
  export class ImageOptimizationService {
20
39
  private readonly logger = new Logger(ImageOptimizationService.name);
@@ -185,4 +204,187 @@ export class ImageOptimizationService {
185
204
  return { buffer, mimetype, originalname };
186
205
  }
187
206
  }
207
+
208
+ /** Keeps the source encoding when the caller did not pick one explicitly. */
209
+ private defaultVariantFormat(mimetype: string): ImageVariantFormat {
210
+ if (mimetype === 'image/png') return 'png';
211
+ if (mimetype === 'image/webp') return 'webp';
212
+ if (mimetype === 'image/avif') return 'avif';
213
+ return 'jpeg';
214
+ }
215
+
216
+ /**
217
+ * Renders a resized variant of an already stored image, for serving a specific
218
+ * `?w=`/`?f=` URL. Unlike `optimizeBuffer`, every parameter is explicit and no
219
+ * setting is consulted: a given variant URL must always return the same bytes,
220
+ * otherwise the immutable cache headers would be a lie.
221
+ *
222
+ * Returns `null` whenever the variant cannot (or should not) be produced, so
223
+ * the caller falls back to streaming the original file.
224
+ */
225
+ async resizeBuffer(
226
+ buffer: Buffer,
227
+ mimetype: string,
228
+ options: ResizeVariantOptions,
229
+ ): Promise<OptimizedImage | null> {
230
+ const width = Math.round(Number(options.width) || 0);
231
+ if (width <= 0) return null;
232
+
233
+ // Vectors scale on their own, and resizing an animated GIF would flatten it
234
+ // to a single frame — both are better served as-is.
235
+ if (mimetype === 'image/svg+xml' || mimetype === 'image/gif') return null;
236
+
237
+ const quality = Math.min(100, Math.max(1, Number(options.quality) || 80));
238
+ const format = options.format ?? this.defaultVariantFormat(mimetype);
239
+
240
+ try {
241
+ const instance = sharp(buffer).resize({
242
+ width,
243
+ fit: 'inside',
244
+ withoutEnlargement: true,
245
+ });
246
+
247
+ if (format === 'png') {
248
+ return {
249
+ buffer: await instance
250
+ .png({ compressionLevel: this.pngCompressionLevel(quality) })
251
+ .toBuffer(),
252
+ mimetype: 'image/png',
253
+ originalname: 'image.png',
254
+ };
255
+ }
256
+
257
+ const encoded =
258
+ format === 'webp'
259
+ ? await instance.webp({ quality }).toBuffer()
260
+ : format === 'avif'
261
+ ? await instance.avif({ quality }).toBuffer()
262
+ : await instance.jpeg({ quality }).toBuffer();
263
+
264
+ return {
265
+ buffer: encoded,
266
+ mimetype: `image/${format}`,
267
+ originalname: `image.${format === 'jpeg' ? 'jpg' : format}`,
268
+ };
269
+ } catch (err) {
270
+ this.logger.warn(
271
+ `Image resize failed (${mimetype}, w=${width}, f=${format}): ${err instanceof Error ? err.message : String(err)}. Serving the original.`,
272
+ );
273
+ return null;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Monta um cartão de compartilhamento (Open Graph) a partir das imagens já
279
+ * cadastradas: fundo desfocado e escurecido, com a arte principal centralizada.
280
+ *
281
+ * A saída é sempre JPEG, e é esse o ponto do método: as imagens do acervo são
282
+ * convertidas para WebP no upload (ver `image-webp-conversion-enabled`), e o
283
+ * robô do WhatsApp não renderiza WebP de forma confiável — apontar `og:image`
284
+ * para o arquivo original deixaria o preview sem imagem.
285
+ *
286
+ * Sem texto de propósito: o título vem na tag `og:title`, que as redes já
287
+ * desenham ao lado do cartão, e assim não é preciso embutir fonte nem tratar
288
+ * transbordo de título longo no bitmap.
289
+ */
290
+ async composeShareCard(
291
+ background: Buffer | null | undefined,
292
+ overlay: Buffer | null | undefined,
293
+ options: ShareCardOptions = {},
294
+ ): Promise<Buffer | null> {
295
+ const width = options.width ?? 1200;
296
+ const height = options.height ?? 630;
297
+ const quality = Math.min(100, Math.max(1, options.quality ?? 82));
298
+ const canvasColor = this.parseHexColor(options.background) ?? {
299
+ r: 11,
300
+ g: 11,
301
+ b: 15,
302
+ };
303
+
304
+ const backdropSource = background ?? overlay;
305
+ const foregroundSource = overlay ?? background;
306
+
307
+ if (!backdropSource && !foregroundSource) {
308
+ return null;
309
+ }
310
+
311
+ try {
312
+ const layers: sharp.OverlayOptions[] = [];
313
+
314
+ if (backdropSource) {
315
+ const backdrop = await sharp(backdropSource)
316
+ .resize(width, height, { fit: 'cover', position: 'centre' })
317
+ .blur(32)
318
+ .modulate({ brightness: 0.45 })
319
+ .toBuffer()
320
+ .catch(() => null);
321
+
322
+ if (backdrop) {
323
+ layers.push({ input: backdrop, top: 0, left: 0 });
324
+ }
325
+ }
326
+
327
+ if (foregroundSource) {
328
+ const foreground = await sharp(foregroundSource)
329
+ .resize(Math.round(width * 0.64), Math.round(height * 0.64), {
330
+ fit: 'inside',
331
+ withoutEnlargement: false,
332
+ })
333
+ .toBuffer()
334
+ .catch(() => null);
335
+
336
+ if (foreground) {
337
+ layers.push({ input: foreground, gravity: 'centre' });
338
+ }
339
+ }
340
+
341
+ if (layers.length === 0) {
342
+ return null;
343
+ }
344
+
345
+ return await sharp({
346
+ create: {
347
+ width,
348
+ height,
349
+ channels: 3,
350
+ background: canvasColor,
351
+ },
352
+ })
353
+ .composite(layers)
354
+ .jpeg({ quality, mozjpeg: true })
355
+ .toBuffer();
356
+ } catch (err) {
357
+ this.logger.warn(
358
+ `Share card composition failed: ${err instanceof Error ? err.message : String(err)}.`,
359
+ );
360
+ return null;
361
+ }
362
+ }
363
+
364
+ private parseHexColor(
365
+ raw: string | null | undefined,
366
+ ): { r: number; g: number; b: number } | null {
367
+ if (!raw) {
368
+ return null;
369
+ }
370
+
371
+ const hex = raw.trim().replace(/^#/, '');
372
+ const full =
373
+ hex.length === 3
374
+ ? hex
375
+ .split('')
376
+ .map((char) => char + char)
377
+ .join('')
378
+ : hex;
379
+
380
+ if (!/^[0-9a-fA-F]{6}$/.test(full)) {
381
+ return null;
382
+ }
383
+
384
+ return {
385
+ r: parseInt(full.slice(0, 2), 16),
386
+ g: parseInt(full.slice(2, 4), 16),
387
+ b: parseInt(full.slice(4, 6), 16),
388
+ };
389
+ }
188
390
  }
@@ -24,6 +24,22 @@ interface IProvider {
24
24
  initValidation(): Promise<any>;
25
25
  }
26
26
 
27
+ /**
28
+ * Copies an object that is already stored, without moving the bytes through the app.
29
+ *
30
+ * Optional on purpose: `FileService.duplicate` falls back to streaming the file through a temp
31
+ * file when the active provider does not implement it, so no provider is ever left broken.
32
+ */
33
+ export interface ProviderServerSideCopy {
34
+ copy(sourcePath: string, destination: string, filename: string): Promise<string>;
35
+ }
36
+
37
+ export function supportsServerSideCopy(
38
+ provider: AbstractProvider,
39
+ ): provider is AbstractProvider & ProviderServerSideCopy {
40
+ return typeof (provider as Partial<ProviderServerSideCopy>).copy === 'function';
41
+ }
42
+
27
43
  export abstract class AbstractProvider implements IProvider {
28
44
  abstract uploadFromString(
29
45
  destination: string,
@@ -44,6 +60,13 @@ export abstract class AbstractProvider implements IProvider {
44
60
  abstract tempURL(filepath: string, expires?: number): Promise<any>;
45
61
  abstract initValidation(): Promise<any>;
46
62
 
63
+ /**
64
+ * Server-side copy of an already-stored object into `destination`, returning the new stored
65
+ * filename (same contract as `upload*`). Implemented by providers whose SDK supports it; see
66
+ * `supportsServerSideCopy`.
67
+ */
68
+ copy?(sourcePath: string, destination: string, filename: string): Promise<string>;
69
+
47
70
  getExtension(filename: string) {
48
71
  return filename.split('.').pop();
49
72
  }
@@ -96,6 +96,23 @@ export class GCSProvider extends AbstractProvider {
96
96
  if (options?.end !== undefined) rangeOpts.end = options.end;
97
97
  return file.createReadStream(Object.keys(rangeOpts).length > 0 ? rangeOpts : undefined);
98
98
  }
99
+ /** Server-side copy — the bytes never leave GCS. */
100
+ async copy(
101
+ sourcePath: string,
102
+ destination: string,
103
+ filename: string,
104
+ ): Promise<string> {
105
+ const storage = await this.getClient();
106
+ const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
107
+ const storedFilename = this.getFilename(filename);
108
+
109
+ await bucket
110
+ .file(sourcePath)
111
+ .copy(bucket.file([destination.replace(/\/$/, ''), storedFilename].join('/')));
112
+
113
+ return storedFilename;
114
+ }
115
+
99
116
  async delete(filepath: string): Promise<any> {
100
117
  const storage = await this.getClient();
101
118
  const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
@@ -117,6 +117,27 @@ export class LocalProvider extends AbstractProvider {
117
117
  return targetFilename;
118
118
  }
119
119
 
120
+ async copy(
121
+ sourcePath: string,
122
+ destination: string,
123
+ filename: string,
124
+ ): Promise<string> {
125
+ const fullSourcePath = this.resolveFilepath(sourcePath);
126
+
127
+ if (!existsSync(fullSourcePath)) {
128
+ throw new BadRequestException(`File not found: ${fullSourcePath}`);
129
+ }
130
+
131
+ const storagePath = join(this.setting['storage-local-path'], destination);
132
+ if (!existsSync(storagePath)) {
133
+ await this.createFolderRecursive(storagePath);
134
+ }
135
+
136
+ const storedFilename = this.getFilename(filename);
137
+ await copyFile(fullSourcePath, join(storagePath, storedFilename));
138
+ return storedFilename;
139
+ }
140
+
120
141
  async delete(filepath: string): Promise<any> {
121
142
  const fullPath = this.resolveFilepath(filepath);
122
143
 
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CopyObjectCommand,
2
3
  DeleteObjectCommand,
3
4
  GetObjectCommand,
4
5
  HeadBucketCommand,
@@ -181,6 +182,30 @@ export class S3Provider extends AbstractProvider {
181
182
  return storedFilename;
182
183
  }
183
184
 
185
+ /**
186
+ * Server-side copy — the bytes never leave S3. Capped at 5GB per object by CopyObject;
187
+ * FileService catches the failure and falls back to streaming through a temp file.
188
+ */
189
+ async copy(
190
+ sourcePath: string,
191
+ destination: string,
192
+ filename: string,
193
+ ): Promise<string> {
194
+ const s3 = await this.getClient();
195
+ const bucket = this.setting['storage-s3-bucket'];
196
+ const storedFilename = this.getFilename(filename);
197
+
198
+ await s3.send(
199
+ new CopyObjectCommand({
200
+ Bucket: bucket,
201
+ Key: [destination, storedFilename].join('/'),
202
+ CopySource: encodeURI(`${bucket}/${this.resolveS3Key(sourcePath)}`),
203
+ }),
204
+ );
205
+
206
+ return storedFilename;
207
+ }
208
+
184
209
  async delete(filepath: string): Promise<any> {
185
210
  const s3 = await this.getClient();
186
211
  const key = this.resolveS3Key(filepath);
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CopyObjectCommand,
2
3
  DeleteObjectCommand,
3
4
  GetObjectCommand,
4
5
  HeadBucketCommand,
@@ -207,6 +208,31 @@ export class SpacesProvider extends AbstractProvider {
207
208
  return storedFilename;
208
209
  }
209
210
 
211
+ /**
212
+ * Server-side copy — the bytes never leave Spaces. Capped at 5GB per object by CopyObject;
213
+ * FileService catches the failure and falls back to streaming through a temp file.
214
+ */
215
+ async copy(
216
+ sourcePath: string,
217
+ destination: string,
218
+ filename: string,
219
+ ): Promise<string> {
220
+ const s3 = await this.getClient();
221
+ const bucket = this.setting['storage-spaces-bucket'];
222
+ const storedFilename = this.getFilename(filename);
223
+
224
+ await s3.send(
225
+ new CopyObjectCommand({
226
+ Bucket: bucket,
227
+ Key: [destination, storedFilename].join('/'),
228
+ CopySource: encodeURI(`${bucket}/${this.resolveKey(sourcePath)}`),
229
+ ACL: 'public-read',
230
+ }),
231
+ );
232
+
233
+ return storedFilename;
234
+ }
235
+
210
236
  async delete(filepath: string): Promise<any> {
211
237
  const s3 = await this.getClient();
212
238
  const key = this.resolveKey(filepath);
@@ -0,0 +1,8 @@
1
+ import { IsString, MaxLength, MinLength } from 'class-validator';
2
+
3
+ export class RedeemImpersonationDTO {
4
+ @IsString()
5
+ @MinLength(16)
6
+ @MaxLength(255)
7
+ code: string;
8
+ }
@@ -0,0 +1,13 @@
1
+ import { IsIn, IsOptional, IsString, MaxLength } from 'class-validator';
2
+
3
+ export class StartImpersonationDTO {
4
+ /** Chave do app de destino em `app-urls`. */
5
+ @IsIn(['admin', 'class', 'partners'])
6
+ app: string;
7
+
8
+ /** Justificativa livre do operador; vai para a trilha de auditoria. */
9
+ @IsOptional()
10
+ @IsString()
11
+ @MaxLength(255)
12
+ reason?: string;
13
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Rotas bloqueadas enquanto uma sessao simulada esta ativa.
3
+ *
4
+ * POR QUE ISTO EXISTE
5
+ *
6
+ * Numa simulacao o JWT tem `sub` = ALVO, entao para todo o resto do sistema o
7
+ * request E o alvo. Isso e o desejado (o RoleGuard resolve os papeis do alvo),
8
+ * mas abre buracos em qualquer endpoint que transforme "quem eu sou" em algo
9
+ * duravel. O caso mais grave e `GET /auth/desktop-token`: ele e `@NoRole()` e
10
+ * chama `getAuthenticationPayload` para `@User().id`, ou seja, o operador
11
+ * cunharia para o alvo uma sessao completa, RENOVAVEL e de 30 dias — furando o
12
+ * teto de 30 minutos e saindo da trilha de auditoria.
13
+ *
14
+ * `POST /auth/logout` e igualmente perigoso pelo motivo oposto: ele le
15
+ * `req.cookies['rt']`, que na mesma origem e o cookie do OPERADOR, e revogaria
16
+ * a sessao real dele.
17
+ *
18
+ * Por isso a checagem roda ANTES dos atalhos de `@Public()` e `@NoRole()` no
19
+ * RoleGuard — sao justamente esses dois decorators que protegem as rotas
20
+ * perigosas.
21
+ */
22
+
23
+ /** `METHOD /path` exatos, no formato que o RoleGuard monta a partir do Nest. */
24
+ const RESTRICTED_EXACT = new Set([
25
+ // Emissao de credencial duravel para o alvo.
26
+ 'GET /auth/desktop-token',
27
+ 'POST /auth/token',
28
+ // Mexe no cookie `rt`, que e do operador.
29
+ 'POST /auth/logout',
30
+ // Encerrar sessao alheia a partir de uma simulacao.
31
+ 'DELETE /sessions/:sessionId/revoke',
32
+ 'DELETE /sessions/revoke-all',
33
+ 'DELETE /sessions/revoke-all-other',
34
+ // Tomada de conta.
35
+ 'PUT /profile/change-password',
36
+ 'PUT /profile/change-email',
37
+ 'DELETE /profile/account',
38
+ 'PATCH /user/:userId/reset-password',
39
+ // Simulacao aninhada.
40
+ 'POST /impersonation/start',
41
+ ]);
42
+
43
+ /** Familias inteiras: pareamento, OAuth e todo o material de segunda etapa. */
44
+ const RESTRICTED_PREFIXES = [
45
+ '/auth/pairing',
46
+ '/auth/authorize',
47
+ '/profile/mfa',
48
+ '/profile/totp',
49
+ '/profile/webauthn',
50
+ '/profile/recovery-codes',
51
+ '/profile/email',
52
+ ];
53
+
54
+ /**
55
+ * Precisam funcionar justamente porque a sessao e simulada — sem elas o
56
+ * operador nao conseguiria sair.
57
+ */
58
+ const ALWAYS_ALLOWED = new Set([
59
+ 'POST /impersonation/stop',
60
+ 'GET /impersonation/session',
61
+ ]);
62
+
63
+ export function isRestrictedDuringImpersonation(
64
+ httpMethod: string,
65
+ fullPath: string,
66
+ ): boolean {
67
+ const key = `${httpMethod} ${fullPath}`;
68
+
69
+ if (ALWAYS_ALLOWED.has(key)) {
70
+ return false;
71
+ }
72
+
73
+ if (RESTRICTED_EXACT.has(key)) {
74
+ return true;
75
+ }
76
+
77
+ return RESTRICTED_PREFIXES.some(
78
+ (prefix) => fullPath === prefix || fullPath.startsWith(`${prefix}/`),
79
+ );
80
+ }
81
+
82
+ export const IMPERSONATION_RESTRICTED_EXACT = RESTRICTED_EXACT;
83
+ export const IMPERSONATION_RESTRICTED_PREFIXES = RESTRICTED_PREFIXES;
84
+ export const IMPERSONATION_ALWAYS_ALLOWED = ALWAYS_ALLOWED;
@@ -0,0 +1,109 @@
1
+ import { Impersonation, ImpersonationContext, NoRole, Public, Role, User } from '@hed-hog/api';
2
+ import { Locale } from '@hed-hog/api-locale';
3
+ import { Pagination } from '@hed-hog/api-pagination';
4
+ import {
5
+ Body,
6
+ Controller,
7
+ Get,
8
+ Headers,
9
+ Inject,
10
+ Ip,
11
+ Post,
12
+ Query,
13
+ UseGuards,
14
+ forwardRef,
15
+ } from '@nestjs/common';
16
+ import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
17
+ import { RedeemImpersonationDTO } from './dto/redeem-impersonation.dto';
18
+ import { StartImpersonationDTO } from './dto/start-impersonation.dto';
19
+ import { ImpersonationService } from './impersonation.service';
20
+
21
+ /**
22
+ * Quem chama o quê:
23
+ * - `start` e `history`: a aba do OPERADOR, com papel administrativo;
24
+ * - `redeem`: a aba NOVA, que ainda nao tem sessao nenhuma — por isso @Public();
25
+ * - `stop` e `session`: a propria sessao SIMULADA, cujos papeis sao os do alvo
26
+ * (um aluno, por exemplo) — por isso @NoRole(), e nao @Role().
27
+ */
28
+ @Role()
29
+ @Controller('impersonation')
30
+ export class ImpersonationController {
31
+ constructor(
32
+ @Inject(forwardRef(() => ImpersonationService))
33
+ private readonly service: ImpersonationService,
34
+ ) {}
35
+
36
+ @UseGuards(ThrottlerGuard)
37
+ @Throttle({ default: { limit: 10, ttl: 60_000 } })
38
+ @Post('start')
39
+ async start(
40
+ @Locale() locale: string,
41
+ @User() operator,
42
+ @Impersonation() impersonation: ImpersonationContext,
43
+ @Body() body: StartImpersonationDTO & { targetUserId: number },
44
+ @Ip() ipAddress: string,
45
+ @Headers('user-agent') userAgent: string,
46
+ ) {
47
+ return this.service.start({
48
+ locale,
49
+ operatorUserId: operator.id,
50
+ targetUserId: Number(body.targetUserId),
51
+ app: body.app,
52
+ reason: body.reason,
53
+ isImpersonating: impersonation.active,
54
+ ipAddress,
55
+ userAgent,
56
+ });
57
+ }
58
+
59
+ @Public()
60
+ @UseGuards(ThrottlerGuard)
61
+ @Throttle({ default: { limit: 20, ttl: 60_000 } })
62
+ @Post('redeem')
63
+ async redeem(
64
+ @Locale() locale: string,
65
+ @Body() { code }: RedeemImpersonationDTO,
66
+ @Ip() ipAddress: string,
67
+ @Headers('user-agent') userAgent: string,
68
+ ) {
69
+ return this.service.redeem({ locale, code, ipAddress, userAgent });
70
+ }
71
+
72
+ @NoRole()
73
+ @Post('stop')
74
+ async stop(
75
+ @User() target,
76
+ @Impersonation() impersonation: ImpersonationContext,
77
+ @Ip() ipAddress: string,
78
+ ) {
79
+ return this.service.stop({
80
+ sessionId: impersonation.sessionId,
81
+ targetUserId: target.id,
82
+ operatorUserId: impersonation.operatorUserId,
83
+ ipAddress,
84
+ });
85
+ }
86
+
87
+ @NoRole()
88
+ @Get('session')
89
+ async session(@Impersonation() impersonation: ImpersonationContext) {
90
+ return this.service.currentSession(
91
+ impersonation.active ? impersonation.sessionId : null,
92
+ );
93
+ }
94
+
95
+ @Get('apps')
96
+ async apps() {
97
+ return { apps: await this.service.listRedeemableApps() };
98
+ }
99
+
100
+ @Get('history')
101
+ async history(
102
+ @Pagination() paginationParams,
103
+ @Query('targetUserId') targetUserId?: string,
104
+ ) {
105
+ return this.service.history(paginationParams, {
106
+ targetUserId: targetUserId ? Number(targetUserId) : undefined,
107
+ });
108
+ }
109
+ }
@@ -0,0 +1,31 @@
1
+ import { PaginationModule } from '@hed-hog/api-pagination';
2
+ import { PrismaModule } from '@hed-hog/api-prisma';
3
+ import { Module, forwardRef } from '@nestjs/common';
4
+ import { SecurityModule } from '../security/security.module';
5
+ import { SessionModule } from '../session/session.module';
6
+ import { SettingModule } from '../setting/setting.module';
7
+ import { TokenModule } from '../token/token.module';
8
+ import { UserModule } from '../user/user.module';
9
+ import { ImpersonationController } from './impersonation.controller';
10
+ import { ImpersonationService } from './impersonation.service';
11
+
12
+ /**
13
+ * Modulo proprio, e nao mais um endpoint no AuthModule, porque a simulacao tem
14
+ * um ciclo de vida completo (emitir → resgatar → encerrar → auditar) e uma
15
+ * denylist de rotas que o RoleGuard consulta.
16
+ */
17
+ @Module({
18
+ imports: [
19
+ forwardRef(() => PrismaModule),
20
+ forwardRef(() => SecurityModule),
21
+ forwardRef(() => SettingModule),
22
+ forwardRef(() => TokenModule),
23
+ forwardRef(() => SessionModule),
24
+ forwardRef(() => UserModule),
25
+ forwardRef(() => PaginationModule),
26
+ ],
27
+ controllers: [ImpersonationController],
28
+ providers: [ImpersonationService],
29
+ exports: [ImpersonationService],
30
+ })
31
+ export class ImpersonationModule {}