@solidxai/core 0.1.12-beta.0 → 0.1.12-beta.10
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/commands/seed.command.d.ts.map +1 -1
- package/dist/commands/seed.command.js +10 -2
- package/dist/commands/seed.command.js.map +1 -1
- package/dist/constants/error-messages.d.ts +1 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +1 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/constants/media-file-types.d.ts +5 -0
- package/dist/constants/media-file-types.d.ts.map +1 -0
- package/dist/constants/media-file-types.js +27 -0
- package/dist/constants/media-file-types.js.map +1 -0
- package/dist/controllers/media.controller.d.ts +14 -11
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +72 -33
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/dtos/create-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/create-chatter-message.dto.js +7 -1
- package/dist/dtos/create-chatter-message.dto.js.map +1 -1
- package/dist/dtos/create-media.dto.d.ts +1 -0
- package/dist/dtos/create-media.dto.d.ts.map +1 -1
- package/dist/dtos/create-media.dto.js +7 -1
- package/dist/dtos/create-media.dto.js.map +1 -1
- package/dist/dtos/create-role-metadata.dto.d.ts +2 -0
- package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-role-metadata.dto.js +3 -1
- package/dist/dtos/create-role-metadata.dto.js.map +1 -1
- package/dist/dtos/post-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/post-chatter-message.dto.js +6 -1
- package/dist/dtos/post-chatter-message.dto.js.map +1 -1
- package/dist/dtos/update-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/update-chatter-message.dto.js +8 -1
- package/dist/dtos/update-chatter-message.dto.js.map +1 -1
- package/dist/dtos/update-chatter-note-message.dto.d.ts +1 -0
- package/dist/dtos/update-chatter-note-message.dto.d.ts.map +1 -1
- package/dist/dtos/update-chatter-note-message.dto.js +7 -1
- package/dist/dtos/update-chatter-note-message.dto.js.map +1 -1
- package/dist/dtos/update-media.dto.d.ts +1 -0
- package/dist/dtos/update-media.dto.d.ts.map +1 -1
- package/dist/dtos/update-media.dto.js +7 -1
- package/dist/dtos/update-media.dto.js.map +1 -1
- package/dist/entities/chatter-message.entity.d.ts +1 -0
- package/dist/entities/chatter-message.entity.d.ts.map +1 -1
- package/dist/entities/chatter-message.entity.js +5 -1
- package/dist/entities/chatter-message.entity.js.map +1 -1
- package/dist/entities/media.entity.js.map +1 -1
- package/dist/enums/auth-type.enum.d.ts +2 -1
- package/dist/enums/auth-type.enum.d.ts.map +1 -1
- package/dist/enums/auth-type.enum.js +1 -0
- package/dist/enums/auth-type.enum.js.map +1 -1
- package/dist/guards/authentication.guard.d.ts +3 -1
- package/dist/guards/authentication.guard.d.ts.map +1 -1
- package/dist/guards/authentication.guard.js +5 -1
- package/dist/guards/authentication.guard.js.map +1 -1
- package/dist/guards/media-signed-url.guard.d.ts +9 -0
- package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
- package/dist/guards/media-signed-url.guard.js +49 -0
- package/dist/guards/media-signed-url.guard.js.map +1 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.d.ts.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.js +9 -0
- package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -1
- package/dist/interfaces/chatter-mention-notification.interface.d.ts +27 -0
- package/dist/interfaces/chatter-mention-notification.interface.d.ts.map +1 -0
- package/dist/interfaces/chatter-mention-notification.interface.js +3 -0
- package/dist/interfaces/chatter-mention-notification.interface.js.map +1 -0
- package/dist/interfaces.d.ts +19 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js +2 -0
- package/dist/interfaces.js.map +1 -1
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts +17 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts.map +1 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js +106 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts +12 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js +39 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts +8 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js +10 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts +20 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js +48 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts +12 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js +39 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts +8 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js +10 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts +18 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js +45 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts +12 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js +39 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts +8 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js +10 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts +18 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js +45 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js.map +1 -0
- package/dist/repository/media.repository.d.ts.map +1 -1
- package/dist/repository/media.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +87 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -0
- package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.js +6 -1
- package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +95 -13
- package/dist/services/authentication.service.d.ts +2 -2
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +52 -43
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +7 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +85 -4
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/media-download-url.service.d.ts +22 -0
- package/dist/services/media-download-url.service.d.ts.map +1 -0
- package/dist/services/media-download-url.service.js +70 -0
- package/dist/services/media-download-url.service.js.map +1 -0
- package/dist/services/media-storage.utils.d.ts +24 -0
- package/dist/services/media-storage.utils.d.ts.map +1 -0
- package/dist/services/media-storage.utils.js +73 -0
- package/dist/services/media-storage.utils.js.map +1 -0
- package/dist/services/media.service.d.ts +20 -4
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +155 -55
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +34 -63
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +34 -38
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +8 -5
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +25 -1
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.js +1 -1
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/seed.command.ts +12 -3
- package/src/constants/error-messages.ts +1 -0
- package/src/constants/media-file-types.ts +44 -0
- package/src/controllers/media.controller.ts +44 -24
- package/src/dtos/create-chatter-message.dto.ts +5 -0
- package/src/dtos/create-media.dto.ts +8 -3
- package/src/dtos/create-role-metadata.dto.ts +2 -0
- package/src/dtos/post-chatter-message.dto.ts +4 -0
- package/src/dtos/update-chatter-message.dto.ts +6 -0
- package/src/dtos/update-chatter-note-message.dto.ts +5 -0
- package/src/dtos/update-media.dto.ts +8 -3
- package/src/entities/chatter-message.entity.ts +3 -0
- package/src/entities/media.entity.ts +1 -1
- package/src/enums/auth-type.enum.ts +1 -0
- package/src/guards/authentication.guard.ts +3 -0
- package/src/guards/media-signed-url.guard.ts +41 -0
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
- package/src/helpers/solid-core-error-codes-provider.service.ts +9 -0
- package/src/interfaces/chatter-mention-notification.interface.ts +29 -0
- package/src/interfaces.ts +20 -0
- package/src/jobs/chatter-mention-notification-email-queue-handler.service.ts +114 -0
- package/src/jobs/database/chatter-mention-notification-email-publisher-database.service.ts +23 -0
- package/src/jobs/database/chatter-mention-notification-email-queue-options-database.ts +9 -0
- package/src/jobs/database/chatter-mention-notification-email-subscriber-database.service.ts +32 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.ts +23 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-queue-options.ts +9 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.ts +30 -0
- package/src/jobs/redis/chatter-mention-notification-email-publisher-redis.service.ts +23 -0
- package/src/jobs/redis/chatter-mention-notification-email-queue-options-redis.ts +9 -0
- package/src/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.ts +30 -0
- package/src/repository/media.repository.ts +1 -0
- package/src/seeders/module-metadata-seeder.service.ts +109 -3
- package/src/seeders/permission-metadata-seeder.service.ts +6 -1
- package/src/seeders/seed-data/email-templates/chatter-mention-notification.handlebars.html +200 -0
- package/src/seeders/seed-data/solid-core-metadata.json +95 -13
- package/src/services/authentication.service.ts +66 -55
- package/src/services/chatter-message.service.ts +97 -2
- package/src/services/media-download-url.service.ts +74 -0
- package/src/services/media-storage.utils.ts +70 -0
- package/src/services/media.service.ts +187 -59
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +49 -76
- package/src/services/mediaStorageProviders/file-storage-provider.ts +41 -48
- package/src/services/role-metadata.service.ts +17 -5
- package/src/solid-core.module.ts +35 -2
- package/src/subscribers/audit.subscriber.ts +1 -1
|
@@ -9,6 +9,13 @@ import { MediaStorageProvider } from "src/interfaces";
|
|
|
9
9
|
import { DiskFileService, S3FileService } from "src/services/file";
|
|
10
10
|
import { Readable } from "stream";
|
|
11
11
|
import { MediaRepository } from "src/repository/media.repository";
|
|
12
|
+
import { MediaDownloadUrlService } from "src/services/media-download-url.service";
|
|
13
|
+
import {
|
|
14
|
+
buildMediaRecordCreateInput,
|
|
15
|
+
buildStoredMediaFileName,
|
|
16
|
+
getEffectiveS3Region,
|
|
17
|
+
resolveMediaIsPublic,
|
|
18
|
+
} from "src/services/media-storage.utils";
|
|
12
19
|
|
|
13
20
|
@Injectable()
|
|
14
21
|
export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
@@ -19,18 +26,14 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
19
26
|
readonly diskFileService: DiskFileService,
|
|
20
27
|
readonly s3FileService: S3FileService,
|
|
21
28
|
readonly mediaRepository: MediaRepository,
|
|
29
|
+
private readonly mediaDownloadUrlService: MediaDownloadUrlService,
|
|
22
30
|
) { }
|
|
23
31
|
|
|
24
32
|
async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
|
|
25
|
-
const
|
|
26
|
-
|| entity instanceof LegacyCommonEntityWithExistingId
|
|
27
|
-
|| entity instanceof LegacyCommonEntityWithGeneratedId;
|
|
28
|
-
if (!isSupportedEntity) {
|
|
29
|
-
throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
|
|
30
|
-
}
|
|
33
|
+
const entityId = this.getSupportedEntityId(entity);
|
|
31
34
|
const result: Media[] = [];
|
|
32
35
|
const storageProvider = mediaFieldMetadata.mediaStorageProvider;
|
|
33
|
-
const region = this.
|
|
36
|
+
const region = getEffectiveS3Region(this.configService, storageProvider.region);
|
|
34
37
|
|
|
35
38
|
for (const [stream, fileName] of streamPairs) {
|
|
36
39
|
const bucketName = storageProvider.bucketName;
|
|
@@ -45,15 +48,12 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
45
48
|
|
|
46
49
|
await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { region });
|
|
47
50
|
|
|
48
|
-
const mediaEntity = await this.mediaRepository.createMedia(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
|
|
55
|
-
fieldMetadataId: mediaFieldMetadata.id
|
|
56
|
-
}) as unknown as Media;
|
|
51
|
+
const mediaEntity = await this.mediaRepository.createMedia(
|
|
52
|
+
buildMediaRecordCreateInput(entityId, mediaFieldMetadata, storageProvider, {
|
|
53
|
+
relativeUri: fileName,
|
|
54
|
+
fileSize,
|
|
55
|
+
})
|
|
56
|
+
) as unknown as Media;
|
|
57
57
|
result.push(mediaEntity);
|
|
58
58
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
59
59
|
}
|
|
@@ -61,29 +61,22 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
|
|
64
|
-
const
|
|
65
|
-
|| entity instanceof LegacyCommonEntityWithExistingId
|
|
66
|
-
|| entity instanceof LegacyCommonEntityWithGeneratedId;
|
|
67
|
-
if (!isSupportedEntity) {
|
|
68
|
-
throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId"); // FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
69
|
-
}
|
|
64
|
+
const entityId = this.getSupportedEntityId(entity);
|
|
70
65
|
// @ts-ignore
|
|
71
|
-
const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(
|
|
66
|
+
const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
|
|
72
67
|
|
|
73
68
|
// TODO: Check if the mediaStorageProvider (s3 in this case) is configured with a public bucket or not.
|
|
74
69
|
// If private bucket then we need to return a "signed-url", the timeout for the signed url can be configured in the media storage provider entity and modified using the CRUD interface.
|
|
75
70
|
// Add the full URL to the media
|
|
76
71
|
for (const m of media) {
|
|
77
72
|
const storageMeta = m.mediaStorageProviderMetadata;
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
m['_full_url'] = this.getFullFilePath(m);
|
|
86
|
-
}
|
|
73
|
+
const isPublic = resolveMediaIsPublic(storageMeta);
|
|
74
|
+
m['_full_url'] = isPublic === false
|
|
75
|
+
? await this.mediaDownloadUrlService.getPrivateUrl(m.id, m.relativeUri, storageMeta)
|
|
76
|
+
: await this.s3FileService.getUrl(
|
|
77
|
+
`${storageMeta.bucketName}:${m.relativeUri}`,
|
|
78
|
+
{ region: getEffectiveS3Region(this.configService, storageMeta.region), expiresIn: 0 },
|
|
79
|
+
);
|
|
87
80
|
}
|
|
88
81
|
|
|
89
82
|
return media;
|
|
@@ -94,18 +87,13 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
async store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
|
|
97
|
-
const
|
|
98
|
-
|| entity instanceof LegacyCommonEntityWithExistingId
|
|
99
|
-
|| entity instanceof LegacyCommonEntityWithGeneratedId;
|
|
100
|
-
if (!isSupportedEntity) {
|
|
101
|
-
throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId"); // FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
102
|
-
}
|
|
90
|
+
const entityId = this.getSupportedEntityId(entity);
|
|
103
91
|
const result: Media[] = [];
|
|
104
92
|
const storageProvider = mediaFieldMetadata.mediaStorageProvider;
|
|
105
|
-
const region = this.
|
|
93
|
+
const region = getEffectiveS3Region(this.configService, storageProvider.region);
|
|
106
94
|
|
|
107
95
|
for (const file of files) {
|
|
108
|
-
const fileName =
|
|
96
|
+
const fileName = buildStoredMediaFileName(file);
|
|
109
97
|
const bucketName = storageProvider.bucketName;
|
|
110
98
|
|
|
111
99
|
// Read file from disk and upload to S3
|
|
@@ -118,17 +106,14 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
118
106
|
const awsFileUrl = fileName;
|
|
119
107
|
|
|
120
108
|
// Create an entry in the media table
|
|
121
|
-
const mediaEntity = await this.mediaRepository.createMedia(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
|
|
130
|
-
fieldMetadataId: mediaFieldMetadata.id
|
|
131
|
-
}) as unknown as Media;
|
|
109
|
+
const mediaEntity = await this.mediaRepository.createMedia(
|
|
110
|
+
buildMediaRecordCreateInput(entityId, mediaFieldMetadata, storageProvider, {
|
|
111
|
+
relativeUri: awsFileUrl,
|
|
112
|
+
mimeType: file.mimetype,
|
|
113
|
+
fileSize: file.size,
|
|
114
|
+
originalFileName: file.originalname,
|
|
115
|
+
})
|
|
116
|
+
) as unknown as Media;
|
|
132
117
|
result.push(mediaEntity);
|
|
133
118
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
134
119
|
};
|
|
@@ -136,19 +121,14 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
136
121
|
}
|
|
137
122
|
|
|
138
123
|
async delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void> {
|
|
139
|
-
const
|
|
140
|
-
|| entity instanceof LegacyCommonEntityWithExistingId
|
|
141
|
-
|| entity instanceof LegacyCommonEntityWithGeneratedId;
|
|
142
|
-
if (!isSupportedEntity) {
|
|
143
|
-
throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId"); // FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
144
|
-
}
|
|
124
|
+
const entityId = this.getSupportedEntityId(entity);
|
|
145
125
|
const storageProvider = mediaFieldMetadata.mediaStorageProvider;
|
|
146
|
-
const region = this.
|
|
126
|
+
const region = getEffectiveS3Region(this.configService, storageProvider.region);
|
|
147
127
|
|
|
148
128
|
// @ts-ignore
|
|
149
|
-
const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(
|
|
129
|
+
const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
|
|
150
130
|
// @ts-ignore
|
|
151
|
-
this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(
|
|
131
|
+
this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
|
|
152
132
|
for (const media of existingMedia) {
|
|
153
133
|
const bucketName = storageProvider.bucketName;
|
|
154
134
|
await this.s3FileService.delete(`${bucketName}:${media.relativeUri}`, { region });
|
|
@@ -163,26 +143,19 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
163
143
|
if (!storageProvider?.bucketName || !media?.relativeUri) {
|
|
164
144
|
return;
|
|
165
145
|
}
|
|
166
|
-
const region = this.
|
|
146
|
+
const region = getEffectiveS3Region(this.configService, storageProvider.region);
|
|
167
147
|
await this.s3FileService.delete(`${storageProvider.bucketName}:${media.relativeUri}`, { region });
|
|
168
148
|
}
|
|
169
149
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
private getEffectiveRegion(providerRegion?: string): string | undefined {
|
|
175
|
-
return providerRegion || this.configService.get('S3_AWS_REGION_NAME');
|
|
176
|
-
}
|
|
150
|
+
private getSupportedEntityId(entity: T): number {
|
|
151
|
+
const isSupportedEntity = entity instanceof CommonEntity
|
|
152
|
+
|| entity instanceof LegacyCommonEntityWithExistingId
|
|
153
|
+
|| entity instanceof LegacyCommonEntityWithGeneratedId;
|
|
177
154
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// https://lunarismedia.s3.ap-south-1.amazonaws.com/LUNARIS_CP_REGISTRATION_CREATIVE.jpg
|
|
182
|
-
return `https://${media.mediaStorageProviderMetadata.bucketName}.s3.${region}.amazonaws.com/${media.relativeUri}`;
|
|
183
|
-
}
|
|
155
|
+
if (!isSupportedEntity) {
|
|
156
|
+
throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
|
|
157
|
+
}
|
|
184
158
|
|
|
185
|
-
|
|
186
|
-
return `${file.filename}-${file.originalname}`;
|
|
159
|
+
return (entity as any).id;
|
|
187
160
|
}
|
|
188
161
|
}
|
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import { Injectable, Logger } from "@nestjs/common";
|
|
2
|
-
import { ConfigService } from "@nestjs/config";
|
|
3
|
-
import { CommonEntity } from "src/entities/common.entity";
|
|
4
2
|
import { FieldMetadata } from "src/entities/field-metadata.entity";
|
|
5
3
|
import { Media } from "src/entities/media.entity";
|
|
4
|
+
import { MediaStorageProviderMetadata } from "src/entities/media-storage-provider-metadata.entity";
|
|
6
5
|
import { MediaStorageProvider } from "src/interfaces";
|
|
7
6
|
import { MediaRepository } from "src/repository/media.repository";
|
|
8
7
|
import { DiskFileService } from "src/services/file";
|
|
9
8
|
import { Readable } from "stream";
|
|
10
|
-
import * as path from "path";
|
|
11
9
|
import * as fs from "fs";
|
|
12
10
|
import { SettingService } from "../setting.service";
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
11
|
+
import { MediaDownloadUrlService } from "src/services/media-download-url.service";
|
|
12
|
+
import {buildDiskMediaPath,buildMediaRecordCreateInput,buildStoredMediaFileName,resolveMediaIsPublic,} from "src/services/media-storage.utils";
|
|
15
13
|
|
|
16
14
|
@Injectable()
|
|
17
15
|
export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
18
16
|
private logger = new Logger(FileStorageProvider.name);
|
|
19
17
|
|
|
20
18
|
constructor(
|
|
21
|
-
// @Inject(appBuilderConfig.KEY)
|
|
22
|
-
// private readonly appBuilderConfiguration: ConfigType<typeof appBuilderConfig>,
|
|
23
|
-
private readonly configService: ConfigService,
|
|
24
19
|
readonly fileService: DiskFileService,
|
|
25
20
|
readonly mediaRepository: MediaRepository,
|
|
26
|
-
private readonly settingService: SettingService
|
|
21
|
+
private readonly settingService: SettingService,
|
|
22
|
+
private readonly mediaDownloadUrlService: MediaDownloadUrlService,
|
|
27
23
|
|
|
28
24
|
) { }
|
|
29
25
|
|
|
@@ -32,12 +28,16 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
32
28
|
// throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
33
29
|
// }
|
|
34
30
|
//@ts-ignore
|
|
35
|
-
const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity
|
|
31
|
+
const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(this.getEntityId(entity), mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
|
|
36
32
|
// Add the full URL to the media
|
|
37
33
|
// media.forEach(m => {
|
|
38
34
|
// });
|
|
39
35
|
for (const m of media) {
|
|
40
|
-
|
|
36
|
+
const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);
|
|
37
|
+
const isPublic = resolveMediaIsPublic(storageProvider);
|
|
38
|
+
m['_full_url'] = isPublic === false
|
|
39
|
+
? await this.mediaDownloadUrlService.getPrivateUrl(m.id, m.relativeUri, storageProvider)
|
|
40
|
+
: await this.fileService.getUrl(buildDiskMediaPath(m.relativeUri, this.settingService, storageProvider));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
|
|
@@ -49,24 +49,23 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
49
49
|
// throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
50
50
|
// }
|
|
51
51
|
const result: Media[] = [];
|
|
52
|
+
const storageProvider = mediaFieldMetadata.mediaStorageProvider;
|
|
52
53
|
for (const file of files) {
|
|
53
54
|
// Store the file in the configured file storage directory
|
|
54
|
-
const
|
|
55
|
+
const fileName = buildStoredMediaFileName(file);
|
|
56
|
+
const fileStoragePath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
|
|
55
57
|
await this.fileService.copy(file.path, fileStoragePath);
|
|
56
58
|
await this.fileService.delete(file.path);
|
|
57
59
|
|
|
58
60
|
// Create an entry in the media table
|
|
59
|
-
const mediaEntity = await this.mediaRepository.createMedia(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
|
|
68
|
-
fieldMetadataId: mediaFieldMetadata.id
|
|
69
|
-
}) as unknown as Media;
|
|
61
|
+
const mediaEntity = await this.mediaRepository.createMedia(
|
|
62
|
+
buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
|
|
63
|
+
relativeUri: fileName,
|
|
64
|
+
mimeType: file.mimetype,
|
|
65
|
+
fileSize: file.size,
|
|
66
|
+
originalFileName: file.originalname,
|
|
67
|
+
})
|
|
68
|
+
) as unknown as Media;
|
|
70
69
|
result.push(mediaEntity);
|
|
71
70
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
72
71
|
};
|
|
@@ -78,21 +77,20 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
78
77
|
// throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
79
78
|
// }
|
|
80
79
|
const result: Media[] = [];
|
|
80
|
+
const storageProvider = mediaFieldMetadata.mediaStorageProvider;
|
|
81
81
|
for (const pair of streamPairs) {
|
|
82
82
|
const stream = pair[0];
|
|
83
83
|
const fileName = pair[1];
|
|
84
|
-
const fullPath = this.
|
|
84
|
+
const fullPath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
|
|
85
85
|
await this.fileService.writeStream(fullPath, stream);
|
|
86
86
|
const { size: fileSize } = await fs.promises.stat(fullPath);
|
|
87
|
-
const mediaEntity = await this.mediaRepository.createMedia(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
fieldMetadataId: mediaFieldMetadata.id
|
|
95
|
-
}) as unknown as Media;
|
|
87
|
+
const mediaEntity = await this.mediaRepository.createMedia(
|
|
88
|
+
buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
|
|
89
|
+
relativeUri: fileName,
|
|
90
|
+
fileSize,
|
|
91
|
+
})
|
|
92
|
+
) as unknown as Media;
|
|
93
|
+
result.push(mediaEntity);
|
|
96
94
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
97
95
|
};
|
|
98
96
|
return result;
|
|
@@ -102,13 +100,13 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
102
100
|
// if (!(entity instanceof CommonEntity)) {
|
|
103
101
|
// throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
|
|
104
102
|
// }
|
|
105
|
-
|
|
106
|
-
const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(
|
|
107
|
-
|
|
108
|
-
this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
|
|
103
|
+
const entityId = this.getEntityId(entity);
|
|
104
|
+
const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
|
|
105
|
+
await this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
|
|
109
106
|
|
|
110
107
|
for (const media of existingMedia) {
|
|
111
|
-
|
|
108
|
+
const storageProvider = this.resolveMediaStorageProvider(media, mediaFieldMetadata);
|
|
109
|
+
await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, storageProvider));
|
|
112
110
|
}
|
|
113
111
|
// existingMedia.forEach(media => {
|
|
114
112
|
// });
|
|
@@ -118,19 +116,14 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
118
116
|
if (!media?.relativeUri) {
|
|
119
117
|
return;
|
|
120
118
|
}
|
|
121
|
-
await this.fileService.delete(this.
|
|
119
|
+
await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, media.mediaStorageProviderMetadata));
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
private
|
|
125
|
-
|
|
126
|
-
|| DEFAULT_MEDIA_FILE_STORAGE_DIR;
|
|
127
|
-
if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {
|
|
128
|
-
return fileName;
|
|
129
|
-
}
|
|
130
|
-
return `${base}/${fileName}`;
|
|
122
|
+
private resolveMediaStorageProvider(media: Media, mediaFieldMetadata?: FieldMetadata): MediaStorageProviderMetadata | undefined {
|
|
123
|
+
return media.mediaStorageProviderMetadata || mediaFieldMetadata?.mediaStorageProvider;
|
|
131
124
|
}
|
|
132
125
|
|
|
133
|
-
private
|
|
134
|
-
return
|
|
126
|
+
private getEntityId(entity: T): number {
|
|
127
|
+
return (entity as any).id;
|
|
135
128
|
}
|
|
136
129
|
}
|
|
@@ -140,6 +140,10 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
|
|
|
140
140
|
return await this._addPermissionsToRole(roleName, null);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
async addAllPermissionsToRoleExceptPrefixes(roleName: string, excludedPermissionPrefixes: string[]): Promise<RoleMetadata> {
|
|
144
|
+
return await this._addPermissionsToRole(roleName, null, excludedPermissionPrefixes);
|
|
145
|
+
}
|
|
146
|
+
|
|
143
147
|
async addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {
|
|
144
148
|
return await this._addPermissionsToRole(roleName, permissionNames);
|
|
145
149
|
}
|
|
@@ -148,7 +152,11 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
|
|
|
148
152
|
return await this._addPermissionsToRole(roleName, permissionName);
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
private async _addPermissionsToRole(
|
|
155
|
+
private async _addPermissionsToRole(
|
|
156
|
+
roleName: string,
|
|
157
|
+
permissionNames: string[],
|
|
158
|
+
excludedPermissionPrefixes: string[] = [],
|
|
159
|
+
): Promise<RoleMetadata> {
|
|
152
160
|
const role = await this.repo.findOne({
|
|
153
161
|
where: { name: roleName },
|
|
154
162
|
select: {
|
|
@@ -161,7 +169,7 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
|
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
const joinInfo = this.getRolePermissionJoinInfo();
|
|
164
|
-
const targetPermissions = await this.loadTargetPermissions(permissionNames);
|
|
172
|
+
const targetPermissions = await this.loadTargetPermissions(permissionNames, excludedPermissionPrefixes);
|
|
165
173
|
|
|
166
174
|
if (!permissionNames || permissionNames.length === 0) {
|
|
167
175
|
await this.entityManager
|
|
@@ -226,21 +234,25 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
|
|
|
226
234
|
return existingPermission;
|
|
227
235
|
}
|
|
228
236
|
|
|
229
|
-
private async loadTargetPermissions(permissionNames: string[] | null): Promise<PermissionMetadata[]> {
|
|
237
|
+
private async loadTargetPermissions(permissionNames: string[] | null, excludedPermissionPrefixes: string[] = []): Promise<PermissionMetadata[]> {
|
|
230
238
|
if (permissionNames && permissionNames.length !== 0) {
|
|
231
239
|
const uniquePermissionNames = [...new Set(permissionNames)];
|
|
232
240
|
const permissions = await this.permissionRepository.find({ where: { name: In(uniquePermissionNames) } });
|
|
233
241
|
if (permissions.length !== uniquePermissionNames.length) {
|
|
234
242
|
throw new Error(`One or more permissions not found.`);
|
|
235
243
|
}
|
|
236
|
-
return permissions
|
|
244
|
+
return permissions.filter((permission) =>
|
|
245
|
+
!excludedPermissionPrefixes.some((prefix) => permission.name.startsWith(prefix)),
|
|
246
|
+
);
|
|
237
247
|
}
|
|
238
248
|
|
|
239
249
|
const permissions = await this.permissionRepository.find();
|
|
240
250
|
if (permissions.length === 0) {
|
|
241
251
|
throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);
|
|
242
252
|
}
|
|
243
|
-
return permissions
|
|
253
|
+
return permissions.filter((permission) =>
|
|
254
|
+
!excludedPermissionPrefixes.some((prefix) => permission.name.startsWith(prefix)),
|
|
255
|
+
);
|
|
244
256
|
}
|
|
245
257
|
|
|
246
258
|
private getRolePermissionJoinInfo(): { tableName: string; roleIdColumn: string; permissionIdColumn: string } {
|
package/src/solid-core.module.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { ListOfValuesService } from "./services/list-of-values.service";
|
|
|
47
47
|
// import { MediaStorageProviderMetadataSeederService } from './services/media-storage-provider-metadata-seeder.service';
|
|
48
48
|
import { MediaStorageProviderMetadataService } from "./services/media-storage-provider-metadata.service";
|
|
49
49
|
import { MediaService } from "./services/media.service";
|
|
50
|
+
import { MediaDownloadUrlService } from "./services/media-download-url.service";
|
|
50
51
|
import { ModelMetadataService } from "./services/model-metadata.service";
|
|
51
52
|
import { RemovedFieldMigrationService } from "./services/removed-field-migration.service";
|
|
52
53
|
import { ModuleMetadataExplorerService } from "./services/module-metadata-explorer.service";
|
|
@@ -55,9 +56,10 @@ import { ModulePackageService } from "./services/module-package.service";
|
|
|
55
56
|
import { SolidIntrospectService } from "./services/solid-introspect.service";
|
|
56
57
|
// import { ListOfComputedFieldProvider } from './providers/list-of-computed-field-provider.service';
|
|
57
58
|
import { ServeStaticModule } from "@nestjs/serve-static";
|
|
58
|
-
import { join } from "path";
|
|
59
|
+
import { extname, join } from "path";
|
|
59
60
|
import { RefreshModelCommand } from "./commands/refresh-model.command";
|
|
60
61
|
import { MediaController } from "./controllers/media.controller";
|
|
62
|
+
import { INLINE_SAFE_EXTENSIONS } from "./constants/media-file-types";
|
|
61
63
|
|
|
62
64
|
import { RefreshModuleCommand } from "./commands/refresh-module.command";
|
|
63
65
|
import { ModelMetadataSubscriber } from "./subscribers/model-metadata.subscriber";
|
|
@@ -106,6 +108,7 @@ import { MqMessage } from "./entities/mq-message.entity";
|
|
|
106
108
|
import { SmsTemplate } from "./entities/sms-template.entity";
|
|
107
109
|
import { AccessTokenGuard } from "./guards/access-token.guard";
|
|
108
110
|
import { ApiKeyGuard } from "./guards/api-key.guard";
|
|
111
|
+
import { MediaSignedUrlGuard } from "./guards/media-signed-url.guard";
|
|
109
112
|
import { AuthenticationGuard } from "./guards/authentication.guard";
|
|
110
113
|
import { PermissionsGuard } from "./guards/permissions.guard";
|
|
111
114
|
import { SolidRegistry } from "./helpers/solid-registry";
|
|
@@ -130,10 +133,17 @@ import { ChatterQueuePublisherRabbitmq } from "./jobs/rabbitmq/chatter-queue-pub
|
|
|
130
133
|
import { ChatterQueueSubscriberRabbitmq } from "./jobs/rabbitmq/chatter-queue-subscriber.service";
|
|
131
134
|
import { ChatterQueuePublisherDatabase } from "./jobs/database/chatter-queue-publisher-database.service";
|
|
132
135
|
import { ChatterQueueSubscriberDatabase } from "./jobs/database/chatter-queue-subscriber-database.service";
|
|
136
|
+
import { ChatterMentionNotificationEmailQueueHandler } from "./jobs/chatter-mention-notification-email-queue-handler.service";
|
|
137
|
+
import { ChatterMentionNotificationEmailQueuePublisherDatabase } from "./jobs/database/chatter-mention-notification-email-publisher-database.service";
|
|
138
|
+
import { ChatterMentionNotificationEmailQueueSubscriberDatabase } from "./jobs/database/chatter-mention-notification-email-subscriber-database.service";
|
|
139
|
+
import { ChatterMentionNotificationEmailQueuePublisherRabbitmq } from "./jobs/rabbitmq/chatter-mention-notification-email-publisher.service";
|
|
140
|
+
import { ChatterMentionNotificationEmailQueueSubscriberRabbitmq } from "./jobs/rabbitmq/chatter-mention-notification-email-subscriber.service";
|
|
133
141
|
import { ApiEmailQueuePublisherRedis } from "./jobs/redis/api-email-publisher-redis.service";
|
|
134
142
|
import { ApiEmailQueueSubscriberRedis } from "./jobs/redis/api-email-subscriber-redis.service";
|
|
135
143
|
import { ChatterQueuePublisherRedis } from "./jobs/redis/chatter-queue-publisher-redis.service";
|
|
136
144
|
import { ChatterQueueSubscriberRedis } from "./jobs/redis/chatter-queue-subscriber-redis.service";
|
|
145
|
+
import { ChatterMentionNotificationEmailQueuePublisherRedis } from "./jobs/redis/chatter-mention-notification-email-publisher-redis.service";
|
|
146
|
+
import { ChatterMentionNotificationEmailQueueSubscriberRedis } from "./jobs/redis/chatter-mention-notification-email-subscriber-redis.service";
|
|
137
147
|
import { ComputedFieldEvaluationPublisherRedis } from "./jobs/redis/computed-field-evaluation-publisher-redis.service";
|
|
138
148
|
import { ComputedFieldEvaluationSubscriberRedis } from "./jobs/redis/computed-field-evaluation-subscriber-redis.service";
|
|
139
149
|
import { GenerateCodePublisherRedis } from "./jobs/redis/generate-code-publisher-redis.service";
|
|
@@ -437,7 +447,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
437
447
|
rootPath: join(process.cwd(), "media-files-storage"),
|
|
438
448
|
serveRoot: "/media-files-storage",
|
|
439
449
|
serveStaticOptions: {
|
|
440
|
-
setHeaders: (res
|
|
450
|
+
setHeaders: (res, path) => {
|
|
441
451
|
// Allow use of these files from a different origin (e.g., :3000 UI)
|
|
442
452
|
// Use 'same-site' if both origins are on the same site (localhost:* counts as same-site)
|
|
443
453
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); // or 'same-site'
|
|
@@ -445,6 +455,20 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
445
455
|
// If you need to load into <canvas> without tainting or fetch images via XHR,
|
|
446
456
|
// you can also expose CORS here (not needed for simple <img>):
|
|
447
457
|
// res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
|
|
458
|
+
|
|
459
|
+
// Prevent the browser from ever content-sniffing a served file into something
|
|
460
|
+
// more dangerous than its declared Content-Type (e.g. rendering a mislabeled
|
|
461
|
+
// upload as HTML and executing embedded script).
|
|
462
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
463
|
+
|
|
464
|
+
// Only a small allowlist of media types is ever safe to render inline. Everything
|
|
465
|
+
// else (including svg, html, and any other uploaded file) is forced to download
|
|
466
|
+
// rather than be displayed/executed by the browser, regardless of what mimetype or
|
|
467
|
+
// extension it was uploaded with.
|
|
468
|
+
if (!INLINE_SAFE_EXTENSIONS.has(extname(path).toLowerCase().replace(/^\./, ''))) {
|
|
469
|
+
res.setHeader("Content-Type", "application/octet-stream");
|
|
470
|
+
res.setHeader("Content-Disposition", "attachment");
|
|
471
|
+
}
|
|
448
472
|
},
|
|
449
473
|
},
|
|
450
474
|
}),
|
|
@@ -563,6 +587,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
563
587
|
SchematicService,
|
|
564
588
|
MediaStorageProviderMetadataService,
|
|
565
589
|
MediaService,
|
|
590
|
+
MediaDownloadUrlService,
|
|
566
591
|
// MediaStorageProviderMetadataSeederService,
|
|
567
592
|
ModuleMetadataSeederService,
|
|
568
593
|
ModuleTestDataService,
|
|
@@ -647,6 +672,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
647
672
|
SoftDeleteAwareEventSubscriber,
|
|
648
673
|
AccessTokenGuard,
|
|
649
674
|
ApiKeyGuard,
|
|
675
|
+
MediaSignedUrlGuard,
|
|
650
676
|
ApiKeyService,
|
|
651
677
|
ActiveSessionStorageService,
|
|
652
678
|
AuthenticationService,
|
|
@@ -665,6 +691,11 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
665
691
|
ChatterQueueSubscriberRabbitmq,
|
|
666
692
|
ChatterQueuePublisherDatabase,
|
|
667
693
|
ChatterQueueSubscriberDatabase,
|
|
694
|
+
ChatterMentionNotificationEmailQueueHandler,
|
|
695
|
+
ChatterMentionNotificationEmailQueuePublisherRabbitmq,
|
|
696
|
+
ChatterMentionNotificationEmailQueueSubscriberRabbitmq,
|
|
697
|
+
ChatterMentionNotificationEmailQueuePublisherDatabase,
|
|
698
|
+
ChatterMentionNotificationEmailQueueSubscriberDatabase,
|
|
668
699
|
|
|
669
700
|
TestQueuePublisherDatabase,
|
|
670
701
|
TestQueueSubscriberDatabase,
|
|
@@ -674,6 +705,8 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
674
705
|
ApiEmailQueueSubscriberRedis,
|
|
675
706
|
ChatterQueuePublisherRedis,
|
|
676
707
|
ChatterQueueSubscriberRedis,
|
|
708
|
+
ChatterMentionNotificationEmailQueuePublisherRedis,
|
|
709
|
+
ChatterMentionNotificationEmailQueueSubscriberRedis,
|
|
677
710
|
ComputedFieldEvaluationPublisherRedis,
|
|
678
711
|
ComputedFieldEvaluationSubscriberRedis,
|
|
679
712
|
GenerateCodePublisherRedis,
|
|
@@ -106,7 +106,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
106
106
|
|
|
107
107
|
const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
|
|
108
108
|
|
|
109
|
-
const relationAfter = await
|
|
109
|
+
const relationAfter = await event.queryRunner.manager.getRepository(event.metadata.target as any).findOne({
|
|
110
110
|
where: { id: entityId } as any,
|
|
111
111
|
relations: relations as any,
|
|
112
112
|
});
|