@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.
- package/dist/ai/ai.service.d.ts +3 -12
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +34 -125
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +3 -3
- package/dist/core.module.js.map +1 -1
- package/dist/file/file.service.d.ts +1 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +50 -64
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/provider/provider.enum.d.ts +2 -1
- package/dist/file/provider/provider.enum.d.ts.map +1 -1
- package/dist/file/provider/provider.enum.js +1 -0
- package/dist/file/provider/provider.enum.js.map +1 -1
- package/dist/file/provider/provider.factory.d.ts.map +1 -1
- package/dist/file/provider/provider.factory.js +3 -0
- package/dist/file/provider/provider.factory.js.map +1 -1
- package/dist/file/provider/spaces.provider.d.ts +27 -0
- package/dist/file/provider/spaces.provider.d.ts.map +1 -0
- package/dist/file/provider/spaces.provider.js +218 -0
- package/dist/file/provider/spaces.provider.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/dto/create.dto.d.ts +9 -0
- package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/create.dto.js +54 -0
- package/dist/integration-profile/dto/create.dto.js.map +1 -0
- package/dist/integration-profile/dto/export.dto.d.ts +5 -0
- package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/export.dto.js +29 -0
- package/dist/integration-profile/dto/export.dto.js.map +1 -0
- package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
- package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
- package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
- package/dist/integration-profile/dto/update.dto.d.ts +6 -0
- package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/update.dto.js +9 -0
- package/dist/integration-profile/dto/update.dto.js.map +1 -0
- package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
- package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.controller.js +165 -0
- package/dist/integration-profile/integration-profile.controller.js.map +1 -0
- package/dist/integration-profile/integration-profile.module.d.ts +3 -0
- package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.module.js +29 -0
- package/dist/integration-profile/integration-profile.module.js.map +1 -0
- package/dist/integration-profile/integration-profile.service.d.ts +160 -0
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.service.js +606 -0
- package/dist/integration-profile/integration-profile.service.js.map +1 -0
- package/dist/integration-profile/integration-profile.utils.d.ts +24 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.utils.js +147 -0
- package/dist/integration-profile/integration-profile.utils.js.map +1 -0
- package/dist/mail/mail.module.d.ts.map +1 -1
- package/dist/mail/mail.module.js +27 -19
- package/dist/mail/mail.module.js.map +1 -1
- package/dist/mail/mail.service.d.ts +3 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +25 -47
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mcp/mcp.module.js +2 -2
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +3 -15
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js +12 -94
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +17 -16
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +0 -2
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
- package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
- package/dist/webhook-integration/event-webhook.controller.js +10 -0
- package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
- package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
- package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
- package/dist/webhook-integration/event-webhook.service.js +30 -0
- package/dist/webhook-integration/event-webhook.service.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.d.ts +1 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +10 -0
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +2 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +62 -44
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/dist/whatsapp-profile/dto/create.dto.d.ts +11 -0
- package/dist/whatsapp-profile/dto/create.dto.d.ts.map +1 -0
- package/dist/whatsapp-profile/dto/create.dto.js +55 -0
- package/dist/whatsapp-profile/dto/create.dto.js.map +1 -0
- package/dist/whatsapp-profile/dto/update.dto.d.ts +6 -0
- package/dist/whatsapp-profile/dto/update.dto.d.ts.map +1 -0
- package/dist/whatsapp-profile/dto/update.dto.js +9 -0
- package/dist/whatsapp-profile/dto/update.dto.js.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts +54 -0
- package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.controller.js +85 -0
- package/dist/whatsapp-profile/whatsapp-profile.controller.js.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.module.d.ts +3 -0
- package/dist/whatsapp-profile/whatsapp-profile.module.d.ts.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.module.js +26 -0
- package/dist/whatsapp-profile/whatsapp-profile.module.js.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.service.d.ts +63 -0
- package/dist/whatsapp-profile/whatsapp-profile.service.d.ts.map +1 -0
- package/dist/whatsapp-profile/whatsapp-profile.service.js +138 -0
- package/dist/whatsapp-profile/whatsapp-profile.service.js.map +1 -0
- package/hedhog/data/file_provider.yaml +9 -1
- package/hedhog/data/integration_provider.yaml +132 -0
- package/hedhog/data/integration_type.yaml +29 -0
- package/hedhog/data/menu.yaml +15 -14
- package/hedhog/data/role.yaml +8 -0
- package/hedhog/data/route.yaml +66 -15
- package/hedhog/data/setting_group.yaml +30 -191
- package/hedhog/data/setting_subgroup.yaml +5 -13
- package/hedhog/data/whatsapp_profile.yaml +6 -0
- package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +579 -204
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1662 -0
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +233 -108
- package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +3 -822
- package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
- package/hedhog/frontend/messages/en.json +172 -0
- package/hedhog/frontend/messages/pt.json +172 -0
- package/hedhog/table/integration_profile.yaml +34 -0
- package/hedhog/table/integration_provider.yaml +23 -0
- package/hedhog/table/integration_type.yaml +19 -0
- package/hedhog/table/webhook_action.yaml +85 -85
- package/hedhog/table/whatsapp_profile.yaml +26 -0
- package/package.json +2 -2
- package/src/ai/ai.service.ts +35 -153
- package/src/core.module.ts +3 -3
- package/src/file/file.service.ts +60 -76
- package/src/file/provider/provider.enum.ts +1 -0
- package/src/file/provider/provider.factory.ts +3 -0
- package/src/file/provider/spaces.provider.ts +261 -0
- package/src/index.ts +6 -3
- package/src/integration-profile/dto/create.dto.ts +44 -0
- package/src/integration-profile/dto/export.dto.ts +15 -0
- package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
- package/src/integration-profile/dto/update.dto.ts +4 -0
- package/src/integration-profile/integration-profile.controller.ts +129 -0
- package/src/integration-profile/integration-profile.module.ts +16 -0
- package/src/integration-profile/integration-profile.service.ts +788 -0
- package/src/integration-profile/integration-profile.utils.ts +156 -0
- package/src/mail/mail.module.ts +27 -18
- package/src/mail/mail.service.ts +31 -60
- package/src/mcp/mcp.module.ts +2 -2
- package/src/mcp/tools/core-mail-profile.mcp-tools.ts +10 -69
- package/src/mcp-chat/mcp-chat.service.ts +23 -20
- package/src/setting/setting.service.ts +0 -2
- package/src/webhook-integration/event-webhook.controller.ts +55 -50
- package/src/webhook-integration/event-webhook.service.ts +422 -384
- package/src/webhook-integration/webhook-integration.controller.ts +5 -0
- package/src/webhook-integration/webhook-integration.service.ts +74 -46
- package/src/whatsapp-profile/dto/create.dto.ts +36 -0
- package/src/whatsapp-profile/dto/update.dto.ts +4 -0
- package/src/whatsapp-profile/whatsapp-profile.controller.ts +51 -0
- package/src/whatsapp-profile/whatsapp-profile.module.ts +13 -0
- package/src/whatsapp-profile/whatsapp-profile.service.ts +160 -0
package/src/file/file.service.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
'storage
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!
|
|
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
|
-
`
|
|
91
|
+
`Integration profile with id ${profileId} is not a storage profile.`,
|
|
78
92
|
);
|
|
79
93
|
}
|
|
80
94
|
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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 ${
|
|
106
|
+
throw new BadRequestException(`Provider ${fileProviderSlug} not found.`);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
this.providerId = providerData.id;
|
|
102
|
-
return
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
222
|
+
return resolveStorageEnumProvider(profile.integration_provider.slug);
|
|
239
223
|
}
|
|
240
224
|
|
|
241
225
|
private getProviderPath(storage: string, filepath: string) {
|
|
@@ -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
|
-
|
|
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
|
+
}
|