@solidxai/core 0.1.12-beta.2 → 0.1.12-beta.4

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 (162) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/CLAUDE.md +26 -0
  3. package/dist/commands/seed.command.d.ts.map +1 -1
  4. package/dist/commands/seed.command.js +15 -2
  5. package/dist/commands/seed.command.js.map +1 -1
  6. package/dist/constants/media-file-types.d.ts +5 -0
  7. package/dist/constants/media-file-types.d.ts.map +1 -0
  8. package/dist/constants/media-file-types.js +27 -0
  9. package/dist/constants/media-file-types.js.map +1 -0
  10. package/dist/controllers/media.controller.d.ts +14 -11
  11. package/dist/controllers/media.controller.d.ts.map +1 -1
  12. package/dist/controllers/media.controller.js +72 -33
  13. package/dist/controllers/media.controller.js.map +1 -1
  14. package/dist/dtos/create-media.dto.d.ts +1 -0
  15. package/dist/dtos/create-media.dto.d.ts.map +1 -1
  16. package/dist/dtos/create-media.dto.js +7 -1
  17. package/dist/dtos/create-media.dto.js.map +1 -1
  18. package/dist/dtos/create-role-metadata.dto.d.ts +2 -0
  19. package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
  20. package/dist/dtos/create-role-metadata.dto.js +3 -1
  21. package/dist/dtos/create-role-metadata.dto.js.map +1 -1
  22. package/dist/dtos/update-media.dto.d.ts +1 -0
  23. package/dist/dtos/update-media.dto.d.ts.map +1 -1
  24. package/dist/dtos/update-media.dto.js +7 -1
  25. package/dist/dtos/update-media.dto.js.map +1 -1
  26. package/dist/entities/media.entity.d.ts +1 -0
  27. package/dist/entities/media.entity.d.ts.map +1 -1
  28. package/dist/entities/media.entity.js +9 -1
  29. package/dist/entities/media.entity.js.map +1 -1
  30. package/dist/enums/auth-type.enum.d.ts +2 -1
  31. package/dist/enums/auth-type.enum.d.ts.map +1 -1
  32. package/dist/enums/auth-type.enum.js +1 -0
  33. package/dist/enums/auth-type.enum.js.map +1 -1
  34. package/dist/guards/authentication.guard.d.ts +3 -1
  35. package/dist/guards/authentication.guard.d.ts.map +1 -1
  36. package/dist/guards/authentication.guard.js +5 -1
  37. package/dist/guards/authentication.guard.js.map +1 -1
  38. package/dist/guards/media-signed-url.guard.d.ts +9 -0
  39. package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
  40. package/dist/guards/media-signed-url.guard.js +49 -0
  41. package/dist/guards/media-signed-url.guard.js.map +1 -0
  42. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  43. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
  44. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  45. package/dist/interfaces.d.ts +19 -1
  46. package/dist/interfaces.d.ts.map +1 -1
  47. package/dist/interfaces.js +2 -0
  48. package/dist/interfaces.js.map +1 -1
  49. package/dist/repository/media.repository.d.ts.map +1 -1
  50. package/dist/repository/media.repository.js +2 -0
  51. package/dist/repository/media.repository.js.map +1 -1
  52. package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
  53. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  54. package/dist/seeders/module-metadata-seeder.service.js +87 -3
  55. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  56. package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -0
  57. package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
  58. package/dist/seeders/permission-metadata-seeder.service.js +6 -1
  59. package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
  60. package/dist/seeders/seed-data/solid-core-metadata.json +114 -13
  61. package/dist/services/authentication.service.d.ts +1 -0
  62. package/dist/services/authentication.service.d.ts.map +1 -1
  63. package/dist/services/authentication.service.js +19 -1
  64. package/dist/services/authentication.service.js.map +1 -1
  65. package/dist/services/media-download-url.service.d.ts +21 -0
  66. package/dist/services/media-download-url.service.d.ts.map +1 -0
  67. package/dist/services/media-download-url.service.js +67 -0
  68. package/dist/services/media-download-url.service.js.map +1 -0
  69. package/dist/services/media-storage.utils.d.ts +27 -0
  70. package/dist/services/media-storage.utils.d.ts.map +1 -0
  71. package/dist/services/media-storage.utils.js +82 -0
  72. package/dist/services/media-storage.utils.js.map +1 -0
  73. package/dist/services/media.service.d.ts +20 -4
  74. package/dist/services/media.service.d.ts.map +1 -1
  75. package/dist/services/media.service.js +157 -55
  76. package/dist/services/media.service.js.map +1 -1
  77. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
  78. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  79. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +37 -63
  80. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  81. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
  82. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  83. package/dist/services/mediaStorageProviders/file-storage-provider.js +37 -38
  84. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  85. package/dist/services/role-metadata.service.d.ts +1 -0
  86. package/dist/services/role-metadata.service.d.ts.map +1 -1
  87. package/dist/services/role-metadata.service.js +8 -5
  88. package/dist/services/role-metadata.service.js.map +1 -1
  89. package/dist/solid-core.module.d.ts.map +1 -1
  90. package/dist/solid-core.module.js +11 -1
  91. package/dist/solid-core.module.js.map +1 -1
  92. package/dist/subscribers/audit.subscriber.js +1 -1
  93. package/dist/subscribers/audit.subscriber.js.map +1 -1
  94. package/package.json +1 -1
  95. package/src/commands/seed.command.ts +17 -3
  96. package/src/constants/media-file-types.ts +44 -0
  97. package/src/controllers/media.controller.ts +44 -24
  98. package/src/dtos/create-media.dto.ts +8 -3
  99. package/src/dtos/create-role-metadata.dto.ts +2 -0
  100. package/src/dtos/update-media.dto.ts +8 -3
  101. package/src/entities/media.entity.ts +4 -1
  102. package/src/enums/auth-type.enum.ts +1 -0
  103. package/src/guards/authentication.guard.ts +3 -0
  104. package/src/guards/media-signed-url.guard.ts +41 -0
  105. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
  106. package/src/interfaces.ts +20 -0
  107. package/src/repository/media.repository.ts +2 -0
  108. package/src/seeders/module-metadata-seeder.service.ts +109 -3
  109. package/src/seeders/permission-metadata-seeder.service.ts +6 -1
  110. package/src/seeders/seed-data/solid-core-metadata.json +114 -13
  111. package/src/services/1.js +6 -0
  112. package/src/services/authentication.service.ts +24 -1
  113. package/src/services/media-download-url.service.ts +67 -0
  114. package/src/services/media-storage.utils.ts +79 -0
  115. package/src/services/media.service.ts +189 -59
  116. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +52 -76
  117. package/src/services/mediaStorageProviders/file-storage-provider.ts +44 -48
  118. package/src/services/role-metadata.service.ts +17 -5
  119. package/src/solid-core.module.ts +21 -2
  120. package/src/subscribers/audit.subscriber.ts +1 -1
  121. package/dist-tests/api/authenticate.spec.js +0 -119
  122. package/dist-tests/api/authenticate.spec.js.map +0 -1
  123. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  124. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  125. package/dist-tests/api/ping.spec.js +0 -21
  126. package/dist-tests/api/ping.spec.js.map +0 -1
  127. package/dist-tests/helpers/auth.js +0 -41
  128. package/dist-tests/helpers/auth.js.map +0 -1
  129. package/dist-tests/helpers/env.js +0 -11
  130. package/dist-tests/helpers/env.js.map +0 -1
  131. package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +0 -136
  132. package/docs/agent-builder/team-ready-note-agent-builder.md +0 -305
  133. package/docs/agent-hub-grooming.md +0 -301
  134. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
  135. package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
  136. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
  137. package/docs/datasource-introspection-ddl-analysis.md +0 -326
  138. package/docs/datasource-introspection-implementation-plan.md +0 -306
  139. package/docs/grouping-enhancements.md +0 -89
  140. package/docs/java-spring/README.md +0 -3
  141. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  142. package/docs/module-package-import-handoff.md +0 -691
  143. package/docs/seed-changes.md +0 -65
  144. package/docs/test-data-workflow.md +0 -200
  145. package/docs/type-declaration-import-issue.md +0 -24
  146. package/docs/workflow/business-automation-example-notes.md +0 -309
  147. package/docs/workflow/data-engineering-pipeline-example-notes.md +0 -330
  148. package/docs/workflow/foreach-example-notes.md +0 -187
  149. package/docs/workflow/hello-world-example-notes.md +0 -271
  150. package/docs/workflow/kestra-concepts-plugins-blueprints.md +0 -315
  151. package/docs/workflow/loop-until-example-notes.md +0 -198
  152. package/docs/workflow/microservices-and-apis-example-notes.md +0 -264
  153. package/docs/workflow/samples.md +0 -25
  154. package/docs/workflow/what-is-kestra.md +0 -79
  155. package/docs/workflow/workflow-core-module-handoff-summary.md +0 -191
  156. package/docs/workflow/workflow-module-brd.md +0 -185
  157. package/docs/workflow/workflow-module-domain-model.md +0 -252
  158. package/docs/workflow/workflow-module-metadata-dsl.md +0 -302
  159. package/docs/workflow/workflow-module-technical-spec.md +0 -293
  160. package/docs/workflow/workflow-node-type-contracts.md +0 -635
  161. package/docs/workflow/workflow-node-ui-metadata-contract.md +0 -1262
  162. package/docs/workflow/workflow-solidx-model-field-spec.md +0 -284
@@ -0,0 +1,21 @@
1
+ import { ConfigService } from '@nestjs/config';
2
+ import { JwtService } from '@nestjs/jwt';
3
+ import { MediaStorageProviderMetadata } from '../entities/media-storage-provider-metadata.entity';
4
+ import { S3FileService } from '../services/file';
5
+ import { SettingService } from '../services/setting.service';
6
+ export declare const MEDIA_DOWNLOAD_TOKEN_AUDIENCE = "media-download";
7
+ export interface MediaDownloadTokenPayload {
8
+ mediaId: number;
9
+ }
10
+ export declare class MediaDownloadUrlService {
11
+ private readonly jwtService;
12
+ private readonly s3FileService;
13
+ private readonly settingService;
14
+ private readonly configService;
15
+ private readonly logger;
16
+ constructor(jwtService: JwtService, s3FileService: S3FileService, settingService: SettingService, configService: ConfigService);
17
+ resolveDownloadUrl(mediaId: number, relativeUri: string, storageProvider?: MediaStorageProviderMetadata): Promise<string>;
18
+ verifyDownloadToken(token: string): Promise<MediaDownloadTokenPayload>;
19
+ private mintDownloadToken;
20
+ }
21
+ //# sourceMappingURL=media-download-url.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download-url.service.d.ts","sourceRoot":"","sources":["../../src/services/media-download-url.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,4BAA4B,EAAE,MAAM,qDAAqD,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAG9D,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,uBAAuB;IAIhC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IANhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAGhD,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,aAAa,EAAE,aAAa;IAGzC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBzH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAO5E,OAAO,CAAC,iBAAiB;CAQ1B"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var MediaDownloadUrlService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.MediaDownloadUrlService = exports.MEDIA_DOWNLOAD_TOKEN_AUDIENCE = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const config_1 = require("@nestjs/config");
16
+ const jwt_1 = require("@nestjs/jwt");
17
+ const create_media_storage_provider_metadata_dto_1 = require("../dtos/create-media-storage-provider-metadata.dto");
18
+ const file_1 = require("./file");
19
+ const media_storage_utils_1 = require("./media-storage.utils");
20
+ const setting_service_1 = require("./setting.service");
21
+ exports.MEDIA_DOWNLOAD_TOKEN_AUDIENCE = 'media-download';
22
+ const DEFAULT_SIGNED_URL_EXPIRY_MINUTES = 60;
23
+ let MediaDownloadUrlService = MediaDownloadUrlService_1 = class MediaDownloadUrlService {
24
+ constructor(jwtService, s3FileService, settingService, configService) {
25
+ this.jwtService = jwtService;
26
+ this.s3FileService = s3FileService;
27
+ this.settingService = settingService;
28
+ this.configService = configService;
29
+ this.logger = new common_1.Logger(MediaDownloadUrlService_1.name);
30
+ }
31
+ async resolveDownloadUrl(mediaId, relativeUri, storageProvider) {
32
+ const expiryMinutes = storageProvider?.signedUrlExpiry ?? DEFAULT_SIGNED_URL_EXPIRY_MINUTES;
33
+ if (storageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3) {
34
+ if (storageProvider?.bucketName) {
35
+ return this.s3FileService.getUrl(`${storageProvider.bucketName}:${relativeUri}`, { region: (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region), expiresIn: expiryMinutes * 60 });
36
+ }
37
+ this.logger.warn(`Media id ${mediaId}: AwsS3 provider missing bucketName; falling back to app-hosted token URL`);
38
+ }
39
+ const token = await this.mintDownloadToken(mediaId, expiryMinutes);
40
+ const downloadPath = `/api/media/${mediaId}/download?token=${token}`;
41
+ const baseUrl = this.settingService.getConfigValue('baseUrl');
42
+ return `${String(baseUrl).replace(/\/+$/, '')}${downloadPath}`;
43
+ }
44
+ async verifyDownloadToken(token) {
45
+ return this.jwtService.verifyAsync(token, {
46
+ secret: this.settingService.getConfigValue('secret'),
47
+ audience: exports.MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
48
+ });
49
+ }
50
+ mintDownloadToken(mediaId, expiryMinutes) {
51
+ const payload = { mediaId };
52
+ return this.jwtService.signAsync(payload, {
53
+ secret: this.settingService.getConfigValue('secret'),
54
+ audience: exports.MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
55
+ expiresIn: expiryMinutes * 60,
56
+ });
57
+ }
58
+ };
59
+ exports.MediaDownloadUrlService = MediaDownloadUrlService;
60
+ exports.MediaDownloadUrlService = MediaDownloadUrlService = MediaDownloadUrlService_1 = __decorate([
61
+ (0, common_1.Injectable)(),
62
+ __metadata("design:paramtypes", [jwt_1.JwtService,
63
+ file_1.S3FileService,
64
+ setting_service_1.SettingService,
65
+ config_1.ConfigService])
66
+ ], MediaDownloadUrlService);
67
+ //# sourceMappingURL=media-download-url.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download-url.service.js","sourceRoot":"","sources":["../../src/services/media-download-url.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAC/C,qCAAyC;AACzC,mHAA+F;AAE/F,iCAAkD;AAClD,+DAAwE;AACxE,uDAA8D;AAGjD,QAAA,6BAA6B,GAAG,gBAAgB,CAAC;AAC9D,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAOtC,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAGlC,YACmB,UAAsB,EACtB,aAA4B,EAC5B,cAA8B,EAC9B,aAA4B;QAH5B,eAAU,GAAV,UAAU,CAAY;QACtB,kBAAa,GAAb,aAAa,CAAe;QAC5B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,kBAAa,GAAb,aAAa,CAAe;QAN9B,WAAM,GAAG,IAAI,eAAM,CAAC,yBAAuB,CAAC,IAAI,CAAC,CAAC;IAO/D,CAAC;IAEL,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,WAAmB,EAAE,eAA8C;QAC3G,MAAM,aAAa,GAAG,eAAe,EAAE,eAAe,IAAI,iCAAiC,CAAC;QAE5F,IAAK,eAAe,EAAE,IAAiC,KAAK,qEAAwB,CAAC,KAAK,EAAE,CAAC;YAC3F,IAAI,eAAe,EAAE,UAAU,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAC9B,GAAG,eAAe,CAAC,UAAU,IAAI,WAAW,EAAE,EAC9C,EAAE,MAAM,EAAE,IAAA,0CAAoB,EAAC,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,aAAa,GAAG,EAAE,EAAE,CAC5G,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,OAAO,2EAA2E,CAAC,CAAC;QACnH,CAAC;QAMD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,cAAc,OAAO,mBAAmB,KAAK,EAAE,CAAC;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAmB,SAAS,CAAC,CAAC;QAChF,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAa;QACrC,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAA4B,KAAK,EAAE;YACnE,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,cAAc,CAAmB,QAAQ,CAAC;YACtE,QAAQ,EAAE,qCAA6B;SACxC,CAAC,CAAC;IACL,CAAC;IAEO,iBAAiB,CAAC,OAAe,EAAE,aAAqB;QAC9D,MAAM,OAAO,GAA8B,EAAE,OAAO,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE;YACxC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,cAAc,CAAmB,QAAQ,CAAC;YACtE,QAAQ,EAAE,qCAA6B;YACvC,SAAS,EAAE,aAAa,GAAG,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAhDY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAKoB,gBAAU;QACP,oBAAa;QACZ,gCAAc;QACf,sBAAa;GAPpC,uBAAuB,CAgDnC","sourcesContent":["import { Injectable, Logger } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport { JwtService } from '@nestjs/jwt';\nimport { MediaStorageProviderType } from 'src/dtos/create-media-storage-provider-metadata.dto';\nimport { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';\nimport { S3FileService } from 'src/services/file';\nimport { getEffectiveS3Region } from 'src/services/media-storage.utils';\nimport { SettingService } from 'src/services/setting.service';\nimport type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';\n\nexport const MEDIA_DOWNLOAD_TOKEN_AUDIENCE = 'media-download';\nconst DEFAULT_SIGNED_URL_EXPIRY_MINUTES = 60;\n\nexport interface MediaDownloadTokenPayload {\n mediaId: number;\n}\n\n@Injectable()\nexport class MediaDownloadUrlService {\n private readonly logger = new Logger(MediaDownloadUrlService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n private readonly s3FileService: S3FileService,\n private readonly settingService: SettingService,\n private readonly configService: ConfigService,\n ) { }\n\n async resolveDownloadUrl(mediaId: number, relativeUri: string, storageProvider?: MediaStorageProviderMetadata): Promise<string> {\n const expiryMinutes = storageProvider?.signedUrlExpiry ?? DEFAULT_SIGNED_URL_EXPIRY_MINUTES;\n\n if ((storageProvider?.type as MediaStorageProviderType) === MediaStorageProviderType.AwsS3) {\n if (storageProvider?.bucketName) {\n return this.s3FileService.getUrl(\n `${storageProvider.bucketName}:${relativeUri}`,\n { region: getEffectiveS3Region(this.configService, storageProvider.region), expiresIn: expiryMinutes * 60 },\n );\n }\n this.logger.warn(`Media id ${mediaId}: AwsS3 provider missing bucketName; falling back to app-hosted token URL`);\n }\n\n // Filesystem (or misconfigured S3) has no native presigned-URL mechanism, so mint our\n // own short-lived token and embed it in the download URL - the token alone authorizes\n // the request (see MediaSignedUrlGuard), so it works in a plain <img src> with no\n // Authorization header needed.\n const token = await this.mintDownloadToken(mediaId, expiryMinutes);\n const downloadPath = `/api/media/${mediaId}/download?token=${token}`;\n const baseUrl = this.settingService.getConfigValue<SolidCoreSetting>('baseUrl');\n return `${String(baseUrl).replace(/\\/+$/, '')}${downloadPath}`;\n }\n\n async verifyDownloadToken(token: string): Promise<MediaDownloadTokenPayload> {\n return this.jwtService.verifyAsync<MediaDownloadTokenPayload>(token, {\n secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),\n audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,\n });\n }\n\n private mintDownloadToken(mediaId: number, expiryMinutes: number): Promise<string> {\n const payload: MediaDownloadTokenPayload = { mediaId };\n return this.jwtService.signAsync(payload, {\n secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),\n audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,\n expiresIn: expiryMinutes * 60,\n });\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { ConfigService } from '@nestjs/config';
2
+ import { FieldMetadata } from '../entities/field-metadata.entity';
3
+ import { MediaStorageProviderMetadata } from '../entities/media-storage-provider-metadata.entity';
4
+ import { SettingService } from '../services/setting.service';
5
+ export declare const DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = "media-private-files-storage";
6
+ export interface MediaRecordCreateInput {
7
+ entityId: number;
8
+ modelMetadataId: number;
9
+ mediaStorageProviderMetadataId: number;
10
+ fieldMetadataId: number;
11
+ relativeUri: string;
12
+ mimeType?: string;
13
+ fileSize?: number;
14
+ originalFileName?: string;
15
+ isPublic?: boolean;
16
+ }
17
+ export declare function buildStoredMediaFileName(file: Pick<Express.Multer.File, 'filename' | 'originalname'>): string;
18
+ export declare function buildDiskMediaPath(fileName: string, settingService: Pick<SettingService, 'getConfigValue'>, storageProvider?: Pick<MediaStorageProviderMetadata, 'localPath' | 'isPublic'>): string;
19
+ export declare function getEffectiveS3Region(configService: Pick<ConfigService, 'get'>, providerRegion?: string): string | undefined;
20
+ export declare function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'>): boolean | undefined;
21
+ export declare function resolveStoredMediaIsPublic(currentValue: boolean | undefined, storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'>): boolean | undefined;
22
+ export declare function buildMediaRecordCreateInput(entityId: number, mediaFieldMetadata: Pick<FieldMetadata, 'id' | 'model'> & {
23
+ model: {
24
+ id: number;
25
+ };
26
+ }, storageProvider: Pick<MediaStorageProviderMetadata, 'id'>, overrides: Pick<MediaRecordCreateInput, 'relativeUri'> & Partial<Omit<MediaRecordCreateInput, 'entityId' | 'modelMetadataId' | 'mediaStorageProviderMetadataId' | 'fieldMetadataId' | 'relativeUri'>>): MediaRecordCreateInput;
27
+ //# sourceMappingURL=media-storage.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-storage.utils.d.ts","sourceRoot":"","sources":["../../src/services/media-storage.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,4BAA4B,EAAE,MAAM,qDAAqD,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAE/E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,8BAA8B,EAAE,MAAM,CAAC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC,GAAG,MAAM,CAE7G;AAGD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAC,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,EAAC,eAAe,CAAC,EAAE,IAAI,CAAC,4BAA4B,EAAE,WAAW,GAAG,UAAU,CAAC,GAAI,MAAM,CAUlM;AAGD,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,EAAC,cAAc,CAAC,EAAE,MAAM,GAAI,MAAM,GAAG,SAAS,CAE3H;AAGD,wBAAgB,oBAAoB,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,4BAA4B,EAAE,UAAU,CAAC,GAAI,OAAO,GAAG,SAAS,CAM3H;AAGD,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,OAAO,GAAG,SAAS,EAAC,eAAe,CAAC,EAAC,IAAI,CAAC,4BAA4B,EAAE,UAAU,CAAC,GAAI,OAAO,GAAG,SAAS,CAMlK;AAGD,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnF,eAAe,EAAE,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,EACzD,SAAS,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,CAAC,GAClD,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,UAAU,GAAG,iBAAiB,GAAG,gCAAgC,GAAG,iBAAiB,GAAG,aAAa,CAAC,CAAC,GAC/I,sBAAsB,CAQxB"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = void 0;
37
+ exports.buildStoredMediaFileName = buildStoredMediaFileName;
38
+ exports.buildDiskMediaPath = buildDiskMediaPath;
39
+ exports.getEffectiveS3Region = getEffectiveS3Region;
40
+ exports.resolveMediaIsPublic = resolveMediaIsPublic;
41
+ exports.resolveStoredMediaIsPublic = resolveStoredMediaIsPublic;
42
+ exports.buildMediaRecordCreateInput = buildMediaRecordCreateInput;
43
+ const path = __importStar(require("path"));
44
+ const default_settings_provider_service_1 = require("./settings/default-settings-provider.service");
45
+ exports.DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = 'media-private-files-storage';
46
+ function buildStoredMediaFileName(file) {
47
+ return `${file.filename}-${file.originalname}`;
48
+ }
49
+ function buildDiskMediaPath(fileName, settingService, storageProvider) {
50
+ const publicBase = settingService.getConfigValue('fileStorageDir') || default_settings_provider_service_1.DEFAULT_MEDIA_FILE_STORAGE_DIR;
51
+ const privateBase = exports.DEFAULT_PRIVATE_MEDIA_STORAGE_DIR;
52
+ const providerBase = storageProvider?.localPath || (storageProvider?.isPublic === false ? privateBase : publicBase);
53
+ if (path.isAbsolute(fileName) || fileName.startsWith(`${publicBase}/`) || fileName.startsWith(`${privateBase}/`) || (!!storageProvider?.localPath && fileName.startsWith(`${storageProvider.localPath}/`))) {
54
+ return fileName;
55
+ }
56
+ return `${providerBase}/${fileName}`;
57
+ }
58
+ function getEffectiveS3Region(configService, providerRegion) {
59
+ return providerRegion || configService.get('S3_AWS_REGION_NAME');
60
+ }
61
+ function resolveMediaIsPublic(storageProvider) {
62
+ if (!storageProvider) {
63
+ return undefined;
64
+ }
65
+ return storageProvider.isPublic !== false;
66
+ }
67
+ function resolveStoredMediaIsPublic(currentValue, storageProvider) {
68
+ if (typeof currentValue === 'boolean') {
69
+ return currentValue;
70
+ }
71
+ return resolveMediaIsPublic(storageProvider);
72
+ }
73
+ function buildMediaRecordCreateInput(entityId, mediaFieldMetadata, storageProvider, overrides) {
74
+ return {
75
+ entityId,
76
+ modelMetadataId: mediaFieldMetadata.model.id,
77
+ mediaStorageProviderMetadataId: storageProvider.id,
78
+ fieldMetadataId: mediaFieldMetadata.id,
79
+ ...overrides,
80
+ };
81
+ }
82
+ //# sourceMappingURL=media-storage.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-storage.utils.js","sourceRoot":"","sources":["../../src/services/media-storage.utils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,4DAEC;AAGD,gDAUC;AAGD,oDAEC;AAGD,oDAMC;AAGD,gEAMC;AAGD,kEAcC;AA7ED,2CAA6B;AAI7B,oGAAyG;AAG5F,QAAA,iCAAiC,GAAG,6BAA6B,CAAC;AAe/E,SAAgB,wBAAwB,CAAC,IAA4D;IACnG,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AACjD,CAAC;AAGD,SAAgB,kBAAkB,CAAC,QAAgB,EAAC,cAAsD,EAAC,eAA8E;IACvL,MAAM,UAAU,GAAG,cAAc,CAAC,cAAc,CAAmB,gBAAgB,CAAC,IAAI,kEAA8B,CAAC;IACvH,MAAM,WAAW,GAAG,yCAAiC,CAAC;IACtD,MAAM,YAAY,GAAG,eAAe,EAAE,SAAS,IAAI,CAAC,eAAe,EAAE,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAEpH,IAAK,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,EAAE,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,eAAe,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5M,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,GAAG,YAAY,IAAI,QAAQ,EAAE,CAAC;AACvC,CAAC;AAGD,SAAgB,oBAAoB,CAAC,aAAyC,EAAC,cAAuB;IACpG,OAAO,cAAc,IAAI,aAAa,CAAC,GAAG,CAAS,oBAAoB,CAAC,CAAC;AAC3E,CAAC;AAGD,SAAgB,oBAAoB,CAAC,eAAgE;IACnG,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,eAAe,CAAC,QAAQ,KAAK,KAAK,CAAC;AAC5C,CAAC;AAGD,SAAgB,0BAA0B,CAAC,YAAiC,EAAC,eAA+D;IAC1I,IAAI,OAAO,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,oBAAoB,CAAC,eAAe,CAAC,CAAC;AAC/C,CAAC;AAGD,SAAgB,2BAA2B,CACzC,QAAgB,EAChB,kBAAmF,EACnF,eAAyD,EACzD,SACgJ;IAEhJ,OAAO;QACL,QAAQ;QACR,eAAe,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE;QAC5C,8BAA8B,EAAE,eAAe,CAAC,EAAE;QAClD,eAAe,EAAE,kBAAkB,CAAC,EAAE;QACtC,GAAG,SAAS;KACb,CAAC;AACJ,CAAC","sourcesContent":["import { ConfigService } from '@nestjs/config';\nimport * as path from 'path';\nimport { FieldMetadata } from 'src/entities/field-metadata.entity';\nimport { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';\nimport { SettingService } from 'src/services/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\nexport const DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = 'media-private-files-storage';\n\nexport interface MediaRecordCreateInput {\n entityId: number;\n modelMetadataId: number;\n mediaStorageProviderMetadataId: number;\n fieldMetadataId: number;\n relativeUri: string;\n mimeType?: string;\n fileSize?: number;\n originalFileName?: string;\n isPublic?: boolean;\n}\n\n// Builds the stored file name format shared by media storage implementations.\nexport function buildStoredMediaFileName(file: Pick<Express.Multer.File, 'filename' | 'originalname'>): string {\n return `${file.filename}-${file.originalname}`;\n}\n\n// Resolves the on-disk path for a media file based on settings and provider visibility.\nexport function buildDiskMediaPath(fileName: string,settingService: Pick<SettingService, 'getConfigValue'>,storageProvider?: Pick<MediaStorageProviderMetadata, 'localPath' | 'isPublic'>,): string {\n const publicBase = settingService.getConfigValue<SolidCoreSetting>('fileStorageDir') || DEFAULT_MEDIA_FILE_STORAGE_DIR;\n const privateBase = DEFAULT_PRIVATE_MEDIA_STORAGE_DIR;\n const providerBase = storageProvider?.localPath || (storageProvider?.isPublic === false ? privateBase : publicBase);\n\n if ( path.isAbsolute(fileName) || fileName.startsWith(`${publicBase}/`) || fileName.startsWith(`${privateBase}/`) || (!!storageProvider?.localPath && fileName.startsWith(`${storageProvider.localPath}/`))) {\n return fileName;\n }\n\n return `${providerBase}/${fileName}`;\n}\n\n// Returns the S3 region from the provider when present, otherwise falls back to app config.\nexport function getEffectiveS3Region(configService: Pick<ConfigService, 'get'>,providerRegion?: string,): string | undefined {\n return providerRegion || configService.get<string>('S3_AWS_REGION_NAME');\n}\n\n// Resolves the effective public/private flag for a media storage provider.\nexport function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'>,): boolean | undefined {\n if (!storageProvider) {\n return undefined;\n }\n\n return storageProvider.isPublic !== false;\n}\n\n// Preserves an explicit media visibility value or derives it from the storage provider.\nexport function resolveStoredMediaIsPublic(currentValue: boolean | undefined,storageProvider?:Pick<MediaStorageProviderMetadata, 'isPublic'>,): boolean | undefined {\n if (typeof currentValue === 'boolean') {\n return currentValue;\n }\n\n return resolveMediaIsPublic(storageProvider);\n}\n\n// Builds the shared media record payload used before saving media metadata to the database.\nexport function buildMediaRecordCreateInput(\n entityId: number,\n mediaFieldMetadata: Pick<FieldMetadata, 'id' | 'model'> & { model: { id: number } },\n storageProvider: Pick<MediaStorageProviderMetadata, 'id'>,\n overrides: Pick<MediaRecordCreateInput, 'relativeUri'>\n & Partial<Omit<MediaRecordCreateInput, 'entityId' | 'modelMetadataId' | 'mediaStorageProviderMetadataId' | 'fieldMetadataId' | 'relativeUri'>>,\n): MediaRecordCreateInput {\n return {\n entityId,\n modelMetadataId: mediaFieldMetadata.model.id,\n mediaStorageProviderMetadataId: storageProvider.id,\n fieldMetadataId: mediaFieldMetadata.id,\n ...overrides,\n };\n}\n"]}
@@ -1,8 +1,10 @@
1
1
  import { ModuleRef } from "@nestjs/core";
2
2
  import { EntityManager } from 'typeorm';
3
+ import { Readable } from 'stream';
3
4
  import { ConfigService } from '@nestjs/config';
4
5
  import { CRUDService } from '../services/crud.service';
5
6
  import { DiskFileService, S3FileService } from '../services/file';
7
+ import { MediaDownloadUrlService } from '../services/media-download-url.service';
6
8
  import { BasicFilterDto } from '../dtos/basic-filters.dto';
7
9
  import { Media } from '../entities/media.entity';
8
10
  import { FieldMetadataRepository } from '../repository/field-metadata.repository';
@@ -13,17 +15,31 @@ export declare class MediaService extends CRUDService<Media> {
13
15
  readonly configService: ConfigService;
14
16
  readonly diskFileService: DiskFileService;
15
17
  readonly s3FileService: S3FileService;
18
+ private readonly mediaDownloadUrlService;
16
19
  readonly entityManager: EntityManager;
17
20
  readonly repo: MediaRepository;
18
21
  private readonly modelMetadataRepo;
19
22
  private readonly mediaStorageProviderMetadataRepo;
20
23
  private readonly fieldMetadataRepo;
21
24
  readonly moduleRef: ModuleRef;
22
- constructor(configService: ConfigService, diskFileService: DiskFileService, s3FileService: S3FileService, entityManager: EntityManager, repo: MediaRepository, modelMetadataRepo: ModelMetadataRepository, mediaStorageProviderMetadataRepo: MediaStorageProviderMetadataRepository, fieldMetadataRepo: FieldMetadataRepository, moduleRef: ModuleRef);
25
+ private readonly logger;
26
+ constructor(configService: ConfigService, diskFileService: DiskFileService, s3FileService: S3FileService, mediaDownloadUrlService: MediaDownloadUrlService, entityManager: EntityManager, repo: MediaRepository, modelMetadataRepo: ModelMetadataRepository, mediaStorageProviderMetadataRepo: MediaStorageProviderMetadataRepository, fieldMetadataRepo: FieldMetadataRepository, moduleRef: ModuleRef);
23
27
  find(basicFilterDto: BasicFilterDto, solidRequestContext?: any): Promise<any>;
24
- upload(createDto: any, files: Array<Express.Multer.File>): Promise<any[]>;
28
+ findOne(id: number, query?: any, solidRequestContext?: any): Promise<Media>;
29
+ upload(createDto: any, files: Array<Express.Multer.File>, _solidRequestContext?: any): Promise<any[]>;
25
30
  remove(id: number): Promise<Media>;
26
- private getFullFilePathForDisk;
27
- private getFileName;
31
+ fileDownloadStream(media: Media): Promise<{
32
+ stream: Readable | null;
33
+ fileName: string;
34
+ mimeType: string;
35
+ redirectUrl?: string;
36
+ }>;
37
+ delete(id: number, solidRequestContext?: any): Promise<Media>;
38
+ deleteMany(ids: number[], solidRequestContext?: any): Promise<any>;
39
+ private decorateMediaRecords;
40
+ private decorateMediaRecord;
41
+ private resolveMediaUrl;
42
+ private deletePhysicalFile;
43
+ private resolveMediaStorageProvider;
28
44
  }
29
45
  //# sourceMappingURL=media.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../src/services/media.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAM,MAAM,SAAS,CAAC;AAK5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAInE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,sCAAsC,EAAE,MAAM,2DAA2D,CAAC;AACnH,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAInF,qBACa,YAAa,SAAQ,WAAW,CAAC,KAAK,CAAC;IAEhD,QAAQ,CAAC,aAAa,EAAE,aAAa;IACrC,QAAQ,CAAC,eAAe,EAAE,eAAe;IACzC,QAAQ,CAAC,aAAa,EAAE,aAAa;IAErC,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,eAAe;IAQ9B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,gCAAgC;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS;gBAlBpB,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAE5B,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,eAAe,EAQb,iBAAiB,EAAE,uBAAuB,EAC1C,gCAAgC,EAAE,sCAAsC,EACxE,iBAAiB,EAAE,uBAAuB,EAClD,SAAS,EAAE,SAAS;IAKzB,IAAI,CAAC,cAAc,EAAE,cAAc,EAAE,mBAAmB,GAAE,GAAQ;IAgClE,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;IAwDxD,MAAM,CAAC,EAAE,EAAE,MAAM;IAqBvB,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,WAAW;CAIpB"}
1
+ {"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../src/services/media.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAM,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAIlF,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAElD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,sCAAsC,EAAE,MAAM,2DAA2D,CAAC;AACnH,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAInF,qBACa,YAAa,SAAQ,WAAW,CAAC,KAAK,CAAC;IAIhD,QAAQ,CAAC,aAAa,EAAE,aAAa;IACrC,QAAQ,CAAC,eAAe,EAAE,eAAe;IACzC,QAAQ,CAAC,aAAa,EAAE,aAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAExC,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,eAAe;IAQ9B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,gCAAgC;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS;IAtB/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAG7C,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EACpB,uBAAuB,EAAE,uBAAuB,EAExD,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,eAAe,EAQb,iBAAiB,EAAE,uBAAuB,EAC1C,gCAAgC,EAAE,sCAAsC,EACxE,iBAAiB,EAAE,uBAAuB,EAClD,SAAS,EAAE,SAAS;IAKzB,IAAI,CAAC,cAAc,EAAE,cAAc,EAAE,mBAAmB,GAAE,GAAQ;IAalE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,GAAQ,EAAE,mBAAmB,GAAE,GAAQ;IAQlE,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,oBAAoB,GAAE,GAAQ;IAmExF,MAAM,CAAC,EAAE,EAAE,MAAM;IAqBjB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA2BhI,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,mBAAmB,GAAE,GAAQ;IAehD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,mBAAmB,GAAE,GAAQ;YAiB/C,oBAAoB;YAMpB,mBAAmB;YAMnB,eAAe;YAsBf,kBAAkB;YA8BlB,2BAA2B;CAgB1C"}
@@ -44,6 +44,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
44
44
  var __param = (this && this.__param) || function (paramIndex, decorator) {
45
45
  return function (target, key) { decorator(target, key, paramIndex); }
46
46
  };
47
+ var MediaService_1;
47
48
  Object.defineProperty(exports, "__esModule", { value: true });
48
49
  exports.MediaService = void 0;
49
50
  const common_1 = require("@nestjs/common");
@@ -51,99 +52,103 @@ const core_1 = require("@nestjs/core");
51
52
  const typeorm_1 = require("@nestjs/typeorm");
52
53
  const typeorm_2 = require("typeorm");
53
54
  const path = __importStar(require("path"));
54
- const default_settings_provider_service_1 = require("./settings/default-settings-provider.service");
55
55
  const config_1 = require("@nestjs/config");
56
56
  const crud_service_1 = require("./crud.service");
57
57
  const file_1 = require("./file");
58
+ const media_download_url_service_1 = require("./media-download-url.service");
58
59
  const error_messages_1 = require("../constants/error-messages");
59
60
  const create_media_storage_provider_metadata_dto_1 = require("../dtos/create-media-storage-provider-metadata.dto");
60
61
  const field_metadata_repository_1 = require("../repository/field-metadata.repository");
61
62
  const media_storage_provider_metadata_repository_1 = require("../repository/media-storage-provider-metadata.repository");
62
63
  const media_repository_1 = require("../repository/media.repository");
63
64
  const model_metadata_repository_1 = require("../repository/model-metadata.repository");
65
+ const media_storage_utils_1 = require("./media-storage.utils");
64
66
  const mediaStorageProviders_1 = require("./mediaStorageProviders");
65
- let MediaService = class MediaService extends crud_service_1.CRUDService {
66
- constructor(configService, diskFileService, s3FileService, entityManager, repo, modelMetadataRepo, mediaStorageProviderMetadataRepo, fieldMetadataRepo, moduleRef) {
67
+ let MediaService = MediaService_1 = class MediaService extends crud_service_1.CRUDService {
68
+ constructor(configService, diskFileService, s3FileService, mediaDownloadUrlService, entityManager, repo, modelMetadataRepo, mediaStorageProviderMetadataRepo, fieldMetadataRepo, moduleRef) {
67
69
  super(entityManager, repo, 'media', 'solid-core', moduleRef);
68
70
  this.configService = configService;
69
71
  this.diskFileService = diskFileService;
70
72
  this.s3FileService = s3FileService;
73
+ this.mediaDownloadUrlService = mediaDownloadUrlService;
71
74
  this.entityManager = entityManager;
72
75
  this.repo = repo;
73
76
  this.modelMetadataRepo = modelMetadataRepo;
74
77
  this.mediaStorageProviderMetadataRepo = mediaStorageProviderMetadataRepo;
75
78
  this.fieldMetadataRepo = fieldMetadataRepo;
76
79
  this.moduleRef = moduleRef;
80
+ this.logger = new common_1.Logger(MediaService_1.name);
77
81
  }
78
82
  async find(basicFilterDto, solidRequestContext = {}) {
79
83
  const data = await super.find(basicFilterDto, solidRequestContext);
80
84
  if (data.records) {
81
- for (const media of data.records) {
82
- const mediaStorageProvider = media.mediaStorageProviderMetadata;
83
- if (mediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem) {
84
- media.relativeUri = await this.diskFileService.getUrl(this.getFullFilePathForDisk(media.relativeUri));
85
- }
86
- else if (mediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3) {
87
- media.relativeUri = await this.s3FileService.getUrl(`${mediaStorageProvider.bucketName}:${media.relativeUri}`, { region: mediaStorageProvider.region });
88
- }
89
- }
85
+ await this.decorateMediaRecords(data.records);
90
86
  }
91
87
  if (data.groupRecords) {
92
88
  for (const group of data.groupRecords) {
93
- for (const media of group.groupData.records) {
94
- const mediaStorageProvider = media.mediaStorageProviderMetadata;
95
- if (mediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem) {
96
- media.relativeUri = await this.diskFileService.getUrl(this.getFullFilePathForDisk(media.relativeUri));
97
- }
98
- else if (mediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3) {
99
- media.relativeUri = await this.s3FileService.getUrl(`${mediaStorageProvider.bucketName}:${media.relativeUri}`, { region: mediaStorageProvider.region });
100
- }
101
- }
89
+ await this.decorateMediaRecords(group.groupData.records);
102
90
  }
103
91
  }
104
92
  return data;
105
93
  }
106
- async upload(createDto, files) {
107
- if (!files) {
94
+ async findOne(id, query = {}, solidRequestContext = {}) {
95
+ const media = await super.findOne(id, query, solidRequestContext);
96
+ if (media) {
97
+ await this.decorateMediaRecord(media);
98
+ }
99
+ return media;
100
+ }
101
+ async upload(createDto, files, _solidRequestContext = {}) {
102
+ if (!files || files.length === 0) {
108
103
  throw new common_1.NotFoundException(error_messages_1.ERROR_MESSAGES.FILE_NOT_FOUND);
109
104
  }
105
+ createDto['fieldMetadata'] = await this.fieldMetadataRepo.findOne({
106
+ where: {
107
+ id: createDto['fieldMetadataId']
108
+ },
109
+ relations: ['mediaStorageProvider', 'model'],
110
+ });
111
+ createDto['modelMetadata'] = await this.modelMetadataRepo.findOne({
112
+ where: {
113
+ id: createDto['modelMetadataId']
114
+ },
115
+ });
116
+ createDto['mediaStorageProviderMetadata'] = await this.mediaStorageProviderMetadataRepo.findOne({
117
+ where: {
118
+ id: createDto['mediaStorageProviderMetadataId']
119
+ },
120
+ });
121
+ createDto['mediaStorageProviderMetadata'] = createDto['mediaStorageProviderMetadata'] || createDto['fieldMetadata']?.mediaStorageProvider;
122
+ createDto['modelMetadata'] = createDto['modelMetadata'] || createDto['fieldMetadata']?.model;
123
+ if (!createDto['mediaStorageProviderMetadata']) {
124
+ throw new common_1.NotFoundException('Media storage provider metadata not found');
125
+ }
110
126
  const savedMedias = [];
111
127
  for (let i = 0; i < files.length; i++) {
112
- createDto['fieldMetadata'] = await this.fieldMetadataRepo.findOne({
113
- where: {
114
- id: createDto['fieldMetadataId']
115
- },
116
- });
117
- createDto['modelMetadata'] = await this.modelMetadataRepo.findOne({
118
- where: {
119
- id: createDto['modelMetadataId']
120
- },
121
- });
122
- createDto['mediaStorageProviderMetadata'] = await this.mediaStorageProviderMetadataRepo.findOne({
123
- where: {
124
- id: createDto['mediaStorageProviderMetadataId']
125
- },
126
- });
127
128
  const file = files[i];
128
- switch (createDto.mediaStorageProviderMetadata.type) {
129
+ const storageProvider = createDto.mediaStorageProviderMetadata;
130
+ const fileName = (0, media_storage_utils_1.buildStoredMediaFileName)(file);
131
+ switch (storageProvider.type) {
129
132
  case create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem:
130
- const fileStoragePath = this.getFullFilePathForDisk(this.getFileName(file));
133
+ const fileStoragePath = (0, media_storage_utils_1.buildDiskMediaPath)(fileName, this.settingService, storageProvider);
131
134
  await this.diskFileService.copy(file.path, fileStoragePath);
132
- createDto['relativeUri'] = this.getFileName(file);
135
+ createDto['relativeUri'] = fileName;
133
136
  break;
134
137
  case create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3:
135
- const fileName = this.getFileName(file);
136
- const bucketName = createDto.mediaStorageProviderMetadata.bucketName;
138
+ const bucketName = storageProvider.bucketName;
139
+ const region = (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, storageProvider.region);
137
140
  const fileData = await this.diskFileService.read(file.path);
138
- await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { contentType: file.mimetype });
141
+ await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { contentType: file.mimetype, region });
139
142
  createDto['relativeUri'] = fileName;
140
143
  break;
141
144
  default:
142
145
  break;
143
146
  }
144
147
  await this.diskFileService.delete(file.path);
148
+ createDto['isPublic'] = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(createDto['isPublic'], storageProvider);
145
149
  const media = this.repo.create(createDto);
146
150
  const savedMedia = await this.repo.save(media);
151
+ savedMedia.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(savedMedia.isPublic, storageProvider);
147
152
  savedMedias.push(savedMedia);
148
153
  }
149
154
  return savedMedias;
@@ -165,26 +170,123 @@ let MediaService = class MediaService extends crud_service_1.CRUDService {
165
170
  await storageProvider.delete(modelEntity, media.fieldMetadata);
166
171
  return this.repo.remove(media);
167
172
  }
168
- getFullFilePathForDisk(fileName) {
169
- const base = this.settingService.getConfigValue("fileStorageDir")
170
- || default_settings_provider_service_1.DEFAULT_MEDIA_FILE_STORAGE_DIR;
171
- if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {
172
- return fileName;
173
+ async fileDownloadStream(media) {
174
+ const loadedMedia = await this.repo.findOne({
175
+ where: { id: media.id },
176
+ relations: ['mediaStorageProviderMetadata'],
177
+ });
178
+ if (!loadedMedia || !loadedMedia.mediaStorageProviderMetadata) {
179
+ throw new common_1.NotFoundException(`Media with id ${media.id} not found`);
180
+ }
181
+ const fileName = loadedMedia.originalFileName || path.basename(loadedMedia.relativeUri || `${loadedMedia.id}`);
182
+ const mimeType = loadedMedia.mimeType || 'application/octet-stream';
183
+ switch (loadedMedia.mediaStorageProviderMetadata.type) {
184
+ case create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem:
185
+ return {
186
+ stream: await this.diskFileService.readStream((0, media_storage_utils_1.buildDiskMediaPath)(loadedMedia.relativeUri, this.settingService, loadedMedia.mediaStorageProviderMetadata)),
187
+ fileName,
188
+ mimeType,
189
+ };
190
+ default:
191
+ throw new Error(`Unsupported media storage provider type ${loadedMedia.mediaStorageProviderMetadata.type}`);
192
+ }
193
+ }
194
+ async delete(id, solidRequestContext = {}) {
195
+ const media = await this.repo.findOne({
196
+ where: { id },
197
+ relations: ['mediaStorageProviderMetadata'],
198
+ });
199
+ const result = await super.delete(id, solidRequestContext);
200
+ if (media) {
201
+ await this.deletePhysicalFile(media);
202
+ }
203
+ return result;
204
+ }
205
+ async deleteMany(ids, solidRequestContext = {}) {
206
+ const mediaRecords = await this.repo.find({
207
+ where: {
208
+ id: (0, typeorm_2.In)(ids),
209
+ },
210
+ relations: ['mediaStorageProviderMetadata'],
211
+ });
212
+ const result = await super.deleteMany(ids, solidRequestContext);
213
+ for (const media of mediaRecords) {
214
+ await this.deletePhysicalFile(media);
215
+ }
216
+ return result;
217
+ }
218
+ async decorateMediaRecords(medias) {
219
+ for (const media of medias) {
220
+ await this.decorateMediaRecord(media);
173
221
  }
174
- return `${base}/${fileName}`;
175
222
  }
176
- getFileName(file) {
177
- return `${file.filename}-${file.originalname}`;
223
+ async decorateMediaRecord(media) {
224
+ const mediaStorageProvider = await this.resolveMediaStorageProvider(media);
225
+ media.isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(media.isPublic, mediaStorageProvider);
226
+ media.relativeUri = await this.resolveMediaUrl(media, mediaStorageProvider);
227
+ }
228
+ async resolveMediaUrl(media, mediaStorageProvider) {
229
+ const resolvedMediaStorageProvider = mediaStorageProvider || await this.resolveMediaStorageProvider(media);
230
+ const isPublic = (0, media_storage_utils_1.resolveStoredMediaIsPublic)(media.isPublic, resolvedMediaStorageProvider);
231
+ if (isPublic === false) {
232
+ return this.mediaDownloadUrlService.resolveDownloadUrl(media.id, media.relativeUri, resolvedMediaStorageProvider);
233
+ }
234
+ if (resolvedMediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem) {
235
+ return this.diskFileService.getUrl((0, media_storage_utils_1.buildDiskMediaPath)(media.relativeUri, this.settingService, resolvedMediaStorageProvider));
236
+ }
237
+ if (resolvedMediaStorageProvider?.type === create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3) {
238
+ return this.s3FileService.getUrl(`${resolvedMediaStorageProvider.bucketName}:${media.relativeUri}`, { region: (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, resolvedMediaStorageProvider.region), expiresIn: 0 });
239
+ }
240
+ return media.relativeUri;
241
+ }
242
+ async deletePhysicalFile(media) {
243
+ if (!media?.relativeUri) {
244
+ return;
245
+ }
246
+ try {
247
+ const mediaStorageProvider = await this.resolveMediaStorageProvider(media);
248
+ if (!mediaStorageProvider) {
249
+ return;
250
+ }
251
+ switch (mediaStorageProvider.type) {
252
+ case create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.Filesystem:
253
+ await this.diskFileService.delete((0, media_storage_utils_1.buildDiskMediaPath)(media.relativeUri, this.settingService, mediaStorageProvider));
254
+ return;
255
+ case create_media_storage_provider_metadata_dto_1.MediaStorageProviderType.AwsS3:
256
+ await this.s3FileService.delete(`${mediaStorageProvider.bucketName}:${media.relativeUri}`, { region: (0, media_storage_utils_1.getEffectiveS3Region)(this.configService, mediaStorageProvider.region) });
257
+ return;
258
+ default:
259
+ this.logger.warn(`Skipping physical delete for unsupported media storage provider type ${mediaStorageProvider.type}`);
260
+ }
261
+ }
262
+ catch (error) {
263
+ const message = error?.message ?? String(error);
264
+ this.logger.warn(`Failed to delete physical media file for media id ${media.id}: ${message}`);
265
+ }
266
+ }
267
+ async resolveMediaStorageProvider(media) {
268
+ if (media.mediaStorageProviderMetadata) {
269
+ return media.mediaStorageProviderMetadata;
270
+ }
271
+ if (!media.id) {
272
+ return undefined;
273
+ }
274
+ const loadedMedia = await this.repo.findOne({
275
+ where: { id: media.id },
276
+ relations: ['mediaStorageProviderMetadata'],
277
+ });
278
+ return loadedMedia?.mediaStorageProviderMetadata;
178
279
  }
179
280
  };
180
281
  exports.MediaService = MediaService;
181
- exports.MediaService = MediaService = __decorate([
282
+ exports.MediaService = MediaService = MediaService_1 = __decorate([
182
283
  (0, common_1.Injectable)(),
183
- __param(3, (0, typeorm_1.InjectEntityManager)()),
184
- __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => model_metadata_repository_1.ModelMetadataRepository))),
284
+ __param(4, (0, typeorm_1.InjectEntityManager)()),
285
+ __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => model_metadata_repository_1.ModelMetadataRepository))),
185
286
  __metadata("design:paramtypes", [config_1.ConfigService,
186
287
  file_1.DiskFileService,
187
288
  file_1.S3FileService,
289
+ media_download_url_service_1.MediaDownloadUrlService,
188
290
  typeorm_2.EntityManager,
189
291
  media_repository_1.MediaRepository,
190
292
  model_metadata_repository_1.ModelMetadataRepository,