@hed-hog/core 0.0.390 → 0.0.393

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,614 @@
1
+ // `Prisma` vem de @hed-hog/api-prisma, não de '@prisma/client': o tsconfig do
2
+ // monorepo mapeia '@prisma/client' para o client gerado (.prisma/client), que
3
+ // não expõe o helper `Prisma.sql`. É a convenção do repo inteiro.
4
+ import { Prisma, PrismaService } from '@hed-hog/api-prisma';
5
+ import {
6
+ BadRequestException,
7
+ Inject,
8
+ Injectable,
9
+ Logger,
10
+ forwardRef,
11
+ } from '@nestjs/common';
12
+ import { Cron, CronExpression } from '@nestjs/schedule';
13
+ import { timingSafeEqual } from 'crypto';
14
+ import { AuthService } from '../auth/auth.service';
15
+ import { SecurityService } from '../security/security.service';
16
+
17
+ /**
18
+ * Pareamento de aparelhos por QR code, nas duas direções.
19
+ *
20
+ * Modelado no `AuthorizeService`: token opaco de que só o hash é persistido, TTL
21
+ * curto, e toda transição de estado decidida pelo banco com
22
+ * `UPDATE ... WHERE ... IS NULL RETURNING`. Dois requests concorrentes — o caso
23
+ * de replay — só podem ter um vencedor, e quem decide é o Postgres.
24
+ *
25
+ * ## O que este serviço NÃO faz
26
+ *
27
+ * Ele nunca decifra `payload_enc`. Aquele blob é AES-256-GCM feito no cliente,
28
+ * com uma chave que jamais chega até aqui: na direção A ela sai do fragmento do
29
+ * QR (que o navegador não transmite), na direção B da chave privada efêmera que
30
+ * vive só na memória da aba. Chamar `security.decrypt` nele não daria erro de
31
+ * compilação — daria um cofre a menos de zero-knowledge. O spec cobre isso.
32
+ *
33
+ * ## Por que a direção A tem duas fases
34
+ *
35
+ * Se o navegador subisse o blob junto com o QR, quem fotografasse a tela nos 2
36
+ * minutos de validade levaria a sessão e as chaves. Com claim → confirm →
37
+ * redeem, o aparelho que reivindica não recebe nada; o blob só sobe depois que a
38
+ * pessoa aprovou, olhando o IP e o user-agent de quem pediu. Um QR
39
+ * fotografado vira um alerta recusável na tela da própria vítima.
40
+ */
41
+ @Injectable()
42
+ export class PairingService {
43
+ private readonly logger = new Logger(PairingService.name);
44
+
45
+ /**
46
+ * Curto de propósito: o QR fica visível numa tela, e o único trabalho dele é
47
+ * sobreviver ao tempo de pegar o celular e apontar a câmera.
48
+ */
49
+ private readonly pairingTtlMs = 120_000;
50
+
51
+ static readonly BROWSER_TO_DEVICE = 'browser-to-device';
52
+ static readonly DEVICE_TO_BROWSER = 'device-to-browser';
53
+
54
+ /**
55
+ * Uma mensagem só para "não existe", "expirou", "já foi usado" e "está no
56
+ * estado errado". Distinguir esses casos contaria a um atacante exatamente
57
+ * onde ele está no fluxo.
58
+ */
59
+ private static readonly GENERIC_ERROR =
60
+ 'Pareamento inválido, expirado ou já utilizado. Gere um novo QR code.';
61
+
62
+ constructor(
63
+ @Inject(forwardRef(() => PrismaService))
64
+ private readonly prisma: PrismaService,
65
+ @Inject(forwardRef(() => SecurityService))
66
+ private readonly security: SecurityService,
67
+ @Inject(forwardRef(() => AuthService))
68
+ private readonly auth: AuthService,
69
+ ) {}
70
+
71
+ /* ------------------------------------------------------------------ */
72
+ /* Comum */
73
+ /* ------------------------------------------------------------------ */
74
+
75
+ private invalid(): BadRequestException {
76
+ return new BadRequestException(PairingService.GENERIC_ERROR);
77
+ }
78
+
79
+ private async assertClient(clientId: string) {
80
+ const client = await this.prisma.oauth_app_client.findUnique({
81
+ where: { client_id: clientId },
82
+ });
83
+
84
+ if (!client || !client.enabled) {
85
+ throw new BadRequestException('Aplicativo desconhecido ou desabilitado.');
86
+ }
87
+
88
+ return client;
89
+ }
90
+
91
+ private expiresAt(): Date {
92
+ return new Date(Date.now() + this.pairingTtlMs);
93
+ }
94
+
95
+ private parseId(id: string): bigint {
96
+ try {
97
+ const parsed = BigInt(id);
98
+ // BigInt(0) e não 0n: o literal exige target ES2020, e as libraries
99
+ // compilam com es2017 no tsconfig.production.json — ver
100
+ // libraries/fiscal/src/document/fiscal-document.totals.ts.
101
+ if (parsed <= BigInt(0)) throw new Error('id inválido');
102
+ return parsed;
103
+ } catch {
104
+ throw this.invalid();
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Mesma tabela que `UserService.registerUserActivity` escreve. O insert é
110
+ * direto para não arrastar o UserModule inteiro — com todas as suas
111
+ * dependências — para dentro de um módulo de auth por causa de uma linha.
112
+ */
113
+ private async logActivity(userId: number | null, action: string) {
114
+ if (!userId) return;
115
+ await this.prisma.user_activity
116
+ .create({ data: { user_id: userId, action } })
117
+ .catch(() => null);
118
+ }
119
+
120
+ /**
121
+ * Comparação em tempo constante de duas strings base64. Usada na prova de
122
+ * posse da chave efêmera, que é pública mas cujo desencontro não deve vazar
123
+ * quantos bytes bateram.
124
+ */
125
+ private safeEquals(a: string, b: string): boolean {
126
+ const left = Buffer.from(a ?? '');
127
+ const right = Buffer.from(b ?? '');
128
+ if (left.length !== right.length) return false;
129
+ try {
130
+ return timingSafeEqual(left, right);
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ /* ------------------------------------------------------------------ */
137
+ /* Direção A — navegador destravado → celular */
138
+ /* ------------------------------------------------------------------ */
139
+
140
+ /** Abre o pareamento. Chamado pelo navegador, que já está autenticado. */
141
+ async start(params: {
142
+ userId: number;
143
+ authTokenHash: string;
144
+ clientId: string;
145
+ ipAddress: string;
146
+ userAgent: string;
147
+ }) {
148
+ await this.assertClient(params.clientId);
149
+
150
+ const expiresAt = this.expiresAt();
151
+ const row = await this.prisma.device_pairing_request.create({
152
+ data: {
153
+ direction: PairingService.BROWSER_TO_DEVICE,
154
+ auth_hash: params.authTokenHash,
155
+ user_id: params.userId,
156
+ client_id: params.clientId,
157
+ issued_ip: params.ipAddress,
158
+ issued_user_agent: params.userAgent,
159
+ expires_at: expiresAt,
160
+ },
161
+ });
162
+
163
+ await this.logActivity(params.userId, 'device-pairing-started');
164
+ this.logger.debug(
165
+ `[PairingService] pareamento aberto id=${row.id} userId=${params.userId}`,
166
+ );
167
+
168
+ return { id: String(row.id), expiresAt: expiresAt.toISOString() };
169
+ }
170
+
171
+ /** Estado para a tela do navegador, incluindo quem reivindicou. */
172
+ async status(userId: number, id: string) {
173
+ const row = await this.prisma.device_pairing_request.findFirst({
174
+ where: {
175
+ id: this.parseId(id),
176
+ user_id: userId,
177
+ direction: PairingService.BROWSER_TO_DEVICE,
178
+ },
179
+ });
180
+
181
+ if (!row) throw this.invalid();
182
+
183
+ return {
184
+ status: this.deriveStatus(row),
185
+ claim: row.claimed_at
186
+ ? {
187
+ ip: row.claim_ip,
188
+ userAgent: row.claim_user_agent,
189
+ at: row.claimed_at.toISOString(),
190
+ }
191
+ : null,
192
+ expiresAt: row.expires_at.toISOString(),
193
+ };
194
+ }
195
+
196
+ private deriveStatus(row: {
197
+ cancelled_at: Date | null;
198
+ consumed_at: Date | null;
199
+ confirmed_at: Date | null;
200
+ claimed_at: Date | null;
201
+ expires_at: Date;
202
+ }): string {
203
+ if (row.cancelled_at) return 'cancelled';
204
+ if (row.consumed_at) return 'consumed';
205
+ if (row.expires_at.getTime() <= Date.now()) return 'expired';
206
+ if (row.confirmed_at) return 'confirmed';
207
+ if (row.claimed_at) return 'claimed';
208
+ return 'pending';
209
+ }
210
+
211
+ /**
212
+ * O aparelho que leu o QR se anuncia. Não recebe nada além de "aguarde": é
213
+ * este passo que transforma uma foto do QR num alerta na tela da vítima em vez
214
+ * de um comprometimento silencioso.
215
+ */
216
+ async claim(params: {
217
+ authToken: string;
218
+ clientId: string;
219
+ ipAddress: string;
220
+ userAgent: string;
221
+ }) {
222
+ const authHash = this.security.hashSha256(params.authToken);
223
+
224
+ const rows = await this.prisma.$queryRaw<
225
+ Array<{ id: bigint; user_id: number; expires_at: Date }>
226
+ >(
227
+ Prisma.sql`
228
+ UPDATE device_pairing_request
229
+ SET claimed_at = NOW(),
230
+ claim_ip = ${params.ipAddress},
231
+ claim_user_agent = ${params.userAgent}
232
+ WHERE auth_hash = ${authHash}
233
+ AND client_id = ${params.clientId}
234
+ AND direction = ${PairingService.BROWSER_TO_DEVICE}
235
+ AND expires_at > NOW()
236
+ AND claimed_at IS NULL
237
+ AND cancelled_at IS NULL
238
+ AND consumed_at IS NULL
239
+ RETURNING id, user_id, expires_at
240
+ `,
241
+ );
242
+
243
+ const row = rows[0];
244
+ if (!row) {
245
+ // Um segundo leitor do mesmo QR perde a corrida aqui, decidida pelo banco.
246
+ this.logger.warn('[PairingService] claim recusado (hash inválido ou já reivindicado)');
247
+ throw this.invalid();
248
+ }
249
+
250
+ await this.logActivity(row.user_id, 'device-pairing-claimed');
251
+
252
+ return {
253
+ status: 'claimed',
254
+ expiresAt: row.expires_at.toISOString(),
255
+ };
256
+ }
257
+
258
+ /** O navegador aprova o aparelho e só então entrega o blob cifrado. */
259
+ async confirm(params: { userId: number; id: string; payloadEnc: string }) {
260
+ const rows = await this.prisma.$queryRaw<Array<{ id: bigint }>>(
261
+ Prisma.sql`
262
+ UPDATE device_pairing_request
263
+ SET confirmed_at = NOW(),
264
+ payload_enc = ${params.payloadEnc}
265
+ WHERE id = ${this.parseId(params.id)}
266
+ AND user_id = ${params.userId}
267
+ AND direction = ${PairingService.BROWSER_TO_DEVICE}
268
+ AND expires_at > NOW()
269
+ AND claimed_at IS NOT NULL
270
+ AND confirmed_at IS NULL
271
+ AND cancelled_at IS NULL
272
+ AND consumed_at IS NULL
273
+ RETURNING id
274
+ `,
275
+ );
276
+
277
+ if (!rows[0]) {
278
+ this.logger.warn(
279
+ `[PairingService] confirm recusado id=${params.id} userId=${params.userId}`,
280
+ );
281
+ throw this.invalid();
282
+ }
283
+
284
+ await this.logActivity(params.userId, 'device-pairing-confirmed');
285
+ return { status: 'confirmed' };
286
+ }
287
+
288
+ /** "Rejeitar", fechar o diálogo, ou regerar o QR. */
289
+ async cancel(userId: number, id: string) {
290
+ await this.prisma.device_pairing_request.updateMany({
291
+ where: { id: this.parseId(id), user_id: userId, consumed_at: null },
292
+ data: { cancelled_at: new Date() },
293
+ });
294
+
295
+ await this.logActivity(userId, 'device-pairing-cancelled');
296
+ return { status: 'cancelled' };
297
+ }
298
+
299
+ /**
300
+ * O aparelho resgata. Antes de confirmado devolve `pending` SEM consumir — do
301
+ * contrário o primeiro poll queimaria o registro e a aprovação chegaria tarde
302
+ * demais.
303
+ */
304
+ async redeem(params: {
305
+ authToken: string;
306
+ clientId: string;
307
+ locale: string;
308
+ ipAddress: string;
309
+ userAgent: string;
310
+ }) {
311
+ const authHash = this.security.hashSha256(params.authToken);
312
+
313
+ const pending = await this.prisma.device_pairing_request.findFirst({
314
+ where: {
315
+ auth_hash: authHash,
316
+ client_id: params.clientId,
317
+ direction: PairingService.BROWSER_TO_DEVICE,
318
+ },
319
+ });
320
+
321
+ if (
322
+ !pending ||
323
+ pending.cancelled_at ||
324
+ pending.consumed_at ||
325
+ pending.expires_at.getTime() <= Date.now()
326
+ ) {
327
+ throw this.invalid();
328
+ }
329
+
330
+ if (!pending.confirmed_at) {
331
+ return { status: 'pending' as const };
332
+ }
333
+
334
+ const rows = await this.prisma.$queryRaw<
335
+ Array<{ user_id: number; payload_enc: string }>
336
+ >(
337
+ Prisma.sql`
338
+ UPDATE device_pairing_request
339
+ SET consumed_at = NOW()
340
+ WHERE auth_hash = ${authHash}
341
+ AND client_id = ${params.clientId}
342
+ AND direction = ${PairingService.BROWSER_TO_DEVICE}
343
+ AND confirmed_at IS NOT NULL
344
+ AND consumed_at IS NULL
345
+ AND cancelled_at IS NULL
346
+ AND expires_at > NOW()
347
+ RETURNING user_id, payload_enc
348
+ `,
349
+ );
350
+
351
+ const row = rows[0];
352
+ if (!row) {
353
+ this.logger.warn('[PairingService] redeem após consumo ou expiração');
354
+ throw this.invalid();
355
+ }
356
+
357
+ const { accessToken, refreshToken } = await this.auth.getAuthenticationPayload(
358
+ params.locale,
359
+ row.user_id,
360
+ params.ipAddress,
361
+ params.userAgent,
362
+ );
363
+
364
+ await this.logActivity(row.user_id, 'device-pairing-consumed');
365
+
366
+ return {
367
+ status: 'confirmed' as const,
368
+ accessToken,
369
+ refreshToken,
370
+ tokenType: 'Bearer',
371
+ // Informativo para o cliente agendar refresh; a verdade é o exp do JWT.
372
+ expiresIn: 3600,
373
+ // Devolvido intacto: este serviço não sabe (nem pode saber) o que há dentro.
374
+ payload: row.payload_enc,
375
+ };
376
+ }
377
+
378
+ /* ------------------------------------------------------------------ */
379
+ /* Direção B — celular destravado → navegador */
380
+ /* ------------------------------------------------------------------ */
381
+
382
+ /**
383
+ * O navegador que QUER acesso abre o pareamento. Público: o caso principal é
384
+ * um computador onde ninguém está logado. O registro nasce sem dono.
385
+ */
386
+ async startReverse(params: {
387
+ pollHash: string;
388
+ ephPublicKey: string;
389
+ wantSession: boolean;
390
+ clientId: string;
391
+ ipAddress: string;
392
+ userAgent: string;
393
+ }) {
394
+ await this.assertClient(params.clientId);
395
+
396
+ const expiresAt = this.expiresAt();
397
+ const row = await this.prisma.device_pairing_request.create({
398
+ data: {
399
+ direction: PairingService.DEVICE_TO_BROWSER,
400
+ auth_hash: params.pollHash,
401
+ client_id: params.clientId,
402
+ eph_public_key: params.ephPublicKey,
403
+ want_session: params.wantSession,
404
+ issued_ip: params.ipAddress,
405
+ issued_user_agent: params.userAgent,
406
+ expires_at: expiresAt,
407
+ },
408
+ });
409
+
410
+ return { id: String(row.id), expiresAt: expiresAt.toISOString() };
411
+ }
412
+
413
+ /**
414
+ * Metadados do navegador que pediu, para o aparelho decidir se aprova.
415
+ *
416
+ * Exige a pública efêmera como prova de que quem pergunta leu mesmo o QR: o
417
+ * `id` é sequencial, e sem essa prova qualquer usuário autenticado poderia
418
+ * varrer os ids e descobrir de que IP alguém está tentando entrar.
419
+ */
420
+ async getReverseRequest(id: string, ephPublicKey: string) {
421
+ const row = await this.prisma.device_pairing_request.findFirst({
422
+ where: {
423
+ id: this.parseId(id),
424
+ direction: PairingService.DEVICE_TO_BROWSER,
425
+ },
426
+ });
427
+
428
+ if (
429
+ !row ||
430
+ !row.eph_public_key ||
431
+ !this.safeEquals(row.eph_public_key, ephPublicKey) ||
432
+ row.cancelled_at ||
433
+ row.consumed_at ||
434
+ row.confirmed_at ||
435
+ row.expires_at.getTime() <= Date.now()
436
+ ) {
437
+ this.logger.warn(`[PairingService] inspeção recusada id=${id}`);
438
+ throw this.invalid();
439
+ }
440
+
441
+ // Registrar a leitura deixa o navegador mostrar "celular conectado,
442
+ // aguardando confirmação" em vez de um QR parado.
443
+ await this.prisma.device_pairing_request.updateMany({
444
+ where: { id: row.id, claimed_at: null },
445
+ data: { claimed_at: new Date() },
446
+ });
447
+
448
+ return {
449
+ issuedIp: row.issued_ip,
450
+ issuedUserAgent: row.issued_user_agent,
451
+ ephPublicKey: row.eph_public_key,
452
+ wantSession: row.want_session,
453
+ expiresAt: row.expires_at.toISOString(),
454
+ };
455
+ }
456
+
457
+ /** O aparelho autenticado aprova e entrega o blob cifrado para a efêmera. */
458
+ async approveReverse(params: {
459
+ userId: number;
460
+ id: string;
461
+ ephPublicKey: string;
462
+ payloadEnc: string;
463
+ }) {
464
+ const rows = await this.prisma.$queryRaw<Array<{ id: bigint }>>(
465
+ Prisma.sql`
466
+ UPDATE device_pairing_request
467
+ SET confirmed_at = NOW(),
468
+ user_id = ${params.userId},
469
+ payload_enc = ${params.payloadEnc}
470
+ WHERE id = ${this.parseId(params.id)}
471
+ AND direction = ${PairingService.DEVICE_TO_BROWSER}
472
+ AND eph_public_key = ${params.ephPublicKey}
473
+ AND expires_at > NOW()
474
+ AND confirmed_at IS NULL
475
+ AND cancelled_at IS NULL
476
+ AND consumed_at IS NULL
477
+ RETURNING id
478
+ `,
479
+ );
480
+
481
+ if (!rows[0]) {
482
+ this.logger.warn(
483
+ `[PairingService] aprovação recusada id=${params.id} userId=${params.userId}`,
484
+ );
485
+ throw this.invalid();
486
+ }
487
+
488
+ await this.logActivity(params.userId, 'device-pairing-approved');
489
+ return { status: 'confirmed' };
490
+ }
491
+
492
+ async rejectReverse(params: {
493
+ userId: number;
494
+ id: string;
495
+ ephPublicKey: string;
496
+ }) {
497
+ await this.prisma.device_pairing_request.updateMany({
498
+ where: {
499
+ id: this.parseId(params.id),
500
+ direction: PairingService.DEVICE_TO_BROWSER,
501
+ eph_public_key: params.ephPublicKey,
502
+ consumed_at: null,
503
+ },
504
+ data: { cancelled_at: new Date() },
505
+ });
506
+
507
+ await this.logActivity(params.userId, 'device-pairing-rejected');
508
+ return { status: 'cancelled' };
509
+ }
510
+
511
+ /**
512
+ * O navegador pergunta se já foi aprovado. Enquanto não, devolve `pending` sem
513
+ * consumir. A sessão, quando pedida, nasce com o IP e o user-agent do
514
+ * NAVEGADOR — é ele quem vai usá-la, não o celular que aprovou.
515
+ */
516
+ async pollReverse(params: { pollToken: string; locale: string }) {
517
+ const pollHash = this.security.hashSha256(params.pollToken);
518
+
519
+ const pending = await this.prisma.device_pairing_request.findFirst({
520
+ where: {
521
+ auth_hash: pollHash,
522
+ direction: PairingService.DEVICE_TO_BROWSER,
523
+ },
524
+ });
525
+
526
+ if (
527
+ !pending ||
528
+ pending.cancelled_at ||
529
+ pending.consumed_at ||
530
+ pending.expires_at.getTime() <= Date.now()
531
+ ) {
532
+ throw this.invalid();
533
+ }
534
+
535
+ if (!pending.confirmed_at) {
536
+ return {
537
+ status: 'pending' as const,
538
+ claimed: Boolean(pending.claimed_at),
539
+ };
540
+ }
541
+
542
+ const rows = await this.prisma.$queryRaw<
543
+ Array<{
544
+ user_id: number;
545
+ payload_enc: string;
546
+ want_session: boolean;
547
+ issued_ip: string | null;
548
+ issued_user_agent: string | null;
549
+ }>
550
+ >(
551
+ Prisma.sql`
552
+ UPDATE device_pairing_request
553
+ SET consumed_at = NOW()
554
+ WHERE auth_hash = ${pollHash}
555
+ AND direction = ${PairingService.DEVICE_TO_BROWSER}
556
+ AND confirmed_at IS NOT NULL
557
+ AND consumed_at IS NULL
558
+ AND cancelled_at IS NULL
559
+ AND expires_at > NOW()
560
+ RETURNING user_id, payload_enc, want_session, issued_ip, issued_user_agent
561
+ `,
562
+ );
563
+
564
+ const row = rows[0];
565
+ if (!row) {
566
+ this.logger.warn('[PairingService] poll após consumo ou expiração');
567
+ throw this.invalid();
568
+ }
569
+
570
+ await this.logActivity(row.user_id, 'device-pairing-consumed');
571
+
572
+ if (!row.want_session) {
573
+ return { status: 'confirmed' as const, payload: row.payload_enc };
574
+ }
575
+
576
+ const { accessToken, refreshToken, session } =
577
+ await this.auth.getAuthenticationPayload(
578
+ params.locale,
579
+ row.user_id,
580
+ row.issued_ip ?? '',
581
+ row.issued_user_agent ?? '',
582
+ );
583
+
584
+ return {
585
+ status: 'confirmed' as const,
586
+ payload: row.payload_enc,
587
+ accessToken,
588
+ refreshToken,
589
+ session,
590
+ tokenType: 'Bearer',
591
+ expiresIn: 3600,
592
+ };
593
+ }
594
+
595
+ /* ------------------------------------------------------------------ */
596
+ /* Limpeza */
597
+ /* ------------------------------------------------------------------ */
598
+
599
+ /**
600
+ * Os registros são minúsculos e de vida curta, mas nada os apagava — mesmo
601
+ * buraco que `oauth_authorization_code` tem. Sem isto, ciphertext aprovado e
602
+ * nunca resgatado ficaria no banco para sempre.
603
+ */
604
+ @Cron(CronExpression.EVERY_DAY_AT_4AM)
605
+ async purgeExpired() {
606
+ const { count } = await this.prisma.device_pairing_request.deleteMany({
607
+ where: { expires_at: { lt: new Date(Date.now() - 24 * 60 * 60 * 1000) } },
608
+ });
609
+
610
+ if (count > 0) {
611
+ this.logger.log(`[PairingService] ${count} pareamentos expirados removidos`);
612
+ }
613
+ }
614
+ }
@@ -1,6 +1,7 @@
1
1
  import { Inject, Injectable, InternalServerErrorException, Logger, forwardRef } from '@nestjs/common';
2
2
  import { ConfigService } from '@nestjs/config';
3
3
 
4
+ import { BrandAssetService } from '../setting/brand-asset.service';
4
5
  import { SettingService } from '../setting/setting.service';
5
6
  import { PdfAreaType, PdfThemeConfig } from './interfaces/pdf-theme-config.interface';
6
7
  import { hcodeInstitutionalTemplate } from './templates/hcode-institutional.template';
@@ -43,6 +44,8 @@ export class PdfService {
43
44
  constructor(
44
45
  @Inject(forwardRef(() => SettingService))
45
46
  private readonly settingService: SettingService,
47
+ @Inject(forwardRef(() => BrandAssetService))
48
+ private readonly brandAsset: BrandAssetService,
46
49
  private readonly configService: ConfigService,
47
50
  ) {}
48
51
 
@@ -87,7 +90,9 @@ export class PdfService {
87
90
  (values['pdf-logo'] as string) ||
88
91
  (values['image-url'] as string) ||
89
92
  '';
90
- const logoOverride = rawLogoUrl ? this.resolveUrl(rawLogoUrl) : '';
93
+ const logoOverride = rawLogoUrl
94
+ ? await this.brandAsset.resolveAssetUrl(rawLogoUrl)
95
+ : '';
91
96
 
92
97
  return {
93
98
  area,
@@ -115,29 +120,6 @@ export class PdfService {
115
120
  }
116
121
  }
117
122
 
118
- /**
119
- * Resolves a potentially relative URL (e.g. /logo.svg) to an absolute URL
120
- * using the API base URL derived from environment variables.
121
- */
122
- private resolveUrl(url: string): string {
123
- if (!url) return '';
124
- if (
125
- url.startsWith('data:') ||
126
- url.startsWith('http://') ||
127
- url.startsWith('https://')
128
- ) {
129
- return url;
130
- }
131
- const port = this.configService.get<string | number>('PORT') ?? 3100;
132
- const baseUrl =
133
- this.configService.get<string>('API_URL') ??
134
- `http://localhost:${port}`;
135
- return (
136
- baseUrl.replace(/\/$/, '') +
137
- (url.startsWith('/') ? url : '/' + url)
138
- );
139
- }
140
-
141
123
  // ── HTML → PDF ────────────────────────────────────────────────────────────
142
124
 
143
125
  /**