@solidxai/core 0.1.12-beta.8 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +504 -0
- package/dist/entities/media.entity.d.ts +0 -1
- package/dist/entities/media.entity.d.ts.map +1 -1
- package/dist/entities/media.entity.js +1 -9
- package/dist/entities/media.entity.js.map +1 -1
- package/dist/repository/media.repository.d.ts.map +1 -1
- package/dist/repository/media.repository.js +0 -2
- package/dist/repository/media.repository.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +1 -41
- package/dist/services/media-download-url.service.d.ts +3 -2
- package/dist/services/media-download-url.service.d.ts.map +1 -1
- package/dist/services/media-download-url.service.js +6 -3
- package/dist/services/media-download-url.service.js.map +1 -1
- package/dist/services/media-storage.utils.d.ts +0 -3
- package/dist/services/media-storage.utils.d.ts.map +1 -1
- package/dist/services/media-storage.utils.js +3 -12
- package/dist/services/media-storage.utils.js.map +1 -1
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +3 -5
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +2 -5
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +2 -5
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/package.json +1 -1
- package/src/entities/media.entity.ts +0 -3
- package/src/repository/media.repository.ts +1 -2
- package/src/seeders/seed-data/solid-core-metadata.json +1 -41
- package/src/services/media-download-url.service.ts +13 -6
- package/src/services/media-storage.utils.ts +7 -16
- package/src/services/media.service.ts +4 -6
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +6 -9
- package/src/services/mediaStorageProviders/file-storage-provider.ts +3 -6
|
@@ -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 { 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
|
+
{"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 { 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,resolveMediaIsPublic,} 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 = resolveMediaIsPublic(storageProvider);\n m['_full_url'] = isPublic === false\n ? await this.mediaDownloadUrlService.getPrivateUrl(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 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 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"]}
|
package/package.json
CHANGED
|
@@ -7,7 +7,6 @@ import { MediaStorageProviderMetadataRepository } from './media-storage-provider
|
|
|
7
7
|
import { ModelMetadataRepository } from './model-metadata.repository';
|
|
8
8
|
import { SecurityRuleRepository } from './security-rule.repository';
|
|
9
9
|
import { SolidBaseRepository } from './solid-base.repository';
|
|
10
|
-
import { resolveStoredMediaIsPublic } from 'src/services/media-storage.utils';
|
|
11
10
|
|
|
12
11
|
@Injectable()
|
|
13
12
|
export class MediaRepository extends SolidBaseRepository<Media> {
|
|
@@ -45,7 +44,7 @@ export class MediaRepository extends SolidBaseRepository<Media> {
|
|
|
45
44
|
id: createDto['mediaStorageProviderMetadataId']
|
|
46
45
|
},
|
|
47
46
|
});
|
|
48
|
-
|
|
47
|
+
// delete createDto['isPublic'];
|
|
49
48
|
const media = this.create(createDto);
|
|
50
49
|
return this.save(media);
|
|
51
50
|
}
|
|
@@ -1704,18 +1704,6 @@
|
|
|
1704
1704
|
"columnName": "original_file_name",
|
|
1705
1705
|
"isSystem": true
|
|
1706
1706
|
},
|
|
1707
|
-
{
|
|
1708
|
-
"name": "isPublic",
|
|
1709
|
-
"displayName": "Is Public",
|
|
1710
|
-
"type": "boolean",
|
|
1711
|
-
"required": false,
|
|
1712
|
-
"unique": false,
|
|
1713
|
-
"index": false,
|
|
1714
|
-
"private": false,
|
|
1715
|
-
"encrypt": false,
|
|
1716
|
-
"columnName": "is_public",
|
|
1717
|
-
"isSystem": true
|
|
1718
|
-
},
|
|
1719
1707
|
{
|
|
1720
1708
|
"name": "modelMetadata",
|
|
1721
1709
|
"displayName": "Model Metadata",
|
|
@@ -8789,13 +8777,6 @@
|
|
|
8789
8777
|
"sortable": false
|
|
8790
8778
|
}
|
|
8791
8779
|
},
|
|
8792
|
-
{
|
|
8793
|
-
"type": "field",
|
|
8794
|
-
"attrs": {
|
|
8795
|
-
"name": "isPublic",
|
|
8796
|
-
"sortable": true
|
|
8797
|
-
}
|
|
8798
|
-
},
|
|
8799
8780
|
{
|
|
8800
8781
|
"type": "field",
|
|
8801
8782
|
"attrs": {
|
|
@@ -8891,14 +8872,6 @@
|
|
|
8891
8872
|
"isSearchable": true
|
|
8892
8873
|
}
|
|
8893
8874
|
},
|
|
8894
|
-
{
|
|
8895
|
-
"type": "field",
|
|
8896
|
-
"attrs": {
|
|
8897
|
-
"name": "isPublic",
|
|
8898
|
-
"label": "isPublic",
|
|
8899
|
-
"isSearchable": true
|
|
8900
|
-
}
|
|
8901
|
-
},
|
|
8902
8875
|
{
|
|
8903
8876
|
"type": "field",
|
|
8904
8877
|
"attrs": {
|
|
@@ -8967,12 +8940,6 @@
|
|
|
8967
8940
|
"name": "mediaStorageProviderMetadata"
|
|
8968
8941
|
}
|
|
8969
8942
|
},
|
|
8970
|
-
{
|
|
8971
|
-
"type": "field",
|
|
8972
|
-
"attrs": {
|
|
8973
|
-
"name": "isPublic"
|
|
8974
|
-
}
|
|
8975
|
-
},
|
|
8976
8943
|
{
|
|
8977
8944
|
"type": "field",
|
|
8978
8945
|
"attrs": {
|
|
@@ -14578,14 +14545,7 @@
|
|
|
14578
14545
|
{
|
|
14579
14546
|
"name": "default-filesystem",
|
|
14580
14547
|
"type": "filesystem",
|
|
14581
|
-
"isPublic": true
|
|
14582
|
-
"localPath": "media-files-storage"
|
|
14583
|
-
},
|
|
14584
|
-
{
|
|
14585
|
-
"name": "private-filesystem",
|
|
14586
|
-
"type": "filesystem",
|
|
14587
|
-
"isPublic": false,
|
|
14588
|
-
"localPath": "media-private-files-storage"
|
|
14548
|
+
"isPublic": true
|
|
14589
14549
|
},
|
|
14590
14550
|
{
|
|
14591
14551
|
"name": "default-aws-s3",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
-
import { ConfigService } from '@nestjs/config';
|
|
3
2
|
import { JwtService } from '@nestjs/jwt';
|
|
4
3
|
import { MediaStorageProviderType } from 'src/dtos/create-media-storage-provider-metadata.dto';
|
|
5
4
|
import { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';
|
|
6
|
-
import { S3FileService } from 'src/services/file';
|
|
7
|
-
import { getEffectiveS3Region } from 'src/services/media-storage.utils';
|
|
8
5
|
import { SettingService } from 'src/services/setting.service';
|
|
9
6
|
import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
|
|
7
|
+
import { getEffectiveS3Region } from './media-storage.utils';
|
|
8
|
+
import { ConfigService } from '@nestjs/config';
|
|
9
|
+
import { S3FileService } from './file';
|
|
10
10
|
|
|
11
11
|
export const MEDIA_DOWNLOAD_TOKEN_AUDIENCE = 'media-download';
|
|
12
12
|
const DEFAULT_SIGNED_URL_EXPIRY_MINUTES = 60;
|
|
@@ -26,10 +26,14 @@ export class MediaDownloadUrlService {
|
|
|
26
26
|
private readonly configService: ConfigService,
|
|
27
27
|
) { }
|
|
28
28
|
|
|
29
|
-
async resolveDownloadUrl(
|
|
29
|
+
async resolveDownloadUrl(
|
|
30
|
+
mediaId: number,
|
|
31
|
+
relativeUri: string,
|
|
32
|
+
storageProvider?: MediaStorageProviderMetadata,
|
|
33
|
+
): Promise<string> {
|
|
30
34
|
const expiryMinutes = storageProvider?.signedUrlExpiry ?? DEFAULT_SIGNED_URL_EXPIRY_MINUTES;
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
if ((storageProvider?.type as MediaStorageProviderType) === MediaStorageProviderType.AwsS3) {
|
|
33
37
|
if (storageProvider?.bucketName) {
|
|
34
38
|
return this.s3FileService.getUrl(
|
|
35
39
|
`${storageProvider.bucketName}:${relativeUri}`,
|
|
@@ -38,7 +42,6 @@ export class MediaDownloadUrlService {
|
|
|
38
42
|
}
|
|
39
43
|
this.logger.warn(`Media id ${mediaId}: AwsS3 provider missing bucketName; falling back to app-hosted token URL`);
|
|
40
44
|
}
|
|
41
|
-
|
|
42
45
|
// Filesystem (or misconfigured S3) has no native presigned-URL mechanism, so mint our
|
|
43
46
|
// own short-lived token and embed it in the download URL - the token alone authorizes
|
|
44
47
|
// the request (see MediaSignedUrlGuard), so it works in a plain <img src> with no
|
|
@@ -49,6 +52,10 @@ export class MediaDownloadUrlService {
|
|
|
49
52
|
return `${String(baseUrl).replace(/\/+$/, '')}${downloadPath}`;
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
async getPrivateUrl(mediaId: number, relativeUri: string, storageProvider?: MediaStorageProviderMetadata): Promise<string> {
|
|
56
|
+
return this.resolveDownloadUrl(mediaId, relativeUri, storageProvider);
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
async verifyDownloadToken(token: string): Promise<MediaDownloadTokenPayload> {
|
|
53
60
|
return this.jwtService.verifyAsync<MediaDownloadTokenPayload>(token, {
|
|
54
61
|
secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),
|
|
@@ -6,7 +6,6 @@ import { SettingService } from 'src/services/setting.service';
|
|
|
6
6
|
import { DEFAULT_MEDIA_FILE_STORAGE_DIR } from 'src/services/settings/default-settings-provider.service';
|
|
7
7
|
import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
|
|
8
8
|
|
|
9
|
-
export const DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = 'media-private-files-storage';
|
|
10
9
|
|
|
11
10
|
export interface MediaRecordCreateInput {
|
|
12
11
|
entityId: number;
|
|
@@ -17,7 +16,6 @@ export interface MediaRecordCreateInput {
|
|
|
17
16
|
mimeType?: string;
|
|
18
17
|
fileSize?: number;
|
|
19
18
|
originalFileName?: string;
|
|
20
|
-
isPublic?: boolean;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
// Builds the stored file name format shared by media storage implementations.
|
|
@@ -28,10 +26,12 @@ export function buildStoredMediaFileName(file: Pick<Express.Multer.File, 'filena
|
|
|
28
26
|
// Resolves the on-disk path for a media file based on settings and provider visibility.
|
|
29
27
|
export function buildDiskMediaPath(fileName: string,settingService: Pick<SettingService, 'getConfigValue'>,storageProvider?: Pick<MediaStorageProviderMetadata, 'localPath' | 'isPublic'>,): string {
|
|
30
28
|
const publicBase = settingService.getConfigValue<SolidCoreSetting>('fileStorageDir') || DEFAULT_MEDIA_FILE_STORAGE_DIR;
|
|
31
|
-
const privateBase =
|
|
32
|
-
const
|
|
29
|
+
const privateBase = `${publicBase}-private`;
|
|
30
|
+
// const localPath = storageProvider?.localPath;
|
|
31
|
+
// const isUsingLegacyDefaultPath = localPath === publicBase || localPath === privateBase || localPath === privateBase;
|
|
32
|
+
const providerBase = storageProvider?.isPublic === false ? privateBase : publicBase
|
|
33
33
|
|
|
34
|
-
if ( path.isAbsolute(fileName) || fileName.startsWith(`${publicBase}/`) || fileName.startsWith(`${privateBase}/`)
|
|
34
|
+
if ( path.isAbsolute(fileName) || fileName.startsWith(`${publicBase}/`) || fileName.startsWith(`${privateBase}/`) ) {
|
|
35
35
|
return fileName;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -39,12 +39,12 @@ export function buildDiskMediaPath(fileName: string,settingService: Pick<Setting
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// Returns the S3 region from the provider when present, otherwise falls back to app config.
|
|
42
|
-
export function getEffectiveS3Region(configService: Pick<ConfigService, 'get'>,providerRegion?: string
|
|
42
|
+
export function getEffectiveS3Region(configService: Pick<ConfigService, 'get'>, providerRegion?: string): string | undefined {
|
|
43
43
|
return providerRegion || configService.get<string>('S3_AWS_REGION_NAME');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// Resolves the effective public/private flag for a media storage provider.
|
|
47
|
-
export function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'
|
|
47
|
+
export function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'>): boolean | undefined {
|
|
48
48
|
if (!storageProvider) {
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
@@ -52,15 +52,6 @@ export function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProvider
|
|
|
52
52
|
return storageProvider.isPublic !== false;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// Preserves an explicit media visibility value or derives it from the storage provider.
|
|
56
|
-
export function resolveStoredMediaIsPublic(currentValue: boolean | undefined,storageProvider?:Pick<MediaStorageProviderMetadata, 'isPublic'>,): boolean | undefined {
|
|
57
|
-
if (typeof currentValue === 'boolean') {
|
|
58
|
-
return currentValue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return resolveMediaIsPublic(storageProvider);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
55
|
// Builds the shared media record payload used before saving media metadata to the database.
|
|
65
56
|
export function buildMediaRecordCreateInput(
|
|
66
57
|
entityId: number,
|
|
@@ -20,7 +20,7 @@ import { FieldMetadataRepository } from 'src/repository/field-metadata.repositor
|
|
|
20
20
|
import { MediaStorageProviderMetadataRepository } from 'src/repository/media-storage-provider-metadata.repository';
|
|
21
21
|
import { MediaRepository } from 'src/repository/media.repository';
|
|
22
22
|
import { ModelMetadataRepository } from 'src/repository/model-metadata.repository';
|
|
23
|
-
import { buildDiskMediaPath, buildStoredMediaFileName, getEffectiveS3Region,
|
|
23
|
+
import { buildDiskMediaPath, buildStoredMediaFileName, getEffectiveS3Region, resolveMediaIsPublic, } from 'src/services/media-storage.utils';
|
|
24
24
|
import { getMediaStorageProvider } from "./mediaStorageProviders";
|
|
25
25
|
|
|
26
26
|
@Injectable()
|
|
@@ -131,10 +131,9 @@ export class MediaService extends CRUDService<Media> {
|
|
|
131
131
|
// Delete temp file from disk
|
|
132
132
|
await this.diskFileService.delete(file.path);
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
delete createDto['isPublic'];
|
|
135
135
|
const media = this.repo.create(createDto as Partial<Media>) as Media;
|
|
136
136
|
const savedMedia = await this.repo.save(media);
|
|
137
|
-
savedMedia.isPublic = resolveStoredMediaIsPublic(savedMedia.isPublic, storageProvider);
|
|
138
137
|
savedMedias.push(savedMedia)
|
|
139
138
|
}
|
|
140
139
|
return savedMedias
|
|
@@ -228,16 +227,15 @@ export class MediaService extends CRUDService<Media> {
|
|
|
228
227
|
|
|
229
228
|
private async decorateMediaRecord(media: Media): Promise<void> {
|
|
230
229
|
const mediaStorageProvider = await this.resolveMediaStorageProvider(media);
|
|
231
|
-
media.isPublic = resolveStoredMediaIsPublic(media.isPublic, mediaStorageProvider);
|
|
232
230
|
media.relativeUri = await this.resolveMediaUrl(media, mediaStorageProvider);
|
|
233
231
|
}
|
|
234
232
|
|
|
235
233
|
private async resolveMediaUrl(media: Media, mediaStorageProvider?: MediaStorageProviderMetadata): Promise<string> {
|
|
236
234
|
const resolvedMediaStorageProvider = mediaStorageProvider || await this.resolveMediaStorageProvider(media);
|
|
237
|
-
const isPublic =
|
|
235
|
+
const isPublic = resolveMediaIsPublic(resolvedMediaStorageProvider);
|
|
238
236
|
|
|
239
237
|
if (isPublic === false) {
|
|
240
|
-
return this.mediaDownloadUrlService.
|
|
238
|
+
return this.mediaDownloadUrlService.getPrivateUrl(media.id, media.relativeUri, resolvedMediaStorageProvider);
|
|
241
239
|
}
|
|
242
240
|
|
|
243
241
|
if (resolvedMediaStorageProvider?.type === MediaStorageProviderType.Filesystem) {
|
|
@@ -11,10 +11,10 @@ import { Readable } from "stream";
|
|
|
11
11
|
import { MediaRepository } from "src/repository/media.repository";
|
|
12
12
|
import { MediaDownloadUrlService } from "src/services/media-download-url.service";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
buildMediaRecordCreateInput,
|
|
15
|
+
buildStoredMediaFileName,
|
|
16
|
+
getEffectiveS3Region,
|
|
17
|
+
resolveMediaIsPublic,
|
|
18
18
|
} from "src/services/media-storage.utils";
|
|
19
19
|
|
|
20
20
|
@Injectable()
|
|
@@ -54,7 +54,6 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
54
54
|
fileSize,
|
|
55
55
|
})
|
|
56
56
|
) as unknown as Media;
|
|
57
|
-
mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
|
|
58
57
|
result.push(mediaEntity);
|
|
59
58
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
60
59
|
}
|
|
@@ -71,10 +70,9 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
71
70
|
// Add the full URL to the media
|
|
72
71
|
for (const m of media) {
|
|
73
72
|
const storageMeta = m.mediaStorageProviderMetadata;
|
|
74
|
-
const isPublic =
|
|
75
|
-
m.isPublic = isPublic;
|
|
73
|
+
const isPublic = resolveMediaIsPublic(storageMeta);
|
|
76
74
|
m['_full_url'] = isPublic === false
|
|
77
|
-
? await this.mediaDownloadUrlService.
|
|
75
|
+
? await this.mediaDownloadUrlService.getPrivateUrl(m.id, m.relativeUri, storageMeta)
|
|
78
76
|
: await this.s3FileService.getUrl(
|
|
79
77
|
`${storageMeta.bucketName}:${m.relativeUri}`,
|
|
80
78
|
{ region: getEffectiveS3Region(this.configService, storageMeta.region), expiresIn: 0 },
|
|
@@ -116,7 +114,6 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
116
114
|
originalFileName: file.originalname,
|
|
117
115
|
})
|
|
118
116
|
) as unknown as Media;
|
|
119
|
-
mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
|
|
120
117
|
result.push(mediaEntity);
|
|
121
118
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
122
119
|
};
|
|
@@ -9,7 +9,7 @@ import { Readable } from "stream";
|
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import { SettingService } from "../setting.service";
|
|
11
11
|
import { MediaDownloadUrlService } from "src/services/media-download-url.service";
|
|
12
|
-
import {
|
|
12
|
+
import {buildDiskMediaPath,buildMediaRecordCreateInput,buildStoredMediaFileName,resolveMediaIsPublic,} from "src/services/media-storage.utils";
|
|
13
13
|
|
|
14
14
|
@Injectable()
|
|
15
15
|
export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
@@ -34,10 +34,9 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
34
34
|
// });
|
|
35
35
|
for (const m of media) {
|
|
36
36
|
const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);
|
|
37
|
-
const isPublic =
|
|
38
|
-
m.isPublic = isPublic;
|
|
37
|
+
const isPublic = resolveMediaIsPublic(storageProvider);
|
|
39
38
|
m['_full_url'] = isPublic === false
|
|
40
|
-
? await this.mediaDownloadUrlService.
|
|
39
|
+
? await this.mediaDownloadUrlService.getPrivateUrl(m.id, m.relativeUri, storageProvider)
|
|
41
40
|
: await this.fileService.getUrl(buildDiskMediaPath(m.relativeUri, this.settingService, storageProvider));
|
|
42
41
|
}
|
|
43
42
|
|
|
@@ -67,7 +66,6 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
67
66
|
originalFileName: file.originalname,
|
|
68
67
|
})
|
|
69
68
|
) as unknown as Media;
|
|
70
|
-
mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
|
|
71
69
|
result.push(mediaEntity);
|
|
72
70
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
73
71
|
};
|
|
@@ -92,7 +90,6 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
92
90
|
fileSize,
|
|
93
91
|
})
|
|
94
92
|
) as unknown as Media;
|
|
95
|
-
mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
|
|
96
93
|
result.push(mediaEntity);
|
|
97
94
|
this.logger.debug(`Stored media with`, mediaEntity);
|
|
98
95
|
};
|