@solidxai/core 0.1.12-beta.1 → 0.1.12-beta.3

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 (169) hide show
  1. package/dist/constants/media-file-types.d.ts +5 -0
  2. package/dist/constants/media-file-types.d.ts.map +1 -0
  3. package/dist/constants/media-file-types.js +27 -0
  4. package/dist/constants/media-file-types.js.map +1 -0
  5. package/dist/controllers/media.controller.d.ts +14 -11
  6. package/dist/controllers/media.controller.d.ts.map +1 -1
  7. package/dist/controllers/media.controller.js +72 -33
  8. package/dist/controllers/media.controller.js.map +1 -1
  9. package/dist/dtos/create-chatter-message.dto.d.ts +1 -0
  10. package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -1
  11. package/dist/dtos/create-chatter-message.dto.js +7 -1
  12. package/dist/dtos/create-chatter-message.dto.js.map +1 -1
  13. package/dist/dtos/create-media.dto.d.ts +1 -0
  14. package/dist/dtos/create-media.dto.d.ts.map +1 -1
  15. package/dist/dtos/create-media.dto.js +7 -1
  16. package/dist/dtos/create-media.dto.js.map +1 -1
  17. package/dist/dtos/post-chatter-message.dto.d.ts +1 -0
  18. package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -1
  19. package/dist/dtos/post-chatter-message.dto.js +6 -1
  20. package/dist/dtos/post-chatter-message.dto.js.map +1 -1
  21. package/dist/dtos/update-chatter-message.dto.d.ts +1 -0
  22. package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -1
  23. package/dist/dtos/update-chatter-message.dto.js +8 -1
  24. package/dist/dtos/update-chatter-message.dto.js.map +1 -1
  25. package/dist/dtos/update-chatter-note-message.dto.d.ts +1 -0
  26. package/dist/dtos/update-chatter-note-message.dto.d.ts.map +1 -1
  27. package/dist/dtos/update-chatter-note-message.dto.js +7 -1
  28. package/dist/dtos/update-chatter-note-message.dto.js.map +1 -1
  29. package/dist/dtos/update-media.dto.d.ts +1 -0
  30. package/dist/dtos/update-media.dto.d.ts.map +1 -1
  31. package/dist/dtos/update-media.dto.js +7 -1
  32. package/dist/dtos/update-media.dto.js.map +1 -1
  33. package/dist/entities/chatter-message.entity.d.ts +1 -0
  34. package/dist/entities/chatter-message.entity.d.ts.map +1 -1
  35. package/dist/entities/chatter-message.entity.js +5 -1
  36. package/dist/entities/chatter-message.entity.js.map +1 -1
  37. package/dist/entities/media.entity.d.ts +1 -0
  38. package/dist/entities/media.entity.d.ts.map +1 -1
  39. package/dist/entities/media.entity.js +9 -1
  40. package/dist/entities/media.entity.js.map +1 -1
  41. package/dist/enums/auth-type.enum.d.ts +2 -1
  42. package/dist/enums/auth-type.enum.d.ts.map +1 -1
  43. package/dist/enums/auth-type.enum.js +1 -0
  44. package/dist/enums/auth-type.enum.js.map +1 -1
  45. package/dist/guards/authentication.guard.d.ts +3 -1
  46. package/dist/guards/authentication.guard.d.ts.map +1 -1
  47. package/dist/guards/authentication.guard.js +5 -1
  48. package/dist/guards/authentication.guard.js.map +1 -1
  49. package/dist/guards/media-signed-url.guard.d.ts +9 -0
  50. package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
  51. package/dist/guards/media-signed-url.guard.js +49 -0
  52. package/dist/guards/media-signed-url.guard.js.map +1 -0
  53. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  54. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
  55. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  56. package/dist/interfaces/chatter-mention-notification.interface.d.ts +27 -0
  57. package/dist/interfaces/chatter-mention-notification.interface.d.ts.map +1 -0
  58. package/dist/interfaces/chatter-mention-notification.interface.js +3 -0
  59. package/dist/interfaces/chatter-mention-notification.interface.js.map +1 -0
  60. package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts +17 -0
  61. package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts.map +1 -0
  62. package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js +106 -0
  63. package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js.map +1 -0
  64. package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts +12 -0
  65. package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts.map +1 -0
  66. package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js +39 -0
  67. package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js.map +1 -0
  68. package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts +8 -0
  69. package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts.map +1 -0
  70. package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js +10 -0
  71. package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js.map +1 -0
  72. package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts +20 -0
  73. package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts.map +1 -0
  74. package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js +48 -0
  75. package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js.map +1 -0
  76. package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts +12 -0
  77. package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts.map +1 -0
  78. package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js +39 -0
  79. package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js.map +1 -0
  80. package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts +8 -0
  81. package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts.map +1 -0
  82. package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js +10 -0
  83. package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js.map +1 -0
  84. package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts +18 -0
  85. package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts.map +1 -0
  86. package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js +45 -0
  87. package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js.map +1 -0
  88. package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts +12 -0
  89. package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts.map +1 -0
  90. package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js +39 -0
  91. package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js.map +1 -0
  92. package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts +8 -0
  93. package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts.map +1 -0
  94. package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js +10 -0
  95. package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js.map +1 -0
  96. package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts +18 -0
  97. package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts.map +1 -0
  98. package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js +45 -0
  99. package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js.map +1 -0
  100. package/dist/repository/media.repository.d.ts.map +1 -1
  101. package/dist/repository/media.repository.js +2 -0
  102. package/dist/repository/media.repository.js.map +1 -1
  103. package/dist/seeders/seed-data/solid-core-metadata.json +109 -3
  104. package/dist/services/chatter-message.service.d.ts +7 -1
  105. package/dist/services/chatter-message.service.d.ts.map +1 -1
  106. package/dist/services/chatter-message.service.js +85 -4
  107. package/dist/services/chatter-message.service.js.map +1 -1
  108. package/dist/services/media-download-url.service.d.ts +21 -0
  109. package/dist/services/media-download-url.service.d.ts.map +1 -0
  110. package/dist/services/media-download-url.service.js +67 -0
  111. package/dist/services/media-download-url.service.js.map +1 -0
  112. package/dist/services/media-storage.utils.d.ts +27 -0
  113. package/dist/services/media-storage.utils.d.ts.map +1 -0
  114. package/dist/services/media-storage.utils.js +82 -0
  115. package/dist/services/media-storage.utils.js.map +1 -0
  116. package/dist/services/media.service.d.ts +20 -4
  117. package/dist/services/media.service.d.ts.map +1 -1
  118. package/dist/services/media.service.js +157 -55
  119. package/dist/services/media.service.js.map +1 -1
  120. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
  121. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  122. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +37 -63
  123. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  124. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
  125. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  126. package/dist/services/mediaStorageProviders/file-storage-provider.js +37 -38
  127. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  128. package/dist/solid-core.module.d.ts.map +1 -1
  129. package/dist/solid-core.module.js +25 -1
  130. package/dist/solid-core.module.js.map +1 -1
  131. package/dist/subscribers/audit.subscriber.js +1 -1
  132. package/dist/subscribers/audit.subscriber.js.map +1 -1
  133. package/package.json +1 -1
  134. package/src/constants/media-file-types.ts +44 -0
  135. package/src/controllers/media.controller.ts +44 -24
  136. package/src/dtos/create-chatter-message.dto.ts +5 -0
  137. package/src/dtos/create-media.dto.ts +8 -3
  138. package/src/dtos/post-chatter-message.dto.ts +4 -0
  139. package/src/dtos/update-chatter-message.dto.ts +6 -0
  140. package/src/dtos/update-chatter-note-message.dto.ts +5 -0
  141. package/src/dtos/update-media.dto.ts +8 -3
  142. package/src/entities/chatter-message.entity.ts +3 -0
  143. package/src/entities/media.entity.ts +4 -1
  144. package/src/enums/auth-type.enum.ts +1 -0
  145. package/src/guards/authentication.guard.ts +3 -0
  146. package/src/guards/media-signed-url.guard.ts +41 -0
  147. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
  148. package/src/interfaces/chatter-mention-notification.interface.ts +29 -0
  149. package/src/jobs/chatter-mention-notification-email-queue-handler.service.ts +114 -0
  150. package/src/jobs/database/chatter-mention-notification-email-publisher-database.service.ts +23 -0
  151. package/src/jobs/database/chatter-mention-notification-email-queue-options-database.ts +9 -0
  152. package/src/jobs/database/chatter-mention-notification-email-subscriber-database.service.ts +32 -0
  153. package/src/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.ts +23 -0
  154. package/src/jobs/rabbitmq/chatter-mention-notification-email-queue-options.ts +9 -0
  155. package/src/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.ts +30 -0
  156. package/src/jobs/redis/chatter-mention-notification-email-publisher-redis.service.ts +23 -0
  157. package/src/jobs/redis/chatter-mention-notification-email-queue-options-redis.ts +9 -0
  158. package/src/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.ts +30 -0
  159. package/src/repository/media.repository.ts +2 -0
  160. package/src/seeders/seed-data/email-templates/chatter-mention-notification.handlebars.html +200 -0
  161. package/src/seeders/seed-data/solid-core-metadata.json +109 -3
  162. package/src/services/chatter-message.service.ts +97 -2
  163. package/src/services/media-download-url.service.ts +67 -0
  164. package/src/services/media-storage.utils.ts +79 -0
  165. package/src/services/media.service.ts +189 -59
  166. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +52 -76
  167. package/src/services/mediaStorageProviders/file-storage-provider.ts +44 -48
  168. package/src/solid-core.module.ts +35 -2
  169. package/src/subscribers/audit.subscriber.ts +1 -1
@@ -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 { DEFAULT_MEDIA_FILE_STORAGE_DIR } from "src/services/settings/default-settings-provider.service";
14
- import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
11
+ import { MediaDownloadUrlService } from "src/services/media-download-url.service";
12
+ import { buildDiskMediaPath, buildMediaRecordCreateInput, buildStoredMediaFileName, resolveStoredMediaIsPublic, } 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,17 @@ 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.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
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
- m['_full_url'] = `${this.settingService.getConfigValue<SolidCoreSetting>("baseUrl")}/${this.getFullFilePath(m.relativeUri)}`;
36
+ const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);
37
+ const isPublic = resolveStoredMediaIsPublic(m.isPublic, storageProvider);
38
+ m.isPublic = isPublic;
39
+ m['_full_url'] = isPublic === false
40
+ ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageProvider)
41
+ : await this.fileService.getUrl(buildDiskMediaPath(m.relativeUri, this.settingService, storageProvider));
41
42
  }
42
43
 
43
44
 
@@ -49,24 +50,24 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
49
50
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
50
51
  // }
51
52
  const result: Media[] = [];
53
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
52
54
  for (const file of files) {
53
55
  // Store the file in the configured file storage directory
54
- const fileStoragePath = this.getFullFilePath(this.getFileName(file));
56
+ const fileName = buildStoredMediaFileName(file);
57
+ const fileStoragePath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
55
58
  await this.fileService.copy(file.path, fileStoragePath);
56
59
  await this.fileService.delete(file.path);
57
60
 
58
61
  // Create an entry in the media table
59
- const mediaEntity = await this.mediaRepository.createMedia({
60
- //@ts-ignore
61
- entityId: entity.id,
62
- modelMetadataId: mediaFieldMetadata.model.id,
63
- relativeUri: this.getFileName(file),
64
- mimeType: file.mimetype,
65
- fileSize: file.size,
66
- originalFileName: file.originalname,
67
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
68
- fieldMetadataId: mediaFieldMetadata.id
69
- }) as unknown as Media;
62
+ const mediaEntity = await this.mediaRepository.createMedia(
63
+ buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
64
+ relativeUri: fileName,
65
+ mimeType: file.mimetype,
66
+ fileSize: file.size,
67
+ originalFileName: file.originalname,
68
+ })
69
+ ) as unknown as Media;
70
+ mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
70
71
  result.push(mediaEntity);
71
72
  this.logger.debug(`Stored media with`, mediaEntity);
72
73
  };
@@ -78,21 +79,21 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
78
79
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
79
80
  // }
80
81
  const result: Media[] = [];
82
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
81
83
  for (const pair of streamPairs) {
82
84
  const stream = pair[0];
83
85
  const fileName = pair[1];
84
- const fullPath = this.getFullFilePath(fileName);
86
+ const fullPath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
85
87
  await this.fileService.writeStream(fullPath, stream);
86
88
  const { size: fileSize } = await fs.promises.stat(fullPath);
87
- const mediaEntity = await this.mediaRepository.createMedia({
88
- //@ts-ignore
89
- entityId: entity.id,
90
- modelMetadataId: mediaFieldMetadata.model.id,
91
- relativeUri: fileName,
92
- fileSize,
93
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
94
- fieldMetadataId: mediaFieldMetadata.id
95
- }) as unknown as Media;
89
+ const mediaEntity = await this.mediaRepository.createMedia(
90
+ buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
91
+ relativeUri: fileName,
92
+ fileSize,
93
+ })
94
+ ) as unknown as Media;
95
+ mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
96
+ result.push(mediaEntity);
96
97
  this.logger.debug(`Stored media with`, mediaEntity);
97
98
  };
98
99
  return result;
@@ -102,13 +103,13 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
102
103
  // if (!(entity instanceof CommonEntity)) {
103
104
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
104
105
  // }
105
- //@ts-ignore
106
- const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
107
- //@ts-ignore
108
- this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
106
+ const entityId = this.getEntityId(entity);
107
+ const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
108
+ await this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
109
109
 
110
110
  for (const media of existingMedia) {
111
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
111
+ const storageProvider = this.resolveMediaStorageProvider(media, mediaFieldMetadata);
112
+ await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, storageProvider));
112
113
  }
113
114
  // existingMedia.forEach(media => {
114
115
  // });
@@ -118,19 +119,14 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
118
119
  if (!media?.relativeUri) {
119
120
  return;
120
121
  }
121
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
122
+ await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, media.mediaStorageProviderMetadata));
122
123
  }
123
124
 
124
- private getFullFilePath(fileName: string): string {
125
- const base = this.settingService.getConfigValue<SolidCoreSetting>("fileStorageDir")
126
- || DEFAULT_MEDIA_FILE_STORAGE_DIR;
127
- if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {
128
- return fileName;
129
- }
130
- return `${base}/${fileName}`;
125
+ private resolveMediaStorageProvider(media: Media, mediaFieldMetadata?: FieldMetadata): MediaStorageProviderMetadata | undefined {
126
+ return media.mediaStorageProviderMetadata || mediaFieldMetadata?.mediaStorageProvider;
131
127
  }
132
128
 
133
- private getFileName(file: Express.Multer.File): string {
134
- return `${file.filename}-${file.originalname}`;
129
+ private getEntityId(entity: T): number {
130
+ return (entity as any).id;
135
131
  }
136
132
  }
@@ -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 /*, path, stat*/) => {
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 this.dataSource.getRepository(event.metadata.target as any).findOne({
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
  });