@solidxai/core 0.1.12-beta.2 → 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 (128) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/CLAUDE.md +26 -0
  3. package/dist/constants/media-file-types.d.ts +5 -0
  4. package/dist/constants/media-file-types.d.ts.map +1 -0
  5. package/dist/constants/media-file-types.js +27 -0
  6. package/dist/constants/media-file-types.js.map +1 -0
  7. package/dist/controllers/media.controller.d.ts +14 -11
  8. package/dist/controllers/media.controller.d.ts.map +1 -1
  9. package/dist/controllers/media.controller.js +72 -33
  10. package/dist/controllers/media.controller.js.map +1 -1
  11. package/dist/dtos/create-media.dto.d.ts +1 -0
  12. package/dist/dtos/create-media.dto.d.ts.map +1 -1
  13. package/dist/dtos/create-media.dto.js +7 -1
  14. package/dist/dtos/create-media.dto.js.map +1 -1
  15. package/dist/dtos/update-media.dto.d.ts +1 -0
  16. package/dist/dtos/update-media.dto.d.ts.map +1 -1
  17. package/dist/dtos/update-media.dto.js +7 -1
  18. package/dist/dtos/update-media.dto.js.map +1 -1
  19. package/dist/entities/media.entity.d.ts +1 -0
  20. package/dist/entities/media.entity.d.ts.map +1 -1
  21. package/dist/entities/media.entity.js +9 -1
  22. package/dist/entities/media.entity.js.map +1 -1
  23. package/dist/enums/auth-type.enum.d.ts +2 -1
  24. package/dist/enums/auth-type.enum.d.ts.map +1 -1
  25. package/dist/enums/auth-type.enum.js +1 -0
  26. package/dist/enums/auth-type.enum.js.map +1 -1
  27. package/dist/guards/authentication.guard.d.ts +3 -1
  28. package/dist/guards/authentication.guard.d.ts.map +1 -1
  29. package/dist/guards/authentication.guard.js +5 -1
  30. package/dist/guards/authentication.guard.js.map +1 -1
  31. package/dist/guards/media-signed-url.guard.d.ts +9 -0
  32. package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
  33. package/dist/guards/media-signed-url.guard.js +49 -0
  34. package/dist/guards/media-signed-url.guard.js.map +1 -0
  35. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  36. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
  37. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  38. package/dist/repository/media.repository.d.ts.map +1 -1
  39. package/dist/repository/media.repository.js +2 -0
  40. package/dist/repository/media.repository.js.map +1 -1
  41. package/dist/seeders/seed-data/solid-core-metadata.json +88 -3
  42. package/dist/services/media-download-url.service.d.ts +21 -0
  43. package/dist/services/media-download-url.service.d.ts.map +1 -0
  44. package/dist/services/media-download-url.service.js +67 -0
  45. package/dist/services/media-download-url.service.js.map +1 -0
  46. package/dist/services/media-storage.utils.d.ts +27 -0
  47. package/dist/services/media-storage.utils.d.ts.map +1 -0
  48. package/dist/services/media-storage.utils.js +82 -0
  49. package/dist/services/media-storage.utils.js.map +1 -0
  50. package/dist/services/media.service.d.ts +20 -4
  51. package/dist/services/media.service.d.ts.map +1 -1
  52. package/dist/services/media.service.js +157 -55
  53. package/dist/services/media.service.js.map +1 -1
  54. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
  55. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  56. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +37 -63
  57. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  58. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
  59. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  60. package/dist/services/mediaStorageProviders/file-storage-provider.js +37 -38
  61. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  62. package/dist/solid-core.module.d.ts.map +1 -1
  63. package/dist/solid-core.module.js +11 -1
  64. package/dist/solid-core.module.js.map +1 -1
  65. package/dist/subscribers/audit.subscriber.js +1 -1
  66. package/dist/subscribers/audit.subscriber.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/constants/media-file-types.ts +44 -0
  69. package/src/controllers/media.controller.ts +44 -24
  70. package/src/dtos/create-media.dto.ts +8 -3
  71. package/src/dtos/update-media.dto.ts +8 -3
  72. package/src/entities/media.entity.ts +4 -1
  73. package/src/enums/auth-type.enum.ts +1 -0
  74. package/src/guards/authentication.guard.ts +3 -0
  75. package/src/guards/media-signed-url.guard.ts +41 -0
  76. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
  77. package/src/repository/media.repository.ts +2 -0
  78. package/src/seeders/seed-data/solid-core-metadata.json +88 -3
  79. package/src/services/1.js +6 -0
  80. package/src/services/media-download-url.service.ts +67 -0
  81. package/src/services/media-storage.utils.ts +79 -0
  82. package/src/services/media.service.ts +189 -59
  83. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +52 -76
  84. package/src/services/mediaStorageProviders/file-storage-provider.ts +44 -48
  85. package/src/solid-core.module.ts +21 -2
  86. package/src/subscribers/audit.subscriber.ts +1 -1
  87. package/dist-tests/api/authenticate.spec.js +0 -119
  88. package/dist-tests/api/authenticate.spec.js.map +0 -1
  89. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  90. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  91. package/dist-tests/api/ping.spec.js +0 -21
  92. package/dist-tests/api/ping.spec.js.map +0 -1
  93. package/dist-tests/helpers/auth.js +0 -41
  94. package/dist-tests/helpers/auth.js.map +0 -1
  95. package/dist-tests/helpers/env.js +0 -11
  96. package/dist-tests/helpers/env.js.map +0 -1
  97. package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +0 -136
  98. package/docs/agent-builder/team-ready-note-agent-builder.md +0 -305
  99. package/docs/agent-hub-grooming.md +0 -301
  100. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
  101. package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
  102. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
  103. package/docs/datasource-introspection-ddl-analysis.md +0 -326
  104. package/docs/datasource-introspection-implementation-plan.md +0 -306
  105. package/docs/grouping-enhancements.md +0 -89
  106. package/docs/java-spring/README.md +0 -3
  107. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  108. package/docs/module-package-import-handoff.md +0 -691
  109. package/docs/seed-changes.md +0 -65
  110. package/docs/test-data-workflow.md +0 -200
  111. package/docs/type-declaration-import-issue.md +0 -24
  112. package/docs/workflow/business-automation-example-notes.md +0 -309
  113. package/docs/workflow/data-engineering-pipeline-example-notes.md +0 -330
  114. package/docs/workflow/foreach-example-notes.md +0 -187
  115. package/docs/workflow/hello-world-example-notes.md +0 -271
  116. package/docs/workflow/kestra-concepts-plugins-blueprints.md +0 -315
  117. package/docs/workflow/loop-until-example-notes.md +0 -198
  118. package/docs/workflow/microservices-and-apis-example-notes.md +0 -264
  119. package/docs/workflow/samples.md +0 -25
  120. package/docs/workflow/what-is-kestra.md +0 -79
  121. package/docs/workflow/workflow-core-module-handoff-summary.md +0 -191
  122. package/docs/workflow/workflow-module-brd.md +0 -185
  123. package/docs/workflow/workflow-module-domain-model.md +0 -252
  124. package/docs/workflow/workflow-module-metadata-dsl.md +0 -302
  125. package/docs/workflow/workflow-module-technical-spec.md +0 -293
  126. package/docs/workflow/workflow-node-type-contracts.md +0 -635
  127. package/docs/workflow/workflow-node-ui-metadata-contract.md +0 -1262
  128. package/docs/workflow/workflow-solidx-model-field-spec.md +0 -284
@@ -18,24 +18,22 @@ const legacy_common_entity_with_existing_id_entity_1 = require("../../entities/l
18
18
  const legacy_common_with_generated_id_entity_1 = require("../../entities/legacy-common-with-generated-id.entity");
19
19
  const file_1 = require("../file");
20
20
  const media_repository_1 = require("../../repository/media.repository");
21
+ const media_download_url_service_1 = require("../media-download-url.service");
22
+ const media_storage_utils_1 = require("../media-storage.utils");
21
23
  let FileS3StorageProvider = FileS3StorageProvider_1 = class FileS3StorageProvider {
22
- constructor(configService, diskFileService, s3FileService, mediaRepository) {
24
+ constructor(configService, diskFileService, s3FileService, mediaRepository, mediaDownloadUrlService) {
23
25
  this.configService = configService;
24
26
  this.diskFileService = diskFileService;
25
27
  this.s3FileService = s3FileService;
26
28
  this.mediaRepository = mediaRepository;
29
+ this.mediaDownloadUrlService = mediaDownloadUrlService;
27
30
  this.logger = new common_1.Logger(FileS3StorageProvider_1.name);
28
31
  }
29
32
  async storeStreams(streamPairs, entity, mediaFieldMetadata) {
30
- const isSupportedEntity = entity instanceof common_entity_1.CommonEntity
31
- || entity instanceof legacy_common_entity_with_existing_id_entity_1.LegacyCommonEntityWithExistingId
32
- || entity instanceof legacy_common_with_generated_id_entity_1.LegacyCommonEntityWithGeneratedId;
33
- if (!isSupportedEntity) {
34
- throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
35
- }
33
+ const entityId = this.getSupportedEntityId(entity);
36
34
  const result = [];
37
35
  const storageProvider = mediaFieldMetadata.mediaStorageProvider;
38
- const region = this.getEffectiveRegion(storageProvider.region);
36
+ const region = (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region);
39
37
  for (const [stream, fileName] of streamPairs) {
40
38
  const bucketName = storageProvider.bucketName;
41
39
  const chunks = [];
@@ -45,67 +43,48 @@ let FileS3StorageProvider = FileS3StorageProvider_1 = class FileS3StorageProvide
45
43
  const fileData = Buffer.concat(chunks);
46
44
  const fileSize = fileData.length;
47
45
  await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { region });
48
- const mediaEntity = await this.mediaRepository.createMedia({
49
- entityId: entity.id,
50
- modelMetadataId: mediaFieldMetadata.model.id,
46
+ const mediaEntity = await this.mediaRepository.createMedia((0, media_storage_utils_1.buildMediaRecordCreateInput)(entityId, mediaFieldMetadata, storageProvider, {
51
47
  relativeUri: fileName,
52
48
  fileSize,
53
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
54
- fieldMetadataId: mediaFieldMetadata.id
55
- });
49
+ }));
50
+ mediaEntity.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(mediaEntity.isPublic, storageProvider);
56
51
  result.push(mediaEntity);
57
52
  this.logger.debug(`Stored media with`, mediaEntity);
58
53
  }
59
54
  return result;
60
55
  }
61
56
  async retrieve(entity, mediaFieldMetadata) {
62
- const isSupportedEntity = entity instanceof common_entity_1.CommonEntity
63
- || entity instanceof legacy_common_entity_with_existing_id_entity_1.LegacyCommonEntityWithExistingId
64
- || entity instanceof legacy_common_with_generated_id_entity_1.LegacyCommonEntityWithGeneratedId;
65
- if (!isSupportedEntity) {
66
- throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
67
- }
68
- const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
57
+ const entityId = this.getSupportedEntityId(entity);
58
+ const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
69
59
  for (const m of media) {
70
60
  const storageMeta = m.mediaStorageProviderMetadata;
71
- const region = this.getEffectiveRegion(storageMeta.region);
72
- if (storageMeta.isPublic === false) {
73
- const expiryInSeconds = (storageMeta.signedUrlExpiry ?? 60) * 60;
74
- m['_full_url'] = await this.s3FileService.getUrl(`${storageMeta?.bucketName}:${m.relativeUri}`, { expiresIn: expiryInSeconds, region });
75
- }
76
- else {
77
- m['_full_url'] = this.getFullFilePath(m);
78
- }
61
+ const isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(m.isPublic, storageMeta);
62
+ m.isPublic = isPublic;
63
+ m['_full_url'] = isPublic === false
64
+ ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageMeta)
65
+ : await this.s3FileService.getUrl(`${storageMeta.bucketName}:${m.relativeUri}`, { region: (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageMeta.region), expiresIn: 0 });
79
66
  }
80
67
  return media;
81
68
  }
82
69
  async store(files, entity, mediaFieldMetadata) {
83
- const isSupportedEntity = entity instanceof common_entity_1.CommonEntity
84
- || entity instanceof legacy_common_entity_with_existing_id_entity_1.LegacyCommonEntityWithExistingId
85
- || entity instanceof legacy_common_with_generated_id_entity_1.LegacyCommonEntityWithGeneratedId;
86
- if (!isSupportedEntity) {
87
- throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
88
- }
70
+ const entityId = this.getSupportedEntityId(entity);
89
71
  const result = [];
90
72
  const storageProvider = mediaFieldMetadata.mediaStorageProvider;
91
- const region = this.getEffectiveRegion(storageProvider.region);
73
+ const region = (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region);
92
74
  for (const file of files) {
93
- const fileName = this.getFileName(file);
75
+ const fileName = (0, media_storage_utils_1.buildStoredMediaFileName)(file);
94
76
  const bucketName = storageProvider.bucketName;
95
77
  const fileData = await this.diskFileService.read(file.path);
96
78
  await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { contentType: file.mimetype, region });
97
79
  await this.diskFileService.delete(file.path);
98
80
  const awsFileUrl = fileName;
99
- const mediaEntity = await this.mediaRepository.createMedia({
100
- entityId: entity.id,
101
- modelMetadataId: mediaFieldMetadata.model.id,
81
+ const mediaEntity = await this.mediaRepository.createMedia((0, media_storage_utils_1.buildMediaRecordCreateInput)(entityId, mediaFieldMetadata, storageProvider, {
102
82
  relativeUri: awsFileUrl,
103
83
  mimeType: file.mimetype,
104
84
  fileSize: file.size,
105
85
  originalFileName: file.originalname,
106
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
107
- fieldMetadataId: mediaFieldMetadata.id
108
- });
86
+ }));
87
+ mediaEntity.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(mediaEntity.isPublic, storageProvider);
109
88
  result.push(mediaEntity);
110
89
  this.logger.debug(`Stored media with`, mediaEntity);
111
90
  }
@@ -113,16 +92,11 @@ let FileS3StorageProvider = FileS3StorageProvider_1 = class FileS3StorageProvide
113
92
  return result;
114
93
  }
115
94
  async delete(entity, mediaFieldMetadata) {
116
- const isSupportedEntity = entity instanceof common_entity_1.CommonEntity
117
- || entity instanceof legacy_common_entity_with_existing_id_entity_1.LegacyCommonEntityWithExistingId
118
- || entity instanceof legacy_common_with_generated_id_entity_1.LegacyCommonEntityWithGeneratedId;
119
- if (!isSupportedEntity) {
120
- throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
121
- }
95
+ const entityId = this.getSupportedEntityId(entity);
122
96
  const storageProvider = mediaFieldMetadata.mediaStorageProvider;
123
- const region = this.getEffectiveRegion(storageProvider.region);
124
- const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
125
- this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
97
+ const region = (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region);
98
+ const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
99
+ this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
126
100
  for (const media of existingMedia) {
127
101
  const bucketName = storageProvider.bucketName;
128
102
  await this.s3FileService.delete(`${bucketName}:${media.relativeUri}`, { region });
@@ -136,18 +110,17 @@ let FileS3StorageProvider = FileS3StorageProvider_1 = class FileS3StorageProvide
136
110
  if (!storageProvider?.bucketName || !media?.relativeUri) {
137
111
  return;
138
112
  }
139
- const region = this.getEffectiveRegion(storageProvider.region);
113
+ const region = (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region);
140
114
  await this.s3FileService.delete(`${storageProvider.bucketName}:${media.relativeUri}`, { region });
141
115
  }
142
- getEffectiveRegion(providerRegion) {
143
- return providerRegion || this.configService.get('S3_AWS_REGION_NAME');
144
- }
145
- getFullFilePath(media) {
146
- const region = this.getEffectiveRegion(media.mediaStorageProviderMetadata.region);
147
- return `https://${media.mediaStorageProviderMetadata.bucketName}.s3.${region}.amazonaws.com/${media.relativeUri}`;
148
- }
149
- getFileName(file) {
150
- return `${file.filename}-${file.originalname}`;
116
+ getSupportedEntityId(entity) {
117
+ const isSupportedEntity = entity instanceof common_entity_1.CommonEntity
118
+ || entity instanceof legacy_common_entity_with_existing_id_entity_1.LegacyCommonEntityWithExistingId
119
+ || entity instanceof legacy_common_with_generated_id_entity_1.LegacyCommonEntityWithGeneratedId;
120
+ if (!isSupportedEntity) {
121
+ throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
122
+ }
123
+ return entity.id;
151
124
  }
152
125
  };
153
126
  exports.FileS3StorageProvider = FileS3StorageProvider;
@@ -156,6 +129,7 @@ exports.FileS3StorageProvider = FileS3StorageProvider = FileS3StorageProvider_1
156
129
  __metadata("design:paramtypes", [config_1.ConfigService,
157
130
  file_1.DiskFileService,
158
131
  file_1.S3FileService,
159
- media_repository_1.MediaRepository])
132
+ media_repository_1.MediaRepository,
133
+ media_download_url_service_1.MediaDownloadUrlService])
160
134
  ], FileS3StorageProvider);
161
135
  //# sourceMappingURL=file-s3-storage-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-s3-storage-provider.js","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-s3-storage-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { ConfigService } from \"@nestjs/config\";\nimport { CommonEntity } from \"src/entities/common.entity\";\nimport { FieldMetadata } from \"src/entities/field-metadata.entity\";\nimport { LegacyCommonEntityWithExistingId } from \"src/entities/legacy-common.entity-with-existing-id.entity\";\nimport { LegacyCommonEntityWithGeneratedId } from \"src/entities/legacy-common-with-generated-id.entity\";\nimport { Media } from \"src/entities/media.entity\";\nimport { MediaStorageProvider } from \"src/interfaces\";\nimport { DiskFileService, S3FileService } from \"src/services/file\";\nimport { Readable } from \"stream\";\nimport { MediaRepository } from \"src/repository/media.repository\";\n\n@Injectable()\nexport class FileS3StorageProvider<T> implements MediaStorageProvider<T> {\n private logger = new Logger(FileS3StorageProvider.name);\n\n constructor(\n private readonly configService: ConfigService,\n readonly diskFileService: DiskFileService,\n readonly s3FileService: S3FileService,\n readonly mediaRepository: MediaRepository,\n ) { }\n\n async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const isSupportedEntity = entity instanceof CommonEntity\n || entity instanceof LegacyCommonEntityWithExistingId\n || entity instanceof LegacyCommonEntityWithGeneratedId;\n if (!isSupportedEntity) {\n throw new Error(\"Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId\");\n }\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = this.getEffectiveRegion(storageProvider.region);\n\n for (const [stream, fileName] of streamPairs) {\n const bucketName = storageProvider.bucketName;\n\n // Buffer the stream so we can get the byte count and upload in one pass\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const fileData = Buffer.concat(chunks);\n const fileSize = fileData.length;\n\n await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { region });\n\n const mediaEntity = await this.mediaRepository.createMedia({\n // @ts-ignore\n entityId: entity.id,\n modelMetadataId: mediaFieldMetadata.model.id,\n relativeUri: fileName,\n fileSize,\n mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,\n fieldMetadataId: mediaFieldMetadata.id\n }) as unknown as Media;\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n }\n return result;\n }\n\n async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const isSupportedEntity = entity instanceof CommonEntity\n || entity instanceof LegacyCommonEntityWithExistingId\n || entity instanceof LegacyCommonEntityWithGeneratedId;\n if (!isSupportedEntity) {\n 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\n }\n // @ts-ignore\n const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n\n // TODO: Check if the mediaStorageProvider (s3 in this case) is configured with a public bucket or not.\n // 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.\n // Add the full URL to the media\n for (const m of media) {\n const storageMeta = m.mediaStorageProviderMetadata;\n const region = this.getEffectiveRegion(storageMeta.region);\n if (storageMeta.isPublic === false) {\n // Generate signed URL\n const expiryInSeconds = (storageMeta.signedUrlExpiry ?? 60) * 60;\n m['_full_url'] = await this.s3FileService.getUrl(`${storageMeta?.bucketName}:${m.relativeUri}`, { expiresIn: expiryInSeconds, region });\n } else {\n // Public S3 or local filesystem: use normal URL\n m['_full_url'] = this.getFullFilePath(m);\n }\n }\n\n return media;\n // media.forEach(m => {\n // m['_full_url'] = this.getFullFilePath(m);\n // });\n // return media;\n }\n\n async store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const isSupportedEntity = entity instanceof CommonEntity\n || entity instanceof LegacyCommonEntityWithExistingId\n || entity instanceof LegacyCommonEntityWithGeneratedId;\n if (!isSupportedEntity) {\n 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\n }\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = this.getEffectiveRegion(storageProvider.region);\n\n for (const file of files) {\n const fileName = this.getFileName(file);\n const bucketName = storageProvider.bucketName;\n\n // Read file from disk and upload to S3\n const fileData = await this.diskFileService.read(file.path);\n await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { contentType: file.mimetype, region });\n\n // Delete temp file from disk\n await this.diskFileService.delete(file.path);\n\n const awsFileUrl = fileName;\n\n // Create an entry in the media table\n const mediaEntity = await this.mediaRepository.createMedia({\n // @ts-ignore\n entityId: entity.id,\n modelMetadataId: mediaFieldMetadata.model.id,\n relativeUri: awsFileUrl,\n mimeType: file.mimetype,\n fileSize: file.size,\n originalFileName: file.originalname,\n mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,\n fieldMetadataId: mediaFieldMetadata.id\n }) as unknown as Media;\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void> {\n const isSupportedEntity = entity instanceof CommonEntity\n || entity instanceof LegacyCommonEntityWithExistingId\n || entity instanceof LegacyCommonEntityWithGeneratedId;\n if (!isSupportedEntity) {\n 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\n }\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = this.getEffectiveRegion(storageProvider.region);\n\n // @ts-ignore\n const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n // @ts-ignore\n this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);\n for (const media of existingMedia) {\n const bucketName = storageProvider.bucketName;\n await this.s3FileService.delete(`${bucketName}:${media.relativeUri}`, { region });\n }\n }\n\n async deleteByMediaRecord(media: Media): Promise<void> {\n const storageProvider = media?.mediaStorageProviderMetadata;\n if (!storageProvider) {\n throw new Error(`mediaStorageProviderMetadata is not populated for media id ${media?.id ?? 'unknown'}`);\n }\n if (!storageProvider?.bucketName || !media?.relativeUri) {\n return;\n }\n const region = this.getEffectiveRegion(storageProvider.region);\n await this.s3FileService.delete(`${storageProvider.bucketName}:${media.relativeUri}`, { region });\n }\n\n /**\n * Get the effective region to use for S3 operations.\n * Uses the provider-specific region if configured, otherwise falls back to env variable.\n */\n private getEffectiveRegion(providerRegion?: string): string | undefined {\n return providerRegion || this.configService.get('S3_AWS_REGION_NAME');\n }\n\n private getFullFilePath(media: Media): string {\n // Use provider region if available, fallback to env variable\n const region = this.getEffectiveRegion(media.mediaStorageProviderMetadata.region);\n // https://lunarismedia.s3.ap-south-1.amazonaws.com/LUNARIS_CP_REGISTRATION_CREATIVE.jpg\n return `https://${media.mediaStorageProviderMetadata.bucketName}.s3.${region}.amazonaws.com/${media.relativeUri}`;\n }\n\n private getFileName(file: Express.Multer.File): string {\n return `${file.filename}-${file.originalname}`;\n }\n}\n"]}
1
+ {"version":3,"file":"file-s3-storage-provider.js","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-s3-storage-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { ConfigService } from \"@nestjs/config\";\nimport { CommonEntity } from \"src/entities/common.entity\";\nimport { FieldMetadata } from \"src/entities/field-metadata.entity\";\nimport { LegacyCommonEntityWithExistingId } from \"src/entities/legacy-common.entity-with-existing-id.entity\";\nimport { LegacyCommonEntityWithGeneratedId } from \"src/entities/legacy-common-with-generated-id.entity\";\nimport { Media } from \"src/entities/media.entity\";\nimport { MediaStorageProvider } from \"src/interfaces\";\nimport { DiskFileService, S3FileService } from \"src/services/file\";\nimport { Readable } from \"stream\";\nimport { MediaRepository } from \"src/repository/media.repository\";\nimport { MediaDownloadUrlService } from \"src/services/media-download-url.service\";\nimport {\n buildMediaRecordCreateInput,\n buildStoredMediaFileName,\n getEffectiveS3Region,\n resolveStoredMediaIsPublic,\n} from \"src/services/media-storage.utils\";\n\n@Injectable()\nexport class FileS3StorageProvider<T> implements MediaStorageProvider<T> {\n private logger = new Logger(FileS3StorageProvider.name);\n\n constructor(\n private readonly configService: ConfigService,\n readonly diskFileService: DiskFileService,\n readonly s3FileService: S3FileService,\n readonly mediaRepository: MediaRepository,\n private readonly mediaDownloadUrlService: MediaDownloadUrlService,\n ) { }\n\n async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const entityId = this.getSupportedEntityId(entity);\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = getEffectiveS3Region(this.configService, storageProvider.region);\n\n for (const [stream, fileName] of streamPairs) {\n const bucketName = storageProvider.bucketName;\n\n // Buffer the stream so we can get the byte count and upload in one pass\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const fileData = Buffer.concat(chunks);\n const fileSize = fileData.length;\n\n await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { region });\n\n const mediaEntity = await this.mediaRepository.createMedia(\n buildMediaRecordCreateInput(entityId, mediaFieldMetadata, storageProvider, {\n relativeUri: fileName,\n fileSize,\n })\n ) as unknown as Media;\n mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n }\n return result;\n }\n\n async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const entityId = this.getSupportedEntityId(entity);\n // @ts-ignore\n const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n\n // TODO: Check if the mediaStorageProvider (s3 in this case) is configured with a public bucket or not.\n // 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.\n // Add the full URL to the media\n for (const m of media) {\n const storageMeta = m.mediaStorageProviderMetadata;\n const isPublic = resolveStoredMediaIsPublic(m.isPublic, storageMeta);\n m.isPublic = isPublic;\n m['_full_url'] = isPublic === false\n ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageMeta)\n : await this.s3FileService.getUrl(\n `${storageMeta.bucketName}:${m.relativeUri}`,\n { region: getEffectiveS3Region(this.configService, storageMeta.region), expiresIn: 0 },\n );\n }\n\n return media;\n // media.forEach(m => {\n // m['_full_url'] = this.getFullFilePath(m);\n // });\n // return media;\n }\n\n async store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n const entityId = this.getSupportedEntityId(entity);\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = getEffectiveS3Region(this.configService, storageProvider.region);\n\n for (const file of files) {\n const fileName = buildStoredMediaFileName(file);\n const bucketName = storageProvider.bucketName;\n\n // Read file from disk and upload to S3\n const fileData = await this.diskFileService.read(file.path);\n await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { contentType: file.mimetype, region });\n\n // Delete temp file from disk\n await this.diskFileService.delete(file.path);\n\n const awsFileUrl = fileName;\n\n // Create an entry in the media table\n const mediaEntity = await this.mediaRepository.createMedia(\n buildMediaRecordCreateInput(entityId, mediaFieldMetadata, storageProvider, {\n relativeUri: awsFileUrl,\n mimeType: file.mimetype,\n fileSize: file.size,\n originalFileName: file.originalname,\n })\n ) as unknown as Media;\n mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void> {\n const entityId = this.getSupportedEntityId(entity);\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n const region = getEffectiveS3Region(this.configService, storageProvider.region);\n\n // @ts-ignore\n const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n // @ts-ignore\n this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);\n for (const media of existingMedia) {\n const bucketName = storageProvider.bucketName;\n await this.s3FileService.delete(`${bucketName}:${media.relativeUri}`, { region });\n }\n }\n\n async deleteByMediaRecord(media: Media): Promise<void> {\n const storageProvider = media?.mediaStorageProviderMetadata;\n if (!storageProvider) {\n throw new Error(`mediaStorageProviderMetadata is not populated for media id ${media?.id ?? 'unknown'}`);\n }\n if (!storageProvider?.bucketName || !media?.relativeUri) {\n return;\n }\n const region = getEffectiveS3Region(this.configService, storageProvider.region);\n await this.s3FileService.delete(`${storageProvider.bucketName}:${media.relativeUri}`, { region });\n }\n\n private getSupportedEntityId(entity: T): number {\n const isSupportedEntity = entity instanceof CommonEntity\n || entity instanceof LegacyCommonEntityWithExistingId\n || entity instanceof LegacyCommonEntityWithGeneratedId;\n\n if (!isSupportedEntity) {\n throw new Error(\"Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId\");\n }\n\n return (entity as any).id;\n }\n}\n"]}
@@ -1,4 +1,3 @@
1
- import { ConfigService } from "@nestjs/config";
2
1
  import { FieldMetadata } from "../../entities/field-metadata.entity";
3
2
  import { Media } from "../../entities/media.entity";
4
3
  import { MediaStorageProvider } from "../../interfaces";
@@ -6,19 +5,20 @@ import { MediaRepository } from "../../repository/media.repository";
6
5
  import { DiskFileService } from "../../services/file";
7
6
  import { Readable } from "stream";
8
7
  import { SettingService } from "../setting.service";
8
+ import { MediaDownloadUrlService } from "../../services/media-download-url.service";
9
9
  export declare class FileStorageProvider<T> implements MediaStorageProvider<T> {
10
- private readonly configService;
11
10
  readonly fileService: DiskFileService;
12
11
  readonly mediaRepository: MediaRepository;
13
12
  private readonly settingService;
13
+ private readonly mediaDownloadUrlService;
14
14
  private logger;
15
- constructor(configService: ConfigService, fileService: DiskFileService, mediaRepository: MediaRepository, settingService: SettingService);
15
+ constructor(fileService: DiskFileService, mediaRepository: MediaRepository, settingService: SettingService, mediaDownloadUrlService: MediaDownloadUrlService);
16
16
  retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
17
17
  store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
18
18
  storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
19
19
  delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void>;
20
20
  deleteByMediaRecord(media: Media): Promise<void>;
21
- private getFullFilePath;
22
- private getFileName;
21
+ private resolveMediaStorageProvider;
22
+ private getEntityId;
23
23
  }
24
24
  //# sourceMappingURL=file-storage-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-storage-provider.d.ts","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-storage-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGlC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBACa,mBAAmB,CAAC,CAAC,CAAE,YAAW,oBAAoB,CAAC,CAAC,CAAC;IAM9D,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,QAAQ,CAAC,WAAW,EAAE,eAAe;IACrC,QAAQ,CAAC,eAAe,EAAE,eAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc;IARnC,OAAO,CAAC,MAAM,CAAwC;gBAKjC,aAAa,EAAE,aAAa,EACpC,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,eAAe,EACxB,cAAc,EAAE,cAAc;IAI7C,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAiBxE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA6BnG,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAyB/G,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnE,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAOtD,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,WAAW;CAGtB"}
1
+ {"version":3,"file":"file-storage-provider.d.ts","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-storage-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAGlF,qBACa,mBAAmB,CAAC,CAAC,CAAE,YAAW,oBAAoB,CAAC,CAAC,CAAC;IAI9D,QAAQ,CAAC,WAAW,EAAE,eAAe;IACrC,QAAQ,CAAC,eAAe,EAAE,eAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAN5C,OAAO,CAAC,MAAM,CAAwC;gBAGzC,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,eAAe,EACxB,cAAc,EAAE,cAAc,EAC9B,uBAAuB,EAAE,uBAAuB;IAI/D,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAsBxE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA6BnG,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAyB/G,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnE,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAOtD,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,WAAW;CAGtB"}
@@ -45,44 +45,47 @@ var FileStorageProvider_1;
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
46
  exports.FileStorageProvider = void 0;
47
47
  const common_1 = require("@nestjs/common");
48
- const config_1 = require("@nestjs/config");
49
48
  const media_repository_1 = require("../../repository/media.repository");
50
49
  const file_1 = require("../file");
51
- const path = __importStar(require("path"));
52
50
  const fs = __importStar(require("fs"));
53
51
  const setting_service_1 = require("../setting.service");
54
- const default_settings_provider_service_1 = require("../settings/default-settings-provider.service");
52
+ const media_download_url_service_1 = require("../media-download-url.service");
53
+ const media_storage_utils_1 = require("../media-storage.utils");
55
54
  let FileStorageProvider = FileStorageProvider_1 = class FileStorageProvider {
56
- constructor(configService, fileService, mediaRepository, settingService) {
57
- this.configService = configService;
55
+ constructor(fileService, mediaRepository, settingService, mediaDownloadUrlService) {
58
56
  this.fileService = fileService;
59
57
  this.mediaRepository = mediaRepository;
60
58
  this.settingService = settingService;
59
+ this.mediaDownloadUrlService = mediaDownloadUrlService;
61
60
  this.logger = new common_1.Logger(FileStorageProvider_1.name);
62
61
  }
63
62
  async retrieve(entity, mediaFieldMetadata) {
64
- const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
63
+ const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(this.getEntityId(entity), mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
65
64
  for (const m of media) {
66
- m['_full_url'] = `${this.settingService.getConfigValue("baseUrl")}/${this.getFullFilePath(m.relativeUri)}`;
65
+ const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);
66
+ const isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(m.isPublic, storageProvider);
67
+ m.isPublic = isPublic;
68
+ m['_full_url'] = isPublic === false
69
+ ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageProvider)
70
+ : await this.fileService.getUrl((0, media_storage_utils_1.buildDiskMediaPath)(m.relativeUri, this.settingService, storageProvider));
67
71
  }
68
72
  return media;
69
73
  }
70
74
  async store(files, entity, mediaFieldMetadata) {
71
75
  const result = [];
76
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
72
77
  for (const file of files) {
73
- const fileStoragePath = this.getFullFilePath(this.getFileName(file));
78
+ const fileName = (0, media_storage_utils_1.buildStoredMediaFileName)(file);
79
+ const fileStoragePath = (0, media_storage_utils_1.buildDiskMediaPath)(fileName, this.settingService, storageProvider);
74
80
  await this.fileService.copy(file.path, fileStoragePath);
75
81
  await this.fileService.delete(file.path);
76
- const mediaEntity = await this.mediaRepository.createMedia({
77
- entityId: entity.id,
78
- modelMetadataId: mediaFieldMetadata.model.id,
79
- relativeUri: this.getFileName(file),
82
+ const mediaEntity = await this.mediaRepository.createMedia((0, media_storage_utils_1.buildMediaRecordCreateInput)(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
83
+ relativeUri: fileName,
80
84
  mimeType: file.mimetype,
81
85
  fileSize: file.size,
82
86
  originalFileName: file.originalname,
83
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
84
- fieldMetadataId: mediaFieldMetadata.id
85
- });
87
+ }));
88
+ mediaEntity.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(mediaEntity.isPublic, storageProvider);
86
89
  result.push(mediaEntity);
87
90
  this.logger.debug(`Stored media with`, mediaEntity);
88
91
  }
@@ -91,56 +94,52 @@ let FileStorageProvider = FileStorageProvider_1 = class FileStorageProvider {
91
94
  }
92
95
  async storeStreams(streamPairs, entity, mediaFieldMetadata) {
93
96
  const result = [];
97
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
94
98
  for (const pair of streamPairs) {
95
99
  const stream = pair[0];
96
100
  const fileName = pair[1];
97
- const fullPath = this.getFullFilePath(fileName);
101
+ const fullPath = (0, media_storage_utils_1.buildDiskMediaPath)(fileName, this.settingService, storageProvider);
98
102
  await this.fileService.writeStream(fullPath, stream);
99
103
  const { size: fileSize } = await fs.promises.stat(fullPath);
100
- const mediaEntity = await this.mediaRepository.createMedia({
101
- entityId: entity.id,
102
- modelMetadataId: mediaFieldMetadata.model.id,
104
+ const mediaEntity = await this.mediaRepository.createMedia((0, media_storage_utils_1.buildMediaRecordCreateInput)(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
103
105
  relativeUri: fileName,
104
106
  fileSize,
105
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
106
- fieldMetadataId: mediaFieldMetadata.id
107
- });
107
+ }));
108
+ mediaEntity.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(mediaEntity.isPublic, storageProvider);
109
+ result.push(mediaEntity);
108
110
  this.logger.debug(`Stored media with`, mediaEntity);
109
111
  }
110
112
  ;
111
113
  return result;
112
114
  }
113
115
  async delete(entity, mediaFieldMetadata) {
114
- const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
115
- this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
116
+ const entityId = this.getEntityId(entity);
117
+ const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
118
+ await this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
116
119
  for (const media of existingMedia) {
117
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
120
+ const storageProvider = this.resolveMediaStorageProvider(media, mediaFieldMetadata);
121
+ await this.fileService.delete((0, media_storage_utils_1.buildDiskMediaPath)(media.relativeUri, this.settingService, storageProvider));
118
122
  }
119
123
  }
120
124
  async deleteByMediaRecord(media) {
121
125
  if (!media?.relativeUri) {
122
126
  return;
123
127
  }
124
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
128
+ await this.fileService.delete((0, media_storage_utils_1.buildDiskMediaPath)(media.relativeUri, this.settingService, media.mediaStorageProviderMetadata));
125
129
  }
126
- getFullFilePath(fileName) {
127
- const base = this.settingService.getConfigValue("fileStorageDir")
128
- || default_settings_provider_service_1.DEFAULT_MEDIA_FILE_STORAGE_DIR;
129
- if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {
130
- return fileName;
131
- }
132
- return `${base}/${fileName}`;
130
+ resolveMediaStorageProvider(media, mediaFieldMetadata) {
131
+ return media.mediaStorageProviderMetadata || mediaFieldMetadata?.mediaStorageProvider;
133
132
  }
134
- getFileName(file) {
135
- return `${file.filename}-${file.originalname}`;
133
+ getEntityId(entity) {
134
+ return entity.id;
136
135
  }
137
136
  };
138
137
  exports.FileStorageProvider = FileStorageProvider;
139
138
  exports.FileStorageProvider = FileStorageProvider = FileStorageProvider_1 = __decorate([
140
139
  (0, common_1.Injectable)(),
141
- __metadata("design:paramtypes", [config_1.ConfigService,
142
- file_1.DiskFileService,
140
+ __metadata("design:paramtypes", [file_1.DiskFileService,
143
141
  media_repository_1.MediaRepository,
144
- setting_service_1.SettingService])
142
+ setting_service_1.SettingService,
143
+ media_download_url_service_1.MediaDownloadUrlService])
145
144
  ], FileStorageProvider);
146
145
  //# sourceMappingURL=file-storage-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-storage-provider.js","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-storage-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { ConfigService } from \"@nestjs/config\";\nimport { CommonEntity } from \"src/entities/common.entity\";\nimport { FieldMetadata } from \"src/entities/field-metadata.entity\";\nimport { Media } from \"src/entities/media.entity\";\nimport { MediaStorageProvider } from \"src/interfaces\";\nimport { MediaRepository } from \"src/repository/media.repository\";\nimport { DiskFileService } from \"src/services/file\";\nimport { Readable } from \"stream\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport { SettingService } from \"../setting.service\";\nimport { DEFAULT_MEDIA_FILE_STORAGE_DIR } from \"src/services/settings/default-settings-provider.service\";\nimport type { SolidCoreSetting } from \"src/services/settings/default-settings-provider.service\";\n\n@Injectable()\nexport class FileStorageProvider<T> implements MediaStorageProvider<T> {\n private logger = new Logger(FileStorageProvider.name);\n\n constructor(\n // @Inject(appBuilderConfig.KEY)\n // private readonly appBuilderConfiguration: ConfigType<typeof appBuilderConfig>,\n private readonly configService: ConfigService,\n readonly fileService: DiskFileService,\n readonly mediaRepository: MediaRepository,\n private readonly settingService: SettingService\n\n ) { }\n\n async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n //@ts-ignore\n const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n // Add the full URL to the media\n // media.forEach(m => {\n // });\n for (const m of media) {\n m['_full_url'] = `${this.settingService.getConfigValue<SolidCoreSetting>(\"baseUrl\")}/${this.getFullFilePath(m.relativeUri)}`;\n }\n\n\n return media;\n }\n\n async store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n const result: Media[] = [];\n for (const file of files) {\n // Store the file in the configured file storage directory\n const fileStoragePath = this.getFullFilePath(this.getFileName(file));\n await this.fileService.copy(file.path, fileStoragePath);\n await this.fileService.delete(file.path);\n\n // Create an entry in the media table\n const mediaEntity = await this.mediaRepository.createMedia({\n //@ts-ignore\n entityId: entity.id,\n modelMetadataId: mediaFieldMetadata.model.id,\n relativeUri: this.getFileName(file),\n mimeType: file.mimetype,\n fileSize: file.size,\n originalFileName: file.originalname,\n mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,\n fieldMetadataId: mediaFieldMetadata.id\n }) as unknown as Media;\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n const result: Media[] = [];\n for (const pair of streamPairs) {\n const stream = pair[0];\n const fileName = pair[1];\n const fullPath = this.getFullFilePath(fileName);\n await this.fileService.writeStream(fullPath, stream);\n const { size: fileSize } = await fs.promises.stat(fullPath);\n const mediaEntity = await this.mediaRepository.createMedia({\n //@ts-ignore\n entityId: entity.id,\n modelMetadataId: mediaFieldMetadata.model.id,\n relativeUri: fileName,\n fileSize,\n mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,\n fieldMetadataId: mediaFieldMetadata.id\n }) as unknown as Media;\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n //@ts-ignore\n const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n //@ts-ignore\n this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);\n\n for (const media of existingMedia) {\n await this.fileService.delete(this.getFullFilePath(media.relativeUri));\n }\n // existingMedia.forEach(media => {\n // });\n }\n\n async deleteByMediaRecord(media: Media): Promise<void> {\n if (!media?.relativeUri) {\n return;\n }\n await this.fileService.delete(this.getFullFilePath(media.relativeUri));\n }\n\n private getFullFilePath(fileName: string): string {\n const base = this.settingService.getConfigValue<SolidCoreSetting>(\"fileStorageDir\")\n || DEFAULT_MEDIA_FILE_STORAGE_DIR;\n if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {\n return fileName;\n }\n return `${base}/${fileName}`;\n }\n\n private getFileName(file: Express.Multer.File): string {\n return `${file.filename}-${file.originalname}`;\n }\n}\n"]}
1
+ {"version":3,"file":"file-storage-provider.js","sourceRoot":"","sources":["../../../src/services/mediaStorageProviders/file-storage-provider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AAKpD,wEAAkE;AAClE,kCAAoD;AAEpD,uCAAyB;AACzB,wDAAoD;AACpD,8EAAkF;AAClF,gEAA0J;AAGnJ,IAAM,mBAAmB,2BAAzB,MAAM,mBAAmB;IAG5B,YACa,WAA4B,EAC5B,eAAgC,EACxB,cAA8B,EAC9B,uBAAgD;QAHxD,gBAAW,GAAX,WAAW,CAAiB;QAC5B,oBAAe,GAAf,eAAe,CAAiB;QACxB,mBAAc,GAAd,cAAc,CAAgB;QAC9B,4BAAuB,GAAvB,uBAAuB,CAAyB;QAN7D,WAAM,GAAG,IAAI,eAAM,CAAC,qBAAmB,CAAC,IAAI,CAAC,CAAC;IAQlD,CAAC;IAEL,KAAK,CAAC,QAAQ,CAAC,MAAS,EAAE,kBAAiC;QAKvD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,0CAA0C,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,EAAE,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,8BAA8B,CAAC,CAAC,CAAC;QAIpM,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACpB,MAAM,eAAe,GAAG,IAAI,CAAC,2BAA2B,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;YAChF,MAAM,QAAQ,GAAG,IAAA,gDAA0B,EAAC,CAAC,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;YACzE,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACtB,CAAC,CAAC,WAAW,CAAC,GAAG,QAAQ,KAAK,KAAK;gBAC/B,CAAC,CAAC,MAAM,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,eAAe,CAAC;gBAC7F,CAAC,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAA,wCAAkB,EAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;QACjH,CAAC;QAGD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAA4B,EAAE,MAAS,EAAE,kBAAiC;QAIlF,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,MAAM,eAAe,GAAG,kBAAkB,CAAC,oBAAoB,CAAC;QAChE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAEvB,MAAM,QAAQ,GAAG,IAAA,8CAAwB,EAAC,IAAI,CAAC,CAAC;YAChD,MAAM,eAAe,GAAG,IAAA,wCAAkB,EAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;YAC3F,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACxD,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAGzC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CACtD,IAAA,iDAA2B,EAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,EAAE,eAAe,EAAE;gBACvF,WAAW,EAAE,QAAQ;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,gBAAgB,EAAE,IAAI,CAAC,YAAY;aACtC,CAAC,CACe,CAAC;YACtB,WAAW,CAAC,QAAQ,GAAG,IAAA,gDAA0B,EAAC,WAAW,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;YACzF,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;QACxD,CAAC;QAAA,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAiC,EAAE,MAAS,EAAE,kBAAiC;QAI9F,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,MAAM,eAAe,GAAG,kBAAkB,CAAC,oBAAoB,CAAC;QAChE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAA,wCAAkB,EAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;YACpF,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACrD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5D,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CACtD,IAAA,iDAA2B,EAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,EAAE,eAAe,EAAE;gBACvF,WAAW,EAAE,QAAQ;gBACrB,QAAQ;aACX,CAAC,CACe,CAAC;YACtB,WAAW,CAAC,QAAQ,GAAG,IAAA,gDAA0B,EAAC,WAAW,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;YACzF,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;QACxD,CAAC;QAAA,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAS,EAAE,kBAAiC;QAIrD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,0CAA0C,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,8BAA8B,CAAC,CAAC,CAAC;QAC5L,MAAM,IAAI,CAAC,eAAe,CAAC,4CAA4C,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEtI,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,eAAe,GAAG,IAAI,CAAC,2BAA2B,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;YACpF,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAA,wCAAkB,EAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;QAC/G,CAAC;IAGL,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAY;QAClC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;YACtB,OAAO;QACX,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAA,wCAAkB,EAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAClI,CAAC;IAEO,2BAA2B,CAAC,KAAY,EAAE,kBAAkC;QAChF,OAAO,KAAK,CAAC,4BAA4B,IAAI,kBAAkB,EAAE,oBAAoB,CAAC;IAC1F,CAAC;IAEO,WAAW,CAAC,MAAS;QACzB,OAAQ,MAAc,CAAC,EAAE,CAAC;IAC9B,CAAC;CACJ,CAAA;AArHY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;qCAKiB,sBAAe;QACX,kCAAe;QACR,gCAAc;QACL,oDAAuB;GAP5D,mBAAmB,CAqH/B","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { FieldMetadata } from \"src/entities/field-metadata.entity\";\nimport { Media } from \"src/entities/media.entity\";\nimport { MediaStorageProviderMetadata } from \"src/entities/media-storage-provider-metadata.entity\";\nimport { MediaStorageProvider } from \"src/interfaces\";\nimport { MediaRepository } from \"src/repository/media.repository\";\nimport { DiskFileService } from \"src/services/file\";\nimport { Readable } from \"stream\";\nimport * as fs from \"fs\";\nimport { SettingService } from \"../setting.service\";\nimport { MediaDownloadUrlService } from \"src/services/media-download-url.service\";\nimport { buildDiskMediaPath, buildMediaRecordCreateInput, buildStoredMediaFileName, resolveStoredMediaIsPublic, } from \"src/services/media-storage.utils\";\n\n@Injectable()\nexport class FileStorageProvider<T> implements MediaStorageProvider<T> {\n private logger = new Logger(FileStorageProvider.name);\n\n constructor(\n readonly fileService: DiskFileService,\n readonly mediaRepository: MediaRepository,\n private readonly settingService: SettingService,\n private readonly mediaDownloadUrlService: MediaDownloadUrlService,\n\n ) { }\n\n async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n //@ts-ignore\n const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(this.getEntityId(entity), mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n // Add the full URL to the media\n // media.forEach(m => {\n // });\n for (const m of media) {\n const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);\n const isPublic = resolveStoredMediaIsPublic(m.isPublic, storageProvider);\n m.isPublic = isPublic;\n m['_full_url'] = isPublic === false\n ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageProvider)\n : await this.fileService.getUrl(buildDiskMediaPath(m.relativeUri, this.settingService, storageProvider));\n }\n\n\n return media;\n }\n\n async store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n for (const file of files) {\n // Store the file in the configured file storage directory\n const fileName = buildStoredMediaFileName(file);\n const fileStoragePath = buildDiskMediaPath(fileName, this.settingService, storageProvider);\n await this.fileService.copy(file.path, fileStoragePath);\n await this.fileService.delete(file.path);\n\n // Create an entry in the media table\n const mediaEntity = await this.mediaRepository.createMedia(\n buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {\n relativeUri: fileName,\n mimeType: file.mimetype,\n fileSize: file.size,\n originalFileName: file.originalname,\n })\n ) as unknown as Media;\n mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n const result: Media[] = [];\n const storageProvider = mediaFieldMetadata.mediaStorageProvider;\n for (const pair of streamPairs) {\n const stream = pair[0];\n const fileName = pair[1];\n const fullPath = buildDiskMediaPath(fileName, this.settingService, storageProvider);\n await this.fileService.writeStream(fullPath, stream);\n const { size: fileSize } = await fs.promises.stat(fullPath);\n const mediaEntity = await this.mediaRepository.createMedia(\n buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {\n relativeUri: fileName,\n fileSize,\n })\n ) as unknown as Media;\n mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);\n result.push(mediaEntity);\n this.logger.debug(`Stored media with`, mediaEntity);\n };\n return result;\n }\n\n async delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void> {\n // if (!(entity instanceof CommonEntity)) {\n // throw new Error(\"Entity must be an instance of CommonEntity\"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity\n // }\n const entityId = this.getEntityId(entity);\n const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);\n await this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);\n\n for (const media of existingMedia) {\n const storageProvider = this.resolveMediaStorageProvider(media, mediaFieldMetadata);\n await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, storageProvider));\n }\n // existingMedia.forEach(media => {\n // });\n }\n\n async deleteByMediaRecord(media: Media): Promise<void> {\n if (!media?.relativeUri) {\n return;\n }\n await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, media.mediaStorageProviderMetadata));\n }\n\n private resolveMediaStorageProvider(media: Media, mediaFieldMetadata?: FieldMetadata): MediaStorageProviderMetadata | undefined {\n return media.mediaStorageProviderMetadata || mediaFieldMetadata?.mediaStorageProvider;\n }\n\n private getEntityId(entity: T): number {\n return (entity as any).id;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"solid-core.module.d.ts","sourceRoot":"","sources":["../src/solid-core.module.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,CAAC;AAChB,OAAO,EAAU,kBAAkB,EAAU,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA8YhF,qBA6ea,eAAgB,YAAW,UAAU;IAChD,SAAS,CAAC,QAAQ,EAAE,kBAAkB;CAQvC"}
1
+ {"version":3,"file":"solid-core.module.d.ts","sourceRoot":"","sources":["../src/solid-core.module.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,CAAC;AAChB,OAAO,EAAU,kBAAkB,EAAU,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAiZhF,qBA6fa,eAAgB,YAAW,UAAU;IAChD,SAAS,CAAC,QAAQ,EAAE,kBAAkB;CAQvC"}
@@ -81,6 +81,7 @@ const dashboard_runtime_service_1 = require("./services/dashboard-runtime.servic
81
81
  const list_of_values_service_1 = require("./services/list-of-values.service");
82
82
  const media_storage_provider_metadata_service_1 = require("./services/media-storage-provider-metadata.service");
83
83
  const media_service_1 = require("./services/media.service");
84
+ const media_download_url_service_1 = require("./services/media-download-url.service");
84
85
  const model_metadata_service_1 = require("./services/model-metadata.service");
85
86
  const removed_field_migration_service_1 = require("./services/removed-field-migration.service");
86
87
  const module_metadata_explorer_service_1 = require("./services/module-metadata-explorer.service");
@@ -91,6 +92,7 @@ const serve_static_1 = require("@nestjs/serve-static");
91
92
  const path_1 = require("path");
92
93
  const refresh_model_command_1 = require("./commands/refresh-model.command");
93
94
  const media_controller_1 = require("./controllers/media.controller");
95
+ const media_file_types_1 = require("./constants/media-file-types");
94
96
  const refresh_module_command_1 = require("./commands/refresh-module.command");
95
97
  const model_metadata_subscriber_1 = require("./subscribers/model-metadata.subscriber");
96
98
  const view_metadata_controller_1 = require("./controllers/view-metadata.controller");
@@ -133,6 +135,7 @@ const mq_message_entity_1 = require("./entities/mq-message.entity");
133
135
  const sms_template_entity_1 = require("./entities/sms-template.entity");
134
136
  const access_token_guard_1 = require("./guards/access-token.guard");
135
137
  const api_key_guard_1 = require("./guards/api-key.guard");
138
+ const media_signed_url_guard_1 = require("./guards/media-signed-url.guard");
136
139
  const authentication_guard_1 = require("./guards/authentication.guard");
137
140
  const permissions_guard_1 = require("./guards/permissions.guard");
138
141
  const solid_registry_1 = require("./helpers/solid-registry");
@@ -460,8 +463,13 @@ exports.SolidCoreModule = SolidCoreModule = __decorate([
460
463
  rootPath: (0, path_1.join)(process.cwd(), "media-files-storage"),
461
464
  serveRoot: "/media-files-storage",
462
465
  serveStaticOptions: {
463
- setHeaders: (res) => {
466
+ setHeaders: (res, path) => {
464
467
  res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
468
+ res.setHeader("X-Content-Type-Options", "nosniff");
469
+ if (!media_file_types_1.INLINE_SAFE_EXTENSIONS.has((0, path_1.extname)(path).toLowerCase().replace(/^\./, ''))) {
470
+ res.setHeader("Content-Type", "application/octet-stream");
471
+ res.setHeader("Content-Disposition", "attachment");
472
+ }
465
473
  },
466
474
  },
467
475
  }),
@@ -580,6 +588,7 @@ exports.SolidCoreModule = SolidCoreModule = __decorate([
580
588
  schematic_service_1.SchematicService,
581
589
  media_storage_provider_metadata_service_1.MediaStorageProviderMetadataService,
582
590
  media_service_1.MediaService,
591
+ media_download_url_service_1.MediaDownloadUrlService,
583
592
  module_metadata_seeder_service_1.ModuleMetadataSeederService,
584
593
  module_test_data_service_1.ModuleTestDataService,
585
594
  list_of_values_service_1.ListOfValuesService,
@@ -662,6 +671,7 @@ exports.SolidCoreModule = SolidCoreModule = __decorate([
662
671
  soft_delete_aware_event_subscriber_1.SoftDeleteAwareEventSubscriber,
663
672
  access_token_guard_1.AccessTokenGuard,
664
673
  api_key_guard_1.ApiKeyGuard,
674
+ media_signed_url_guard_1.MediaSignedUrlGuard,
665
675
  api_key_service_1.ApiKeyService,
666
676
  active_session_storage_service_1.ActiveSessionStorageService,
667
677
  authentication_service_1.AuthenticationService,