@hed-hog/core 0.0.398 → 0.0.400

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (664) hide show
  1. package/dist/ai/ai-agent.sort.d.ts +6 -0
  2. package/dist/ai/ai-agent.sort.d.ts.map +1 -0
  3. package/dist/ai/ai-agent.sort.js +21 -0
  4. package/dist/ai/ai-agent.sort.js.map +1 -0
  5. package/dist/ai/ai.controller.d.ts +4 -4
  6. package/dist/ai/ai.service.d.ts +35 -4
  7. package/dist/ai/ai.service.d.ts.map +1 -1
  8. package/dist/ai/ai.service.js +51 -137
  9. package/dist/ai/ai.service.js.map +1 -1
  10. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  11. package/dist/ai-instruction/ai-instruction.service.js +26 -23
  12. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  13. package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
  14. package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
  15. package/dist/ai-instruction/ai-instruction.sort.js +32 -0
  16. package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
  17. package/dist/auth/auth.controller.d.ts +24 -8
  18. package/dist/auth/auth.controller.d.ts.map +1 -1
  19. package/dist/auth/auth.module.d.ts.map +1 -1
  20. package/dist/auth/auth.module.js +8 -1
  21. package/dist/auth/auth.module.js.map +1 -1
  22. package/dist/auth/auth.service.d.ts +72 -25
  23. package/dist/auth/auth.service.d.ts.map +1 -1
  24. package/dist/auth/auth.service.js +41 -4
  25. package/dist/auth/auth.service.js.map +1 -1
  26. package/dist/auth/guards/auth.guard.d.ts +3 -1
  27. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  28. package/dist/auth/guards/auth.guard.js +12 -3
  29. package/dist/auth/guards/auth.guard.js.map +1 -1
  30. package/dist/authorize/authorize.controller.d.ts +30 -6
  31. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  32. package/dist/authorize/authorize.controller.js +120 -13
  33. package/dist/authorize/authorize.controller.js.map +1 -1
  34. package/dist/authorize/authorize.errors.d.ts +34 -0
  35. package/dist/authorize/authorize.errors.d.ts.map +1 -0
  36. package/dist/authorize/authorize.errors.js +37 -0
  37. package/dist/authorize/authorize.errors.js.map +1 -0
  38. package/dist/authorize/authorize.module.d.ts.map +1 -1
  39. package/dist/authorize/authorize.module.js +6 -1
  40. package/dist/authorize/authorize.module.js.map +1 -1
  41. package/dist/authorize/authorize.service.d.ts +78 -4
  42. package/dist/authorize/authorize.service.d.ts.map +1 -1
  43. package/dist/authorize/authorize.service.js +212 -21
  44. package/dist/authorize/authorize.service.js.map +1 -1
  45. package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
  46. package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
  47. package/dist/authorize/dto/authorize-info.dto.js +16 -1
  48. package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
  49. package/dist/authorize/dto/consent.dto.d.ts +10 -1
  50. package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
  51. package/dist/authorize/dto/consent.dto.js +16 -1
  52. package/dist/authorize/dto/consent.dto.js.map +1 -1
  53. package/dist/authorize/dto/token.dto.d.ts +12 -0
  54. package/dist/authorize/dto/token.dto.d.ts.map +1 -1
  55. package/dist/authorize/dto/token.dto.js +21 -1
  56. package/dist/authorize/dto/token.dto.js.map +1 -1
  57. package/dist/authorize/oauth-exception.filter.d.ts +17 -0
  58. package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
  59. package/dist/authorize/oauth-exception.filter.js +54 -0
  60. package/dist/authorize/oauth-exception.filter.js.map +1 -0
  61. package/dist/authorize/oauth-metadata.service.d.ts +39 -0
  62. package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
  63. package/dist/authorize/oauth-metadata.service.js +81 -0
  64. package/dist/authorize/oauth-metadata.service.js.map +1 -0
  65. package/dist/authorize/well-known.controller.d.ts +67 -0
  66. package/dist/authorize/well-known.controller.d.ts.map +1 -0
  67. package/dist/authorize/well-known.controller.js +126 -0
  68. package/dist/authorize/well-known.controller.js.map +1 -0
  69. package/dist/captcha/captcha.service.d.ts +32 -2
  70. package/dist/captcha/captcha.service.d.ts.map +1 -1
  71. package/dist/captcha/captcha.service.js +85 -12
  72. package/dist/captcha/captcha.service.js.map +1 -1
  73. package/dist/challenge/challenge.service.d.ts +2 -2
  74. package/dist/core.module.d.ts.map +1 -1
  75. package/dist/core.module.js +6 -0
  76. package/dist/core.module.js.map +1 -1
  77. package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
  78. package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
  79. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
  80. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
  81. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
  82. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
  83. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  84. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
  85. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
  86. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
  87. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
  88. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
  89. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
  90. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  91. package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
  92. package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
  93. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
  94. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
  95. package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
  96. package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
  97. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
  98. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
  99. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
  100. package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
  101. package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
  102. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
  103. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
  104. package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
  105. package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
  106. package/dist/file/file-upload-ticket.service.d.ts +160 -0
  107. package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
  108. package/dist/file/file-upload-ticket.service.js +382 -0
  109. package/dist/file/file-upload-ticket.service.js.map +1 -0
  110. package/dist/file/file.controller.d.ts +24 -1
  111. package/dist/file/file.controller.d.ts.map +1 -1
  112. package/dist/file/file.controller.js +63 -2
  113. package/dist/file/file.controller.js.map +1 -1
  114. package/dist/file/file.module.d.ts.map +1 -1
  115. package/dist/file/file.module.js +14 -2
  116. package/dist/file/file.module.js.map +1 -1
  117. package/dist/file/file.service.d.ts +182 -6
  118. package/dist/file/file.service.d.ts.map +1 -1
  119. package/dist/file/file.service.js +385 -36
  120. package/dist/file/file.service.js.map +1 -1
  121. package/dist/file/filename-encoding.util.d.ts +24 -0
  122. package/dist/file/filename-encoding.util.d.ts.map +1 -0
  123. package/dist/file/filename-encoding.util.js +63 -0
  124. package/dist/file/filename-encoding.util.js.map +1 -0
  125. package/dist/file/image-optimization.service.d.ts +27 -0
  126. package/dist/file/image-optimization.service.d.ts.map +1 -1
  127. package/dist/file/image-optimization.service.js +63 -14
  128. package/dist/file/image-optimization.service.js.map +1 -1
  129. package/dist/file/mcp-file-source.schema.d.ts +33 -0
  130. package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
  131. package/dist/file/mcp-file-source.schema.js +75 -0
  132. package/dist/file/mcp-file-source.schema.js.map +1 -0
  133. package/dist/file/mcp-file-source.service.d.ts +124 -0
  134. package/dist/file/mcp-file-source.service.d.ts.map +1 -0
  135. package/dist/file/mcp-file-source.service.js +262 -0
  136. package/dist/file/mcp-file-source.service.js.map +1 -0
  137. package/dist/file/mimetype-sniff.util.d.ts +62 -0
  138. package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
  139. package/dist/file/mimetype-sniff.util.js +226 -0
  140. package/dist/file/mimetype-sniff.util.js.map +1 -0
  141. package/dist/file/remote-fetch.util.d.ts +35 -0
  142. package/dist/file/remote-fetch.util.d.ts.map +1 -0
  143. package/dist/file/remote-fetch.util.js +236 -0
  144. package/dist/file/remote-fetch.util.js.map +1 -0
  145. package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
  146. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
  147. package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
  148. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
  149. package/dist/impersonation/impersonation.service.d.ts +8 -3
  150. package/dist/impersonation/impersonation.service.d.ts.map +1 -1
  151. package/dist/impersonation/impersonation.service.js +14 -4
  152. package/dist/impersonation/impersonation.service.js.map +1 -1
  153. package/dist/incident/incident.controller.d.ts +2 -1
  154. package/dist/incident/incident.controller.d.ts.map +1 -1
  155. package/dist/incident/incident.controller.js +10 -6
  156. package/dist/incident/incident.controller.js.map +1 -1
  157. package/dist/incident/incident.service.d.ts +2 -0
  158. package/dist/incident/incident.service.d.ts.map +1 -1
  159. package/dist/incident/incident.service.js +4 -0
  160. package/dist/incident/incident.service.js.map +1 -1
  161. package/dist/index.d.ts +17 -0
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +17 -0
  164. package/dist/index.js.map +1 -1
  165. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  166. package/dist/integration-profile/integration-profile.secrets.js +7 -0
  167. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  168. package/dist/integration-profile/integration-profile.service.d.ts +0 -13
  169. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  170. package/dist/integration-profile/integration-profile.service.js +5 -40
  171. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  172. package/dist/integration-profile/integration-profile.utils.js +5 -5
  173. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  174. package/dist/integration-profile/testers/index.d.ts.map +1 -1
  175. package/dist/integration-profile/testers/index.js +2 -0
  176. package/dist/integration-profile/testers/index.js.map +1 -1
  177. package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
  178. package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
  179. package/dist/integration-profile/testers/shipping.testers.js +71 -0
  180. package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
  181. package/dist/locale/locale.controller.d.ts +1 -1
  182. package/dist/locale/locale.service.d.ts +1 -1
  183. package/dist/mail/dto/send.dto.d.ts +6 -1
  184. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  185. package/dist/mail/dto/send.dto.js.map +1 -1
  186. package/dist/mail/mail.service.d.ts +9 -0
  187. package/dist/mail/mail.service.d.ts.map +1 -1
  188. package/dist/mail/mail.service.js +34 -14
  189. package/dist/mail/mail.service.js.map +1 -1
  190. package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
  191. package/dist/mail-sent/mail-sent.service.d.ts +1 -1
  192. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  193. package/dist/mail-sent/mail-sent.service.js +2 -1
  194. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  195. package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
  196. package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
  197. package/dist/mail-sent/mail-sent.sort.js +19 -0
  198. package/dist/mail-sent/mail-sent.sort.js.map +1 -0
  199. package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
  200. package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
  201. package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
  202. package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
  203. package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
  204. package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
  205. package/dist/mcp/guards/mcp-auth.guard.js +19 -5
  206. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  207. package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
  208. package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
  209. package/dist/mcp/guards/mcp-permission.guard.js +46 -26
  210. package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
  211. package/dist/mcp/mcp-audit.service.d.ts +17 -0
  212. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  213. package/dist/mcp/mcp-audit.service.js +32 -3
  214. package/dist/mcp/mcp-audit.service.js.map +1 -1
  215. package/dist/mcp/mcp-permission.service.d.ts +64 -0
  216. package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
  217. package/dist/mcp/mcp-permission.service.js +178 -0
  218. package/dist/mcp/mcp-permission.service.js.map +1 -0
  219. package/dist/mcp/mcp-server.service.d.ts +3 -1
  220. package/dist/mcp/mcp-server.service.d.ts.map +1 -1
  221. package/dist/mcp/mcp-server.service.js +36 -15
  222. package/dist/mcp/mcp-server.service.js.map +1 -1
  223. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  224. package/dist/mcp/mcp.controller.js +10 -2
  225. package/dist/mcp/mcp.controller.js.map +1 -1
  226. package/dist/mcp/mcp.module.d.ts.map +1 -1
  227. package/dist/mcp/mcp.module.js +5 -1
  228. package/dist/mcp/mcp.module.js.map +1 -1
  229. package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
  230. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
  231. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
  232. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
  233. package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
  234. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
  235. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
  236. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
  237. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
  238. package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
  239. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
  240. package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
  241. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
  242. package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
  243. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
  244. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
  245. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
  246. package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
  247. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
  248. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
  249. package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
  250. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
  251. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
  252. package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
  253. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
  254. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
  255. package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
  256. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
  257. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
  258. package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
  259. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
  260. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
  261. package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
  262. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
  263. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
  264. package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
  265. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
  266. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  267. package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
  268. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  269. package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
  270. package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
  271. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
  272. package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
  273. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
  274. package/dist/mcp/types/mcp-context.type.d.ts +9 -0
  275. package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
  276. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  277. package/dist/mcp-chat/mcp-chat.service.js +8 -0
  278. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  279. package/dist/meeting/meeting-provider.registry.d.ts +115 -0
  280. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
  281. package/dist/meeting/meeting-provider.registry.js.map +1 -1
  282. package/dist/meeting/meeting.constants.d.ts +70 -0
  283. package/dist/meeting/meeting.constants.d.ts.map +1 -1
  284. package/dist/meeting/meeting.constants.js +9 -1
  285. package/dist/meeting/meeting.constants.js.map +1 -1
  286. package/dist/menu/menu.service.d.ts.map +1 -1
  287. package/dist/menu/menu.service.js +29 -9
  288. package/dist/menu/menu.service.js.map +1 -1
  289. package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
  290. package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
  291. package/dist/message-template/dto/update-message-template.dto.js +38 -0
  292. package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
  293. package/dist/message-template/message-template-admin.service.d.ts +70 -0
  294. package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
  295. package/dist/message-template/message-template-admin.service.js +226 -0
  296. package/dist/message-template/message-template-admin.service.js.map +1 -0
  297. package/dist/message-template/message-template.controller.d.ts +29 -0
  298. package/dist/message-template/message-template.controller.d.ts.map +1 -0
  299. package/dist/message-template/message-template.controller.js +101 -0
  300. package/dist/message-template/message-template.controller.js.map +1 -0
  301. package/dist/message-template/message-template.module.d.ts +10 -0
  302. package/dist/message-template/message-template.module.d.ts.map +1 -0
  303. package/dist/message-template/message-template.module.js +33 -0
  304. package/dist/message-template/message-template.module.js.map +1 -0
  305. package/dist/message-template/message-template.registry.d.ts +55 -0
  306. package/dist/message-template/message-template.registry.d.ts.map +1 -0
  307. package/dist/message-template/message-template.registry.js +64 -0
  308. package/dist/message-template/message-template.registry.js.map +1 -0
  309. package/dist/message-template/message-template.service.d.ts +59 -0
  310. package/dist/message-template/message-template.service.d.ts.map +1 -0
  311. package/dist/message-template/message-template.service.js +166 -0
  312. package/dist/message-template/message-template.service.js.map +1 -0
  313. package/dist/notification/notification.constants.d.ts +11 -0
  314. package/dist/notification/notification.constants.d.ts.map +1 -0
  315. package/dist/notification/notification.constants.js +14 -0
  316. package/dist/notification/notification.constants.js.map +1 -0
  317. package/dist/notification/notification.controller.d.ts +20 -20
  318. package/dist/notification/notification.module.d.ts.map +1 -1
  319. package/dist/notification/notification.module.js +3 -0
  320. package/dist/notification/notification.module.js.map +1 -1
  321. package/dist/notification/notification.service.d.ts +33 -21
  322. package/dist/notification/notification.service.d.ts.map +1 -1
  323. package/dist/notification/notification.service.js +36 -2
  324. package/dist/notification/notification.service.js.map +1 -1
  325. package/dist/oauth/oauth.controller.d.ts +4 -6
  326. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  327. package/dist/oauth/oauth.controller.js +0 -1
  328. package/dist/oauth/oauth.controller.js.map +1 -1
  329. package/dist/oauth/oauth.errors.d.ts +15 -1
  330. package/dist/oauth/oauth.errors.d.ts.map +1 -1
  331. package/dist/oauth/oauth.errors.js +9 -1
  332. package/dist/oauth/oauth.errors.js.map +1 -1
  333. package/dist/oauth/oauth.module.d.ts.map +1 -1
  334. package/dist/oauth/oauth.module.js +6 -1
  335. package/dist/oauth/oauth.module.js.map +1 -1
  336. package/dist/oauth/oauth.service.d.ts +25 -5
  337. package/dist/oauth/oauth.service.d.ts.map +1 -1
  338. package/dist/oauth/oauth.service.js +63 -27
  339. package/dist/oauth/oauth.service.js.map +1 -1
  340. package/dist/oauth/providers/abstract.provider.d.ts +10 -0
  341. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  342. package/dist/oauth/providers/abstract.provider.js +39 -12
  343. package/dist/oauth/providers/abstract.provider.js.map +1 -1
  344. package/dist/oauth/providers/github.provider.d.ts +1 -1
  345. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  346. package/dist/oauth/providers/github.provider.js +48 -22
  347. package/dist/oauth/providers/github.provider.js.map +1 -1
  348. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  349. package/dist/oauth/providers/microsoft-entra-id.provider.js +6 -0
  350. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  351. package/dist/observability/monitored-cron.d.ts +29 -12
  352. package/dist/observability/monitored-cron.d.ts.map +1 -1
  353. package/dist/observability/monitored-cron.js +131 -46
  354. package/dist/observability/monitored-cron.js.map +1 -1
  355. package/dist/pairing/pairing.controller.d.ts +9 -9
  356. package/dist/pairing/pairing.service.d.ts +15 -11
  357. package/dist/pairing/pairing.service.d.ts.map +1 -1
  358. package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
  359. package/dist/profile/dto/change-password.dto.js +4 -6
  360. package/dist/profile/dto/change-password.dto.js.map +1 -1
  361. package/dist/profile/profile.controller.d.ts +1 -1
  362. package/dist/profile/profile.service.d.ts +1 -1
  363. package/dist/profile/profile.service.d.ts.map +1 -1
  364. package/dist/profile/profile.service.js +9 -3
  365. package/dist/profile/profile.service.js.map +1 -1
  366. package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
  367. package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
  368. package/dist/realtime/realtime-tick.coalescer.js +60 -0
  369. package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
  370. package/dist/realtime/realtime.service.d.ts +2 -0
  371. package/dist/realtime/realtime.service.d.ts.map +1 -1
  372. package/dist/realtime/realtime.service.js +22 -2
  373. package/dist/realtime/realtime.service.js.map +1 -1
  374. package/dist/redis/redis-kv.service.d.ts +90 -0
  375. package/dist/redis/redis-kv.service.d.ts.map +1 -0
  376. package/dist/redis/redis-kv.service.js +247 -0
  377. package/dist/redis/redis-kv.service.js.map +1 -0
  378. package/dist/redis/redis-master.d.ts +29 -0
  379. package/dist/redis/redis-master.d.ts.map +1 -0
  380. package/dist/redis/redis-master.js +81 -0
  381. package/dist/redis/redis-master.js.map +1 -0
  382. package/dist/redis/redis-pubsub.d.ts +12 -0
  383. package/dist/redis/redis-pubsub.d.ts.map +1 -1
  384. package/dist/redis/redis-pubsub.js +25 -2
  385. package/dist/redis/redis-pubsub.js.map +1 -1
  386. package/dist/redis/redis.module.d.ts +11 -0
  387. package/dist/redis/redis.module.d.ts.map +1 -0
  388. package/dist/redis/redis.module.js +30 -0
  389. package/dist/redis/redis.module.js.map +1 -0
  390. package/dist/session/session.controller.d.ts +6 -2
  391. package/dist/session/session.controller.d.ts.map +1 -1
  392. package/dist/session/session.service.d.ts +118 -15
  393. package/dist/session/session.service.d.ts.map +1 -1
  394. package/dist/session/session.service.js +251 -77
  395. package/dist/session/session.service.js.map +1 -1
  396. package/dist/setting/setting.controller.d.ts +5 -0
  397. package/dist/setting/setting.controller.d.ts.map +1 -1
  398. package/dist/setting/setting.service.d.ts +7 -0
  399. package/dist/setting/setting.service.d.ts.map +1 -1
  400. package/dist/setting/setting.service.js +38 -3
  401. package/dist/setting/setting.service.js.map +1 -1
  402. package/dist/task/task.service.d.ts +17 -0
  403. package/dist/task/task.service.d.ts.map +1 -1
  404. package/dist/task/task.service.js +48 -13
  405. package/dist/task/task.service.js.map +1 -1
  406. package/dist/token/token.service.d.ts +19 -1
  407. package/dist/token/token.service.d.ts.map +1 -1
  408. package/dist/token/token.service.js +39 -2
  409. package/dist/token/token.service.js.map +1 -1
  410. package/dist/user/user-credential.util.d.ts +10 -0
  411. package/dist/user/user-credential.util.d.ts.map +1 -1
  412. package/dist/user/user-credential.util.js +14 -1
  413. package/dist/user/user-credential.util.js.map +1 -1
  414. package/dist/user/user.service.d.ts +12 -4
  415. package/dist/user/user.service.d.ts.map +1 -1
  416. package/dist/user/user.service.js +2 -11
  417. package/dist/user/user.service.js.map +1 -1
  418. package/dist/user/user.sort.d.ts +16 -0
  419. package/dist/user/user.sort.d.ts.map +1 -0
  420. package/dist/user/user.sort.js +25 -0
  421. package/dist/user/user.sort.js.map +1 -0
  422. package/dist/utils/abbreviate-name.d.ts +13 -0
  423. package/dist/utils/abbreviate-name.d.ts.map +1 -0
  424. package/dist/utils/abbreviate-name.js +42 -0
  425. package/dist/utils/abbreviate-name.js.map +1 -0
  426. package/dist/utils/date-range-query.dto.d.ts +11 -0
  427. package/dist/utils/date-range-query.dto.d.ts.map +1 -0
  428. package/dist/utils/date-range-query.dto.js +63 -0
  429. package/dist/utils/date-range-query.dto.js.map +1 -0
  430. package/dist/utils/date-range.d.ts +96 -0
  431. package/dist/utils/date-range.d.ts.map +1 -0
  432. package/dist/utils/date-range.js +172 -0
  433. package/dist/utils/date-range.js.map +1 -0
  434. package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
  435. package/dist/webhook-integration/webhook-utils.js +24 -0
  436. package/dist/webhook-integration/webhook-utils.js.map +1 -1
  437. package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
  438. package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
  439. package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
  440. package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
  441. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
  442. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
  443. package/dist/whatsapp-sender/whatsapp-sender.service.js +46 -5
  444. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
  445. package/hedhog/data/integration_provider.yaml +9 -0
  446. package/hedhog/data/integration_type.yaml +6 -0
  447. package/hedhog/data/locale.yaml +0 -1
  448. package/hedhog/data/mail.yaml +2 -2
  449. package/hedhog/data/mail_layout.yaml +6 -3
  450. package/hedhog/data/menu.yaml +25 -5
  451. package/hedhog/data/route.yaml +140 -0
  452. package/hedhog/data/setting_group.yaml +137 -3
  453. package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
  454. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
  455. package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
  456. package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
  457. package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
  458. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
  459. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
  460. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
  461. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
  462. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
  463. package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
  464. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
  465. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
  466. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
  467. package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
  468. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
  469. package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
  470. package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
  471. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
  472. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
  473. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
  474. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
  475. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
  476. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
  477. package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
  478. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
  479. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
  480. package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
  481. package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
  482. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
  483. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
  484. package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
  485. package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
  486. package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
  487. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
  488. package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
  489. package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
  490. package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
  491. package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
  492. package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
  493. package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
  494. package/hedhog/frontend/messages/en.json +183 -36
  495. package/hedhog/frontend/messages/pt.json +183 -36
  496. package/hedhog/table/cron_lock.yaml +17 -0
  497. package/hedhog/table/file.yaml +13 -1
  498. package/hedhog/table/file_upload_chunk.yaml +23 -0
  499. package/hedhog/table/file_upload_ticket.yaml +67 -0
  500. package/hedhog/table/mcp_tool_call_log.yaml +30 -22
  501. package/hedhog/table/message_template.yaml +33 -0
  502. package/hedhog/table/setting.yaml +8 -0
  503. package/hedhog/table/user_session.yaml +36 -1
  504. package/package.json +8 -6
  505. package/src/ai/ai-agent.sort.ts +21 -0
  506. package/src/ai/ai.service.spec.ts +173 -1
  507. package/src/ai/ai.service.ts +64 -193
  508. package/src/ai-instruction/ai-instruction.service.ts +32 -28
  509. package/src/ai-instruction/ai-instruction.sort.ts +32 -0
  510. package/src/auth/auth.module.ts +8 -1
  511. package/src/auth/auth.service.spec.ts +108 -1
  512. package/src/auth/auth.service.ts +59 -5
  513. package/src/auth/guards/auth.guard.ts +9 -1
  514. package/src/authorize/authorize.controller.ts +126 -11
  515. package/src/authorize/authorize.errors.ts +55 -0
  516. package/src/authorize/authorize.module.ts +6 -1
  517. package/src/authorize/authorize.service.spec.ts +379 -7
  518. package/src/authorize/authorize.service.ts +309 -23
  519. package/src/authorize/dto/authorize-info.dto.ts +21 -3
  520. package/src/authorize/dto/consent.dto.ts +21 -3
  521. package/src/authorize/dto/token.dto.ts +24 -2
  522. package/src/authorize/oauth-exception.filter.ts +58 -0
  523. package/src/authorize/oauth-metadata.service.ts +67 -0
  524. package/src/authorize/well-known.controller.spec.ts +84 -0
  525. package/src/authorize/well-known.controller.ts +96 -0
  526. package/src/captcha/captcha.service.spec.ts +211 -4
  527. package/src/captcha/captcha.service.ts +127 -10
  528. package/src/core.module.ts +6 -0
  529. package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
  530. package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
  531. package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
  532. package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
  533. package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
  534. package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
  535. package/src/file/file-upload-ticket.service.spec.ts +422 -0
  536. package/src/file/file-upload-ticket.service.ts +513 -0
  537. package/src/file/file.controller.ts +65 -0
  538. package/src/file/file.module.ts +14 -2
  539. package/src/file/file.service.spec.ts +94 -0
  540. package/src/file/file.service.ts +432 -30
  541. package/src/file/filename-encoding.util.spec.ts +54 -0
  542. package/src/file/filename-encoding.util.ts +67 -0
  543. package/src/file/image-optimization.service.spec.ts +156 -19
  544. package/src/file/image-optimization.service.ts +106 -21
  545. package/src/file/mcp-file-source.schema.ts +83 -0
  546. package/src/file/mcp-file-source.service.spec.ts +473 -0
  547. package/src/file/mcp-file-source.service.ts +445 -0
  548. package/src/file/mimetype-sniff.util.spec.ts +126 -0
  549. package/src/file/mimetype-sniff.util.ts +265 -0
  550. package/src/file/remote-fetch.util.spec.ts +117 -0
  551. package/src/file/remote-fetch.util.ts +330 -0
  552. package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
  553. package/src/impersonation/impersonation.service.spec.ts +24 -1
  554. package/src/impersonation/impersonation.service.ts +14 -4
  555. package/src/incident/incident.controller.ts +9 -1
  556. package/src/incident/incident.service.ts +7 -0
  557. package/src/index.ts +17 -0
  558. package/src/integration-profile/integration-profile.secrets.ts +7 -0
  559. package/src/integration-profile/integration-profile.service.ts +7 -64
  560. package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
  561. package/src/integration-profile/integration-profile.utils.ts +5 -5
  562. package/src/integration-profile/testers/index.ts +2 -0
  563. package/src/integration-profile/testers/remaining.testers.spec.ts +79 -0
  564. package/src/integration-profile/testers/shipping.testers.ts +94 -0
  565. package/src/mail/dto/send.dto.ts +6 -1
  566. package/src/mail/mail-template.spec.ts +58 -1
  567. package/src/mail/mail.service.spec.ts +112 -0
  568. package/src/mail/mail.service.ts +42 -4
  569. package/src/mail-sent/mail-sent.service.ts +3 -0
  570. package/src/mail-sent/mail-sent.sort.ts +19 -0
  571. package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
  572. package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
  573. package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
  574. package/src/mcp/guards/mcp-auth.guard.ts +24 -1
  575. package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
  576. package/src/mcp/guards/mcp-permission.guard.ts +76 -51
  577. package/src/mcp/mcp-audit.service.spec.ts +107 -0
  578. package/src/mcp/mcp-audit.service.ts +37 -1
  579. package/src/mcp/mcp-permission.service.spec.ts +197 -0
  580. package/src/mcp/mcp-permission.service.ts +184 -0
  581. package/src/mcp/mcp-server.service.spec.ts +84 -2
  582. package/src/mcp/mcp-server.service.ts +111 -87
  583. package/src/mcp/mcp.controller.ts +8 -1
  584. package/src/mcp/mcp.module.ts +5 -1
  585. package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
  586. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
  587. package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
  588. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
  589. package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
  590. package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
  591. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
  592. package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
  593. package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
  594. package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
  595. package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
  596. package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
  597. package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
  598. package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
  599. package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
  600. package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
  601. package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
  602. package/src/mcp/types/mcp-context.type.ts +15 -6
  603. package/src/mcp-chat/mcp-chat.service.ts +8 -0
  604. package/src/meeting/meeting-provider.registry.ts +121 -0
  605. package/src/meeting/meeting.constants.ts +74 -0
  606. package/src/menu/menu.service.spec.ts +89 -0
  607. package/src/menu/menu.service.ts +29 -11
  608. package/src/message-template/dto/update-message-template.dto.ts +22 -0
  609. package/src/message-template/message-template-admin.service.spec.ts +163 -0
  610. package/src/message-template/message-template-admin.service.ts +239 -0
  611. package/src/message-template/message-template.controller.ts +52 -0
  612. package/src/message-template/message-template.module.ts +21 -0
  613. package/src/message-template/message-template.registry.spec.ts +62 -0
  614. package/src/message-template/message-template.registry.ts +125 -0
  615. package/src/message-template/message-template.service.spec.ts +149 -0
  616. package/src/message-template/message-template.service.ts +149 -0
  617. package/src/notification/notification.constants.ts +10 -0
  618. package/src/notification/notification.module.ts +3 -0
  619. package/src/notification/notification.service.spec.ts +75 -1
  620. package/src/notification/notification.service.ts +39 -1
  621. package/src/oauth/oauth.controller.ts +0 -1
  622. package/src/oauth/oauth.errors.ts +7 -0
  623. package/src/oauth/oauth.module.ts +6 -1
  624. package/src/oauth/oauth.service.spec.ts +58 -7
  625. package/src/oauth/oauth.service.ts +71 -32
  626. package/src/oauth/providers/abstract.provider.spec.ts +192 -0
  627. package/src/oauth/providers/abstract.provider.ts +46 -14
  628. package/src/oauth/providers/github.provider.ts +57 -34
  629. package/src/oauth/providers/microsoft-entra-id.provider.ts +8 -1
  630. package/src/observability/monitored-cron.spec.ts +164 -31
  631. package/src/observability/monitored-cron.ts +154 -62
  632. package/src/profile/dto/change-password.dto.ts +25 -7
  633. package/src/profile/profile.service.spec.ts +97 -0
  634. package/src/profile/profile.service.ts +9 -3
  635. package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
  636. package/src/realtime/realtime-tick.coalescer.ts +60 -0
  637. package/src/realtime/realtime.service.spec.ts +33 -0
  638. package/src/realtime/realtime.service.ts +28 -2
  639. package/src/redis/redis-kv.service.ts +261 -0
  640. package/src/redis/redis-master.spec.ts +111 -0
  641. package/src/redis/redis-master.ts +86 -0
  642. package/src/redis/redis-pubsub.ts +27 -3
  643. package/src/redis/redis.module.ts +17 -0
  644. package/src/session/session.service.spec.ts +121 -42
  645. package/src/session/session.service.ts +279 -82
  646. package/src/setting/setting.service.ts +44 -3
  647. package/src/task/task.service.spec.ts +41 -0
  648. package/src/task/task.service.ts +53 -13
  649. package/src/token/token.service.spec.ts +90 -0
  650. package/src/token/token.service.ts +48 -2
  651. package/src/user/user-credential.util.ts +14 -5
  652. package/src/user/user.service.ts +2 -12
  653. package/src/user/user.sort.ts +25 -0
  654. package/src/utils/abbreviate-name.spec.ts +50 -0
  655. package/src/utils/abbreviate-name.ts +44 -0
  656. package/src/utils/date-range-query.dto.ts +56 -0
  657. package/src/utils/date-range.spec.ts +175 -0
  658. package/src/utils/date-range.ts +246 -0
  659. package/src/webhook-integration/webhook-utils.spec.ts +67 -0
  660. package/src/webhook-integration/webhook-utils.ts +26 -0
  661. package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
  662. package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
  663. package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +112 -0
  664. package/src/whatsapp-sender/whatsapp-sender.service.ts +58 -5
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  GetBucketNotificationConfigurationCommand,
3
+ GetObjectCommand,
3
4
  HeadBucketCommand,
4
5
  HeadObjectCommand,
5
6
  PutBucketNotificationConfigurationCommand,
6
7
  S3Client,
7
8
  } from '@aws-sdk/client-s3';
9
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
8
10
  import {
9
11
  AddPermissionCommand,
10
12
  CreateFunctionCommand,
@@ -23,6 +25,7 @@ import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
23
25
  import { PrismaService } from '@hed-hog/api-prisma';
24
26
  import {
25
27
  BadRequestException,
28
+ ForbiddenException,
26
29
  forwardRef,
27
30
  Inject,
28
31
  Injectable,
@@ -31,6 +34,7 @@ import {
31
34
  OnModuleInit
32
35
  } from '@nestjs/common';
33
36
  import { JwtService } from '@nestjs/jwt';
37
+ import { createHash } from 'crypto';
34
38
  import type { Request } from 'express';
35
39
  import { createWriteStream, promises as fs } from 'fs';
36
40
  import { tmpdir } from 'os';
@@ -44,6 +48,8 @@ import {
44
48
  } from '../integration-profile/integration-profile.utils';
45
49
  import { SettingService } from '../setting/setting.service';
46
50
  import { DeleteDTO } from './dto/delete.dto';
51
+ import { fixMojibake } from './filename-encoding.util';
52
+ import { MIMETYPE_SNIFF_BYTES } from './mimetype-sniff.util';
47
53
  import {
48
54
  ImageOptimizationService,
49
55
  ImageVariantFormat,
@@ -96,21 +102,13 @@ export class FileService implements OnModuleInit {
96
102
  return `${file.location}/${file.path}`;
97
103
  }
98
104
 
99
- private hasMojibake(value: string): boolean {
100
- return /Ã.|Â.|â[\u0080-\u00BF]/.test(value);
101
- }
102
-
105
+ /**
106
+ * Nome que chegou com o encoding trocado (busboy entrega `originalname` em
107
+ * latin1, e a base ainda recebe nome importado com o mesmo defeito). A lógica
108
+ * mora no util porque a migration de backfill a reproduz em plpgsql.
109
+ */
103
110
  private normalizeUploadedFilename(filename: string): string {
104
- if (!filename) {
105
- return filename;
106
- }
107
-
108
- if (!this.hasMojibake(filename)) {
109
- return filename;
110
- }
111
-
112
- const decoded = Buffer.from(filename, 'latin1').toString('utf8');
113
- return this.hasMojibake(decoded) ? filename : decoded;
111
+ return filename ? fixMojibake(filename) : filename;
114
112
  }
115
113
 
116
114
  constructor(
@@ -548,6 +546,9 @@ export class FileService implements OnModuleInit {
548
546
  skipMimeValidation?: boolean;
549
547
  skipSizeValidation?: boolean;
550
548
  isPrivate?: boolean;
549
+ // Quem enviou. É o que permite recusar um `fileId` de outro usuário nas tools MCP
550
+ // (ver `assertFileUsable`); fica nulo nos caminhos internos sem usuário.
551
+ createdByUserId?: number | null;
551
552
  },
552
553
  ) {
553
554
 
@@ -610,6 +611,7 @@ export class FileService implements OnModuleInit {
610
611
  mimetype_id: await this.getMimeType(optimized.mimetype),
611
612
  size: optimized.buffer.length,
612
613
  is_private: options?.isPrivate ?? false,
614
+ created_by_user_id: options?.createdByUserId ?? null,
613
615
  },
614
616
  });
615
617
 
@@ -629,6 +631,8 @@ export class FileService implements OnModuleInit {
629
631
  skipSizeValidation?: boolean;
630
632
  // Tira o arquivo das rotas que servem por id sem autenticação.
631
633
  isPrivate?: boolean;
634
+ // Quem enviou. Ver a nota do mesmo campo em `upload`.
635
+ createdByUserId?: number | null;
632
636
  // Mesmo par maxWidth/maxHeight que `upload` aceita — só tem efeito em image/*.
633
637
  resize?: { maxWidth?: number; maxHeight?: number };
634
638
  },
@@ -652,9 +656,18 @@ export class FileService implements OnModuleInit {
652
656
  );
653
657
  // Recupera o tipo real quando o caller não informou o mimetype (default antigo era
654
658
  // application/octet-stream, que fazia imagens caírem fora da otimização e do serve).
659
+ // Diferente de `upload`, aqui só existe um caminho no disco — e ler o arquivo inteiro
660
+ // é justamente o que o branch de streaming abaixo evita para SCORM/vídeo de vários GB.
661
+ // Por isso o sniffing recebe só o cabeçalho, e apenas quando o caller não declarou um
662
+ // tipo concreto: sem isso, uma imagem enviada como application/octet-stream sem
663
+ // extensão no nome escapava da otimização e era gravada byte a byte.
664
+ const declaredMimetype = metadata?.mimetype;
665
+ const isGenericMimetype =
666
+ !declaredMimetype || declaredMimetype === 'application/octet-stream';
655
667
  const mimetype = await this.resolveEffectiveMimetype(
656
- metadata?.mimetype,
668
+ declaredMimetype,
657
669
  originalname,
670
+ isGenericMimetype ? await this.readFileHeader(filePath) : undefined,
658
671
  );
659
672
 
660
673
  if (!metadata?.skipMimeValidation && !(await this.acceptMimetypes(mimetype))) {
@@ -687,6 +700,7 @@ export class FileService implements OnModuleInit {
687
700
  mimetype_id: await this.getMimeType(mimetype),
688
701
  size,
689
702
  is_private: metadata?.isPrivate ?? false,
703
+ created_by_user_id: metadata?.createdByUserId ?? null,
690
704
  },
691
705
  });
692
706
  }
@@ -724,20 +738,102 @@ export class FileService implements OnModuleInit {
724
738
  mimetype_id: await this.getMimeType(optimized.mimetype),
725
739
  size: optimized.buffer.length,
726
740
  is_private: metadata?.isPrivate ?? false,
741
+ created_by_user_id: metadata?.createdByUserId ?? null,
727
742
  },
728
743
  });
729
744
 
730
745
  return file;
731
746
  }
732
747
 
748
+ /**
749
+ * Mimetypes de imagem que `optimizeBuffer` devolve intactos por decisão de projeto
750
+ * (vetor não vira bitmap, GIF animado não vira quadro único). Enfileirá-los para
751
+ * reotimização seria custo de download e reprocessamento com resultado garantidamente
752
+ * nulo, então ficam fora da varredura.
753
+ */
754
+ private static readonly NON_OPTIMIZABLE_IMAGE_MIMETYPES = [
755
+ 'image/svg+xml',
756
+ 'image/gif',
757
+ ];
758
+
759
+ /**
760
+ * Lista as imagens armazenadas que valem uma passada por `reoptimizeFile`, para a
761
+ * operação em massa de reprocessamento do acervo.
762
+ *
763
+ * Devolve junto o total e a soma de bytes do filtro inteiro (não só da página), que é
764
+ * o que permite mostrar uma prévia antes de enfileirar dezenas de milhares de jobs.
765
+ * O core não conhece a fila — quem enfileira é a library `queue`, que depende deste
766
+ * pacote e não o contrário.
767
+ */
768
+ async findOptimizableImages(params?: {
769
+ /** Piso de tamanho em bytes; abaixo disso o ganho não paga o reprocessamento. */
770
+ minSize?: number;
771
+ /** Restringe a `location` exatas (ex.: `lms/xp/skills`), para rollout em ondas. */
772
+ locations?: string[];
773
+ offset?: number;
774
+ limit?: number;
775
+ }): Promise<{ ids: number[]; total: number; totalBytes: number }> {
776
+ const minSize = Math.max(0, Math.trunc(Number(params?.minSize) || 0));
777
+ const locations = (params?.locations ?? [])
778
+ .map((location) => String(location).trim())
779
+ .filter(Boolean);
780
+
781
+ const where: any = {
782
+ size: { gte: minSize },
783
+ file_mimetype: {
784
+ name: {
785
+ startsWith: 'image/',
786
+ notIn: FileService.NON_OPTIMIZABLE_IMAGE_MIMETYPES,
787
+ },
788
+ },
789
+ };
790
+
791
+ if (locations.length) {
792
+ where.location = { in: locations };
793
+ }
794
+
795
+ const [aggregate, rows] = await Promise.all([
796
+ this.prismaService.file.aggregate({
797
+ where,
798
+ _count: { _all: true },
799
+ _sum: { size: true },
800
+ }),
801
+ this.prismaService.file.findMany({
802
+ where,
803
+ select: { id: true },
804
+ // Ordem estável para que o enfileiramento paginado não pule nem repita linhas.
805
+ orderBy: { id: 'asc' },
806
+ skip: Math.max(0, Math.trunc(Number(params?.offset) || 0)),
807
+ take: Math.min(5000, Math.max(1, Math.trunc(Number(params?.limit) || 1000))),
808
+ }),
809
+ ]);
810
+
811
+ return {
812
+ ids: rows.map((row) => row.id),
813
+ total: aggregate._count._all ?? 0,
814
+ totalBytes: Number(aggregate._sum.size ?? 0),
815
+ };
816
+ }
817
+
818
+ /**
819
+ * Ganho mínimo para valer a regravação de um arquivo que já está no formato final.
820
+ *
821
+ * Recomprimir WebP sobre WebP sempre devolve alguns bytes a menos, então um limiar de
822
+ * "estritamente menor" nunca converge: cada passada da varredura trocaria o arquivo
823
+ * por um ganho irrisório e mais uma geração de perda de qualidade. Medido aqui, três
824
+ * passadas seguidas rendiam 10926 → 10664 → 10662 bytes — a última custou uma
825
+ * recompressão inteira para economizar 2 bytes.
826
+ */
827
+ private static readonly REOPTIMIZE_MIN_GAIN_RATIO = 0.1;
828
+
733
829
  /**
734
830
  * Re-applies the current image-optimization settings to an already-stored file,
735
831
  * rewriting it in place: the `file` record keeps the same id, so every row that
736
832
  * references it (course_image, course_lesson_video_frame, ...) stays valid without
737
- * any FK juggling. Only replaces the stored object when the optimized result is
738
- * strictly smaller or the mimetype changed (e.g. PNG → WebP) — this keeps the
739
- * operation idempotent and avoids repeated lossy recompression. Non-image files are
740
- * left untouched.
833
+ * any FK juggling. Only replaces the stored object when the mimetype changed
834
+ * (e.g. PNG → WebP) or the result is smaller by a margin that justifies the rewrite —
835
+ * that margin is what actually makes the operation idempotent and avoids repeated
836
+ * lossy recompression. Non-image files are left untouched.
741
837
  */
742
838
  async reoptimizeFile(fileId: number): Promise<{
743
839
  changed: boolean;
@@ -760,9 +856,15 @@ export class FileService implements OnModuleInit {
760
856
  );
761
857
 
762
858
  const mimetypeChanged = optimized.mimetype !== currentMimetype;
763
- const smaller = optimized.buffer.length < oldSize;
764
-
765
- if (!mimetypeChanged && !smaller) {
859
+ // Mudança de formato é o ganho principal (PNG → WebP) e vale por si; sem ela, só
860
+ // regrava quando a economia passa do limiar. `optimizeBuffer` já garante que o
861
+ // resultado nunca é maior que a entrada neste caminho (sem crop de alvo exato).
862
+ const worthRewriting =
863
+ oldSize > 0 &&
864
+ (oldSize - optimized.buffer.length) / oldSize >=
865
+ FileService.REOPTIMIZE_MIN_GAIN_RATIO;
866
+
867
+ if (!mimetypeChanged && !worthRewriting) {
766
868
  return {
767
869
  changed: false,
768
870
  oldSize,
@@ -850,7 +952,130 @@ export class FileService implements OnModuleInit {
850
952
  return files.map((file) => file.id);
851
953
  }
852
954
 
853
- async getFiles(paginationParams: PaginationDTO) {
955
+ /**
956
+ * `ownerUserId` restringe a listagem aos arquivos enviados por aquele usuário.
957
+ *
958
+ * Existe para as tools MCP: sem ele, `core.files.list` é um índice de tudo o que
959
+ * qualquer empresa já subiu, incluindo o que está gravado como `is_private`. O
960
+ * controller REST continua chamando sem o parâmetro, porque ali a rota já é `admin`
961
+ * e a tela de arquivos é justamente a visão global.
962
+ */
963
+ /**
964
+ * Grava um stream em disco medindo, hasheando e guardando o cabeçalho de uma vez só.
965
+ *
966
+ * O upload assinado recebe o binário cru no corpo do PUT, e nada garante que venha com
967
+ * `Content-Length` — sem o contador abaixo, um corpo aberto encheria o disco do pod. O
968
+ * corte é feito em pleno voo, antes de o excedente chegar ao arquivo.
969
+ *
970
+ * Passa o cabeçalho adiante porque conferir o tipo real é obrigatório nesse caminho, e
971
+ * reler o arquivo depois seria uma segunda passada por nada.
972
+ */
973
+ async writeStreamToPath(
974
+ source: NodeJS.ReadableStream,
975
+ targetPath: string,
976
+ maxBytes: number,
977
+ ): Promise<{ bytes: number; sha256: string; header: Buffer }> {
978
+ const hash = createHash('sha256');
979
+ const headerChunks: Buffer[] = [];
980
+ let headerLength = 0;
981
+ let bytes = 0;
982
+
983
+ const target = createWriteStream(targetPath);
984
+
985
+ await pipeline(
986
+ source as any,
987
+ async function* (chunks: AsyncIterable<Buffer>) {
988
+ for await (const chunk of chunks) {
989
+ const buffer = Buffer.from(chunk);
990
+ bytes += buffer.length;
991
+
992
+ if (bytes > maxBytes) {
993
+ throw new BadRequestException(
994
+ `Upload exceeds the declared size of ${maxBytes} bytes`,
995
+ );
996
+ }
997
+
998
+ hash.update(buffer);
999
+
1000
+ if (headerLength < MIMETYPE_SNIFF_BYTES) {
1001
+ const slice = buffer.subarray(0, MIMETYPE_SNIFF_BYTES - headerLength);
1002
+ headerChunks.push(slice);
1003
+ headerLength += slice.length;
1004
+ }
1005
+
1006
+ yield buffer;
1007
+ }
1008
+ },
1009
+ target,
1010
+ );
1011
+
1012
+ return {
1013
+ bytes,
1014
+ sha256: hash.digest('hex'),
1015
+ header: Buffer.concat(headerChunks),
1016
+ };
1017
+ }
1018
+
1019
+ /**
1020
+ * Conteúdo de um arquivo já armazenado, em memória.
1021
+ *
1022
+ * Existe para as tools que consomem os bytes sem persistir nada (importar extrato,
1023
+ * extrair dados de uma nota com IA): elas recebem um `fileId` e precisam do arquivo,
1024
+ * não de um stream. O teto evita que um `fileId` apontando para um SCORM de vários GB
1025
+ * derrube o pod — quem serve arquivo grande usa `openReadStream` e não passa por aqui.
1026
+ */
1027
+ async bufferById(fileId: number, maxBytes: number): Promise<Buffer> {
1028
+ const { stream } = await this.openReadStreamById(fileId);
1029
+ const chunks: Buffer[] = [];
1030
+ let total = 0;
1031
+
1032
+ for await (const chunk of stream as AsyncIterable<Buffer>) {
1033
+ total += chunk.length;
1034
+
1035
+ if (total > maxBytes) {
1036
+ (stream as any).destroy?.();
1037
+ throw new BadRequestException(
1038
+ `File ${fileId} is larger than the ${maxBytes} bytes this operation can load`,
1039
+ );
1040
+ }
1041
+
1042
+ chunks.push(Buffer.from(chunk));
1043
+ }
1044
+
1045
+ return Buffer.concat(chunks);
1046
+ }
1047
+
1048
+ /**
1049
+ * Nome do mimetype de um arquivo.
1050
+ *
1051
+ * `get()` e `upload()` devolvem a linha crua, sem a relação, e cada chamador que
1052
+ * precisava do tipo vinha refazendo o `include` por conta. Como o cache de
1053
+ * `getMimeType` é id-por-nome, este é o inverso dele e sai do mesmo mapa quando já
1054
+ * tiver sido consultado.
1055
+ */
1056
+ async mimetypeNameById(mimetypeId: number): Promise<string> {
1057
+ const cached = Object.entries(this.mimetypes).find(
1058
+ ([, id]) => id === mimetypeId,
1059
+ );
1060
+
1061
+ if (cached) {
1062
+ return cached[0];
1063
+ }
1064
+
1065
+ const row = await this.prismaService.file_mimetype.findUnique({
1066
+ where: { id: mimetypeId },
1067
+ select: { name: true },
1068
+ });
1069
+
1070
+ if (row?.name) {
1071
+ this.mimetypes[row.name] = mimetypeId;
1072
+ return row.name;
1073
+ }
1074
+
1075
+ return 'application/octet-stream';
1076
+ }
1077
+
1078
+ async getFiles(paginationParams: PaginationDTO, ownerUserId?: number) {
854
1079
  const fields = ['filename', 'path'];
855
1080
  const OR = this.prismaService.createInsensitiveSearch(
856
1081
  fields,
@@ -863,11 +1088,84 @@ export class FileService implements OnModuleInit {
863
1088
  {
864
1089
  where: {
865
1090
  OR,
1091
+ ...(ownerUserId ? { created_by_user_id: ownerUserId } : {}),
866
1092
  },
867
1093
  },
868
1094
  );
869
1095
  }
870
1096
 
1097
+ /**
1098
+ * Papéis que enxergam qualquer arquivo. Os mesmos que já governam as rotas de arquivo
1099
+ * em `route.yaml` — quem administra o acervo pela tela também o administra pelo MCP.
1100
+ */
1101
+ private static readonly FILE_ADMIN_ROLES = ['admin', 'admin-access'];
1102
+
1103
+ /** Cache curto de papéis, no mesmo espírito do McpPermissionService. */
1104
+ private readonly fileAdminCache = new Map<number, { value: boolean; at: number }>();
1105
+ private static readonly FILE_ADMIN_CACHE_TTL_MS = 30_000;
1106
+
1107
+ async isFileAdmin(userId: number): Promise<boolean> {
1108
+ const cached = this.fileAdminCache.get(userId);
1109
+
1110
+ if (cached && Date.now() - cached.at < FileService.FILE_ADMIN_CACHE_TTL_MS) {
1111
+ return cached.value;
1112
+ }
1113
+
1114
+ const found = await this.prismaService.role_user.findFirst({
1115
+ where: {
1116
+ user_id: userId,
1117
+ role: { slug: { in: FileService.FILE_ADMIN_ROLES } },
1118
+ },
1119
+ select: { role_id: true },
1120
+ });
1121
+
1122
+ const value = Boolean(found);
1123
+ this.fileAdminCache.set(userId, { value, at: Date.now() });
1124
+
1125
+ return value;
1126
+ }
1127
+
1128
+ /**
1129
+ * Porteiro do `fileId` vindo de fora: só o dono usa um arquivo, e quem administra o
1130
+ * acervo usa qualquer um.
1131
+ *
1132
+ * Sem isto, apontar um `fileId` numa tool seria enumerar — id de arquivo é sequencial,
1133
+ * exatamente o motivo pelo qual `is_private` existe. Arquivo anterior a esta coluna
1134
+ * (ou gravado por cron/seed/webhook) fica com dono nulo e só o admin alcança: não dá
1135
+ * para provar de quem ele é, e adivinhar a favor do chamador seria abrir a porta que
1136
+ * este método fecha.
1137
+ */
1138
+ async assertFileUsable(fileId: number, userId: number) {
1139
+ if (!Number.isInteger(fileId) || fileId <= 0) {
1140
+ throw new BadRequestException(`Invalid file id: ${fileId}`);
1141
+ }
1142
+
1143
+ const file = await this.prismaService.file.findUnique({
1144
+ where: { id: fileId },
1145
+ include: { file_mimetype: true },
1146
+ });
1147
+
1148
+ // Mesma resposta para inexistente e para alheio seria o ideal contra enumeração, mas
1149
+ // `get()` já responde 404 com o id, e divergir aqui só confundiria quem integra.
1150
+ if (!file) {
1151
+ throw new NotFoundException(`File not found: ${fileId}`);
1152
+ }
1153
+
1154
+ if (file.created_by_user_id === userId) {
1155
+ return file;
1156
+ }
1157
+
1158
+ if (await this.isFileAdmin(userId)) {
1159
+ return file;
1160
+ }
1161
+
1162
+ throw new ForbiddenException(
1163
+ file.created_by_user_id === null
1164
+ ? `File ${fileId} has no recorded owner and can only be used by an administrator`
1165
+ : `File ${fileId} belongs to another user`,
1166
+ );
1167
+ }
1168
+
871
1169
  async get(fileId: number) {
872
1170
  const file = await this.prismaService.file.findUnique({
873
1171
  where: { id: fileId },
@@ -1028,19 +1326,45 @@ export class FileService implements OnModuleInit {
1028
1326
  return undefined;
1029
1327
  }
1030
1328
 
1329
+ /**
1330
+ * Resolve o `file` cujo `resolveProviderPath` (location + path, ou path já combinado) bate com
1331
+ * o `filepath` embutido no token de download — é o inverso de `resolveProviderPath`.
1332
+ *
1333
+ * Bug corrigido (relatado como "Token de download inválido ou expirado" no storage local):
1334
+ * `tempURLById` assina o token com `resolveProviderPath(file)` (ex.:
1335
+ * "class-assignment-tasks/abc123.pdf", já com o prefixo de `location`), mas o upload local/S3
1336
+ * grava só o basename em `file.path` ("abc123.pdf") e o `location` separado em outra coluna.
1337
+ * Um `findFirst({ where: { path: filepath } })` direto nunca batia para esses arquivos — sempre
1338
+ * caía no `catch` abaixo e mostrava a mensagem genérica de token inválido, mesmo com o token
1339
+ * (e o arquivo) perfeitamente válidos. S3 mascarava isso porque `S3Provider.tempURL` devolve uma
1340
+ * URL assinada direto do bucket, sem passar por este método.
1341
+ */
1342
+ private async findFileByResolvedPath(filepath: string) {
1343
+ const bySameValue = await this.prismaService.file.findFirst({
1344
+ where: { path: filepath },
1345
+ include: { file_mimetype: true },
1346
+ });
1347
+ if (bySameValue) return bySameValue;
1348
+
1349
+ const slashIndex = filepath.lastIndexOf('/');
1350
+ if (slashIndex === -1) return null;
1351
+
1352
+ return this.prismaService.file.findFirst({
1353
+ where: {
1354
+ location: filepath.slice(0, slashIndex),
1355
+ path: filepath.slice(slashIndex + 1),
1356
+ },
1357
+ include: { file_mimetype: true },
1358
+ });
1359
+ }
1360
+
1031
1361
  async download(locale:string, token: string) {
1032
1362
  try {
1033
1363
  const { filepath } = this.jwtService.verify(token, {
1034
1364
  secret: process.env.JWT_SECRET,
1035
1365
  });
1036
1366
 
1037
- // Buscar o arquivo no banco para pegar nome original e mimetype
1038
- const file = await this.prismaService.file.findFirst({
1039
- where: { path: filepath },
1040
- include: {
1041
- file_mimetype: true,
1042
- },
1043
- });
1367
+ const file = await this.findFileByResolvedPath(filepath);
1044
1368
 
1045
1369
  if (!file) {
1046
1370
  throw new NotFoundException(getLocaleText('file.download.not_found', locale, `Not found: ${filepath}`));
@@ -1186,6 +1510,30 @@ export class FileService implements OnModuleInit {
1186
1510
  return map[ext] ?? null;
1187
1511
  }
1188
1512
 
1513
+ /**
1514
+ * Lê só o começo do arquivo, o bastante para o sharp reconhecer o formato pelo
1515
+ * cabeçalho. Existe para o sniffing de `uploadFromPath`, que não pode chamar
1516
+ * `fs.readFile`: o mesmo caminho recebe pacotes SCORM e vídeos de vários GB, e
1517
+ * carregá-los inteiros estoura o limite de Buffer. Devolve `undefined` em qualquer
1518
+ * falha de leitura — quem chama já trata a ausência do buffer.
1519
+ */
1520
+ private async readFileHeader(
1521
+ filePath: string,
1522
+ bytes = 64 * 1024,
1523
+ ): Promise<Buffer | undefined> {
1524
+ let handle: fs.FileHandle | undefined;
1525
+ try {
1526
+ handle = await fs.open(filePath, 'r');
1527
+ const buffer = Buffer.alloc(bytes);
1528
+ const { bytesRead } = await handle.read(buffer, 0, bytes, 0);
1529
+ return bytesRead > 0 ? buffer.subarray(0, bytesRead) : undefined;
1530
+ } catch {
1531
+ return undefined;
1532
+ } finally {
1533
+ await handle?.close().catch(() => undefined);
1534
+ }
1535
+ }
1536
+
1189
1537
  /**
1190
1538
  * Resolves the MIME type to persist for an upload. When the incoming mimetype is
1191
1539
  * generic (application/octet-stream or empty), recovers the real image type from
@@ -1492,6 +1840,17 @@ export class FileService implements OnModuleInit {
1492
1840
  return url;
1493
1841
  }
1494
1842
 
1843
+ /** Par de `tempURLById`, pela mesma razão: `path` cru resolve para NoSuchKey no S3. */
1844
+ async tempOpenURLById(fileId: number, expires = 3600) {
1845
+ const url = await this.tempURLById(fileId, expires);
1846
+
1847
+ if (typeof url === 'string') {
1848
+ return url.replace('/file/download/', '/file/open/');
1849
+ }
1850
+
1851
+ return url;
1852
+ }
1853
+
1495
1854
  async getTemporaryCredentials(params: {
1496
1855
  accessKeyId: string;
1497
1856
  secretAccessKey: string;
@@ -1574,6 +1933,49 @@ export class FileService implements OnModuleInit {
1574
1933
  };
1575
1934
  }
1576
1935
 
1936
+ /**
1937
+ * URL assinada de um objeto em bucket avulso.
1938
+ *
1939
+ * O `getFileUrl` do provider resolve arquivos do acervo, que vivem no destino
1940
+ * ativo do core. Este metodo serve para os buckets que outras libraries
1941
+ * configuram por conta propria — o Studio grava no perfil de armazenamento
1942
+ * dele, que pode nao ser o mesmo — e por isso recebe as credenciais e o bucket
1943
+ * de fora, como o `headS3Object` ao lado.
1944
+ */
1945
+ async signS3Object(params: {
1946
+ accessKeyId: string;
1947
+ secretAccessKey: string;
1948
+ sessionToken?: string;
1949
+ region: string;
1950
+ bucket: string;
1951
+ key: string;
1952
+ expiresInSeconds?: number;
1953
+ downloadFileName?: string;
1954
+ }): Promise<string> {
1955
+ const s3 = new S3Client({
1956
+ region: params.region,
1957
+ credentials: {
1958
+ accessKeyId: params.accessKeyId,
1959
+ secretAccessKey: params.secretAccessKey,
1960
+ ...(params.sessionToken ? { sessionToken: params.sessionToken } : {}),
1961
+ },
1962
+ });
1963
+
1964
+ const command = new GetObjectCommand({
1965
+ Bucket: params.bucket,
1966
+ Key: params.key,
1967
+ ...(params.downloadFileName
1968
+ ? {
1969
+ ResponseContentDisposition: `attachment; filename="${params.downloadFileName.replace(/"/g, '')}"`,
1970
+ }
1971
+ : {}),
1972
+ });
1973
+
1974
+ return getSignedUrl(s3 as any, command, {
1975
+ expiresIn: params.expiresInSeconds ?? 900,
1976
+ });
1977
+ }
1978
+
1577
1979
  async headS3Bucket(params: {
1578
1980
  accessKeyId: string;
1579
1981
  secretAccessKey: string;
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { fixMojibake, hasMojibake } from './filename-encoding.util';
3
+
4
+ /**
5
+ * Inverso exato do que o util desfaz: escreve o texto em UTF-8 e lê os bytes
6
+ * como Latin-1. É como o nome se quebra de verdade, e aplicá-lo duas vezes
7
+ * reproduz a camada dupla que existe na base (file id 19768 em produção).
8
+ */
9
+ const breakEncoding = (value: string): string =>
10
+ Buffer.from(value, 'utf8').toString('latin1');
11
+
12
+ describe('fixMojibake', () => {
13
+ it('desfaz uma camada', () => {
14
+ expect(fixMojibake('Introdução ao JavaScript.zip')).toBe(
15
+ 'Introdução ao JavaScript.zip',
16
+ );
17
+ });
18
+
19
+ it('desfaz a camada dupla que veio da importação', () => {
20
+ const original = '04_Aula - Introdução ao JavaScript - Básico I.zip';
21
+ const broken = breakEncoding(breakEncoding(original));
22
+
23
+ expect(broken).not.toBe(original);
24
+ expect(fixMojibake(broken)).toBe(original);
25
+ });
26
+
27
+ it('recupera caractere de três bytes, como o travessão', () => {
28
+ const original = 'Curso 1 — Unidade Biomassa Sul.pdf';
29
+
30
+ expect(fixMojibake(breakEncoding(original))).toBe(original);
31
+ });
32
+
33
+ it('não mexe em nome já correto', () => {
34
+ expect(fixMojibake('Introdução ao JavaScript.zip')).toBe(
35
+ 'Introdução ao JavaScript.zip',
36
+ );
37
+ });
38
+
39
+ /**
40
+ * O nome casa o padrão por causa do "ÃO", mas os bytes Latin-1 (C7 C3 4F) não
41
+ * formam UTF-8. Converter aqui destruiria um nome que está certo.
42
+ */
43
+ it('preserva nome em caixa alta que casa o padrão sem ser mojibake', () => {
44
+ expect(hasMojibake('MANUTENÇÃO PREVENTIVA.pdf')).toBe(true);
45
+ expect(fixMojibake('MANUTENÇÃO PREVENTIVA.pdf')).toBe(
46
+ 'MANUTENÇÃO PREVENTIVA.pdf',
47
+ );
48
+ });
49
+
50
+ it('preserva vazio e ASCII puro', () => {
51
+ expect(fixMojibake('')).toBe('');
52
+ expect(fixMojibake('relatorio-2026.pdf')).toBe('relatorio-2026.pdf');
53
+ });
54
+ });