@hed-hog/core 0.0.355 → 0.0.358

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 (287) hide show
  1. package/dist/ai/ai.service.d.ts +3 -12
  2. package/dist/ai/ai.service.d.ts.map +1 -1
  3. package/dist/ai/ai.service.js +34 -125
  4. package/dist/ai/ai.service.js.map +1 -1
  5. package/dist/auth/guards/auth.guard.d.ts +6 -1
  6. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  7. package/dist/auth/guards/auth.guard.js +33 -2
  8. package/dist/auth/guards/auth.guard.js.map +1 -1
  9. package/dist/core.module.d.ts.map +1 -1
  10. package/dist/core.module.js +3 -3
  11. package/dist/core.module.js.map +1 -1
  12. package/dist/file/file.controller.d.ts +4 -2
  13. package/dist/file/file.controller.d.ts.map +1 -1
  14. package/dist/file/file.controller.js +44 -38
  15. package/dist/file/file.controller.js.map +1 -1
  16. package/dist/file/file.service.d.ts +2 -2
  17. package/dist/file/file.service.d.ts.map +1 -1
  18. package/dist/file/file.service.js +58 -89
  19. package/dist/file/file.service.js.map +1 -1
  20. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  21. package/dist/file/provider/azure.provider.js +10 -15
  22. package/dist/file/provider/azure.provider.js.map +1 -1
  23. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  24. package/dist/file/provider/gcs.provider.js +8 -9
  25. package/dist/file/provider/gcs.provider.js.map +1 -1
  26. package/dist/file/provider/local.provider.d.ts.map +1 -1
  27. package/dist/file/provider/local.provider.js +11 -10
  28. package/dist/file/provider/local.provider.js.map +1 -1
  29. package/dist/file/provider/provider.enum.d.ts +2 -1
  30. package/dist/file/provider/provider.enum.d.ts.map +1 -1
  31. package/dist/file/provider/provider.enum.js +1 -0
  32. package/dist/file/provider/provider.enum.js.map +1 -1
  33. package/dist/file/provider/provider.factory.d.ts +1 -1
  34. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  35. package/dist/file/provider/provider.factory.js +18 -5
  36. package/dist/file/provider/provider.factory.js.map +1 -1
  37. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  38. package/dist/file/provider/s3.provider.js +4 -20
  39. package/dist/file/provider/s3.provider.js.map +1 -1
  40. package/dist/file/provider/spaces.provider.d.ts +27 -0
  41. package/dist/file/provider/spaces.provider.d.ts.map +1 -0
  42. package/dist/file/provider/spaces.provider.js +203 -0
  43. package/dist/file/provider/spaces.provider.js.map +1 -0
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/integration-profile/dto/create.dto.d.ts +9 -0
  49. package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
  50. package/dist/integration-profile/dto/create.dto.js +54 -0
  51. package/dist/integration-profile/dto/create.dto.js.map +1 -0
  52. package/dist/integration-profile/dto/export.dto.d.ts +5 -0
  53. package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
  54. package/dist/integration-profile/dto/export.dto.js +29 -0
  55. package/dist/integration-profile/dto/export.dto.js.map +1 -0
  56. package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
  57. package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
  58. package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
  59. package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
  60. package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
  61. package/dist/integration-profile/dto/update.dto.js.map +1 -0
  62. package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
  63. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
  64. package/dist/integration-profile/integration-profile.controller.js +166 -0
  65. package/dist/integration-profile/integration-profile.controller.js.map +1 -0
  66. package/dist/integration-profile/integration-profile.module.d.ts +3 -0
  67. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
  68. package/dist/{mail-profile/mail-profile.module.js → integration-profile/integration-profile.module.js} +11 -13
  69. package/dist/integration-profile/integration-profile.module.js.map +1 -0
  70. package/dist/integration-profile/integration-profile.service.d.ts +160 -0
  71. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
  72. package/dist/integration-profile/integration-profile.service.js +609 -0
  73. package/dist/integration-profile/integration-profile.service.js.map +1 -0
  74. package/dist/integration-profile/integration-profile.utils.d.ts +30 -0
  75. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
  76. package/dist/integration-profile/integration-profile.utils.js +156 -0
  77. package/dist/integration-profile/integration-profile.utils.js.map +1 -0
  78. package/dist/mail/mail.module.d.ts.map +1 -1
  79. package/dist/mail/mail.module.js +36 -23
  80. package/dist/mail/mail.module.js.map +1 -1
  81. package/dist/mail/mail.service.d.ts +4 -2
  82. package/dist/mail/mail.service.d.ts.map +1 -1
  83. package/dist/mail/mail.service.js +33 -51
  84. package/dist/mail/mail.service.js.map +1 -1
  85. package/dist/mcp/guards/mcp-auth.guard.js +1 -1
  86. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  87. package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
  88. package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
  89. package/dist/mcp/mcp-api-key.controller.js +1 -1
  90. package/dist/mcp/mcp-api-key.controller.js.map +1 -1
  91. package/dist/mcp/mcp-api-key.service.d.ts +2 -1
  92. package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
  93. package/dist/mcp/mcp-api-key.service.js +6 -2
  94. package/dist/mcp/mcp-api-key.service.js.map +1 -1
  95. package/dist/mcp/mcp.module.d.ts.map +1 -1
  96. package/dist/mcp/mcp.module.js +3 -5
  97. package/dist/mcp/mcp.module.js.map +1 -1
  98. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  99. package/dist/mcp-chat/mcp-chat.service.js +17 -16
  100. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  101. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  102. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  103. package/dist/oauth/oauth.controller.d.ts +4 -1
  104. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  105. package/dist/oauth/oauth.controller.js +31 -5
  106. package/dist/oauth/oauth.controller.js.map +1 -1
  107. package/dist/oauth/oauth.service.d.ts +12 -1
  108. package/dist/oauth/oauth.service.d.ts.map +1 -1
  109. package/dist/oauth/oauth.service.js +125 -23
  110. package/dist/oauth/oauth.service.js.map +1 -1
  111. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  112. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  113. package/dist/oauth/providers/facebook.provider.d.ts +5 -2
  114. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  115. package/dist/oauth/providers/facebook.provider.js +34 -19
  116. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  117. package/dist/oauth/providers/github.provider.d.ts +6 -2
  118. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  119. package/dist/oauth/providers/github.provider.js +43 -20
  120. package/dist/oauth/providers/github.provider.js.map +1 -1
  121. package/dist/oauth/providers/google.provider.d.ts +5 -2
  122. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  123. package/dist/oauth/providers/google.provider.js +34 -19
  124. package/dist/oauth/providers/google.provider.js.map +1 -1
  125. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
  126. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  127. package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
  128. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  129. package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
  130. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  131. package/dist/oauth/providers/microsoft.provider.js +34 -19
  132. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  133. package/dist/profile/profile.controller.js +1 -1
  134. package/dist/profile/profile.controller.js.map +1 -1
  135. package/dist/security/security.service.d.ts +2 -0
  136. package/dist/security/security.service.d.ts.map +1 -1
  137. package/dist/security/security.service.js +21 -0
  138. package/dist/security/security.service.js.map +1 -1
  139. package/dist/setting/setting.service.d.ts +26 -1
  140. package/dist/setting/setting.service.d.ts.map +1 -1
  141. package/dist/setting/setting.service.js +240 -7
  142. package/dist/setting/setting.service.js.map +1 -1
  143. package/dist/user/user.controller.d.ts +2 -1
  144. package/dist/user/user.controller.d.ts.map +1 -1
  145. package/dist/user/user.controller.js +5 -4
  146. package/dist/user/user.controller.js.map +1 -1
  147. package/dist/user/user.service.d.ts +4 -2
  148. package/dist/user/user.service.d.ts.map +1 -1
  149. package/dist/user/user.service.js +19 -4
  150. package/dist/user/user.service.js.map +1 -1
  151. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
  152. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
  153. package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
  154. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
  155. package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
  156. package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
  157. package/dist/webhook-integration/event-webhook.controller.js +10 -0
  158. package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
  159. package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
  160. package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
  161. package/dist/webhook-integration/event-webhook.service.js +30 -0
  162. package/dist/webhook-integration/event-webhook.service.js.map +1 -1
  163. package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
  164. package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
  165. package/dist/webhook-integration/webhook-command.registry.js +36 -0
  166. package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
  167. package/dist/webhook-integration/webhook-integration.controller.d.ts +20 -0
  168. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  169. package/dist/webhook-integration/webhook-integration.controller.js +48 -0
  170. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  171. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  172. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  173. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  174. package/dist/webhook-integration/webhook-integration.service.d.ts +27 -2
  175. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  176. package/dist/webhook-integration/webhook-integration.service.js +377 -50
  177. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  178. package/hedhog/data/file_provider.yaml +9 -1
  179. package/hedhog/data/integration_provider.yaml +148 -0
  180. package/hedhog/data/integration_type.yaml +29 -0
  181. package/hedhog/data/menu.yaml +6 -21
  182. package/hedhog/data/role.yaml +8 -0
  183. package/hedhog/data/route.yaml +73 -373
  184. package/hedhog/data/setting_group.yaml +209 -365
  185. package/hedhog/data/setting_subgroup.yaml +21 -13
  186. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
  187. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
  188. package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
  189. package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
  190. package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
  191. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +938 -225
  192. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  193. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
  194. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1830 -0
  195. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +829 -128
  196. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +1 -820
  197. package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
  198. package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
  199. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
  200. package/hedhog/frontend/messages/en.json +239 -3
  201. package/hedhog/frontend/messages/pt.json +239 -3
  202. package/hedhog/table/file.yaml +1 -1
  203. package/hedhog/table/integration_profile.yaml +34 -0
  204. package/hedhog/table/integration_provider.yaml +23 -0
  205. package/hedhog/table/integration_type.yaml +19 -0
  206. package/hedhog/table/mcp_api_key.yaml +4 -0
  207. package/hedhog/table/webhook_action.yaml +141 -85
  208. package/package.json +4 -3
  209. package/src/ai/ai.service.ts +35 -153
  210. package/src/auth/guards/auth.guard.ts +32 -0
  211. package/src/core.module.ts +3 -3
  212. package/src/file/file.controller.ts +51 -42
  213. package/src/file/file.service.ts +67 -104
  214. package/src/file/provider/azure.provider.ts +14 -28
  215. package/src/file/provider/gcs.provider.ts +9 -9
  216. package/src/file/provider/local.provider.ts +10 -13
  217. package/src/file/provider/provider.enum.ts +1 -0
  218. package/src/file/provider/provider.factory.ts +21 -6
  219. package/src/file/provider/s3.provider.ts +4 -20
  220. package/src/file/provider/spaces.provider.ts +246 -0
  221. package/src/index.ts +6 -3
  222. package/src/integration-profile/dto/create.dto.ts +44 -0
  223. package/src/integration-profile/dto/export.dto.ts +15 -0
  224. package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
  225. package/src/integration-profile/integration-profile.controller.ts +131 -0
  226. package/src/integration-profile/integration-profile.module.ts +16 -0
  227. package/src/integration-profile/integration-profile.service.ts +793 -0
  228. package/src/integration-profile/integration-profile.utils.ts +173 -0
  229. package/src/mail/mail.module.ts +41 -22
  230. package/src/mail/mail.service.ts +40 -64
  231. package/src/mcp/guards/mcp-auth.guard.ts +1 -1
  232. package/src/mcp/mcp-api-key.controller.ts +2 -2
  233. package/src/mcp/mcp-api-key.service.ts +6 -2
  234. package/src/mcp/mcp.module.ts +3 -5
  235. package/src/mcp-chat/mcp-chat.service.ts +23 -20
  236. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  237. package/src/oauth/oauth.controller.ts +30 -2
  238. package/src/oauth/oauth.service.ts +194 -35
  239. package/src/oauth/providers/abstract.provider.ts +1 -1
  240. package/src/oauth/providers/facebook.provider.ts +36 -20
  241. package/src/oauth/providers/github.provider.ts +46 -23
  242. package/src/oauth/providers/google.provider.ts +36 -20
  243. package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
  244. package/src/oauth/providers/microsoft.provider.ts +36 -21
  245. package/src/profile/profile.controller.ts +2 -2
  246. package/src/security/security.service.ts +34 -1
  247. package/src/setting/setting.service.ts +307 -9
  248. package/src/user/user.controller.ts +4 -1
  249. package/src/user/user.service.ts +20 -2
  250. package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
  251. package/src/webhook-integration/event-webhook.controller.ts +55 -50
  252. package/src/webhook-integration/event-webhook.service.ts +422 -384
  253. package/src/webhook-integration/webhook-command.registry.ts +42 -0
  254. package/src/webhook-integration/webhook-integration.controller.ts +25 -0
  255. package/src/webhook-integration/webhook-integration.module.ts +50 -45
  256. package/src/webhook-integration/webhook-integration.service.ts +444 -53
  257. package/dist/mail-profile/dto/create.dto.d.ts +0 -23
  258. package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
  259. package/dist/mail-profile/dto/create.dto.js +0 -139
  260. package/dist/mail-profile/dto/create.dto.js.map +0 -1
  261. package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
  262. package/dist/mail-profile/dto/update.dto.js.map +0 -1
  263. package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
  264. package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
  265. package/dist/mail-profile/mail-profile.controller.js +0 -95
  266. package/dist/mail-profile/mail-profile.controller.js.map +0 -1
  267. package/dist/mail-profile/mail-profile.module.d.ts +0 -3
  268. package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
  269. package/dist/mail-profile/mail-profile.module.js.map +0 -1
  270. package/dist/mail-profile/mail-profile.service.d.ts +0 -109
  271. package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
  272. package/dist/mail-profile/mail-profile.service.js +0 -278
  273. package/dist/mail-profile/mail-profile.service.js.map +0 -1
  274. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -32
  275. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
  276. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -178
  277. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
  278. package/hedhog/data/mail_profile.yaml +0 -18
  279. package/hedhog/table/mail_profile.yaml +0 -75
  280. package/src/mail-profile/dto/create.dto.ts +0 -145
  281. package/src/mail-profile/mail-profile.controller.ts +0 -56
  282. package/src/mail-profile/mail-profile.module.ts +0 -18
  283. package/src/mail-profile/mail-profile.service.ts +0 -308
  284. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -128
  285. /package/dist/{mail-profile → integration-profile}/dto/update.dto.d.ts +0 -0
  286. /package/dist/{mail-profile → integration-profile}/dto/update.dto.js +0 -0
  287. /package/src/{mail-profile → integration-profile}/dto/update.dto.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.355",
3
+ "version": "0.0.358",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -27,16 +27,17 @@
27
27
  "playwright": "^1.49.0",
28
28
  "png-to-ico": "^2.1.8",
29
29
  "qrcode": "^1.5.4",
30
+ "redis": "4.7.1",
30
31
  "rxjs": "^7.8.2",
31
32
  "sharp": "^0.34.2",
32
33
  "speakeasy": "^2.0.0",
33
34
  "uuid": "^11.1.0",
34
35
  "@modelcontextprotocol/sdk": "^1.12.0",
35
36
  "@hed-hog/api-locale": "0.0.14",
37
+ "@hed-hog/api-mail": "0.0.9",
36
38
  "@hed-hog/api": "0.0.8",
37
- "@hed-hog/api-pagination": "0.0.7",
38
39
  "@hed-hog/api-prisma": "0.0.6",
39
- "@hed-hog/api-mail": "0.0.9",
40
+ "@hed-hog/api-pagination": "0.0.7",
40
41
  "@hed-hog/api-types": "0.0.1"
41
42
  },
42
43
  "exports": {
@@ -14,7 +14,7 @@ import { createHash } from 'crypto';
14
14
  import pdfParse from 'pdf-parse';
15
15
  import { DeleteDTO } from '../dto/delete.dto';
16
16
  import { FileService } from '../file/file.service';
17
- import { IntegrationDeveloperApiService } from '../integration/services/integration-developer-api.service';
17
+ import { buildAiConfigFromIntegration } from '../integration-profile/integration-profile.utils';
18
18
  import { SettingService } from '../setting/setting.service';
19
19
  import { ChatAgentDTO } from './dto/chat-agent.dto';
20
20
  import { ChatDTO } from './dto/chat.dto';
@@ -40,8 +40,6 @@ type AiAttachment = {
40
40
  buffer: Buffer;
41
41
  };
42
42
 
43
- type AiProviderKeySettingSlug = 'ai-openai-api-key' | 'ai-gemini-api-key';
44
-
45
43
  @Injectable()
46
44
  export class AiService implements OnModuleInit {
47
45
  private readonly logger = new Logger(AiService.name);
@@ -53,27 +51,18 @@ export class AiService implements OnModuleInit {
53
51
  private readonly settingService: SettingService,
54
52
  @Inject(forwardRef(() => FileService))
55
53
  private readonly fileService: FileService,
56
- @Inject(forwardRef(() => IntegrationDeveloperApiService))
57
- private readonly integrationApi: IntegrationDeveloperApiService,
58
54
  ) {}
59
55
 
60
- onModuleInit(): void {
61
- this.integrationApi.subscribe({
62
- eventName: 'core.setting.changed',
63
- consumerName: 'core.ai-provider-key-validator',
64
- priority: 0,
65
- handler: async (event) => {
66
- const slug = String(event.payload?.slug || '').trim();
67
-
68
- if (!this.isAiProviderKeySlug(slug)) {
69
- return;
70
- }
71
-
72
- await this.syncProviderEnabledFlag(slug);
73
- },
74
- });
75
-
76
- void this.syncAllProviderEnabledFlags();
56
+ async onModuleInit() {
57
+ try {
58
+ const s = await this.settingService.getSettingValues([
59
+ 'ai-openai-profile-id',
60
+ 'ai-gemini-profile-id',
61
+ ]);
62
+ if (!Number(s['ai-openai-profile-id']) && !Number(s['ai-gemini-profile-id'])) {
63
+ this.logger.warn('No AI profiles configured. Set profiles in Settings → AI.');
64
+ }
65
+ } catch { /* settings not available yet */ }
77
66
  }
78
67
 
79
68
  async chat(data: ChatDTO, file?: MulterFile) {
@@ -423,119 +412,36 @@ export class AiService implements OnModuleInit {
423
412
  return 'gemini-1.5-flash';
424
413
  }
425
414
 
426
- private isAiProviderKeySlug(slug: string): slug is AiProviderKeySettingSlug {
427
- return slug === 'ai-openai-api-key' || slug === 'ai-gemini-api-key';
428
- }
429
-
430
- private getEnabledSlugFromProviderKey(slug: AiProviderKeySettingSlug) {
431
- return slug === 'ai-openai-api-key'
432
- ? 'ai-openai-api-key-enabled'
433
- : 'ai-gemini-api-key-enabled';
434
- }
435
-
436
- private async syncAllProviderEnabledFlags() {
437
- const results = await Promise.allSettled([
438
- this.syncProviderEnabledFlag('ai-openai-api-key'),
439
- this.syncProviderEnabledFlag('ai-gemini-api-key'),
440
- ]);
441
-
442
- results.forEach((result, index) => {
443
- if (result.status === 'rejected') {
444
- const slug = index === 0 ? 'ai-openai-api-key' : 'ai-gemini-api-key';
445
- this.logger.warn(
446
- `Failed to sync provider availability for ${slug}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`,
447
- );
448
- }
449
- });
450
- }
451
-
452
- private async syncProviderEnabledFlag(slug: AiProviderKeySettingSlug) {
453
- const values = await this.settingService.getSettingValues(slug);
454
- const apiKey = String(values?.[slug] || '').trim();
455
- const enabledSlug = this.getEnabledSlugFromProviderKey(slug);
456
-
457
- const isValid = slug === 'ai-openai-api-key'
458
- ? await this.validateOpenAiKey(apiKey)
459
- : await this.validateGeminiKey(apiKey);
460
-
461
- await this.settingService.setValue(enabledSlug, isValid ? 'true' : 'false');
462
- }
463
-
464
- private getValidationErrorMessage(error: unknown) {
465
- if (axios.isAxiosError(error)) {
466
- const providerMessage =
467
- error.response?.data?.error?.message ||
468
- error.response?.data?.message ||
469
- error.message;
470
- const status = error.response?.status || 'network';
471
- return `${status}: ${providerMessage}`;
472
- }
473
-
474
- return error instanceof Error ? error.message : String(error);
475
- }
476
-
477
- private async validateOpenAiKey(apiKey?: string | null): Promise<boolean> {
478
- const normalizedKey = String(apiKey || '').trim();
479
-
480
- if (!normalizedKey) {
481
- return false;
482
- }
483
-
484
- try {
485
- await axios.get('https://api.openai.com/v1/models', {
486
- headers: {
487
- Authorization: `Bearer ${normalizedKey}`,
488
- },
489
- params: {
490
- limit: 1,
491
- },
492
- timeout: 5000,
493
- });
415
+ private async getAiApiKey(provider: AiProvider): Promise<string> {
416
+ const slug = provider === 'openai' ? 'ai-openai-profile-id' : 'ai-gemini-profile-id';
417
+ const settings = await this.settingService.getSettingValues([slug]);
418
+ const profileId = Number(settings[slug]);
494
419
 
495
- return true;
496
- } catch (error) {
497
- this.logger.warn(
498
- `OpenAI key validation failed: ${this.getValidationErrorMessage(error)}`,
420
+ if (!profileId) {
421
+ throw new BadRequestException(
422
+ `No ${provider} AI profile configured. Set one in Settings → AI.`,
499
423
  );
500
- return false;
501
424
  }
502
- }
503
425
 
504
- private async validateGeminiKey(apiKey?: string | null): Promise<boolean> {
505
- const normalizedKey = String(apiKey || '').trim();
426
+ const profile = await this.prismaService.integration_profile.findUnique({
427
+ where: { id: profileId },
428
+ include: { integration_provider: { select: { slug: true } } },
429
+ });
506
430
 
507
- if (!normalizedKey) {
508
- return false;
431
+ if (!profile) {
432
+ throw new BadRequestException(`AI profile ${profileId} not found.`);
509
433
  }
510
434
 
511
- try {
512
- await axios.get('https://generativelanguage.googleapis.com/v1beta/models', {
513
- params: {
514
- key: normalizedKey,
515
- pageSize: 1,
516
- },
517
- timeout: 5000,
518
- });
435
+ const { apiKey } = buildAiConfigFromIntegration(
436
+ profile.integration_provider.slug,
437
+ profile.config,
438
+ );
519
439
 
520
- return true;
521
- } catch (error) {
522
- this.logger.warn(
523
- `Gemini key validation failed: ${this.getValidationErrorMessage(error)}`,
524
- );
525
- return false;
440
+ if (!apiKey) {
441
+ throw new BadRequestException(`AI profile ${profileId} has no API key configured.`);
526
442
  }
527
- }
528
443
 
529
- private async getApiKeys() {
530
- const settings = await this.settingService.getSettingValues([
531
- 'ai-openai-api-key',
532
- 'ai-gemini-api-key',
533
- ]);
534
-
535
- return {
536
- openai: settings['ai-openai-api-key'],
537
- gemini: settings['ai-gemini-api-key'],
538
- };
444
+ return apiKey;
539
445
  }
540
446
 
541
447
  private async chatWithOpenAi({
@@ -549,13 +455,7 @@ export class AiService implements OnModuleInit {
549
455
  systemPrompt?: string;
550
456
  attachment?: AiAttachment | null;
551
457
  }) {
552
- const { openai } = await this.getApiKeys();
553
-
554
- if (!openai) {
555
- throw new BadRequestException(
556
- 'OpenAI API key is not configured (setting "ai-openai-api-key").',
557
- );
558
- }
458
+ const openai = await this.getAiApiKey('openai');
559
459
 
560
460
  const messages: Array<any> = [];
561
461
 
@@ -592,13 +492,7 @@ export class AiService implements OnModuleInit {
592
492
  model: string;
593
493
  instructions: string | null;
594
494
  }): Promise<string> {
595
- const { openai } = await this.getApiKeys();
596
-
597
- if (!openai) {
598
- throw new BadRequestException(
599
- 'OpenAI API key is not configured (setting "ai-openai-api-key").',
600
- );
601
- }
495
+ const openai = await this.getAiApiKey('openai');
602
496
 
603
497
  const response = await axios.post(
604
498
  'https://api.openai.com/v1/assistants',
@@ -629,13 +523,7 @@ export class AiService implements OnModuleInit {
629
523
  assistantId: string,
630
524
  message: string,
631
525
  ): Promise<string> {
632
- const { openai } = await this.getApiKeys();
633
-
634
- if (!openai) {
635
- throw new BadRequestException(
636
- 'OpenAI API key is not configured (setting "ai-openai-api-key").',
637
- );
638
- }
526
+ const openai = await this.getAiApiKey('openai');
639
527
 
640
528
  const threadResponse = await axios.post(
641
529
  'https://api.openai.com/v1/threads',
@@ -733,13 +621,7 @@ export class AiService implements OnModuleInit {
733
621
  systemPrompt?: string;
734
622
  attachment?: AiAttachment | null;
735
623
  }) {
736
- const { gemini } = await this.getApiKeys();
737
-
738
- if (!gemini) {
739
- throw new BadRequestException(
740
- 'Gemini API key is not configured (setting "ai-gemini-api-key").',
741
- );
742
- }
624
+ const gemini = await this.getAiApiKey('gemini');
743
625
 
744
626
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${gemini}`;
745
627
 
@@ -1,5 +1,6 @@
1
1
  import { IS_PUBLIC_KEY, WITH_NO_ROLE, WITH_ROLE } from '@hed-hog/api';
2
2
  import { getLocaleText } from '@hed-hog/api-locale';
3
+ import { PrismaService } from '@hed-hog/api-prisma';
3
4
  import {
4
5
  CanActivate,
5
6
  ExecutionContext,
@@ -8,6 +9,7 @@ import {
8
9
  } from '@nestjs/common';
9
10
  import { Reflector } from '@nestjs/core';
10
11
  import { Request } from 'express';
12
+ import { SecurityService } from '../../security/security.service';
11
13
  import { TokenService } from '../../token/token.service';
12
14
 
13
15
  @Injectable()
@@ -15,6 +17,8 @@ export class AuthGuard implements CanActivate {
15
17
  constructor(
16
18
  private token: TokenService,
17
19
  private reflector: Reflector,
20
+ private prisma: PrismaService,
21
+ private security: SecurityService,
18
22
  ) { }
19
23
 
20
24
  async canActivate(context: ExecutionContext): Promise<boolean> {
@@ -37,6 +41,10 @@ export class AuthGuard implements CanActivate {
37
41
  const token = this.extractTokenFromHeader(request);
38
42
  const locale = request.headers['locale'] || request.headers['accept-language'] || 'en';
39
43
 
44
+ if (token?.startsWith('hedhog_api_')) {
45
+ return this.verifyApiKey(request, token, isPublic);
46
+ }
47
+
40
48
  // If endpoint requires role/authentication and no token provided
41
49
  if (!token) {
42
50
  if (isPublic) {
@@ -76,6 +84,30 @@ export class AuthGuard implements CanActivate {
76
84
  return true;
77
85
  }
78
86
 
87
+ private async verifyApiKey(request: Request, token: string, isPublic: boolean): Promise<boolean> {
88
+ const hash = this.security.hashWithPepper(token);
89
+ const apiKey = await this.prisma.mcp_api_key.findFirst({
90
+ where: { token_hash: hash, type: 'api', revoked_at: null },
91
+ });
92
+
93
+ if (!apiKey) {
94
+ if (isPublic) return true;
95
+ throw new UnauthorizedException('Invalid API key');
96
+ }
97
+ if (apiKey.expires_at && apiKey.expires_at <= new Date()) {
98
+ if (isPublic) return true;
99
+ throw new UnauthorizedException('API key expired');
100
+ }
101
+
102
+ this.prisma.mcp_api_key.update({
103
+ where: { id: apiKey.id },
104
+ data: { last_used_at: new Date() },
105
+ }).catch(() => {});
106
+
107
+ request['auth'] = { sub: apiKey.user_id, sessionId: null };
108
+ return true;
109
+ }
110
+
79
111
  private extractTokenFromHeader(request: Request): string | undefined {
80
112
  const [type, token] = request.headers.authorization?.split(' ') ?? [];
81
113
 
@@ -18,7 +18,6 @@ import { FileModule } from './file/file.module';
18
18
  import { InstallModule } from './install/install.module';
19
19
  import { IntegrationModule } from './integration/integration.module';
20
20
  import { LocaleModule as CoreLocaleModule } from './locale/locale.module';
21
- import { MailProfileModule } from './mail-profile/mail-profile.module';
22
21
  import { MailSentModule } from './mail-sent/mail-sent.module';
23
22
  import { MailModule } from './mail/mail.module';
24
23
  import { McpChatModule } from './mcp-chat/mcp-chat.module';
@@ -42,6 +41,7 @@ import { IsEmailWithSettingsConstraint } from './validators/is-email-with-settin
42
41
  import { IsPinCodeWithSettingConstraint } from './validators/is-pin-code-with-setting.validator';
43
42
  import { IsStrongPasswordWithSettingsConstraint } from './validators/is-strong-password-with-settings.validator';
44
43
  import { ValidatorServiceLocator } from './validators/service-locator';
44
+ import { IntegrationProfileModule } from './integration-profile/integration-profile.module';
45
45
  import { WebhookIntegrationModule } from './webhook-integration/webhook-integration.module';
46
46
 
47
47
  @Global()
@@ -66,7 +66,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
66
66
  forwardRef(() => SystemModule),
67
67
  forwardRef(() => SettingModule),
68
68
  forwardRef(() => MailModule),
69
- forwardRef(() => MailProfileModule),
69
+ forwardRef(() => IntegrationProfileModule),
70
70
  forwardRef(() => MailSentModule),
71
71
  forwardRef(() => FileModule),
72
72
  forwardRef(() => TaskModule),
@@ -86,7 +86,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
86
86
  UserModule,
87
87
  AuthModule,
88
88
  MailModule,
89
- MailProfileModule,
89
+ IntegrationProfileModule,
90
90
  RouteModule,
91
91
  RoleModule,
92
92
  MenuModule,
@@ -8,6 +8,7 @@ import {
8
8
  forwardRef,
9
9
  Get,
10
10
  Inject,
11
+ Logger,
11
12
  Param,
12
13
  ParseIntPipe,
13
14
  Post,
@@ -29,11 +30,50 @@ import { FileService } from './file.service';
29
30
  @Role()
30
31
  @Controller('file')
31
32
  export class FileController {
33
+ private readonly logger = new Logger(FileController.name);
34
+
32
35
  constructor(
33
36
  @Inject(forwardRef(() => FileService))
34
37
  private readonly fileService: FileService,
35
38
  ) {}
36
39
 
40
+ private renderDownloadErrorHtml(locale: string, error: any): string {
41
+ const errorTitle = getLocaleText('file.download.error_title', locale);
42
+ const errorMessage = getLocaleText('file.download.error_message', locale);
43
+ const errorDetailsLabel = getLocaleText('file.download.error_details', locale);
44
+ const goBackText = getLocaleText('file.download.go_back', locale);
45
+
46
+ const templateData = {
47
+ locale: locale || 'en',
48
+ errorTitle,
49
+ errorMessage,
50
+ errorDetailsLabel,
51
+ errorDetails: error?.message || 'Unknown error occurred',
52
+ goBackText,
53
+ };
54
+
55
+ try {
56
+ const templatePath = join(__dirname, 'templates', 'download-error.html');
57
+ const templateSource = readFileSync(templatePath, 'utf-8');
58
+ const template = Handlebars.compile(templateSource);
59
+ return template(templateData);
60
+ } catch (templateError) {
61
+ this.logger.warn(
62
+ `Download error template missing or unreadable: ${templateError instanceof Error ? templateError.message : String(templateError)}`,
63
+ );
64
+
65
+ const safe = {
66
+ errorTitle: Handlebars.escapeExpression(String(errorTitle)),
67
+ errorMessage: Handlebars.escapeExpression(String(errorMessage)),
68
+ errorDetailsLabel: Handlebars.escapeExpression(String(errorDetailsLabel)),
69
+ errorDetails: Handlebars.escapeExpression(String(templateData.errorDetails)),
70
+ goBackText: Handlebars.escapeExpression(String(goBackText)),
71
+ };
72
+
73
+ return `<!doctype html><html lang="${Handlebars.escapeExpression(locale || 'en')}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${safe.errorTitle}</title></head><body><h1>${safe.errorTitle}</h1><p>${safe.errorMessage}</p><p><strong>${safe.errorDetailsLabel}</strong> ${safe.errorDetails}</p><button type="button" onclick="history.back()">${safe.goBackText}</button></body></html>`;
74
+ }
75
+ }
76
+
37
77
  @Get()
38
78
  async list(@Pagination() paginationParams) {
39
79
  return this.fileService.getFiles(paginationParams);
@@ -45,11 +85,16 @@ export class FileController {
45
85
  }
46
86
 
47
87
  @Put('open/:id')
48
- async getTempOpenURL(@Param('id', ParseIntPipe) id: number) {
88
+ async getTempOpenURL(
89
+ @Param('id', ParseIntPipe) id: number,
90
+ @Req() req: Request,
91
+ ) {
92
+ const file = await this.fileService.get(id);
93
+ const token = this.fileService.createDownloadToken(file.path, 3600);
94
+ const origin = `${req.protocol}://${req.get('host')}`;
95
+
49
96
  return {
50
- url: await this.fileService.tempOpenURL(
51
- (await this.fileService.get(id)).path,
52
- ),
97
+ url: `${origin}/file/open/${encodeURIComponent(token)}`,
53
98
  };
54
99
  }
55
100
 
@@ -96,27 +141,7 @@ export class FileController {
96
141
  return;
97
142
 
98
143
  } catch (error:any) {
99
- // Buscar traduções
100
- const errorTitle = getLocaleText('file.download.error_title', locale);
101
- const errorMessage = getLocaleText('file.download.error_message', locale);
102
- const errorDetailsLabel = getLocaleText('file.download.error_details', locale);
103
- const goBackText = getLocaleText('file.download.go_back', locale);
104
-
105
- // Ler e compilar template
106
- const templatePath = join(__dirname, 'templates', 'download-error.html');
107
- const templateSource = readFileSync(templatePath, 'utf-8');
108
- const template = Handlebars.compile(templateSource);
109
-
110
- // Renderizar template com dados
111
- const html = template({
112
- locale: locale || 'en',
113
- errorTitle,
114
- errorMessage,
115
- errorDetailsLabel,
116
- errorDetails: error?.message || 'Unknown error occurred',
117
- goBackText,
118
- });
119
-
144
+ const html = this.renderDownloadErrorHtml(locale, error);
120
145
  res.status(400).set('Content-Type', 'text/html').send(html);
121
146
  return;
122
147
  }
@@ -176,23 +201,7 @@ export class FileController {
176
201
  return;
177
202
 
178
203
  } catch (error:any) {
179
- const errorTitle = getLocaleText('file.download.error_title', locale);
180
- const errorMessage = getLocaleText('file.download.error_message', locale);
181
- const errorDetailsLabel = getLocaleText('file.download.error_details', locale);
182
- const goBackText = getLocaleText('file.download.go_back', locale);
183
-
184
- const templatePath = join(__dirname, 'templates', 'download-error.html');
185
- const templateSource = readFileSync(templatePath, 'utf-8');
186
- const template = Handlebars.compile(templateSource);
187
-
188
- const html = template({
189
- locale: locale || 'en',
190
- errorTitle,
191
- errorMessage,
192
- errorDetailsLabel,
193
- errorDetails: error?.message || 'Unknown error occurred',
194
- goBackText,
195
- });
204
+ const html = this.renderDownloadErrorHtml(locale, error);
196
205
 
197
206
  res.status(400).set('Content-Type', 'text/html').send(html);
198
207
  return;