@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
@@ -6,6 +6,7 @@ import {
6
6
  forwardRef,
7
7
  Inject,
8
8
  Injectable,
9
+ Logger,
9
10
  NotFoundException,
10
11
  OnModuleInit
11
12
  } from '@nestjs/common';
@@ -13,17 +14,31 @@ import { JwtService } from '@nestjs/jwt';
13
14
  import { createWriteStream, promises as fs } from 'fs';
14
15
  import { basename, dirname } from 'path';
15
16
  import { pipeline } from 'stream/promises';
17
+ import {
18
+ buildStorageConfigFromIntegration,
19
+ resolveFileProviderSlug,
20
+ resolveStorageEnumProvider,
21
+ } from '../integration-profile/integration-profile.utils';
16
22
  import { SettingService } from '../setting/setting.service';
17
23
  import { DeleteDTO } from './dto/delete.dto';
18
24
  import { AbstractProvider } from './provider/abstract.provider';
19
- import { EnumProvider } from './provider/provider.enum';
20
25
  import { ProviderFactory } from './provider/provider.factory';
21
26
 
22
27
  @Injectable()
23
28
  export class FileService implements OnModuleInit {
24
- private providerId: number;
29
+ private readonly logger = new Logger(FileService.name);
30
+ private providerId: number | undefined;
25
31
  private mimetypes: Record<string, number> = {};
26
32
 
33
+ private resolveProviderPath(file: { location: string; path: string }): string {
34
+ // Supports legacy basename-only values while prioritizing full provider keys.
35
+ if (typeof file.path === 'string' && file.path.includes('/')) {
36
+ return file.path;
37
+ }
38
+
39
+ return `${file.location}/${file.path}`;
40
+ }
41
+
27
42
  private hasMojibake(value: string): boolean {
28
43
  return /Ã.|Â.|â[\u0080-\u00BF]/.test(value);
29
44
  }
@@ -53,53 +68,60 @@ export class FileService implements OnModuleInit {
53
68
  ) { }
54
69
 
55
70
  async onModuleInit() {
56
- await this.getProvider();
71
+ try {
72
+ await this.getProvider();
73
+ } catch (error) {
74
+ // No storage profile configured yet — server starts normally, uploads will
75
+ // fail until a profile is selected in Settings → File Storage.
76
+ this.logger.warn(
77
+ `Storage provider not ready on startup: ${error instanceof Error ? error.message : String(error)}`,
78
+ );
79
+ }
57
80
  }
58
81
 
59
82
  async getProvider(): Promise<AbstractProvider> {
60
- const settings = await this.settingService.getSettingValues([
61
- 'storage',
62
- 'storage-local-path',
63
- 'storage-s3-key',
64
- 'storage-s3-secret',
65
- 'storage-s3-region',
66
- 'storage-s3-bucket',
67
- 'storage-max-size',
68
- 'storage-accept-mimetype',
69
- 'storage-abs-account',
70
- 'storage-abs-key',
71
- 'storage-abs-container',
72
- 'storage-gcs-keyfile',
73
- ]);
74
-
75
- if (!settings['storage']) {
83
+ const settings = await this.settingService.getSettingValues(['file-storage-profile-id']);
84
+ const rawProfileId = settings['file-storage-profile-id'];
85
+ const profileId = Number(rawProfileId);
86
+
87
+ if (!Number.isFinite(profileId) || profileId <= 0) {
88
+ throw new BadRequestException('No file storage profile configured. Set a storage profile in the File Storage settings.');
89
+ }
90
+
91
+ const profile = await this.prismaService.integration_profile.findUnique({
92
+ where: { id: profileId },
93
+ include: {
94
+ integration_type: { select: { slug: true } },
95
+ integration_provider: { select: { slug: true } },
96
+ },
97
+ });
98
+
99
+ if (!profile) {
100
+ throw new NotFoundException(`Storage profile with id ${profileId} not found.`);
101
+ }
102
+
103
+ if (profile.integration_type.slug !== 'storage') {
76
104
  throw new BadRequestException(
77
- `You must set the storage provider in the setting.`,
105
+ `Integration profile with id ${profileId} is not a storage profile.`,
78
106
  );
79
107
  }
80
108
 
81
- const providerName = settings['storage'];
82
- const provider = ProviderFactory.create(
83
- providerName as EnumProvider,
84
- settings,
85
- this.jwtService,
86
- );
109
+ const providerSlug = profile.integration_provider.slug;
110
+ const config = buildStorageConfigFromIntegration(providerSlug, profile.config);
111
+ const enumProvider = resolveStorageEnumProvider(providerSlug);
112
+ const fileProviderSlug = resolveFileProviderSlug(providerSlug);
87
113
 
88
114
  const providerData = await this.prismaService.file_provider.findFirst({
89
- where: {
90
- slug: providerName,
91
- },
92
- select: {
93
- id: true,
94
- },
115
+ where: { slug: fileProviderSlug },
116
+ select: { id: true },
95
117
  });
96
118
 
97
119
  if (!providerData) {
98
- throw new BadRequestException(`Provider ${providerName} not found.`);
120
+ throw new BadRequestException(`Provider ${fileProviderSlug} not found.`);
99
121
  }
100
122
 
101
123
  this.providerId = providerData.id;
102
- return provider;
124
+ return await ProviderFactory.create(enumProvider, config, this.jwtService);
103
125
  }
104
126
 
105
127
  async getMimeType(mimetype: string) {
@@ -130,58 +152,22 @@ export class FileService implements OnModuleInit {
130
152
  }
131
153
 
132
154
  async acceptMimetypes(mimetype: string) {
133
-
134
155
  if (!mimetype) {
135
156
  throw new BadRequestException(`No mimetype provided`);
136
157
  }
137
158
 
138
- await this.getProvider();
139
-
140
- const settings = await this.settingService.getSettingValues([
141
- 'storage',
142
- 'storage-local-path',
143
- 'storage-s3-key',
144
- 'storage-s3-secret',
145
- 'storage-s3-region',
146
- 'storage-s3-bucket',
147
- 'storage-max-size',
148
- 'storage-accept-mimetype',
149
- 'storage-abs-account',
150
- 'storage-abs-key',
151
- 'storage-abs-container',
152
- 'storage-gcs-keyfile',
153
- ]);
154
-
159
+ const settings = await this.settingService.getSettingValues(['storage-accept-mimetype']);
155
160
  const acceptMimetypes = settings['storage-accept-mimetype'];
156
161
  return acceptMimetypes.indexOf(mimetype) !== -1;
157
162
  }
158
163
 
159
164
  async maxFileSize(size: number) {
160
-
161
165
  if (!size) {
162
166
  throw new BadRequestException(`No size provided`);
163
167
  }
164
168
 
165
- await this.getProvider();
166
-
167
- const settings = await this.settingService.getSettingValues([
168
- 'storage',
169
- 'storage-local-path',
170
- 'storage-s3-key',
171
- 'storage-s3-secret',
172
- 'storage-s3-region',
173
- 'storage-s3-bucket',
174
- 'storage-max-size',
175
- 'storage-accept-mimetype',
176
- 'storage-abs-account',
177
- 'storage-abs-key',
178
- 'storage-abs-container',
179
- 'storage-gcs-keyfile',
180
- ]);
181
-
182
- const maxSize = settings['storage-max-size'];
183
-
184
- return size <= Number(maxSize);
169
+ const settings = await this.settingService.getSettingValues(['storage-max-size']);
170
+ return size <= Number(settings['storage-max-size']);
185
171
  }
186
172
 
187
173
  async uploadFromString(
@@ -230,26 +216,6 @@ export class FileService implements OnModuleInit {
230
216
  return file;
231
217
  }
232
218
 
233
- async getStorage() {
234
- const settings = await this.settingService.getSettingValues([
235
- 'storage',
236
- ]);
237
-
238
- return settings['storage'];
239
- }
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
219
  private mapStorageNotFoundError(error: any, fileId: number) {
254
220
  const missingInStorage =
255
221
  error?.name === 'NoSuchKey' ||
@@ -281,9 +247,8 @@ export class FileService implements OnModuleInit {
281
247
  throw new NotFoundException(`File not found: ${fileId}`);
282
248
  }
283
249
 
284
- const storage = await this.getStorage();
285
250
  const provider = await this.getProvider();
286
- const providerPath = this.getProviderPath(storage, file.path);
251
+ const providerPath = this.resolveProviderPath(file);
287
252
 
288
253
  let metadata: any;
289
254
  try {
@@ -364,11 +329,8 @@ export class FileService implements OnModuleInit {
364
329
  throw new NotFoundException(`File not found: ${fileId}`);
365
330
  }
366
331
 
367
- const storage = await this.getStorage();
368
-
369
332
  const provider = await this.getProvider();
370
-
371
- const providerPath = this.getProviderPath(storage, file.path);
333
+ const providerPath = this.resolveProviderPath(file);
372
334
 
373
335
  let providerBuffer: Buffer;
374
336
  try {
@@ -542,6 +504,7 @@ export class FileService implements OnModuleInit {
542
504
  },
543
505
  select: {
544
506
  id: true,
507
+ location: true,
545
508
  path: true,
546
509
  },
547
510
  });
@@ -551,7 +514,7 @@ export class FileService implements OnModuleInit {
551
514
  }
552
515
 
553
516
  for (const file of files) {
554
- await (await this.getProvider()).delete(file.path);
517
+ await (await this.getProvider()).delete(this.resolveProviderPath(file));
555
518
 
556
519
  await this.prismaService.file.deleteMany({
557
520
  where: {
@@ -645,8 +608,9 @@ export class FileService implements OnModuleInit {
645
608
  }
646
609
 
647
610
  const provider = await this.getProvider();
648
- const metadata = await provider.metaData(filepath);
649
- const stream = await provider.readStream(filepath);
611
+ const providerPath = this.resolveProviderPath(file);
612
+ const metadata = await provider.metaData(providerPath);
613
+ const stream = await provider.readStream(providerPath);
650
614
  const size = this.getFileSizeFromMetadata(metadata);
651
615
 
652
616
  return {
@@ -674,9 +638,8 @@ export class FileService implements OnModuleInit {
674
638
  );
675
639
  }
676
640
 
677
- const storage = await this.getStorage();
678
641
  const provider = await this.getProvider();
679
- const providerPath = this.getProviderPath(storage, file.path);
642
+ const providerPath = this.resolveProviderPath(file);
680
643
  const metadata = await provider.metaData(providerPath);
681
644
  const stream = await provider.readStream(providerPath);
682
645
  const size = this.getFileSizeFromMetadata(metadata);
@@ -1,8 +1,8 @@
1
1
  import {
2
- BlobServiceClient,
3
- ContainerSASPermissions,
4
- generateBlobSASQueryParameters,
5
- StorageSharedKeyCredential,
2
+ BlobServiceClient,
3
+ ContainerSASPermissions,
4
+ generateBlobSASQueryParameters,
5
+ StorageSharedKeyCredential,
6
6
  } from '@azure/storage-blob';
7
7
  import { BadRequestException } from '@nestjs/common';
8
8
  import { basename } from 'path';
@@ -11,7 +11,6 @@ import { AbstractProvider } from './abstract.provider';
11
11
  export class AzureProvider extends AbstractProvider {
12
12
  constructor(private setting: Record<string, string>) {
13
13
  super();
14
- this.initValidation();
15
14
  }
16
15
 
17
16
  async initValidation() {
@@ -68,13 +67,13 @@ export class AzureProvider extends AbstractProvider {
68
67
  const containerClient = blobServiceClient.getContainerClient(
69
68
  this.setting['storage-abs-container'],
70
69
  );
71
- const filepath = [destination, this.getFilename(filename)].join('/');
72
- const blobClient = containerClient.getBlockBlobClient(filepath);
70
+ const storedFilename = this.getFilename(filename);
71
+ const blobClient = containerClient.getBlockBlobClient([destination, storedFilename].join('/'));
73
72
 
74
73
  await blobClient.upload(fileContent, fileContent.length, {
75
74
  blobHTTPHeaders: { blobContentType: mimetype },
76
75
  });
77
- return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
76
+ return storedFilename;
78
77
  }
79
78
 
80
79
  async upload(destination: string, file: MulterFile): Promise<any> {
@@ -82,15 +81,13 @@ export class AzureProvider extends AbstractProvider {
82
81
  const containerClient = blobServiceClient.getContainerClient(
83
82
  this.setting['storage-abs-container'],
84
83
  );
85
- const filepath = [destination, this.getFilename(file.originalname)].join(
86
- '/',
87
- );
88
- const blobClient = containerClient.getBlockBlobClient(filepath);
84
+ const storedFilename = this.getFilename(file.originalname);
85
+ const blobClient = containerClient.getBlockBlobClient([destination, storedFilename].join('/'));
89
86
 
90
87
  await blobClient.uploadData(file.buffer, {
91
88
  blobHTTPHeaders: { blobContentType: file.mimetype },
92
89
  });
93
- return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
90
+ return storedFilename;
94
91
  }
95
92
 
96
93
  async uploadFromPath(
@@ -102,10 +99,8 @@ export class AzureProvider extends AbstractProvider {
102
99
  const containerClient = blobServiceClient.getContainerClient(
103
100
  this.setting['storage-abs-container'],
104
101
  );
105
- const filepath = [destination, this.getFilename(options?.filename ?? basename(filePath))].join(
106
- '/',
107
- );
108
- const blobClient = containerClient.getBlockBlobClient(filepath);
102
+ const storedFilename = this.getFilename(options?.filename ?? basename(filePath));
103
+ const blobClient = containerClient.getBlockBlobClient([destination, storedFilename].join('/'));
109
104
 
110
105
  await blobClient.uploadFile(filePath, {
111
106
  blobHTTPHeaders: options?.mimetype
@@ -113,24 +108,15 @@ export class AzureProvider extends AbstractProvider {
113
108
  : undefined,
114
109
  });
115
110
 
116
- return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
111
+ return storedFilename;
117
112
  }
118
113
 
119
114
  async delete(filepath: string): Promise<any> {
120
- const url = new URL(filepath);
121
-
122
- if (!url.pathname) {
123
- throw new Error(`Invalid filepath "${filepath}" for S3`);
124
- }
125
-
126
115
  const blobServiceClient = await this.getClient();
127
116
  const containerClient = blobServiceClient.getContainerClient(
128
117
  this.setting['storage-abs-container'],
129
118
  );
130
- const blobClient = containerClient.getBlockBlobClient(
131
- url.pathname.split('/').slice(1).join('/'),
132
- );
133
-
119
+ const blobClient = containerClient.getBlockBlobClient(filepath);
134
120
  return await blobClient.delete();
135
121
  }
136
122
 
@@ -8,7 +8,6 @@ export class GCSProvider extends AbstractProvider {
8
8
 
9
9
  constructor(private setting: Record<string, string>) {
10
10
  super();
11
- this.initValidation();
12
11
  }
13
12
 
14
13
  async initValidation() {
@@ -53,20 +52,21 @@ export class GCSProvider extends AbstractProvider {
53
52
  ): Promise<any> {
54
53
  const storage = await this.getClient();
55
54
  const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
56
- const file = bucket.file(destination + filename);
55
+ const storedFilename = this.getFilename(filename);
56
+ const file = bucket.file([destination, storedFilename].join('/'));
57
57
 
58
- return file.save(fileContent, {
59
- contentType: mimetype,
60
- });
58
+ await file.save(fileContent, { contentType: mimetype });
59
+ return storedFilename;
61
60
  }
61
+
62
62
  async upload(destination: string, file: MulterFile): Promise<any> {
63
63
  const storage = await this.getClient();
64
64
  const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
65
- const fileObject = bucket.file(destination + file.originalname);
65
+ const storedFilename = this.getFilename(file.originalname);
66
+ const fileObject = bucket.file([destination, storedFilename].join('/'));
66
67
 
67
- return fileObject.save(file.buffer, {
68
- contentType: file.mimetype,
69
- });
68
+ await fileObject.save(file.buffer, { contentType: file.mimetype });
69
+ return storedFilename;
70
70
  }
71
71
 
72
72
  async uploadFromPath(
@@ -13,7 +13,6 @@ export class LocalProvider extends AbstractProvider {
13
13
  private jwt: JwtService
14
14
  ) {
15
15
  super();
16
- this.initValidation();
17
16
  }
18
17
 
19
18
  async initValidation() {
@@ -40,7 +39,8 @@ export class LocalProvider extends AbstractProvider {
40
39
  }
41
40
 
42
41
  private resolveFilepath(filepath: string): string {
43
- return path.isAbsolute(filepath) ? filepath : path.resolve(filepath);
42
+ if (path.isAbsolute(filepath)) return filepath;
43
+ return join(this.setting['storage-local-path'], filepath);
44
44
  }
45
45
 
46
46
  async uploadFromString(
@@ -67,11 +67,12 @@ export class LocalProvider extends AbstractProvider {
67
67
  );
68
68
  }
69
69
 
70
- const filePath = join(storagePath, this.getFilename(filename));
70
+ const storedFilename = this.getFilename(filename);
71
+ const filePath = join(storagePath, storedFilename);
71
72
 
72
73
  await writeFile(filePath, fileContent);
73
74
 
74
- return filePath;
75
+ return storedFilename;
75
76
  }
76
77
 
77
78
  async upload(destination: string, file: MulterFile): Promise<any> {
@@ -92,11 +93,9 @@ export class LocalProvider extends AbstractProvider {
92
93
  `The storage path does not exist: ${storagePath}`,
93
94
  );
94
95
  }
95
- const filePath = join(storagePath, this.getFilename(file.originalname));
96
-
97
- await writeFile(filePath, file.buffer);
98
-
99
- return filePath;
96
+ const storedFilename = this.getFilename(file.originalname);
97
+ await writeFile(join(storagePath, storedFilename), file.buffer);
98
+ return storedFilename;
100
99
  }
101
100
 
102
101
  async uploadFromPath(
@@ -121,10 +120,8 @@ export class LocalProvider extends AbstractProvider {
121
120
  }
122
121
 
123
122
  const targetFilename = this.getFilename(options?.filename ?? basename(sourcePath));
124
- const targetPath = join(storagePath, targetFilename);
125
- await copyFile(sourcePath, targetPath);
126
-
127
- return targetPath;
123
+ await copyFile(sourcePath, join(storagePath, targetFilename));
124
+ return targetFilename;
128
125
  }
129
126
 
130
127
  async delete(filepath: string): Promise<any> {
@@ -3,4 +3,5 @@ export enum EnumProvider {
3
3
  LOCAL = 'local',
4
4
  AZURE = 'abs',
5
5
  GCS = 'gcs',
6
+ SPACES = 'spaces',
6
7
  }
@@ -5,22 +5,37 @@ import { GCSProvider } from './gcs.provider';
5
5
  import { LocalProvider } from './local.provider';
6
6
  import { EnumProvider } from './provider.enum';
7
7
  import { S3Provider } from './s3.provider';
8
+ import { SpacesProvider } from './spaces.provider';
8
9
 
9
10
  export class ProviderFactory {
10
- static create(
11
+ static async create(
11
12
  providerType: EnumProvider,
12
13
  setting: Record<string, string>,
13
14
  jwtService: JwtService,
14
- ): AbstractProvider {
15
+ ): Promise<AbstractProvider> {
16
+ let provider: AbstractProvider;
17
+
15
18
  switch (providerType) {
16
19
  case EnumProvider.S3:
17
- return new S3Provider(setting);
20
+ provider = new S3Provider(setting);
21
+ break;
18
22
  case EnumProvider.LOCAL:
19
- return new LocalProvider(setting, jwtService);
23
+ provider = new LocalProvider(setting, jwtService);
24
+ break;
20
25
  case EnumProvider.AZURE:
21
- return new AzureProvider(setting);
26
+ provider = new AzureProvider(setting);
27
+ break;
22
28
  case EnumProvider.GCS:
23
- return new GCSProvider(setting);
29
+ provider = new GCSProvider(setting);
30
+ break;
31
+ case EnumProvider.SPACES:
32
+ provider = new SpacesProvider(setting);
33
+ break;
34
+ default:
35
+ throw new Error(`Unsupported provider type: ${providerType}`);
24
36
  }
37
+
38
+ await provider.initValidation();
39
+ return provider;
25
40
  }
26
41
  }
@@ -20,28 +20,13 @@ export class S3Provider extends AbstractProvider {
20
20
 
21
21
  constructor(private setting: Record<string, string>) {
22
22
  super();
23
- this.initValidation();
24
23
  }
25
24
 
26
25
  private resolveS3Key(filepath: string): string {
27
26
  if (!filepath) {
28
27
  throw new Error('Invalid filepath for S3');
29
28
  }
30
-
31
- try {
32
- const url = new URL(filepath);
33
- const keyFromUrl = url.pathname.split('/').filter(Boolean).join('/');
34
- if (!keyFromUrl) {
35
- throw new Error(`Invalid filepath "${filepath}" for S3`);
36
- }
37
- return keyFromUrl;
38
- } catch {
39
- const keyFromPath = filepath.split('/').filter(Boolean).join('/');
40
- if (!keyFromPath) {
41
- throw new Error(`Invalid filepath "${filepath}" for S3`);
42
- }
43
- return keyFromPath;
44
- }
29
+ return filepath;
45
30
  }
46
31
 
47
32
  async buffer(filepath: string): Promise<Buffer> {
@@ -151,8 +136,7 @@ export class S3Provider extends AbstractProvider {
151
136
  Body: fileContent,
152
137
  });
153
138
  await s3.send(command);
154
- // S3 v3 não retorna Location diretamente, construa a URL:
155
- return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
139
+ return storedFilename;
156
140
  }
157
141
 
158
142
  async upload(destination: string, file: MulterFile): Promise<any> {
@@ -165,7 +149,7 @@ export class S3Provider extends AbstractProvider {
165
149
  Body: file.buffer,
166
150
  });
167
151
  await s3.send(command);
168
- return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
152
+ return storedFilename;
169
153
  }
170
154
 
171
155
  async uploadFromPath(
@@ -185,7 +169,7 @@ export class S3Provider extends AbstractProvider {
185
169
  });
186
170
 
187
171
  await s3.send(command);
188
- return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
172
+ return storedFilename;
189
173
  }
190
174
 
191
175
  async delete(filepath: string): Promise<any> {