@hed-hog/core 0.0.350 → 0.0.353

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 (284) hide show
  1. package/dist/access-log/access-log.controller.d.ts +16 -0
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -0
  3. package/dist/access-log/access-log.controller.js +44 -0
  4. package/dist/access-log/access-log.controller.js.map +1 -0
  5. package/dist/access-log/access-log.interceptor.d.ts +9 -0
  6. package/dist/access-log/access-log.interceptor.d.ts.map +1 -0
  7. package/dist/access-log/access-log.interceptor.js +46 -0
  8. package/dist/access-log/access-log.interceptor.js.map +1 -0
  9. package/dist/access-log/access-log.module.d.ts +3 -0
  10. package/dist/access-log/access-log.module.d.ts.map +1 -0
  11. package/dist/access-log/access-log.module.js +29 -0
  12. package/dist/access-log/access-log.module.js.map +1 -0
  13. package/dist/access-log/access-log.service.d.ts +47 -0
  14. package/dist/access-log/access-log.service.d.ts.map +1 -0
  15. package/dist/access-log/access-log.service.js +113 -0
  16. package/dist/access-log/access-log.service.js.map +1 -0
  17. package/dist/access-log/dto/query-access-log.dto.d.ts +8 -0
  18. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -0
  19. package/dist/access-log/dto/query-access-log.dto.js +42 -0
  20. package/dist/access-log/dto/query-access-log.dto.js.map +1 -0
  21. package/dist/auth/auth.controller.d.ts +9 -9
  22. package/dist/auth/auth.service.d.ts +19 -19
  23. package/dist/auth/auth.service.d.ts.map +1 -1
  24. package/dist/auth/auth.service.js +0 -4
  25. package/dist/auth/auth.service.js.map +1 -1
  26. package/dist/challenge/challenge.service.d.ts +2 -2
  27. package/dist/challenge/challenge.service.d.ts.map +1 -1
  28. package/dist/challenge/challenge.service.js +0 -1
  29. package/dist/challenge/challenge.service.js.map +1 -1
  30. package/dist/core/system.module.d.ts.map +1 -1
  31. package/dist/core/system.module.js +1 -0
  32. package/dist/core/system.module.js.map +1 -1
  33. package/dist/core.module.d.ts.map +1 -1
  34. package/dist/core.module.js +12 -0
  35. package/dist/core.module.js.map +1 -1
  36. package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
  37. package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
  38. package/dist/dashboard/dashboard.module.d.ts.map +1 -1
  39. package/dist/dashboard/dashboard.module.js +4 -1
  40. package/dist/dashboard/dashboard.module.js.map +1 -1
  41. package/dist/file/file.controller.js +0 -1
  42. package/dist/file/file.controller.js.map +1 -1
  43. package/dist/file/file.service.d.ts +65 -0
  44. package/dist/file/file.service.d.ts.map +1 -1
  45. package/dist/file/file.service.js +151 -3
  46. package/dist/file/file.service.js.map +1 -1
  47. package/dist/file/provider/abstract.provider.d.ts +8 -0
  48. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  49. package/dist/file/provider/abstract.provider.js.map +1 -1
  50. package/dist/file/provider/azure.provider.d.ts +4 -0
  51. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  52. package/dist/file/provider/azure.provider.js +14 -0
  53. package/dist/file/provider/azure.provider.js.map +1 -1
  54. package/dist/file/provider/gcs.provider.d.ts +4 -0
  55. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  56. package/dist/file/provider/gcs.provider.js +14 -0
  57. package/dist/file/provider/gcs.provider.js.map +1 -1
  58. package/dist/file/provider/local.provider.d.ts +4 -0
  59. package/dist/file/provider/local.provider.d.ts.map +1 -1
  60. package/dist/file/provider/local.provider.js +19 -0
  61. package/dist/file/provider/local.provider.js.map +1 -1
  62. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  63. package/dist/file/provider/provider.factory.js +3 -0
  64. package/dist/file/provider/provider.factory.js.map +1 -1
  65. package/dist/file/provider/s3.provider.d.ts +4 -0
  66. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  67. package/dist/file/provider/s3.provider.js +17 -0
  68. package/dist/file/provider/s3.provider.js.map +1 -1
  69. package/dist/mcp/mcp-audit.service.d.ts +3 -1
  70. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  71. package/dist/mcp/mcp-audit.service.js +14 -2
  72. package/dist/mcp/mcp-audit.service.js.map +1 -1
  73. package/dist/mcp/mcp.controller.d.ts +3 -1
  74. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  75. package/dist/mcp/mcp.controller.js +10 -2
  76. package/dist/mcp/mcp.controller.js.map +1 -1
  77. package/dist/mcp/mcp.module.d.ts.map +1 -1
  78. package/dist/mcp/mcp.module.js +72 -0
  79. package/dist/mcp/mcp.module.js.map +1 -1
  80. package/dist/mcp/tools/core-ai.mcp-tools.d.ts +45 -0
  81. package/dist/mcp/tools/core-ai.mcp-tools.d.ts.map +1 -0
  82. package/dist/mcp/tools/core-ai.mcp-tools.js +214 -0
  83. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -0
  84. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts +21 -0
  85. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts.map +1 -0
  86. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +89 -0
  87. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -0
  88. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts +29 -0
  89. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts.map +1 -0
  90. package/dist/mcp/tools/core-dashboard.mcp-tools.js +149 -0
  91. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -0
  92. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts +30 -0
  93. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -0
  94. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +185 -0
  95. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -0
  96. package/dist/mcp/tools/core-files.mcp-tools.d.ts +26 -0
  97. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -0
  98. package/dist/mcp/tools/core-files.mcp-tools.js +140 -0
  99. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -0
  100. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts +26 -0
  101. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts.map +1 -0
  102. package/dist/mcp/tools/core-integration-events.mcp-tools.js +168 -0
  103. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -0
  104. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +32 -0
  105. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -0
  106. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +178 -0
  107. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -0
  108. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts +14 -0
  109. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts.map +1 -0
  110. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +106 -0
  111. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -0
  112. package/dist/mcp/tools/core-mail.mcp-tools.d.ts +36 -0
  113. package/dist/mcp/tools/core-mail.mcp-tools.d.ts.map +1 -0
  114. package/dist/mcp/tools/core-mail.mcp-tools.js +184 -0
  115. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -0
  116. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts +21 -0
  117. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts.map +1 -0
  118. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js +118 -0
  119. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js.map +1 -0
  120. package/dist/mcp/tools/core-menus.mcp-tools.d.ts +40 -0
  121. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -0
  122. package/dist/mcp/tools/core-menus.mcp-tools.js +268 -0
  123. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -0
  124. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts +31 -0
  125. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -0
  126. package/dist/mcp/tools/core-notifications.mcp-tools.js +178 -0
  127. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -0
  128. package/dist/mcp/tools/core-profile.mcp-tools.d.ts +20 -0
  129. package/dist/mcp/tools/core-profile.mcp-tools.d.ts.map +1 -0
  130. package/dist/mcp/tools/core-profile.mcp-tools.js +111 -0
  131. package/dist/mcp/tools/core-profile.mcp-tools.js.map +1 -0
  132. package/dist/mcp/tools/core-roles.mcp-tools.d.ts +70 -0
  133. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -0
  134. package/dist/mcp/tools/core-roles.mcp-tools.js +355 -0
  135. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -0
  136. package/dist/mcp/tools/core-routes.mcp-tools.d.ts +52 -0
  137. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -0
  138. package/dist/mcp/tools/core-routes.mcp-tools.js +254 -0
  139. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -0
  140. package/dist/mcp/tools/core-screens.mcp-tools.d.ts +47 -0
  141. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -0
  142. package/dist/mcp/tools/core-screens.mcp-tools.js +250 -0
  143. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -0
  144. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts +20 -0
  145. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -0
  146. package/dist/mcp/tools/core-sessions.mcp-tools.js +128 -0
  147. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -0
  148. package/dist/mcp/tools/core-settings.mcp-tools.d.ts +25 -0
  149. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  150. package/dist/mcp/tools/core-settings.mcp-tools.js +138 -0
  151. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  152. package/dist/mcp/tools/core-system.mcp-tools.d.ts +8 -0
  153. package/dist/mcp/tools/core-system.mcp-tools.d.ts.map +1 -0
  154. package/dist/mcp/tools/core-system.mcp-tools.js +40 -0
  155. package/dist/mcp/tools/core-system.mcp-tools.js.map +1 -0
  156. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts +46 -0
  157. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -0
  158. package/dist/mcp/tools/core-webhooks.mcp-tools.js +266 -0
  159. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -0
  160. package/dist/mcp-chat/mcp-chat.controller.d.ts +1 -1
  161. package/dist/mcp-chat/mcp-chat.service.d.ts +25 -2
  162. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  163. package/dist/mcp-chat/mcp-chat.service.js +666 -36
  164. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  165. package/dist/notification/notification.controller.d.ts +8 -4
  166. package/dist/notification/notification.controller.d.ts.map +1 -1
  167. package/dist/notification/notification.controller.js +12 -0
  168. package/dist/notification/notification.controller.js.map +1 -1
  169. package/dist/notification/notification.service.d.ts +19 -5
  170. package/dist/notification/notification.service.d.ts.map +1 -1
  171. package/dist/notification/notification.service.js +155 -9
  172. package/dist/notification/notification.service.js.map +1 -1
  173. package/dist/pdf/pdf.service.js +1 -1
  174. package/dist/pdf/pdf.service.js.map +1 -1
  175. package/dist/profile/profile.controller.d.ts +3 -3
  176. package/dist/profile/profile.controller.js +0 -1
  177. package/dist/profile/profile.controller.js.map +1 -1
  178. package/dist/profile/profile.module.d.ts.map +1 -1
  179. package/dist/profile/profile.module.js +1 -0
  180. package/dist/profile/profile.module.js.map +1 -1
  181. package/dist/profile/profile.service.d.ts +3 -3
  182. package/dist/session/session.controller.d.ts +2 -2
  183. package/dist/session/session.service.d.ts +6 -6
  184. package/dist/setting/setting.controller.d.ts +1 -1
  185. package/dist/setting/setting.service.d.ts +1 -1
  186. package/dist/setting/setting.service.d.ts.map +1 -1
  187. package/dist/setting/setting.service.js +1 -4
  188. package/dist/setting/setting.service.js.map +1 -1
  189. package/dist/task/task.service.d.ts +1 -0
  190. package/dist/task/task.service.d.ts.map +1 -1
  191. package/dist/task/task.service.js +25 -0
  192. package/dist/task/task.service.js.map +1 -1
  193. package/dist/token/token.service.d.ts.map +1 -1
  194. package/dist/token/token.service.js +0 -1
  195. package/dist/token/token.service.js.map +1 -1
  196. package/dist/user/user.controller.d.ts +9 -9
  197. package/dist/user/user.service.d.ts +21 -21
  198. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +22 -0
  199. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -0
  200. package/dist/webhook-integration/dto/webhook-action.dto.js +120 -0
  201. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -0
  202. package/dist/webhook-integration/webhook-integration.controller.d.ts +7 -5
  203. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  204. package/dist/webhook-integration/webhook-integration.controller.js +18 -5
  205. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  206. package/dist/webhook-integration/webhook-integration.service.d.ts +14 -5
  207. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  208. package/dist/webhook-integration/webhook-integration.service.js +174 -41
  209. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  210. package/hedhog/data/menu.yaml +15 -0
  211. package/hedhog/data/route.yaml +1304 -0
  212. package/hedhog/data/setting_group.yaml +67 -0
  213. package/hedhog/data/setting_subgroup.yaml +16 -0
  214. package/hedhog/frontend/app/access-log/page.tsx.ejs +772 -0
  215. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +10 -3
  216. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +33 -13
  217. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +12 -5
  218. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +12 -5
  219. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +1817 -356
  220. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +43 -5
  221. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +35 -7
  222. package/hedhog/frontend/app/menu/page.tsx.ejs +9 -3
  223. package/hedhog/frontend/app/roles/page.tsx.ejs +18 -6
  224. package/hedhog/frontend/app/routes/page.tsx.ejs +9 -3
  225. package/hedhog/frontend/app/setting-list/page.tsx.ejs +344 -171
  226. package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +310 -304
  227. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +25 -7
  228. package/hedhog/frontend/messages/en.json +104 -3
  229. package/hedhog/frontend/messages/pt.json +104 -3
  230. package/hedhog/table/access_log.yaml +31 -0
  231. package/package.json +4 -3
  232. package/src/access-log/access-log.controller.ts +29 -0
  233. package/src/access-log/access-log.interceptor.ts +38 -0
  234. package/src/access-log/access-log.module.ts +16 -0
  235. package/src/access-log/access-log.service.ts +129 -0
  236. package/src/access-log/dto/query-access-log.dto.ts +23 -0
  237. package/src/auth/auth.service.ts +0 -7
  238. package/src/challenge/challenge.service.ts +0 -2
  239. package/src/core/system.module.ts +1 -0
  240. package/src/core.module.ts +12 -0
  241. package/src/dashboard/dashboard.module.ts +4 -1
  242. package/src/file/file.controller.ts +14 -14
  243. package/src/file/file.service.ts +198 -4
  244. package/src/file/provider/abstract.provider.ts +10 -0
  245. package/src/file/provider/azure.provider.ts +24 -0
  246. package/src/file/provider/gcs.provider.ts +20 -0
  247. package/src/file/provider/local.provider.ts +30 -2
  248. package/src/file/provider/provider.factory.ts +3 -0
  249. package/src/file/provider/s3.provider.ts +22 -0
  250. package/src/mcp/mcp-audit.service.ts +47 -36
  251. package/src/mcp/mcp.controller.ts +40 -30
  252. package/src/mcp/mcp.module.ts +72 -0
  253. package/src/mcp/tools/core-ai.mcp-tools.ts +156 -0
  254. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +63 -0
  255. package/src/mcp/tools/core-dashboard.mcp-tools.ts +103 -0
  256. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +134 -0
  257. package/src/mcp/tools/core-files.mcp-tools.ts +105 -0
  258. package/src/mcp/tools/core-integration-events.mcp-tools.ts +118 -0
  259. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +128 -0
  260. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +76 -0
  261. package/src/mcp/tools/core-mail.mcp-tools.ts +130 -0
  262. package/src/mcp/tools/core-mcp-chat.mcp-tools.ts +81 -0
  263. package/src/mcp/tools/core-menus.mcp-tools.ts +197 -0
  264. package/src/mcp/tools/core-notifications.mcp-tools.ts +128 -0
  265. package/src/mcp/tools/core-profile.mcp-tools.ts +74 -0
  266. package/src/mcp/tools/core-roles.mcp-tools.ts +288 -0
  267. package/src/mcp/tools/core-routes.mcp-tools.ts +196 -0
  268. package/src/mcp/tools/core-screens.mcp-tools.ts +186 -0
  269. package/src/mcp/tools/core-sessions.mcp-tools.ts +97 -0
  270. package/src/mcp/tools/core-settings.mcp-tools.ts +171 -53
  271. package/src/mcp/tools/core-system.mcp-tools.ts +19 -0
  272. package/src/mcp/tools/core-webhooks.mcp-tools.ts +199 -0
  273. package/src/mcp-chat/mcp-chat.service.ts +887 -38
  274. package/src/notification/notification.controller.ts +20 -8
  275. package/src/notification/notification.service.ts +209 -9
  276. package/src/pdf/pdf.service.ts +1 -1
  277. package/src/profile/profile.controller.ts +1 -1
  278. package/src/profile/profile.module.ts +1 -0
  279. package/src/setting/setting.service.ts +8 -11
  280. package/src/task/task.service.ts +31 -0
  281. package/src/token/token.service.ts +0 -2
  282. package/src/webhook-integration/dto/webhook-action.dto.ts +96 -0
  283. package/src/webhook-integration/webhook-integration.controller.ts +124 -103
  284. package/src/webhook-integration/webhook-integration.service.ts +267 -64
@@ -0,0 +1,23 @@
1
+ import { IsIn, IsNumberString, IsOptional, IsString } from 'class-validator';
2
+
3
+ export class QueryAccessLogDto {
4
+ @IsOptional()
5
+ @IsNumberString()
6
+ userId?: string;
7
+
8
+ @IsOptional()
9
+ @IsString()
10
+ userSearch?: string;
11
+
12
+ @IsOptional()
13
+ @IsString()
14
+ createdAtFrom?: string;
15
+
16
+ @IsOptional()
17
+ @IsString()
18
+ createdAtTo?: string;
19
+
20
+ @IsOptional()
21
+ @IsIn(['http', 'mcp', 'all'])
22
+ type?: 'http' | 'mcp' | 'all';
23
+ }
@@ -82,8 +82,6 @@ export class AuthService {
82
82
 
83
83
  async requiresMfaForLogin(locale: string, email: string, user: any) {
84
84
 
85
- console.log('MFA required, setting up email MFA');
86
-
87
85
  const settings = await this.setting.getSettingValues([
88
86
  'require-mfa',
89
87
  'require-email-verification',
@@ -387,16 +385,11 @@ export class AuthService {
387
385
 
388
386
  if (!user) throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
389
387
 
390
- console.debug({ user });
391
-
392
388
  const credentials = (user as unknown as User).user_credential?.filter((c) => c.type === 'password') || [];
393
389
 
394
- console.debug({ credentials });
395
-
396
390
  const identifier = user.user_identifier?.find((i) => i.type === 'email' && i.value === email);
397
391
 
398
392
  if (!(await this.security.validatePassword(locale, credentials, password))) {
399
- console.debug('Invalid password');
400
393
  throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
401
394
  }
402
395
 
@@ -376,8 +376,6 @@ export class ChallengeService {
376
376
 
377
377
  async sendMfaCodeToMultipleEmails(locale: string, userId: number, emails: string[]) {
378
378
 
379
- console.log('Sending MFA codes to multiple emails:', emails);
380
-
381
379
  if (!emails || emails.length === 0) {
382
380
  return { success: true };
383
381
  }
@@ -13,5 +13,6 @@ import { SystemService } from './system.service';
13
13
  ],
14
14
  controllers: [SystemController],
15
15
  providers: [SystemService],
16
+ exports: [SystemService],
16
17
  })
17
18
  export class SystemModule { }
@@ -1,5 +1,8 @@
1
1
  import { LogInterceptor } from '@hed-hog/api';
2
2
  import { LocaleModule } from '@hed-hog/api-locale';
3
+ import { AccessLogInterceptor } from './access-log/access-log.interceptor';
4
+ import { AccessLogModule } from './access-log/access-log.module';
5
+ import { AccessLogService } from './access-log/access-log.service';
3
6
  import { MailModule as MailSendModule } from '@hed-hog/api-mail';
4
7
  import { PaginationModule } from '@hed-hog/api-pagination';
5
8
  import { PrismaModule, PrismaService } from '@hed-hog/api-prisma';
@@ -75,6 +78,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
75
78
  forwardRef(() => McpChatModule),
76
79
  forwardRef(() => PdfModule),
77
80
  forwardRef(() => NotificationModule),
81
+ forwardRef(() => AccessLogModule),
78
82
  ],
79
83
  exports: [
80
84
  UserModule,
@@ -97,6 +101,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
97
101
  IsPinCodeWithSettingConstraint,
98
102
  PdfModule,
99
103
  NotificationModule,
104
+ AccessLogModule,
100
105
  ],
101
106
  providers: [
102
107
  Reflector,
@@ -110,6 +115,13 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
110
115
  },
111
116
  inject: [Reflector, PrismaService],
112
117
  },
118
+ {
119
+ provide: APP_INTERCEPTOR,
120
+ useFactory: (accessLogService: AccessLogService) => {
121
+ return new AccessLogInterceptor(accessLogService);
122
+ },
123
+ inject: [AccessLogService],
124
+ },
113
125
  ]
114
126
  })
115
127
  export class CoreModule implements NestModule, OnModuleInit {
@@ -23,6 +23,9 @@ import { DashboardCrudModule } from './dashboard/dashboard.module';
23
23
  ],
24
24
  controllers: [],
25
25
  providers: [],
26
- exports: [],
26
+ exports: [
27
+ forwardRef(() => DashboardCrudModule),
28
+ forwardRef(() => DashboardItemModule),
29
+ ],
27
30
  })
28
31
  export class DashboardModule {}
@@ -2,19 +2,19 @@ import { Public, Role } from '@hed-hog/api';
2
2
  import { getLocaleText, Locale } from '@hed-hog/api-locale';
3
3
  import { Pagination } from '@hed-hog/api-pagination';
4
4
  import {
5
- Body,
6
- Controller,
7
- Delete,
8
- forwardRef,
9
- Get,
10
- Inject,
11
- Param,
12
- ParseIntPipe,
13
- Post,
14
- Put,
15
- Res,
16
- UploadedFile,
17
- UseInterceptors,
5
+ Body,
6
+ Controller,
7
+ Delete,
8
+ forwardRef,
9
+ Get,
10
+ Inject,
11
+ Param,
12
+ ParseIntPipe,
13
+ Post,
14
+ Put,
15
+ Res,
16
+ UploadedFile,
17
+ UseInterceptors,
18
18
  } from '@nestjs/common';
19
19
  import { FileInterceptor } from '@nestjs/platform-express';
20
20
  import { Response } from 'express';
@@ -54,7 +54,7 @@ export class FileController {
54
54
 
55
55
  @Put('download/:id')
56
56
  async getTempURL(@Param('id', ParseIntPipe) id) {
57
- console.log('Generating temp URL for file ID:', id);
57
+
58
58
  return {
59
59
  url: await this.fileService.tempURL(
60
60
  (await this.fileService.get(id)).path,
@@ -10,6 +10,9 @@ import {
10
10
  OnModuleInit
11
11
  } from '@nestjs/common';
12
12
  import { JwtService } from '@nestjs/jwt';
13
+ import { createWriteStream, promises as fs } from 'fs';
14
+ import { basename, dirname } from 'path';
15
+ import { pipeline } from 'stream/promises';
13
16
  import { SettingService } from '../setting/setting.service';
14
17
  import { DeleteDTO } from './dto/delete.dto';
15
18
  import { AbstractProvider } from './provider/abstract.provider';
@@ -235,6 +238,113 @@ export class FileService implements OnModuleInit {
235
238
  return settings['storage'];
236
239
  }
237
240
 
241
+ private getProviderPath(storage: string, filepath: string) {
242
+ if (storage === EnumProvider.LOCAL) {
243
+ return filepath;
244
+ }
245
+
246
+ try {
247
+ return new URL(filepath).pathname;
248
+ } catch {
249
+ return filepath;
250
+ }
251
+ }
252
+
253
+ private mapStorageNotFoundError(error: any, fileId: number) {
254
+ const missingInStorage =
255
+ error?.name === 'NoSuchKey' ||
256
+ error?.Code === 'NoSuchKey' ||
257
+ error?.$metadata?.httpStatusCode === 404;
258
+
259
+ if (missingInStorage) {
260
+ throw new NotFoundException(`File not found in storage: ${fileId}`);
261
+ }
262
+
263
+ throw error;
264
+ }
265
+
266
+ async openReadStreamById(fileId: number) {
267
+ if (!fileId) {
268
+ throw new BadRequestException(`No file ID provided`);
269
+ }
270
+
271
+ const file = await this.prismaService.file.findUnique({
272
+ where: {
273
+ id: fileId,
274
+ },
275
+ include: {
276
+ file_mimetype: true,
277
+ },
278
+ });
279
+
280
+ if (!file) {
281
+ throw new NotFoundException(`File not found: ${fileId}`);
282
+ }
283
+
284
+ const storage = await this.getStorage();
285
+ const provider = await this.getProvider();
286
+ const providerPath = this.getProviderPath(storage, file.path);
287
+
288
+ let metadata: any;
289
+ try {
290
+ metadata = await provider.metaData(providerPath);
291
+ } catch {
292
+ metadata = undefined;
293
+ }
294
+
295
+ try {
296
+ const stream = await provider.readStream(providerPath);
297
+ return {
298
+ file,
299
+ stream,
300
+ size: file.size ?? this.getFileSizeFromMetadata(metadata),
301
+ };
302
+ } catch (error) {
303
+ this.mapStorageNotFoundError(error, fileId);
304
+ }
305
+ }
306
+
307
+ async downloadToPath(
308
+ fileId: number,
309
+ destinationPath: string,
310
+ options?: { maxBytes?: number },
311
+ ) {
312
+ if (!destinationPath) {
313
+ throw new BadRequestException(`No destination path provided`);
314
+ }
315
+
316
+ const { file, stream, size } = await this.openReadStreamById(fileId);
317
+
318
+ if (options?.maxBytes && size && size > options.maxBytes) {
319
+ throw new BadRequestException(
320
+ `File too large for streaming download: ${size} bytes (max ${options.maxBytes})`,
321
+ );
322
+ }
323
+
324
+ await fs.mkdir(dirname(destinationPath), { recursive: true });
325
+
326
+ try {
327
+ await pipeline(stream, createWriteStream(destinationPath));
328
+ const stats = await fs.stat(destinationPath);
329
+
330
+ if (options?.maxBytes && stats.size > options.maxBytes) {
331
+ await fs.unlink(destinationPath).catch(() => undefined);
332
+ throw new BadRequestException(
333
+ `File too large for streaming download: ${stats.size} bytes (max ${options.maxBytes})`,
334
+ );
335
+ }
336
+
337
+ return {
338
+ file,
339
+ path: destinationPath,
340
+ size: stats.size,
341
+ };
342
+ } catch (error) {
343
+ await fs.unlink(destinationPath).catch(() => undefined);
344
+ throw error;
345
+ }
346
+ }
347
+
238
348
  async getBuffer(fileId: number) {
239
349
 
240
350
  if (!fileId) {
@@ -258,8 +368,7 @@ export class FileService implements OnModuleInit {
258
368
 
259
369
  const provider = await this.getProvider();
260
370
 
261
- const providerPath =
262
- storage === EnumProvider.LOCAL ? file.path : new URL(file.path).pathname;
371
+ const providerPath = this.getProviderPath(storage, file.path);
263
372
 
264
373
  let providerBuffer: Buffer;
265
374
  try {
@@ -341,6 +450,89 @@ export class FileService implements OnModuleInit {
341
450
  return file;
342
451
  }
343
452
 
453
+ async uploadFromPath(
454
+ destination: string,
455
+ filePath: string,
456
+ metadata?: {
457
+ originalname?: string;
458
+ filename?: string;
459
+ mimetype?: string;
460
+ size?: number;
461
+ encoding?: string;
462
+ },
463
+ ) {
464
+ if (!filePath) {
465
+ throw new BadRequestException(`No file path provided`);
466
+ }
467
+
468
+ if (!destination) {
469
+ throw new BadRequestException(`No destination provided`);
470
+ }
471
+
472
+ const stats = await fs.stat(filePath).catch(() => null);
473
+ if (!stats || !stats.isFile()) {
474
+ throw new NotFoundException(`File path not found: ${filePath}`);
475
+ }
476
+
477
+ const size = metadata?.size ?? stats.size;
478
+ const mimetype = metadata?.mimetype ?? 'application/octet-stream';
479
+ const originalname = this.normalizeUploadedFilename(
480
+ metadata?.originalname ?? metadata?.filename ?? basename(filePath),
481
+ );
482
+
483
+ if (!(await this.acceptMimetypes(mimetype))) {
484
+ throw new BadRequestException(`Invalid file type: ${mimetype}`);
485
+ }
486
+
487
+ if (!(await this.maxFileSize(size))) {
488
+ throw new BadRequestException(`File too large: ${size} bytes`);
489
+ }
490
+
491
+ const provider = await this.getProvider();
492
+ const providerWithPathUpload = provider as AbstractProvider & {
493
+ uploadFromPath?: (
494
+ destination: string,
495
+ filePath: string,
496
+ options?: { filename?: string; mimetype?: string },
497
+ ) => Promise<string>;
498
+ };
499
+
500
+ let url: string;
501
+ if (typeof providerWithPathUpload.uploadFromPath === 'function') {
502
+ url = await providerWithPathUpload.uploadFromPath(destination, filePath, {
503
+ filename: originalname,
504
+ mimetype,
505
+ });
506
+ } else {
507
+ // Fallback explícito para providers legados sem suporte nativo por path.
508
+ const buffer = await fs.readFile(filePath);
509
+ url = await provider.upload(destination, {
510
+ fieldname: 'file',
511
+ originalname,
512
+ encoding: metadata?.encoding ?? '7bit',
513
+ mimetype,
514
+ size,
515
+ destination: '',
516
+ filename: originalname,
517
+ path: filePath,
518
+ buffer,
519
+ });
520
+ }
521
+
522
+ const file = await this.prismaService.file.create({
523
+ data: {
524
+ filename: originalname,
525
+ path: url,
526
+ provider_id: this.providerId,
527
+ location: destination,
528
+ mimetype_id: await this.getMimeType(mimetype),
529
+ size,
530
+ },
531
+ });
532
+
533
+ return file;
534
+ }
535
+
344
536
  async delete(locale:string,{ ids }: DeleteDTO) {
345
537
  const files = await this.prismaService.file.findMany({
346
538
  where: {
@@ -482,9 +674,11 @@ export class FileService implements OnModuleInit {
482
674
  );
483
675
  }
484
676
 
677
+ const storage = await this.getStorage();
485
678
  const provider = await this.getProvider();
486
- const metadata = await provider.metaData(file.path);
487
- const stream = await provider.readStream(file.path);
679
+ const providerPath = this.getProviderPath(storage, file.path);
680
+ const metadata = await provider.metaData(providerPath);
681
+ const stream = await provider.readStream(providerPath);
488
682
  const size = this.getFileSizeFromMetadata(metadata);
489
683
 
490
684
  return {
@@ -9,6 +9,11 @@ interface IProvider {
9
9
  mimetype?: string,
10
10
  ): Promise<any>;
11
11
  upload(destination: string, file: MulterFile): Promise<any>;
12
+ uploadFromPath(
13
+ destination: string,
14
+ filePath: string,
15
+ options?: { filename?: string; mimetype?: string },
16
+ ): Promise<any>;
12
17
  readStream(filepath: string): Promise<any>;
13
18
  delete(filepath: string): Promise<any>;
14
19
  metaData(filepath: string): Promise<any>;
@@ -25,6 +30,11 @@ export abstract class AbstractProvider implements IProvider {
25
30
  mimetype?: string,
26
31
  ): Promise<any>;
27
32
  abstract upload(destination: string, file: MulterFile): Promise<any>;
33
+ abstract uploadFromPath(
34
+ destination: string,
35
+ filePath: string,
36
+ options?: { filename?: string; mimetype?: string },
37
+ ): Promise<any>;
28
38
  abstract readStream(filepath: string): Promise<any>;
29
39
  abstract delete(filepath: string): Promise<any>;
30
40
  abstract metaData(filepath: string): Promise<any>;
@@ -5,6 +5,7 @@ import {
5
5
  StorageSharedKeyCredential,
6
6
  } from '@azure/storage-blob';
7
7
  import { BadRequestException } from '@nestjs/common';
8
+ import { basename } from 'path';
8
9
  import { AbstractProvider } from './abstract.provider';
9
10
 
10
11
  export class AzureProvider extends AbstractProvider {
@@ -92,6 +93,29 @@ export class AzureProvider extends AbstractProvider {
92
93
  return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
93
94
  }
94
95
 
96
+ async uploadFromPath(
97
+ destination: string,
98
+ filePath: string,
99
+ options?: { filename?: string; mimetype?: string },
100
+ ): Promise<any> {
101
+ const blobServiceClient = await this.getClient();
102
+ const containerClient = blobServiceClient.getContainerClient(
103
+ this.setting['storage-abs-container'],
104
+ );
105
+ const filepath = [destination, this.getFilename(options?.filename ?? basename(filePath))].join(
106
+ '/',
107
+ );
108
+ const blobClient = containerClient.getBlockBlobClient(filepath);
109
+
110
+ await blobClient.uploadFile(filePath, {
111
+ blobHTTPHeaders: options?.mimetype
112
+ ? { blobContentType: options.mimetype }
113
+ : undefined,
114
+ });
115
+
116
+ return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
117
+ }
118
+
95
119
  async delete(filepath: string): Promise<any> {
96
120
  const url = new URL(filepath);
97
121
 
@@ -1,5 +1,6 @@
1
1
  import { Storage } from '@google-cloud/storage';
2
2
  import { BadRequestException } from '@nestjs/common';
3
+ import { basename } from 'path';
3
4
  import { AbstractProvider } from './abstract.provider';
4
5
 
5
6
  export class GCSProvider extends AbstractProvider {
@@ -67,6 +68,25 @@ export class GCSProvider extends AbstractProvider {
67
68
  contentType: file.mimetype,
68
69
  });
69
70
  }
71
+
72
+ async uploadFromPath(
73
+ destination: string,
74
+ filePath: string,
75
+ options?: { filename?: string; mimetype?: string },
76
+ ): Promise<any> {
77
+ const storage = await this.getClient();
78
+ const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
79
+ const objectPath = [destination.replace(/\/$/, ''), this.getFilename(options?.filename ?? basename(filePath))]
80
+ .filter(Boolean)
81
+ .join('/');
82
+
83
+ await bucket.upload(filePath, {
84
+ destination: objectPath,
85
+ contentType: options?.mimetype,
86
+ });
87
+
88
+ return objectPath;
89
+ }
70
90
  async readStream(filepath: string): Promise<any> {
71
91
  const storage = await this.getClient();
72
92
  const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
@@ -1,9 +1,9 @@
1
1
  import { BadRequestException } from '@nestjs/common';
2
2
  import { JwtService } from '@nestjs/jwt';
3
3
  import { createReadStream, existsSync, promises as fs } from 'fs';
4
- import { mkdir, unlink, writeFile } from 'fs/promises';
4
+ import { copyFile, mkdir, stat, unlink, writeFile } from 'fs/promises';
5
5
  import * as path from 'path';
6
- import { join, sep } from 'path';
6
+ import { basename, join, sep } from 'path';
7
7
  import { Stream } from 'stream';
8
8
  import { AbstractProvider } from './abstract.provider';
9
9
 
@@ -99,6 +99,34 @@ export class LocalProvider extends AbstractProvider {
99
99
  return filePath;
100
100
  }
101
101
 
102
+ async uploadFromPath(
103
+ destination: string,
104
+ filePath: string,
105
+ options?: { filename?: string; mimetype?: string },
106
+ ): Promise<any> {
107
+ const sourcePath = this.resolveFilepath(filePath);
108
+ if (!existsSync(sourcePath)) {
109
+ throw new BadRequestException(`File not found: ${sourcePath}`);
110
+ }
111
+
112
+ const sourceStats = await stat(sourcePath);
113
+ if (!sourceStats.isFile()) {
114
+ throw new BadRequestException(`Invalid file path: ${sourcePath}`);
115
+ }
116
+
117
+ const storagePath = join(this.setting['storage-local-path'], destination);
118
+
119
+ if (!existsSync(storagePath)) {
120
+ await this.createFolderRecursive(storagePath);
121
+ }
122
+
123
+ const targetFilename = this.getFilename(options?.filename ?? basename(sourcePath));
124
+ const targetPath = join(storagePath, targetFilename);
125
+ await copyFile(sourcePath, targetPath);
126
+
127
+ return targetPath;
128
+ }
129
+
102
130
  async delete(filepath: string): Promise<any> {
103
131
  const fullPath = this.resolveFilepath(filepath);
104
132
 
@@ -1,6 +1,7 @@
1
1
  import { JwtService } from '@nestjs/jwt';
2
2
  import { AbstractProvider } from './abstract.provider';
3
3
  import { AzureProvider } from './azure.provider';
4
+ import { GCSProvider } from './gcs.provider';
4
5
  import { LocalProvider } from './local.provider';
5
6
  import { EnumProvider } from './provider.enum';
6
7
  import { S3Provider } from './s3.provider';
@@ -18,6 +19,8 @@ export class ProviderFactory {
18
19
  return new LocalProvider(setting, jwtService);
19
20
  case EnumProvider.AZURE:
20
21
  return new AzureProvider(setting);
22
+ case EnumProvider.GCS:
23
+ return new GCSProvider(setting);
21
24
  }
22
25
  }
23
26
  }
@@ -9,6 +9,8 @@ import {
9
9
  } from '@aws-sdk/client-s3';
10
10
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
11
11
  import { BadRequestException } from '@nestjs/common';
12
+ import { createReadStream } from 'fs';
13
+ import { basename } from 'path';
12
14
  import { Readable } from 'stream';
13
15
  import { AbstractProvider } from './abstract.provider';
14
16
 
@@ -166,6 +168,26 @@ export class S3Provider extends AbstractProvider {
166
168
  return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
167
169
  }
168
170
 
171
+ async uploadFromPath(
172
+ destination: string,
173
+ filePath: string,
174
+ options?: { filename?: string; mimetype?: string },
175
+ ): Promise<any> {
176
+ const s3 = await this.getClient();
177
+ const originalName = options?.filename ?? basename(filePath);
178
+ const storedFilename = this.getFilename(originalName);
179
+ const key = [destination, storedFilename].join('/');
180
+ const command = new PutObjectCommand({
181
+ Bucket: this.setting['storage-s3-bucket'],
182
+ Key: key,
183
+ Body: createReadStream(filePath),
184
+ ContentType: options?.mimetype,
185
+ });
186
+
187
+ await s3.send(command);
188
+ return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
189
+ }
190
+
169
191
  async delete(filepath: string): Promise<any> {
170
192
  const s3 = await this.getClient();
171
193
  const key = this.resolveS3Key(filepath);
@@ -1,36 +1,47 @@
1
- import { PrismaService } from '@hed-hog/api-prisma';
2
- import { Injectable, Logger } from '@nestjs/common';
3
-
4
- interface AuditRecord {
5
- userId: number;
6
- toolName: string;
7
- input: Record<string, any>;
8
- success: boolean;
9
- error?: string;
10
- durationMs: number;
11
- }
12
-
13
- @Injectable()
14
- export class McpAuditService {
15
- private readonly logger = new Logger(McpAuditService.name);
16
-
17
- constructor(private readonly prisma: PrismaService) {}
18
-
19
- async record(data: AuditRecord): Promise<void> {
20
- const sanitized = JSON.stringify(data.input).slice(0, 1000);
21
- try {
22
- await this.prisma.mcp_tool_call_log.create({
23
- data: {
24
- user_id: data.userId || null,
25
- tool_name: data.toolName,
26
- input: sanitized,
27
- success: data.success,
28
- error: data.error ?? null,
29
- duration_ms: data.durationMs,
30
- },
31
- });
32
- } catch (e: any) {
33
- this.logger.warn(`Failed to persist MCP audit record: ${e.message}`);
34
- }
35
- }
36
- }
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
3
+ import { AccessLogService } from '../access-log/access-log.service';
4
+
5
+ interface AuditRecord {
6
+ userId: number;
7
+ toolName: string;
8
+ input: Record<string, any>;
9
+ success: boolean;
10
+ error?: string;
11
+ durationMs: number;
12
+ }
13
+
14
+ @Injectable()
15
+ export class McpAuditService {
16
+ private readonly logger = new Logger(McpAuditService.name);
17
+
18
+ constructor(
19
+ private readonly prisma: PrismaService,
20
+ @Inject(forwardRef(() => AccessLogService))
21
+ private readonly accessLogService: AccessLogService,
22
+ ) {}
23
+
24
+ async record(data: AuditRecord): Promise<void> {
25
+ const sanitized = JSON.stringify(data.input).slice(0, 1000);
26
+ try {
27
+ await this.prisma.mcp_tool_call_log.create({
28
+ data: {
29
+ user_id: data.userId || null,
30
+ tool_name: data.toolName,
31
+ input: sanitized,
32
+ success: data.success,
33
+ error: data.error ?? null,
34
+ duration_ms: data.durationMs,
35
+ },
36
+ });
37
+ } catch (e: any) {
38
+ this.logger.warn(`Failed to persist MCP audit record: ${e.message}`);
39
+ }
40
+
41
+ this.accessLogService.recordMcp({
42
+ userId: data.userId || undefined,
43
+ mcpTool: data.toolName,
44
+ mcpPrompt: sanitized,
45
+ });
46
+ }
47
+ }