@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
@@ -78,6 +78,7 @@ describe('AccessLogService', () => {
78
78
  user_agent: 'UA',
79
79
  method: 'GET',
80
80
  path: '/api/x',
81
+ impersonator_user_id: null,
81
82
  },
82
83
  });
83
84
  });
@@ -89,7 +90,32 @@ describe('AccessLogService', () => {
89
90
  service.recordHttp({ method: 'POST', path: '/x' });
90
91
 
91
92
  expect(prisma.access_log.create).toHaveBeenCalledWith({
92
- data: { user_id: null, ip: null, user_agent: null, method: 'POST', path: '/x' },
93
+ data: {
94
+ user_id: null,
95
+ ip: null,
96
+ user_agent: null,
97
+ method: 'POST',
98
+ path: '/x',
99
+ impersonator_user_id: null,
100
+ },
101
+ });
102
+ });
103
+
104
+ it('grava o operador quando o acesso e simulado', () => {
105
+ const { service, prisma } = makeService();
106
+ prisma.access_log.create.mockResolvedValue({});
107
+
108
+ // `userId` continua sendo o ALVO; o operador entra numa coluna propria,
109
+ // senao o log fica indistinguivel de um acesso genuino do usuario.
110
+ service.recordHttp({
111
+ userId: 3,
112
+ method: 'GET',
113
+ path: '/api/x',
114
+ impersonatorUserId: 42,
115
+ });
116
+
117
+ expect(prisma.access_log.create).toHaveBeenCalledWith({
118
+ data: expect.objectContaining({ user_id: 3, impersonator_user_id: 42 }),
93
119
  });
94
120
  });
95
121
 
@@ -115,7 +141,12 @@ describe('AccessLogService', () => {
115
141
  service.recordMcp({ userId: 9, mcpTool: 'search', mcpPrompt: 'q' });
116
142
 
117
143
  expect(prisma.access_log.create).toHaveBeenCalledWith({
118
- data: { user_id: 9, mcp_tool: 'search', mcp_prompt: 'q' },
144
+ data: {
145
+ user_id: 9,
146
+ mcp_tool: 'search',
147
+ mcp_prompt: 'q',
148
+ impersonator_user_id: null,
149
+ },
119
150
  });
120
151
  });
121
152
  });
@@ -10,12 +10,15 @@ interface RecordHttpData {
10
10
  userAgent?: string;
11
11
  method: string;
12
12
  path: string;
13
+ /** Operador por tras de um acesso simulado; `userId` continua sendo o alvo. */
14
+ impersonatorUserId?: number;
13
15
  }
14
16
 
15
17
  interface RecordMcpData {
16
18
  userId?: number;
17
19
  mcpTool: string;
18
20
  mcpPrompt?: string;
21
+ impersonatorUserId?: number;
19
22
  }
20
23
 
21
24
  type AccessLogListParams = {
@@ -30,6 +33,7 @@ type AccessLogListParams = {
30
33
  createdAtFrom?: string;
31
34
  createdAtTo?: string;
32
35
  type?: 'http' | 'mcp' | 'all';
36
+ simulated?: 'all' | 'only' | 'exclude';
33
37
  };
34
38
 
35
39
  @Injectable()
@@ -52,6 +56,7 @@ export class AccessLogService {
52
56
  user_agent: data.userAgent ?? null,
53
57
  method: data.method,
54
58
  path: data.path,
59
+ impersonator_user_id: data.impersonatorUserId ?? null,
55
60
  },
56
61
  })
57
62
  .catch((e) => this.logger.warn(`Failed to persist HTTP access log: ${e.message}`));
@@ -64,6 +69,7 @@ export class AccessLogService {
64
69
  user_id: data.userId ?? null,
65
70
  mcp_tool: data.mcpTool,
66
71
  mcp_prompt: data.mcpPrompt ?? null,
72
+ impersonator_user_id: data.impersonatorUserId ?? null,
67
73
  },
68
74
  })
69
75
  .catch((e) => this.logger.warn(`Failed to persist MCP access log: ${e.message}`));
@@ -102,6 +108,12 @@ export class AccessLogService {
102
108
  where.mcp_tool = { not: null };
103
109
  }
104
110
 
111
+ if (params.simulated === 'only') {
112
+ where.impersonator_user_id = { not: null };
113
+ } else if (params.simulated === 'exclude') {
114
+ where.impersonator_user_id = null;
115
+ }
116
+
105
117
  const result = await this.pagination.paginate(this.prisma.access_log, {
106
118
  ...params,
107
119
  fields: params.fields ?? '',
@@ -113,6 +125,24 @@ export class AccessLogService {
113
125
  },
114
126
  });
115
127
 
128
+ // `impersonator_user_id` e INT puro, sem FK (senao o db pull renomearia a
129
+ // relacao `user.access_log`), entao o nome do operador vem de uma consulta
130
+ // separada sobre os ids desta pagina.
131
+ const impersonatorIds = Array.from(
132
+ new Set(
133
+ (result.data as any[])
134
+ .map((log) => log.impersonator_user_id)
135
+ .filter((id): id is number => typeof id === 'number'),
136
+ ),
137
+ );
138
+ const impersonators = impersonatorIds.length
139
+ ? await this.prisma.user.findMany({
140
+ where: { id: { in: impersonatorIds } },
141
+ select: { id: true, name: true, photo_id: true },
142
+ })
143
+ : [];
144
+ const impersonatorById = new Map(impersonators.map((user) => [user.id, user]));
145
+
116
146
  const data = (result.data as any[]).map((log) => {
117
147
  const ua = log.user_agent ? new UAParser(log.user_agent).getResult() : null;
118
148
  const geo = log.ip ? geoip.lookup(log.ip) : null;
@@ -124,6 +154,14 @@ export class AccessLogService {
124
154
  os_version: ua?.os?.version ?? null,
125
155
  country: geo?.country ?? null,
126
156
  city: geo?.city ?? null,
157
+ impersonator:
158
+ typeof log.impersonator_user_id === 'number'
159
+ ? (impersonatorById.get(log.impersonator_user_id) ?? {
160
+ id: log.impersonator_user_id,
161
+ name: '',
162
+ photo_id: null,
163
+ })
164
+ : null,
127
165
  };
128
166
  });
129
167
 
@@ -20,4 +20,9 @@ export class QueryAccessLogDto {
20
20
  @IsOptional()
21
21
  @IsIn(['http', 'mcp', 'all'])
22
22
  type?: 'http' | 'mcp' | 'all';
23
+
24
+ /** Filtra acessos feitos durante uma simulacao de acesso. */
25
+ @IsOptional()
26
+ @IsIn(['all', 'only', 'exclude'])
27
+ simulated?: 'all' | 'only' | 'exclude';
23
28
  }
@@ -7,7 +7,9 @@ import { AiService } from './ai.service';
7
7
  import { ChatAgentDTO } from './dto/chat-agent.dto';
8
8
  import { ChatDTO } from './dto/chat.dto';
9
9
  import { CreateAgentDTO } from './dto/create-agent.dto';
10
+ import { RichtextEditHtmlDTO } from './dto/richtext-edit-html.dto';
10
11
  import { UpdateAgentDTO } from './dto/update-agent.dto';
12
+ import { RichtextAiService } from './richtext-ai.service';
11
13
 
12
14
  @Role()
13
15
  @Controller('ai')
@@ -15,6 +17,8 @@ export class AiController {
15
17
  constructor(
16
18
  @Inject(forwardRef(() => AiService))
17
19
  private readonly aiService: AiService,
20
+ @Inject(forwardRef(() => RichtextAiService))
21
+ private readonly richtextAiService: RichtextAiService,
18
22
  ) {}
19
23
 
20
24
  @Post('chat')
@@ -65,4 +69,9 @@ export class AiController {
65
69
  ) {
66
70
  return this.aiService.chatWithAgent(slug, data, file);
67
71
  }
72
+
73
+ @Post('richtext/edit-html')
74
+ async editRichtextHtml(@Body() data: RichtextEditHtmlDTO) {
75
+ return this.richtextAiService.editHtml(data);
76
+ }
68
77
  }
@@ -5,6 +5,7 @@ import { FileModule } from '../file/file.module';
5
5
  import { SettingModule } from '../setting/setting.module';
6
6
  import { AiController } from './ai.controller';
7
7
  import { AiService } from './ai.service';
8
+ import { RichtextAiService } from './richtext-ai.service';
8
9
 
9
10
  @Module({
10
11
  imports: [
@@ -14,7 +15,7 @@ import { AiService } from './ai.service';
14
15
  forwardRef(() => AiInstructionModule),
15
16
  ],
16
17
  controllers: [AiController],
17
- providers: [AiService],
18
- exports: [AiService],
18
+ providers: [AiService, RichtextAiService],
19
+ exports: [AiService, RichtextAiService],
19
20
  })
20
21
  export class AiModule {}
@@ -40,7 +40,7 @@ import { ChatDTO } from './dto/chat.dto';
40
40
  import { CreateAgentDTO } from './dto/create-agent.dto';
41
41
  import { UpdateAgentDTO } from './dto/update-agent.dto';
42
42
 
43
- type AiProvider = 'openai' | 'gemini' | 'deepseek';
43
+ type AiProvider = 'openai' | 'gemini' | 'deepseek' | 'claude';
44
44
 
45
45
  /** Fallback call timeout when the active AI integration profile doesn't set `timeout_ms`. */
46
46
  const DEFAULT_AI_CALL_TIMEOUT_MS = 120_000;
@@ -50,6 +50,20 @@ const AI_PROFILE_SETTING: Record<AiProvider, string> = {
50
50
  openai: 'ai-openai-profile-id',
51
51
  gemini: 'ai-gemini-profile-id',
52
52
  deepseek: 'ai-deepseek-profile-id',
53
+ claude: 'ai-claude-profile-id',
54
+ };
55
+
56
+ /**
57
+ * Slug do provider de integração -> provider do chat.
58
+ *
59
+ * Usado quando o chamador escolhe um PERFIL (e não um provider): o provider sai
60
+ * do perfil, não do setting global.
61
+ */
62
+ const PROVIDER_BY_INTEGRATION_SLUG: Record<string, AiProvider> = {
63
+ openai: 'openai',
64
+ gemini: 'gemini',
65
+ deepseek: 'deepseek',
66
+ claude: 'claude',
53
67
  };
54
68
 
55
69
  /**
@@ -82,6 +96,14 @@ type AiAttachment = {
82
96
  buffer: Buffer;
83
97
  };
84
98
 
99
+ type AiProfileCredentials = {
100
+ apiKey: string;
101
+ organization?: string;
102
+ model?: string;
103
+ timeoutMs?: number;
104
+ baseUrl?: string;
105
+ };
106
+
85
107
  @Injectable()
86
108
  export class AiService implements OnModuleInit {
87
109
  private readonly logger = new Logger(AiService.name);
@@ -170,6 +192,14 @@ export class AiService implements OnModuleInit {
170
192
  userPromptSlug: string;
171
193
  variables?: TemplateVars;
172
194
  provider?: AiProvider;
195
+ /**
196
+ * Perfil de integração a usar, no lugar do setting global por provider.
197
+ *
198
+ * Quando presente, o provider vem do perfil — é o que permite um módulo
199
+ * (o SAC, por exemplo) ter o próprio perfil de IA sem afetar o resto do
200
+ * sistema. Mesmo padrão que `sendTemplatedMail` já usa para e-mail.
201
+ */
202
+ profileSlug?: string;
173
203
  params?: AiChatModelParams;
174
204
  contextSlug?: string;
175
205
  }): Promise<{
@@ -194,8 +224,14 @@ export class AiService implements OnModuleInit {
194
224
  // The system prompt represents the call, so its params win over the user one.
195
225
  const stored = mergeModelParams(systemPrompt, userPrompt);
196
226
 
197
- const provider = input.provider || (await this.getDefaultProvider());
198
- const aiProfile = await this.getAiProfile(provider);
227
+ // Um perfil explícito manda no provider: pedir "openai" e apontar um perfil
228
+ // Claude bateria na API errada com a chave errada.
229
+ const target = await this.resolveProviderAndProfile(
230
+ input.provider,
231
+ input.profileSlug,
232
+ );
233
+ const provider = target.provider;
234
+ const aiProfile = target.profile;
199
235
  const model =
200
236
  stored.model || aiProfile.model || this.getDefaultModel(provider);
201
237
  const timeoutMs = aiProfile.timeoutMs ?? DEFAULT_AI_CALL_TIMEOUT_MS;
@@ -582,6 +618,10 @@ export class AiService implements OnModuleInit {
582
618
  return DEEPSEEK_DEFAULT_MODEL;
583
619
  }
584
620
 
621
+ if (provider === 'claude') {
622
+ return 'claude-sonnet-4-5';
623
+ }
624
+
585
625
  return 'gpt-4o-mini';
586
626
  }
587
627
 
@@ -615,6 +655,10 @@ export class AiService implements OnModuleInit {
615
655
  return this.chatWithGemini(input);
616
656
  }
617
657
 
658
+ if (provider === 'claude') {
659
+ return this.chatWithClaude(input);
660
+ }
661
+
618
662
  if (provider === 'deepseek') {
619
663
  return this.chatWithOpenAi({
620
664
  ...input,
@@ -631,16 +675,42 @@ export class AiService implements OnModuleInit {
631
675
  return (await this.getAiProfile(provider)).apiKey;
632
676
  }
633
677
 
678
+ /**
679
+ * Resolve provider e credenciais juntos.
680
+ *
681
+ * Com `profileSlug`, o provider vem do perfil — o chamador escolheu uma conta
682
+ * concreta, e o setting global do provider não tem nada a dizer sobre ela.
683
+ */
684
+ private async resolveProviderAndProfile(
685
+ provider?: AiProvider,
686
+ profileSlug?: string,
687
+ ): Promise<{ provider: AiProvider; profile: AiProfileCredentials }> {
688
+ const slug = String(profileSlug ?? '').trim();
689
+
690
+ if (!slug) {
691
+ const resolvedProvider = provider || (await this.getDefaultProvider());
692
+ return {
693
+ provider: resolvedProvider,
694
+ profile: await this.getAiProfile(resolvedProvider),
695
+ };
696
+ }
697
+
698
+ const profile = await this.loadAiProfileBySlug(slug);
699
+ const providerSlug = profile.providerSlug;
700
+ const mapped = PROVIDER_BY_INTEGRATION_SLUG[providerSlug];
701
+
702
+ if (!mapped) {
703
+ throw new BadRequestException(
704
+ `AI profile "${slug}" uses provider "${providerSlug}", which is not supported for chat. ` +
705
+ `Use one of: ${Object.keys(PROVIDER_BY_INTEGRATION_SLUG).join(', ')}.`,
706
+ );
707
+ }
708
+
709
+ return { provider: mapped, profile: profile.config };
710
+ }
711
+
634
712
  /** Resolves the active AI integration profile's credentials plus its optional model/timeout overrides. */
635
- private async getAiProfile(
636
- provider: AiProvider,
637
- ): Promise<{
638
- apiKey: string;
639
- organization?: string;
640
- model?: string;
641
- timeoutMs?: number;
642
- baseUrl?: string;
643
- }> {
713
+ private async getAiProfile(provider: AiProvider): Promise<AiProfileCredentials> {
644
714
  const slug = AI_PROFILE_SETTING[provider] ?? AI_PROFILE_SETTING.openai;
645
715
  const settings = await this.settingService.getSettingValues([slug]);
646
716
  const profileSlug = String(settings[slug] ?? '').trim();
@@ -651,6 +721,13 @@ export class AiService implements OnModuleInit {
651
721
  );
652
722
  }
653
723
 
724
+ return (await this.loadAiProfileBySlug(profileSlug)).config;
725
+ }
726
+
727
+ private async loadAiProfileBySlug(profileSlug: string): Promise<{
728
+ providerSlug: string;
729
+ config: AiProfileCredentials;
730
+ }> {
654
731
  const profile = await this.prismaService.integration_profile.findUnique({
655
732
  where: { slug: profileSlug },
656
733
  include: { integration_provider: { select: { slug: true } } },
@@ -669,7 +746,7 @@ export class AiService implements OnModuleInit {
669
746
  throw new BadRequestException(`AI profile "${profileSlug}" has no API key configured.`);
670
747
  }
671
748
 
672
- return aiConfig;
749
+ return { providerSlug: profile.integration_provider.slug, config: aiConfig };
673
750
  }
674
751
 
675
752
  /** Rejects with a timeout error if `promise` doesn't settle within `ms`. */
@@ -750,6 +827,66 @@ export class AiService implements OnModuleInit {
750
827
  };
751
828
  }
752
829
 
830
+ /**
831
+ * Anthropic (Claude).
832
+ *
833
+ * A API é diferente da da OpenAI em três pontos que quebram se reusarmos
834
+ * aquele caminho: autentica por `x-api-key` (não Bearer), exige o header de
835
+ * versão, e o system prompt é um campo à parte em vez de uma mensagem com
836
+ * `role: system`. `max_tokens` é obrigatório — sem ele a requisição é
837
+ * recusada, daí o padrão.
838
+ *
839
+ * Anexo não é suportado aqui: a Anthropic recebe imagem como bloco base64, e
840
+ * nenhum chamador do SAC manda anexo para o modelo.
841
+ */
842
+ private async chatWithClaude({
843
+ message,
844
+ model,
845
+ systemPrompt,
846
+ apiKey,
847
+ params,
848
+ }: {
849
+ message: string;
850
+ model: string;
851
+ systemPrompt?: string;
852
+ apiKey?: string;
853
+ params?: AiChatModelParams;
854
+ }): Promise<{ content: string; usage?: { input: number; output: number; total: number } }> {
855
+ const key = apiKey ?? (await this.getAiApiKey('claude'));
856
+
857
+ const body: Record<string, unknown> = {
858
+ model,
859
+ max_tokens: params?.maxTokens ?? 4096,
860
+ messages: [{ role: 'user', content: message }],
861
+ };
862
+ if (systemPrompt) body.system = systemPrompt;
863
+ if (params?.temperature !== undefined && params?.temperature !== null) {
864
+ body.temperature = params.temperature;
865
+ }
866
+
867
+ const response = await axios.post('https://api.anthropic.com/v1/messages', body, {
868
+ headers: {
869
+ 'x-api-key': key,
870
+ 'anthropic-version': '2023-06-01',
871
+ 'Content-Type': 'application/json',
872
+ },
873
+ });
874
+
875
+ const content = response.data?.content?.[0]?.text ?? '';
876
+ const u = response.data?.usage;
877
+
878
+ return {
879
+ content,
880
+ usage: u
881
+ ? {
882
+ input: u.input_tokens ?? 0,
883
+ output: u.output_tokens ?? 0,
884
+ total: (u.input_tokens ?? 0) + (u.output_tokens ?? 0),
885
+ }
886
+ : undefined,
887
+ };
888
+ }
889
+
753
890
  private async createOpenAiAssistant({
754
891
  slug,
755
892
  model,
@@ -0,0 +1,11 @@
1
+ import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
2
+
3
+ export class RichtextEditHtmlDTO {
4
+ @IsOptional()
5
+ @IsString()
6
+ html?: string;
7
+
8
+ @IsString()
9
+ @IsNotEmpty()
10
+ instruction: string;
11
+ }
@@ -0,0 +1,57 @@
1
+ import { registerAiPrompts } from '../ai-instruction/ai-prompt-registry';
2
+
3
+ export const RICHTEXT_AI_PROMPT_SLUGS = {
4
+ editHtmlSystem: 'task.core.richtext-html.edit.system',
5
+ editHtmlUser: 'task.core.richtext-html.edit.user',
6
+ } as const;
7
+
8
+ const EDIT_HTML_SYSTEM_PROMPT = `Você é um editor especialista em HTML de conteúdo administrativo. Sua tarefa é ajustar o HTML atual de um editor de texto rico seguindo a instrução de um administrador autenticado.
9
+
10
+ REGRAS:
11
+ 1. Responda com o HTML completo e final do conteúdo, e NADA MAIS: sem cercas de código (\`\`\`), sem markdown, sem comentários ou explicações antes ou depois.
12
+ 2. Faça APENAS o ajuste pedido na instrução. Preserve todo o resto do HTML (estrutura, textos, estilos, imagens, links) exatamente como estava.
13
+ 3. Use apenas tags e formatação compatíveis com um editor de texto rico comum (parágrafos, títulos, negrito, itálico, sublinhado, listas, links, imagens) — nada de \`<script>\`, \`<style>\` externo ou recursos que o editor não consiga exibir.
14
+ 4. Se o HTML de entrada estiver vazio, gere um HTML simples e razoável que atenda à instrução.`;
15
+
16
+ const EDIT_HTML_USER_PROMPT = `HTML ATUAL DO CONTEÚDO
17
+ <<<HTML
18
+ {{currentHtml}}
19
+ HTML>>>
20
+
21
+ INSTRUÇÃO DO ADMINISTRADOR
22
+ <<<INSTRUCAO
23
+ {{instruction}}
24
+ INSTRUCAO>>>
25
+
26
+ Responda apenas com o HTML completo e atualizado.`;
27
+
28
+ registerAiPrompts([
29
+ {
30
+ slug: RICHTEXT_AI_PROMPT_SLUGS.editHtmlSystem,
31
+ name: 'Ajuste de HTML do editor de texto rico por IA (sistema)',
32
+ moduleSlug: 'core',
33
+ locale: 'pt',
34
+ fallback: EDIT_HTML_SYSTEM_PROMPT,
35
+ variables: {},
36
+ defaults: { temperature: 0.3, maxTokens: 8000 },
37
+ },
38
+ {
39
+ slug: RICHTEXT_AI_PROMPT_SLUGS.editHtmlUser,
40
+ name: 'Ajuste de HTML do editor de texto rico por IA (usuário)',
41
+ moduleSlug: 'core',
42
+ locale: 'pt',
43
+ fallback: EDIT_HTML_USER_PROMPT,
44
+ variables: {
45
+ currentHtml: {
46
+ type: 'string',
47
+ required: true,
48
+ description: 'HTML atual do conteúdo do editor (pode estar vazio).',
49
+ },
50
+ instruction: {
51
+ type: 'string',
52
+ required: true,
53
+ description: 'Instrução em linguagem natural pedida pelo administrador.',
54
+ },
55
+ },
56
+ },
57
+ ]);
@@ -0,0 +1,109 @@
1
+ import {
2
+ BadRequestException,
3
+ forwardRef,
4
+ Inject,
5
+ Injectable,
6
+ } from '@nestjs/common';
7
+ import { SettingService } from '../setting/setting.service';
8
+ import { AiService } from './ai.service';
9
+ import { RICHTEXT_AI_PROMPT_SLUGS } from './richtext-ai.prompts';
10
+
11
+ /** HTML maior que isso é documento inteiro, não um trecho de conteúdo — evita custo/contexto sem sentido. */
12
+ const MAX_HTML_CHARS = 60_000;
13
+
14
+ /** Instrução maior que isso é um briefing, não um ajuste pontual. */
15
+ const MAX_INSTRUCTION_CHARS = 2_000;
16
+
17
+ export type RichtextAiEditResult = {
18
+ html: string;
19
+ provider: string;
20
+ model: string;
21
+ };
22
+
23
+ /**
24
+ * Ajusta o HTML do editor de texto rico compartilhado do admin
25
+ * (`apps/admin/src/components/rich-text-editor.tsx`) a partir de um prompt em
26
+ * linguagem natural.
27
+ *
28
+ * Todo o trabalho pesado (multi-provider, prompts com override no banco,
29
+ * registro de custo em `ai_execution`) é do `AiService` do core — aqui só se
30
+ * lê a setting `richtext-ai-*` e se monta o contexto. Não persiste nada: quem
31
+ * decide salvar o HTML devolvido é o próprio editor, via `onChange`.
32
+ */
33
+ @Injectable()
34
+ export class RichtextAiService {
35
+ constructor(
36
+ @Inject(forwardRef(() => SettingService))
37
+ private readonly settings: SettingService,
38
+ @Inject(forwardRef(() => AiService))
39
+ private readonly ai: AiService,
40
+ ) {}
41
+
42
+ async editHtml(
43
+ input: { html?: string | null; instruction?: string | null },
44
+ ): Promise<RichtextAiEditResult> {
45
+ const config = await this.getAiConfig();
46
+
47
+ if (!config.enabled) {
48
+ throw new BadRequestException(
49
+ 'AI rich text editing is disabled. Enable it in Settings → AI.',
50
+ );
51
+ }
52
+ if (!config.profileSlug) {
53
+ throw new BadRequestException(
54
+ 'No AI profile configured for the rich text editor. Set one in Settings → AI.',
55
+ );
56
+ }
57
+
58
+ const instruction = String(input.instruction ?? '')
59
+ .trim()
60
+ .slice(0, MAX_INSTRUCTION_CHARS);
61
+ if (!instruction) {
62
+ throw new BadRequestException('Instruction is required.');
63
+ }
64
+
65
+ const currentHtml = String(input.html ?? '').slice(0, MAX_HTML_CHARS);
66
+
67
+ const result = await this.ai.chatWithPrompt({
68
+ systemPromptSlug: RICHTEXT_AI_PROMPT_SLUGS.editHtmlSystem,
69
+ userPromptSlug: RICHTEXT_AI_PROMPT_SLUGS.editHtmlUser,
70
+ variables: { currentHtml, instruction },
71
+ profileSlug: config.profileSlug,
72
+ contextSlug: 'richtext.edit-html',
73
+ });
74
+
75
+ const html = this.extractHtml(result.content);
76
+ if (!html) {
77
+ throw new BadRequestException('The model returned an empty HTML.');
78
+ }
79
+
80
+ return { html, provider: result.provider, model: result.model };
81
+ }
82
+
83
+ private async getAiConfig(): Promise<{
84
+ enabled: boolean;
85
+ profileSlug: string | null;
86
+ }> {
87
+ const slugs = ['richtext-ai-enabled', 'richtext-ai-profile-id'];
88
+ const values = await this.settings.getSettingValues(slugs);
89
+
90
+ const enabled = ['true', '1'].includes(
91
+ String(values['richtext-ai-enabled'] ?? '').trim().toLowerCase(),
92
+ );
93
+ const profileSlug =
94
+ String(values['richtext-ai-profile-id'] ?? '').trim() || null;
95
+
96
+ return { enabled, profileSlug };
97
+ }
98
+
99
+ /**
100
+ * O prompt de sistema já pede HTML puro, mas alguns modelos insistem em
101
+ * cercar a resposta com ```html ... ``` mesmo assim. Remove a cerca quando
102
+ * presente para não gravar markdown como se fosse o conteúdo.
103
+ */
104
+ private extractHtml(content: string): string {
105
+ const trimmed = content.trim();
106
+ const fenced = /^```(?:html)?\s*([\s\S]*?)\s*```$/i.exec(trimmed);
107
+ return (fenced ? fenced[1] : trimmed).trim();
108
+ }
109
+ }