@hed-hog/core 0.0.354 → 0.0.356

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 (167) 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/core.module.d.ts.map +1 -1
  6. package/dist/core.module.js +3 -3
  7. package/dist/core.module.js.map +1 -1
  8. package/dist/file/file.service.d.ts +1 -1
  9. package/dist/file/file.service.d.ts.map +1 -1
  10. package/dist/file/file.service.js +50 -64
  11. package/dist/file/file.service.js.map +1 -1
  12. package/dist/file/provider/provider.enum.d.ts +2 -1
  13. package/dist/file/provider/provider.enum.d.ts.map +1 -1
  14. package/dist/file/provider/provider.enum.js +1 -0
  15. package/dist/file/provider/provider.enum.js.map +1 -1
  16. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  17. package/dist/file/provider/provider.factory.js +3 -0
  18. package/dist/file/provider/provider.factory.js.map +1 -1
  19. package/dist/file/provider/spaces.provider.d.ts +27 -0
  20. package/dist/file/provider/spaces.provider.d.ts.map +1 -0
  21. package/dist/file/provider/spaces.provider.js +218 -0
  22. package/dist/file/provider/spaces.provider.js.map +1 -0
  23. package/dist/index.d.ts +4 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +4 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/integration-profile/dto/create.dto.d.ts +9 -0
  28. package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
  29. package/dist/integration-profile/dto/create.dto.js +54 -0
  30. package/dist/integration-profile/dto/create.dto.js.map +1 -0
  31. package/dist/integration-profile/dto/export.dto.d.ts +5 -0
  32. package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
  33. package/dist/integration-profile/dto/export.dto.js +29 -0
  34. package/dist/integration-profile/dto/export.dto.js.map +1 -0
  35. package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
  36. package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
  37. package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
  38. package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
  39. package/dist/integration-profile/dto/update.dto.d.ts +6 -0
  40. package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
  41. package/dist/integration-profile/dto/update.dto.js +9 -0
  42. package/dist/integration-profile/dto/update.dto.js.map +1 -0
  43. package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
  44. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
  45. package/dist/integration-profile/integration-profile.controller.js +165 -0
  46. package/dist/integration-profile/integration-profile.controller.js.map +1 -0
  47. package/dist/integration-profile/integration-profile.module.d.ts +3 -0
  48. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
  49. package/dist/integration-profile/integration-profile.module.js +29 -0
  50. package/dist/integration-profile/integration-profile.module.js.map +1 -0
  51. package/dist/integration-profile/integration-profile.service.d.ts +160 -0
  52. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
  53. package/dist/integration-profile/integration-profile.service.js +606 -0
  54. package/dist/integration-profile/integration-profile.service.js.map +1 -0
  55. package/dist/integration-profile/integration-profile.utils.d.ts +24 -0
  56. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
  57. package/dist/integration-profile/integration-profile.utils.js +147 -0
  58. package/dist/integration-profile/integration-profile.utils.js.map +1 -0
  59. package/dist/mail/mail.module.d.ts.map +1 -1
  60. package/dist/mail/mail.module.js +27 -19
  61. package/dist/mail/mail.module.js.map +1 -1
  62. package/dist/mail/mail.service.d.ts +3 -2
  63. package/dist/mail/mail.service.d.ts.map +1 -1
  64. package/dist/mail/mail.service.js +25 -47
  65. package/dist/mail/mail.service.js.map +1 -1
  66. package/dist/mcp/mcp.module.js +2 -2
  67. package/dist/mcp/mcp.module.js.map +1 -1
  68. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +3 -15
  69. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -1
  70. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +12 -94
  71. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -1
  72. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  73. package/dist/mcp-chat/mcp-chat.service.js +17 -16
  74. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  75. package/dist/setting/setting.service.d.ts.map +1 -1
  76. package/dist/setting/setting.service.js +0 -2
  77. package/dist/setting/setting.service.js.map +1 -1
  78. package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
  79. package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
  80. package/dist/webhook-integration/event-webhook.controller.js +10 -0
  81. package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
  82. package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
  83. package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
  84. package/dist/webhook-integration/event-webhook.service.js +30 -0
  85. package/dist/webhook-integration/event-webhook.service.js.map +1 -1
  86. package/dist/webhook-integration/webhook-integration.controller.d.ts +1 -0
  87. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  88. package/dist/webhook-integration/webhook-integration.controller.js +10 -0
  89. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  90. package/dist/webhook-integration/webhook-integration.service.d.ts +2 -1
  91. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  92. package/dist/webhook-integration/webhook-integration.service.js +62 -44
  93. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  94. package/dist/whatsapp-profile/dto/create.dto.d.ts +11 -0
  95. package/dist/whatsapp-profile/dto/create.dto.d.ts.map +1 -0
  96. package/dist/whatsapp-profile/dto/create.dto.js +55 -0
  97. package/dist/whatsapp-profile/dto/create.dto.js.map +1 -0
  98. package/dist/whatsapp-profile/dto/update.dto.d.ts +6 -0
  99. package/dist/whatsapp-profile/dto/update.dto.d.ts.map +1 -0
  100. package/dist/whatsapp-profile/dto/update.dto.js +9 -0
  101. package/dist/whatsapp-profile/dto/update.dto.js.map +1 -0
  102. package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts +54 -0
  103. package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts.map +1 -0
  104. package/dist/whatsapp-profile/whatsapp-profile.controller.js +85 -0
  105. package/dist/whatsapp-profile/whatsapp-profile.controller.js.map +1 -0
  106. package/dist/whatsapp-profile/whatsapp-profile.module.d.ts +3 -0
  107. package/dist/whatsapp-profile/whatsapp-profile.module.d.ts.map +1 -0
  108. package/dist/whatsapp-profile/whatsapp-profile.module.js +26 -0
  109. package/dist/whatsapp-profile/whatsapp-profile.module.js.map +1 -0
  110. package/dist/whatsapp-profile/whatsapp-profile.service.d.ts +63 -0
  111. package/dist/whatsapp-profile/whatsapp-profile.service.d.ts.map +1 -0
  112. package/dist/whatsapp-profile/whatsapp-profile.service.js +138 -0
  113. package/dist/whatsapp-profile/whatsapp-profile.service.js.map +1 -0
  114. package/hedhog/data/file_provider.yaml +9 -1
  115. package/hedhog/data/integration_provider.yaml +132 -0
  116. package/hedhog/data/integration_type.yaml +29 -0
  117. package/hedhog/data/menu.yaml +15 -14
  118. package/hedhog/data/role.yaml +8 -0
  119. package/hedhog/data/route.yaml +66 -15
  120. package/hedhog/data/setting_group.yaml +30 -191
  121. package/hedhog/data/setting_subgroup.yaml +5 -13
  122. package/hedhog/data/whatsapp_profile.yaml +6 -0
  123. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
  124. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +579 -204
  125. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1662 -0
  126. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +233 -108
  127. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +3 -822
  128. package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
  129. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
  130. package/hedhog/frontend/messages/en.json +172 -0
  131. package/hedhog/frontend/messages/pt.json +172 -0
  132. package/hedhog/table/integration_profile.yaml +34 -0
  133. package/hedhog/table/integration_provider.yaml +23 -0
  134. package/hedhog/table/integration_type.yaml +19 -0
  135. package/hedhog/table/webhook_action.yaml +85 -85
  136. package/hedhog/table/whatsapp_profile.yaml +26 -0
  137. package/package.json +2 -2
  138. package/src/ai/ai.service.ts +35 -153
  139. package/src/core.module.ts +3 -3
  140. package/src/file/file.service.ts +60 -76
  141. package/src/file/provider/provider.enum.ts +1 -0
  142. package/src/file/provider/provider.factory.ts +3 -0
  143. package/src/file/provider/spaces.provider.ts +261 -0
  144. package/src/index.ts +6 -3
  145. package/src/integration-profile/dto/create.dto.ts +44 -0
  146. package/src/integration-profile/dto/export.dto.ts +15 -0
  147. package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
  148. package/src/integration-profile/dto/update.dto.ts +4 -0
  149. package/src/integration-profile/integration-profile.controller.ts +129 -0
  150. package/src/integration-profile/integration-profile.module.ts +16 -0
  151. package/src/integration-profile/integration-profile.service.ts +788 -0
  152. package/src/integration-profile/integration-profile.utils.ts +156 -0
  153. package/src/mail/mail.module.ts +27 -18
  154. package/src/mail/mail.service.ts +31 -60
  155. package/src/mcp/mcp.module.ts +2 -2
  156. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +10 -69
  157. package/src/mcp-chat/mcp-chat.service.ts +23 -20
  158. package/src/setting/setting.service.ts +0 -2
  159. package/src/webhook-integration/event-webhook.controller.ts +55 -50
  160. package/src/webhook-integration/event-webhook.service.ts +422 -384
  161. package/src/webhook-integration/webhook-integration.controller.ts +5 -0
  162. package/src/webhook-integration/webhook-integration.service.ts +74 -46
  163. package/src/whatsapp-profile/dto/create.dto.ts +36 -0
  164. package/src/whatsapp-profile/dto/update.dto.ts +4 -0
  165. package/src/whatsapp-profile/whatsapp-profile.controller.ts +51 -0
  166. package/src/whatsapp-profile/whatsapp-profile.module.ts +13 -0
  167. package/src/whatsapp-profile/whatsapp-profile.service.ts +160 -0
@@ -13,6 +13,11 @@ import { JwtService } from '@nestjs/jwt';
13
13
  import { createWriteStream, promises as fs } from 'fs';
14
14
  import { basename, dirname } from 'path';
15
15
  import { pipeline } from 'stream/promises';
16
+ import {
17
+ buildStorageConfigFromIntegration,
18
+ resolveFileProviderSlug,
19
+ resolveStorageEnumProvider,
20
+ } from '../integration-profile/integration-profile.utils';
16
21
  import { SettingService } from '../setting/setting.service';
17
22
  import { DeleteDTO } from './dto/delete.dto';
18
23
  import { AbstractProvider } from './provider/abstract.provider';
@@ -53,53 +58,56 @@ export class FileService implements OnModuleInit {
53
58
  ) { }
54
59
 
55
60
  async onModuleInit() {
56
- await this.getProvider();
61
+ try {
62
+ await this.getProvider();
63
+ } catch {
64
+ // No storage profile configured yet — server starts normally, uploads will
65
+ // fail until a profile is selected in Settings → File Storage.
66
+ }
57
67
  }
58
68
 
59
69
  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']) {
70
+ const settings = await this.settingService.getSettingValues(['file-storage-profile-id']);
71
+ const profileId = Number(settings['file-storage-profile-id']);
72
+
73
+ if (!profileId) {
74
+ throw new BadRequestException('No file storage profile configured. Set a storage profile in the File Storage settings.');
75
+ }
76
+
77
+ const profile = await this.prismaService.integration_profile.findUnique({
78
+ where: { id: profileId },
79
+ include: {
80
+ integration_type: { select: { slug: true } },
81
+ integration_provider: { select: { slug: true } },
82
+ },
83
+ });
84
+
85
+ if (!profile) {
86
+ throw new NotFoundException(`Storage profile with id ${profileId} not found.`);
87
+ }
88
+
89
+ if (profile.integration_type.slug !== 'storage') {
76
90
  throw new BadRequestException(
77
- `You must set the storage provider in the setting.`,
91
+ `Integration profile with id ${profileId} is not a storage profile.`,
78
92
  );
79
93
  }
80
94
 
81
- const providerName = settings['storage'];
82
- const provider = ProviderFactory.create(
83
- providerName as EnumProvider,
84
- settings,
85
- this.jwtService,
86
- );
95
+ const providerSlug = profile.integration_provider.slug;
96
+ const config = buildStorageConfigFromIntegration(providerSlug, profile.config);
97
+ const enumProvider = resolveStorageEnumProvider(providerSlug);
98
+ const fileProviderSlug = resolveFileProviderSlug(providerSlug);
87
99
 
88
100
  const providerData = await this.prismaService.file_provider.findFirst({
89
- where: {
90
- slug: providerName,
91
- },
92
- select: {
93
- id: true,
94
- },
101
+ where: { slug: fileProviderSlug },
102
+ select: { id: true },
95
103
  });
96
104
 
97
105
  if (!providerData) {
98
- throw new BadRequestException(`Provider ${providerName} not found.`);
106
+ throw new BadRequestException(`Provider ${fileProviderSlug} not found.`);
99
107
  }
100
108
 
101
109
  this.providerId = providerData.id;
102
- return provider;
110
+ return ProviderFactory.create(enumProvider, config, this.jwtService);
103
111
  }
104
112
 
105
113
  async getMimeType(mimetype: string) {
@@ -130,58 +138,22 @@ export class FileService implements OnModuleInit {
130
138
  }
131
139
 
132
140
  async acceptMimetypes(mimetype: string) {
133
-
134
141
  if (!mimetype) {
135
142
  throw new BadRequestException(`No mimetype provided`);
136
143
  }
137
144
 
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
-
145
+ const settings = await this.settingService.getSettingValues(['storage-accept-mimetype']);
155
146
  const acceptMimetypes = settings['storage-accept-mimetype'];
156
147
  return acceptMimetypes.indexOf(mimetype) !== -1;
157
148
  }
158
149
 
159
150
  async maxFileSize(size: number) {
160
-
161
151
  if (!size) {
162
152
  throw new BadRequestException(`No size provided`);
163
153
  }
164
154
 
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);
155
+ const settings = await this.settingService.getSettingValues(['storage-max-size']);
156
+ return size <= Number(settings['storage-max-size']);
185
157
  }
186
158
 
187
159
  async uploadFromString(
@@ -230,12 +202,24 @@ export class FileService implements OnModuleInit {
230
202
  return file;
231
203
  }
232
204
 
233
- async getStorage() {
234
- const settings = await this.settingService.getSettingValues([
235
- 'storage',
236
- ]);
205
+ async getStorage(): Promise<string> {
206
+ const settings = await this.settingService.getSettingValues(['file-storage-profile-id']);
207
+ const profileId = Number(settings['file-storage-profile-id']);
208
+
209
+ if (!profileId) {
210
+ return EnumProvider.LOCAL;
211
+ }
212
+
213
+ const profile = await this.prismaService.integration_profile.findUnique({
214
+ where: { id: profileId },
215
+ include: { integration_provider: { select: { slug: true } } },
216
+ });
217
+
218
+ if (!profile) {
219
+ return EnumProvider.LOCAL;
220
+ }
237
221
 
238
- return settings['storage'];
222
+ return resolveStorageEnumProvider(profile.integration_provider.slug);
239
223
  }
240
224
 
241
225
  private getProviderPath(storage: string, filepath: string) {
@@ -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,6 +5,7 @@ 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
11
  static create(
@@ -21,6 +22,8 @@ export class ProviderFactory {
21
22
  return new AzureProvider(setting);
22
23
  case EnumProvider.GCS:
23
24
  return new GCSProvider(setting);
25
+ case EnumProvider.SPACES:
26
+ return new SpacesProvider(setting);
24
27
  }
25
28
  }
26
29
  }
@@ -0,0 +1,261 @@
1
+ import {
2
+ DeleteObjectCommand,
3
+ GetObjectCommand,
4
+ HeadBucketCommand,
5
+ HeadObjectCommand,
6
+ PutObjectCommand,
7
+ S3,
8
+ S3Client,
9
+ } from '@aws-sdk/client-s3';
10
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
11
+ import { BadRequestException } from '@nestjs/common';
12
+ import { createReadStream } from 'fs';
13
+ import { basename } from 'path';
14
+ import { Readable } from 'stream';
15
+ import { AbstractProvider } from './abstract.provider';
16
+
17
+ export class SpacesProvider extends AbstractProvider {
18
+ private S3: S3 | undefined;
19
+ private S3Client: S3Client | undefined;
20
+
21
+ constructor(private setting: Record<string, string>) {
22
+ super();
23
+ this.initValidation();
24
+ }
25
+
26
+ private get endpoint() {
27
+ return `https://${this.setting['storage-spaces-region']}.digitaloceanspaces.com`;
28
+ }
29
+
30
+ private get bucketUrl() {
31
+ const cdn = this.setting['storage-spaces-cdn'];
32
+ if (cdn) {
33
+ return cdn.replace(/\/$/, '');
34
+ }
35
+ return `https://${this.setting['storage-spaces-bucket']}.${this.setting['storage-spaces-region']}.digitaloceanspaces.com`;
36
+ }
37
+
38
+ private resolveKey(filepath: string): string {
39
+ if (!filepath) {
40
+ throw new Error('Invalid filepath for DigitalOcean Spaces');
41
+ }
42
+
43
+ try {
44
+ const url = new URL(filepath);
45
+ const key = url.pathname.split('/').filter(Boolean).join('/');
46
+ if (!key) {
47
+ throw new Error(`Invalid filepath "${filepath}" for DigitalOcean Spaces`);
48
+ }
49
+ return key;
50
+ } catch {
51
+ const key = filepath.split('/').filter(Boolean).join('/');
52
+ if (!key) {
53
+ throw new Error(`Invalid filepath "${filepath}" for DigitalOcean Spaces`);
54
+ }
55
+ return key;
56
+ }
57
+ }
58
+
59
+ async initValidation() {
60
+ if (!this.setting['storage-spaces-key']) {
61
+ throw new BadRequestException(
62
+ `You must set the storage-spaces-key in the setting.`,
63
+ );
64
+ }
65
+
66
+ if (!this.setting['storage-spaces-secret']) {
67
+ throw new BadRequestException(
68
+ `You must set the storage-spaces-secret in the setting.`,
69
+ );
70
+ }
71
+
72
+ if (!this.setting['storage-spaces-region']) {
73
+ throw new BadRequestException(
74
+ `You must set the storage-spaces-region in the setting.`,
75
+ );
76
+ }
77
+
78
+ if (!this.setting['storage-spaces-bucket']) {
79
+ throw new BadRequestException(
80
+ `You must set the storage-spaces-bucket in the setting.`,
81
+ );
82
+ }
83
+
84
+ const s3 = await this.getClient();
85
+
86
+ try {
87
+ await s3.send(
88
+ new HeadBucketCommand({
89
+ Bucket: this.setting['storage-spaces-bucket'],
90
+ }),
91
+ );
92
+ } catch (error) {
93
+ throw new BadRequestException(
94
+ `The bucket "${this.setting['storage-spaces-bucket']}" does not exist or you do not have access to it.`,
95
+ );
96
+ }
97
+ }
98
+
99
+ async getClient() {
100
+ if (this.S3) {
101
+ return this.S3;
102
+ }
103
+
104
+ const credentials = {
105
+ accessKeyId: this.setting['storage-spaces-key'],
106
+ secretAccessKey: this.setting['storage-spaces-secret'],
107
+ };
108
+
109
+ this.S3Client = new S3Client({
110
+ credentials,
111
+ endpoint: this.endpoint,
112
+ region: this.setting['storage-spaces-region'],
113
+ forcePathStyle: false,
114
+ });
115
+
116
+ return (this.S3 = new S3({
117
+ credentials,
118
+ endpoint: this.endpoint,
119
+ region: this.setting['storage-spaces-region'],
120
+ forcePathStyle: false,
121
+ }));
122
+ }
123
+
124
+ async getS3Client() {
125
+ if (this.S3Client) {
126
+ return this.S3Client;
127
+ }
128
+
129
+ this.S3Client = new S3Client({
130
+ credentials: {
131
+ accessKeyId: this.setting['storage-spaces-key'],
132
+ secretAccessKey: this.setting['storage-spaces-secret'],
133
+ },
134
+ endpoint: this.endpoint,
135
+ region: this.setting['storage-spaces-region'],
136
+ forcePathStyle: false,
137
+ });
138
+
139
+ return this.S3Client;
140
+ }
141
+
142
+ async buffer(filepath: string): Promise<Buffer> {
143
+ const s3 = await this.getClient();
144
+ const key = this.resolveKey(filepath);
145
+ const result = await s3.send(
146
+ new GetObjectCommand({
147
+ Bucket: this.setting['storage-spaces-bucket'],
148
+ Key: key,
149
+ }),
150
+ );
151
+ // @ts-ignore
152
+ const stream = result.Body as Readable;
153
+ const chunks: Buffer[] = [];
154
+ for await (const chunk of stream) {
155
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
156
+ }
157
+ return Buffer.concat(chunks);
158
+ }
159
+
160
+ async upload(destination: string, file: MulterFile): Promise<any> {
161
+ const s3 = await this.getClient();
162
+ const storedFilename = this.getFilename(file.originalname);
163
+ const key = [destination, storedFilename].join('/');
164
+ await s3.send(
165
+ new PutObjectCommand({
166
+ Bucket: this.setting['storage-spaces-bucket'],
167
+ Key: key,
168
+ Body: file.buffer,
169
+ ACL: 'public-read',
170
+ }),
171
+ );
172
+ return `${this.bucketUrl}/${key}`;
173
+ }
174
+
175
+ async uploadFromString(
176
+ destination: string,
177
+ filename: string,
178
+ fileContent: string,
179
+ ): Promise<any> {
180
+ const s3 = await this.getClient();
181
+ const storedFilename = this.getFilename(filename);
182
+ const key = [destination, storedFilename].join('/');
183
+ await s3.send(
184
+ new PutObjectCommand({
185
+ Bucket: this.setting['storage-spaces-bucket'],
186
+ Key: key,
187
+ Body: fileContent,
188
+ ACL: 'public-read',
189
+ }),
190
+ );
191
+ return `${this.bucketUrl}/${key}`;
192
+ }
193
+
194
+ async uploadFromPath(
195
+ destination: string,
196
+ filePath: string,
197
+ options?: { filename?: string; mimetype?: string },
198
+ ): Promise<any> {
199
+ const s3 = await this.getClient();
200
+ const originalName = options?.filename ?? basename(filePath);
201
+ const storedFilename = this.getFilename(originalName);
202
+ const key = [destination, storedFilename].join('/');
203
+ await s3.send(
204
+ new PutObjectCommand({
205
+ Bucket: this.setting['storage-spaces-bucket'],
206
+ Key: key,
207
+ Body: createReadStream(filePath),
208
+ ContentType: options?.mimetype,
209
+ ACL: 'public-read',
210
+ }),
211
+ );
212
+ return `${this.bucketUrl}/${key}`;
213
+ }
214
+
215
+ async delete(filepath: string): Promise<any> {
216
+ const s3 = await this.getClient();
217
+ const key = this.resolveKey(filepath);
218
+ await s3.send(
219
+ new DeleteObjectCommand({
220
+ Bucket: this.setting['storage-spaces-bucket'],
221
+ Key: key,
222
+ }),
223
+ );
224
+ return true;
225
+ }
226
+
227
+ async readStream(filepath: string): Promise<Readable> {
228
+ const s3 = await this.getClient();
229
+ const key = this.resolveKey(filepath);
230
+ const result = await s3.send(
231
+ new GetObjectCommand({
232
+ Bucket: this.setting['storage-spaces-bucket'],
233
+ Key: key,
234
+ }),
235
+ );
236
+ // @ts-ignore
237
+ return result.Body as Readable;
238
+ }
239
+
240
+ async metaData(filepath: string): Promise<any> {
241
+ const s3 = await this.getClient();
242
+ const key = this.resolveKey(filepath);
243
+ const result = await s3.send(
244
+ new HeadObjectCommand({
245
+ Bucket: this.setting['storage-spaces-bucket'],
246
+ Key: key,
247
+ }),
248
+ );
249
+ return result;
250
+ }
251
+
252
+ async tempURL(filepath: string, expires = 3600): Promise<string> {
253
+ const s3Client = await this.getS3Client();
254
+ const key = this.resolveKey(filepath);
255
+ const command = new GetObjectCommand({
256
+ Bucket: this.setting['storage-spaces-bucket'],
257
+ Key: key,
258
+ });
259
+ return await getSignedUrl(s3Client as any, command, { expiresIn: expires });
260
+ }
261
+ }
package/src/index.ts CHANGED
@@ -19,11 +19,13 @@ export * from './screen/screen.service';
19
19
  export * from './user/constants/user.constants';
20
20
  export * from './user/user.service';
21
21
 
22
- export * from './mail-profile/mail-profile.module';
23
- export * from './mail-profile/mail-profile.service';
24
22
  export * from './mail/mail.module';
25
23
  export * from './mail/mail.service';
26
24
 
25
+ export * from './integration-profile/integration-profile.module';
26
+ export * from './integration-profile/integration-profile.service';
27
+ export * from './integration-profile/integration-profile.utils';
28
+
27
29
  export * from './file/file.module';
28
30
  export * from './file/file.service';
29
31
 
@@ -72,6 +74,7 @@ export * from './utils/locale-context';
72
74
 
73
75
 
74
76
  // Notification Module
77
+ export * from './notification/dto/create-notification.dto';
75
78
  export * from './notification/notification.module';
76
79
  export * from './notification/notification.service';
77
- export * from './notification/dto/create-notification.dto';
80
+
@@ -0,0 +1,44 @@
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { Transform, Type } from 'class-transformer';
3
+ import {
4
+ IsBoolean,
5
+ IsInt,
6
+ IsObject,
7
+ IsOptional,
8
+ IsString,
9
+ MaxLength,
10
+ Min,
11
+ } from 'class-validator';
12
+
13
+ export class CreateDTO {
14
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
15
+ @MaxLength(255)
16
+ @Transform(({ value }) => String(value ?? '').trim())
17
+ slug: string;
18
+
19
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
20
+ @MaxLength(127)
21
+ @Transform(({ value }) => String(value ?? '').trim())
22
+ name: string;
23
+
24
+ @IsInt()
25
+ @Min(1)
26
+ @Type(() => Number)
27
+ type_id: number;
28
+
29
+ @IsInt()
30
+ @Min(1)
31
+ @Type(() => Number)
32
+ provider_id: number;
33
+
34
+ @IsOptional()
35
+ @IsObject()
36
+ config?: Record<string, unknown> | null;
37
+
38
+ @IsOptional()
39
+ @IsBoolean()
40
+ @Transform(({ value }) =>
41
+ value === true || value === 'true' || value === 1 || value === '1',
42
+ )
43
+ is_active?: boolean;
44
+ }
@@ -0,0 +1,15 @@
1
+ import { Transform } from 'class-transformer';
2
+ import { IsBoolean, IsOptional, IsString } from 'class-validator';
3
+
4
+ export class ExportDTO {
5
+ @IsOptional()
6
+ @IsString()
7
+ ids?: string;
8
+
9
+ @IsOptional()
10
+ @IsBoolean()
11
+ @Transform(({ value }) =>
12
+ value === true || value === 'true' || value === 1 || value === '1',
13
+ )
14
+ include_secrets?: boolean;
15
+ }
@@ -0,0 +1,61 @@
1
+ import { Type } from 'class-transformer';
2
+ import {
3
+ ArrayNotEmpty,
4
+ IsArray,
5
+ IsBoolean,
6
+ IsIn,
7
+ IsObject,
8
+ IsOptional,
9
+ IsString,
10
+ MaxLength,
11
+ ValidateNested,
12
+ } from 'class-validator';
13
+
14
+ export class ImportProfileItemDTO {
15
+ @IsString()
16
+ @MaxLength(255)
17
+ slug: string;
18
+
19
+ @IsString()
20
+ @MaxLength(127)
21
+ name: string;
22
+
23
+ @IsString()
24
+ @MaxLength(255)
25
+ type_slug: string;
26
+
27
+ @IsString()
28
+ @MaxLength(255)
29
+ provider_slug: string;
30
+
31
+ @IsOptional()
32
+ @IsObject()
33
+ config?: Record<string, unknown> | null;
34
+
35
+ @IsOptional()
36
+ @IsBoolean()
37
+ is_active?: boolean;
38
+ }
39
+
40
+ export class ConflictResolutionDTO {
41
+ @IsString()
42
+ @MaxLength(255)
43
+ slug: string;
44
+
45
+ @IsIn(['ignore', 'replace', 'rename_auto'])
46
+ action: 'ignore' | 'replace' | 'rename_auto';
47
+ }
48
+
49
+ export class ImportConfirmDTO {
50
+ @IsArray()
51
+ @ArrayNotEmpty()
52
+ @ValidateNested({ each: true })
53
+ @Type(() => ImportProfileItemDTO)
54
+ profiles: ImportProfileItemDTO[];
55
+
56
+ @IsOptional()
57
+ @IsArray()
58
+ @ValidateNested({ each: true })
59
+ @Type(() => ConflictResolutionDTO)
60
+ conflict_resolutions?: ConflictResolutionDTO[];
61
+ }
@@ -0,0 +1,4 @@
1
+ import { PartialType } from '@nestjs/mapped-types';
2
+ import { CreateDTO } from './create.dto';
3
+
4
+ export class UpdateDTO extends PartialType(CreateDTO) {}