@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
@@ -1,29 +1,25 @@
1
1
  import { Injectable, Logger } from "@nestjs/common";
2
- import { ConfigService } from "@nestjs/config";
3
- import { CommonEntity } from "src/entities/common.entity";
4
2
  import { FieldMetadata } from "src/entities/field-metadata.entity";
5
3
  import { Media } from "src/entities/media.entity";
4
+ import { MediaStorageProviderMetadata } from "src/entities/media-storage-provider-metadata.entity";
6
5
  import { MediaStorageProvider } from "src/interfaces";
7
6
  import { MediaRepository } from "src/repository/media.repository";
8
7
  import { DiskFileService } from "src/services/file";
9
8
  import { Readable } from "stream";
10
- import * as path from "path";
11
9
  import * as fs from "fs";
12
10
  import { SettingService } from "../setting.service";
13
- import { DEFAULT_MEDIA_FILE_STORAGE_DIR } from "src/services/settings/default-settings-provider.service";
14
- import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
11
+ import { MediaDownloadUrlService } from "src/services/media-download-url.service";
12
+ import { buildDiskMediaPath, buildMediaRecordCreateInput, buildStoredMediaFileName, resolveStoredMediaIsPublic, } from "src/services/media-storage.utils";
15
13
 
16
14
  @Injectable()
17
15
  export class FileStorageProvider<T> implements MediaStorageProvider<T> {
18
16
  private logger = new Logger(FileStorageProvider.name);
19
17
 
20
18
  constructor(
21
- // @Inject(appBuilderConfig.KEY)
22
- // private readonly appBuilderConfiguration: ConfigType<typeof appBuilderConfig>,
23
- private readonly configService: ConfigService,
24
19
  readonly fileService: DiskFileService,
25
20
  readonly mediaRepository: MediaRepository,
26
- private readonly settingService: SettingService
21
+ private readonly settingService: SettingService,
22
+ private readonly mediaDownloadUrlService: MediaDownloadUrlService,
27
23
 
28
24
  ) { }
29
25
 
@@ -32,12 +28,17 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
32
28
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
33
29
  // }
34
30
  //@ts-ignore
35
- const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
31
+ const media = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(this.getEntityId(entity), mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
36
32
  // Add the full URL to the media
37
33
  // media.forEach(m => {
38
34
  // });
39
35
  for (const m of media) {
40
- m['_full_url'] = `${this.settingService.getConfigValue<SolidCoreSetting>("baseUrl")}/${this.getFullFilePath(m.relativeUri)}`;
36
+ const storageProvider = this.resolveMediaStorageProvider(m, mediaFieldMetadata);
37
+ const isPublic = resolveStoredMediaIsPublic(m.isPublic, storageProvider);
38
+ m.isPublic = isPublic;
39
+ m['_full_url'] = isPublic === false
40
+ ? await this.mediaDownloadUrlService.resolveDownloadUrl(m.id, m.relativeUri, storageProvider)
41
+ : await this.fileService.getUrl(buildDiskMediaPath(m.relativeUri, this.settingService, storageProvider));
41
42
  }
42
43
 
43
44
 
@@ -49,24 +50,24 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
49
50
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
50
51
  // }
51
52
  const result: Media[] = [];
53
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
52
54
  for (const file of files) {
53
55
  // Store the file in the configured file storage directory
54
- const fileStoragePath = this.getFullFilePath(this.getFileName(file));
56
+ const fileName = buildStoredMediaFileName(file);
57
+ const fileStoragePath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
55
58
  await this.fileService.copy(file.path, fileStoragePath);
56
59
  await this.fileService.delete(file.path);
57
60
 
58
61
  // Create an entry in the media table
59
- const mediaEntity = await this.mediaRepository.createMedia({
60
- //@ts-ignore
61
- entityId: entity.id,
62
- modelMetadataId: mediaFieldMetadata.model.id,
63
- relativeUri: this.getFileName(file),
64
- mimeType: file.mimetype,
65
- fileSize: file.size,
66
- originalFileName: file.originalname,
67
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
68
- fieldMetadataId: mediaFieldMetadata.id
69
- }) as unknown as Media;
62
+ const mediaEntity = await this.mediaRepository.createMedia(
63
+ buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
64
+ relativeUri: fileName,
65
+ mimeType: file.mimetype,
66
+ fileSize: file.size,
67
+ originalFileName: file.originalname,
68
+ })
69
+ ) as unknown as Media;
70
+ mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
70
71
  result.push(mediaEntity);
71
72
  this.logger.debug(`Stored media with`, mediaEntity);
72
73
  };
@@ -78,21 +79,21 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
78
79
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
79
80
  // }
80
81
  const result: Media[] = [];
82
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
81
83
  for (const pair of streamPairs) {
82
84
  const stream = pair[0];
83
85
  const fileName = pair[1];
84
- const fullPath = this.getFullFilePath(fileName);
86
+ const fullPath = buildDiskMediaPath(fileName, this.settingService, storageProvider);
85
87
  await this.fileService.writeStream(fullPath, stream);
86
88
  const { size: fileSize } = await fs.promises.stat(fullPath);
87
- const mediaEntity = await this.mediaRepository.createMedia({
88
- //@ts-ignore
89
- entityId: entity.id,
90
- modelMetadataId: mediaFieldMetadata.model.id,
91
- relativeUri: fileName,
92
- fileSize,
93
- mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
94
- fieldMetadataId: mediaFieldMetadata.id
95
- }) as unknown as Media;
89
+ const mediaEntity = await this.mediaRepository.createMedia(
90
+ buildMediaRecordCreateInput(this.getEntityId(entity), mediaFieldMetadata, storageProvider, {
91
+ relativeUri: fileName,
92
+ fileSize,
93
+ })
94
+ ) as unknown as Media;
95
+ mediaEntity.isPublic = resolveStoredMediaIsPublic(mediaEntity.isPublic, storageProvider);
96
+ result.push(mediaEntity);
96
97
  this.logger.debug(`Stored media with`, mediaEntity);
97
98
  };
98
99
  return result;
@@ -102,13 +103,13 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
102
103
  // if (!(entity instanceof CommonEntity)) {
103
104
  // throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
104
105
  // }
105
- //@ts-ignore
106
- const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
107
- //@ts-ignore
108
- this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
106
+ const entityId = this.getEntityId(entity);
107
+ const existingMedia = await this.mediaRepository.findByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
108
+ await this.mediaRepository.deleteByEntityIdAndFieldIdAndModelMetadataId(entityId, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
109
109
 
110
110
  for (const media of existingMedia) {
111
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
111
+ const storageProvider = this.resolveMediaStorageProvider(media, mediaFieldMetadata);
112
+ await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, storageProvider));
112
113
  }
113
114
  // existingMedia.forEach(media => {
114
115
  // });
@@ -118,19 +119,14 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
118
119
  if (!media?.relativeUri) {
119
120
  return;
120
121
  }
121
- await this.fileService.delete(this.getFullFilePath(media.relativeUri));
122
+ await this.fileService.delete(buildDiskMediaPath(media.relativeUri, this.settingService, media.mediaStorageProviderMetadata));
122
123
  }
123
124
 
124
- private getFullFilePath(fileName: string): string {
125
- const base = this.settingService.getConfigValue<SolidCoreSetting>("fileStorageDir")
126
- || DEFAULT_MEDIA_FILE_STORAGE_DIR;
127
- if (path.isAbsolute(fileName) || fileName.startsWith(`${base}/`)) {
128
- return fileName;
129
- }
130
- return `${base}/${fileName}`;
125
+ private resolveMediaStorageProvider(media: Media, mediaFieldMetadata?: FieldMetadata): MediaStorageProviderMetadata | undefined {
126
+ return media.mediaStorageProviderMetadata || mediaFieldMetadata?.mediaStorageProvider;
131
127
  }
132
128
 
133
- private getFileName(file: Express.Multer.File): string {
134
- return `${file.filename}-${file.originalname}`;
129
+ private getEntityId(entity: T): number {
130
+ return (entity as any).id;
135
131
  }
136
132
  }
@@ -140,6 +140,10 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
140
140
  return await this._addPermissionsToRole(roleName, null);
141
141
  }
142
142
 
143
+ async addAllPermissionsToRoleExceptPrefixes(roleName: string, excludedPermissionPrefixes: string[]): Promise<RoleMetadata> {
144
+ return await this._addPermissionsToRole(roleName, null, excludedPermissionPrefixes);
145
+ }
146
+
143
147
  async addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {
144
148
  return await this._addPermissionsToRole(roleName, permissionNames);
145
149
  }
@@ -148,7 +152,11 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
148
152
  return await this._addPermissionsToRole(roleName, permissionName);
149
153
  }
150
154
 
151
- private async _addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {
155
+ private async _addPermissionsToRole(
156
+ roleName: string,
157
+ permissionNames: string[],
158
+ excludedPermissionPrefixes: string[] = [],
159
+ ): Promise<RoleMetadata> {
152
160
  const role = await this.repo.findOne({
153
161
  where: { name: roleName },
154
162
  select: {
@@ -161,7 +169,7 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
161
169
  }
162
170
 
163
171
  const joinInfo = this.getRolePermissionJoinInfo();
164
- const targetPermissions = await this.loadTargetPermissions(permissionNames);
172
+ const targetPermissions = await this.loadTargetPermissions(permissionNames, excludedPermissionPrefixes);
165
173
 
166
174
  if (!permissionNames || permissionNames.length === 0) {
167
175
  await this.entityManager
@@ -226,21 +234,25 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
226
234
  return existingPermission;
227
235
  }
228
236
 
229
- private async loadTargetPermissions(permissionNames: string[] | null): Promise<PermissionMetadata[]> {
237
+ private async loadTargetPermissions(permissionNames: string[] | null, excludedPermissionPrefixes: string[] = []): Promise<PermissionMetadata[]> {
230
238
  if (permissionNames && permissionNames.length !== 0) {
231
239
  const uniquePermissionNames = [...new Set(permissionNames)];
232
240
  const permissions = await this.permissionRepository.find({ where: { name: In(uniquePermissionNames) } });
233
241
  if (permissions.length !== uniquePermissionNames.length) {
234
242
  throw new Error(`One or more permissions not found.`);
235
243
  }
236
- return permissions;
244
+ return permissions.filter((permission) =>
245
+ !excludedPermissionPrefixes.some((prefix) => permission.name.startsWith(prefix)),
246
+ );
237
247
  }
238
248
 
239
249
  const permissions = await this.permissionRepository.find();
240
250
  if (permissions.length === 0) {
241
251
  throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);
242
252
  }
243
- return permissions;
253
+ return permissions.filter((permission) =>
254
+ !excludedPermissionPrefixes.some((prefix) => permission.name.startsWith(prefix)),
255
+ );
244
256
  }
245
257
 
246
258
  private getRolePermissionJoinInfo(): { tableName: string; roleIdColumn: string; permissionIdColumn: string } {
@@ -47,6 +47,7 @@ import { ListOfValuesService } from "./services/list-of-values.service";
47
47
  // import { MediaStorageProviderMetadataSeederService } from './services/media-storage-provider-metadata-seeder.service';
48
48
  import { MediaStorageProviderMetadataService } from "./services/media-storage-provider-metadata.service";
49
49
  import { MediaService } from "./services/media.service";
50
+ import { MediaDownloadUrlService } from "./services/media-download-url.service";
50
51
  import { ModelMetadataService } from "./services/model-metadata.service";
51
52
  import { RemovedFieldMigrationService } from "./services/removed-field-migration.service";
52
53
  import { ModuleMetadataExplorerService } from "./services/module-metadata-explorer.service";
@@ -55,9 +56,10 @@ import { ModulePackageService } from "./services/module-package.service";
55
56
  import { SolidIntrospectService } from "./services/solid-introspect.service";
56
57
  // import { ListOfComputedFieldProvider } from './providers/list-of-computed-field-provider.service';
57
58
  import { ServeStaticModule } from "@nestjs/serve-static";
58
- import { join } from "path";
59
+ import { extname, join } from "path";
59
60
  import { RefreshModelCommand } from "./commands/refresh-model.command";
60
61
  import { MediaController } from "./controllers/media.controller";
62
+ import { INLINE_SAFE_EXTENSIONS } from "./constants/media-file-types";
61
63
 
62
64
  import { RefreshModuleCommand } from "./commands/refresh-module.command";
63
65
  import { ModelMetadataSubscriber } from "./subscribers/model-metadata.subscriber";
@@ -106,6 +108,7 @@ import { MqMessage } from "./entities/mq-message.entity";
106
108
  import { SmsTemplate } from "./entities/sms-template.entity";
107
109
  import { AccessTokenGuard } from "./guards/access-token.guard";
108
110
  import { ApiKeyGuard } from "./guards/api-key.guard";
111
+ import { MediaSignedUrlGuard } from "./guards/media-signed-url.guard";
109
112
  import { AuthenticationGuard } from "./guards/authentication.guard";
110
113
  import { PermissionsGuard } from "./guards/permissions.guard";
111
114
  import { SolidRegistry } from "./helpers/solid-registry";
@@ -444,7 +447,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
444
447
  rootPath: join(process.cwd(), "media-files-storage"),
445
448
  serveRoot: "/media-files-storage",
446
449
  serveStaticOptions: {
447
- setHeaders: (res /*, path, stat*/) => {
450
+ setHeaders: (res, path) => {
448
451
  // Allow use of these files from a different origin (e.g., :3000 UI)
449
452
  // Use 'same-site' if both origins are on the same site (localhost:* counts as same-site)
450
453
  res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); // or 'same-site'
@@ -452,6 +455,20 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
452
455
  // If you need to load into <canvas> without tainting or fetch images via XHR,
453
456
  // you can also expose CORS here (not needed for simple <img>):
454
457
  // res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
458
+
459
+ // Prevent the browser from ever content-sniffing a served file into something
460
+ // more dangerous than its declared Content-Type (e.g. rendering a mislabeled
461
+ // upload as HTML and executing embedded script).
462
+ res.setHeader("X-Content-Type-Options", "nosniff");
463
+
464
+ // Only a small allowlist of media types is ever safe to render inline. Everything
465
+ // else (including svg, html, and any other uploaded file) is forced to download
466
+ // rather than be displayed/executed by the browser, regardless of what mimetype or
467
+ // extension it was uploaded with.
468
+ if (!INLINE_SAFE_EXTENSIONS.has(extname(path).toLowerCase().replace(/^\./, ''))) {
469
+ res.setHeader("Content-Type", "application/octet-stream");
470
+ res.setHeader("Content-Disposition", "attachment");
471
+ }
455
472
  },
456
473
  },
457
474
  }),
@@ -570,6 +587,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
570
587
  SchematicService,
571
588
  MediaStorageProviderMetadataService,
572
589
  MediaService,
590
+ MediaDownloadUrlService,
573
591
  // MediaStorageProviderMetadataSeederService,
574
592
  ModuleMetadataSeederService,
575
593
  ModuleTestDataService,
@@ -654,6 +672,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
654
672
  SoftDeleteAwareEventSubscriber,
655
673
  AccessTokenGuard,
656
674
  ApiKeyGuard,
675
+ MediaSignedUrlGuard,
657
676
  ApiKeyService,
658
677
  ActiveSessionStorageService,
659
678
  AuthenticationService,
@@ -106,7 +106,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
106
106
 
107
107
  const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
108
108
 
109
- const relationAfter = await this.dataSource.getRepository(event.metadata.target as any).findOne({
109
+ const relationAfter = await event.queryRunner.manager.getRepository(event.metadata.target as any).findOne({
110
110
  where: { id: entityId } as any,
111
111
  relations: relations as any,
112
112
  });
@@ -1,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const env_1 = require("../helpers/env");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
7
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
8
- function base64UrlDecode(input) {
9
- const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
10
- const padded = normalized.length % 4 === 0
11
- ? normalized
12
- : normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
13
- return Buffer.from(padded, "base64").toString("utf-8");
14
- }
15
- function validateJwt(token) {
16
- const parts = token.split(".");
17
- if (parts.length !== 3) {
18
- throw new Error("JWT must have three dot-separated parts.");
19
- }
20
- const headerJson = JSON.parse(base64UrlDecode(parts[0]));
21
- const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
22
- if (!headerJson || typeof headerJson !== "object") {
23
- throw new Error("JWT header must be a JSON object.");
24
- }
25
- if (!payloadJson || typeof payloadJson !== "object") {
26
- throw new Error("JWT payload must be a JSON object.");
27
- }
28
- if (typeof payloadJson.exp !== "number") {
29
- throw new Error("JWT payload.exp must be a number.");
30
- }
31
- return payloadJson;
32
- }
33
- (0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
34
- const api = await test_1.request.newContext({
35
- baseURL,
36
- extraHTTPHeaders: {
37
- accept: "*/*",
38
- "content-type": "application/json",
39
- },
40
- });
41
- try {
42
- const res = await api.post("/api/iam/authenticate", {
43
- data: {
44
- email: TEST_USER_EMAIL,
45
- username: "",
46
- password: TEST_USER_PASSWORD,
47
- },
48
- });
49
- (0, test_1.expect)(res.status()).toBe(200);
50
- const json = await res.json();
51
- (0, test_1.expect)(json.statusCode).toBe(200);
52
- (0, test_1.expect)(Array.isArray(json.message)).toBe(true);
53
- (0, test_1.expect)(json.message.length).toBe(0);
54
- (0, test_1.expect)(json.error).toBe("");
55
- const user = json.data?.user;
56
- (0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
57
- (0, test_1.expect)(typeof user).toBe("object");
58
- const email = user?.email;
59
- (0, test_1.expect)(typeof email).toBe("string");
60
- if (email === TEST_USER_EMAIL) {
61
- (0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
62
- }
63
- else {
64
- (0, test_1.expect)(email.length).toBeGreaterThan(0);
65
- }
66
- (0, test_1.expect)(typeof user?.mobile).toBe("string");
67
- (0, test_1.expect)(typeof user?.username).toBe("string");
68
- (0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
69
- (0, test_1.expect)(typeof user?.id).toBe("number");
70
- const roles = user?.roles;
71
- (0, test_1.expect)(Array.isArray(roles)).toBe(true);
72
- if (Array.isArray(roles)) {
73
- (0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
74
- (0, test_1.expect)(roles).toContain("Admin");
75
- }
76
- const accessToken = json.data?.accessToken;
77
- const refreshToken = json.data?.refreshToken;
78
- (0, test_1.expect)(typeof accessToken).toBe("string");
79
- (0, test_1.expect)(typeof refreshToken).toBe("string");
80
- const accessPayload = validateJwt(accessToken);
81
- const refreshPayload = validateJwt(refreshToken);
82
- (0, test_1.expect)(typeof accessPayload.exp).toBe("number");
83
- (0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
84
- }
85
- finally {
86
- await api.dispose();
87
- }
88
- });
89
- (0, test_1.test)("API: authenticate fails with wrong password", async () => {
90
- const api = await test_1.request.newContext({
91
- baseURL,
92
- extraHTTPHeaders: {
93
- accept: "*/*",
94
- "content-type": "application/json",
95
- },
96
- });
97
- try {
98
- const res = await api.post("/api/iam/authenticate", {
99
- data: {
100
- email: TEST_USER_EMAIL,
101
- username: "",
102
- password: `${TEST_USER_PASSWORD}__wrong`,
103
- },
104
- });
105
- (0, test_1.expect)(res.status()).toBe(401);
106
- const json = await res.json();
107
- (0, test_1.expect)(json.statusCode).toBe(401);
108
- (0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
109
- (0, test_1.expect)(json.message).toBe("Invalid credentials");
110
- (0, test_1.expect)(json.error).toBe("Invalid credentials");
111
- (0, test_1.expect)(json.data?.statusCode).toBe(401);
112
- (0, test_1.expect)(json.data?.error).toBe("Unauthorized");
113
- (0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
114
- }
115
- finally {
116
- await api.dispose();
117
- }
118
- });
119
- //# sourceMappingURL=authenticate.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const auth_1 = require("../helpers/auth");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- async function getCityByName(name) {
7
- const api = await test_1.request.newContext({
8
- baseURL,
9
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
10
- });
11
- try {
12
- const res = await api.get(`/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`);
13
- (0, test_1.expect)(res.status()).toBe(200);
14
- const json = await res.json();
15
- const record = json?.data?.records?.[0];
16
- (0, test_1.expect)(record, `Expected city '${name}' in testData`).toBeTruthy();
17
- (0, test_1.expect)(record.id, "Expected record to have id").toBeTruthy();
18
- return record;
19
- }
20
- finally {
21
- await api.dispose();
22
- }
23
- }
24
- test_1.test.describe("CRUDService.findOne (cityMaster)", () => {
25
- (0, test_1.test)("returns a city by id", async () => {
26
- const city = await getCityByName("Mumbai");
27
- const api = await test_1.request.newContext({
28
- baseURL,
29
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
30
- });
31
- try {
32
- const res = await api.get(`/api/city-master/${city.id}`);
33
- (0, test_1.expect)(res.status()).toBe(200);
34
- const json = await res.json();
35
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
36
- (0, test_1.expect)(json?.data?.name).toBe("Mumbai");
37
- (0, test_1.expect)(json?.data?.description).toBe("Mumbai city");
38
- }
39
- finally {
40
- await api.dispose();
41
- }
42
- });
43
- (0, test_1.test)("supports fields[] selection", async () => {
44
- const city = await getCityByName("Pune");
45
- const api = await test_1.request.newContext({
46
- baseURL,
47
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
48
- });
49
- try {
50
- const res = await api.get(`/api/city-master/${city.id}?fields[]=id&fields[]=name`);
51
- (0, test_1.expect)(res.status()).toBe(200);
52
- const json = await res.json();
53
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
54
- (0, test_1.expect)(json?.data?.name).toBe("Pune");
55
- (0, test_1.expect)(json?.data?.description).toBeUndefined();
56
- (0, test_1.expect)(json?.data?.state).toBeUndefined();
57
- }
58
- finally {
59
- await api.dispose();
60
- }
61
- });
62
- (0, test_1.test)("supports populate=state", async () => {
63
- const city = await getCityByName("Bengaluru");
64
- const api = await test_1.request.newContext({
65
- baseURL,
66
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
67
- });
68
- try {
69
- const res = await api.get(`/api/city-master/${city.id}?populate=state`);
70
- (0, test_1.expect)(res.status()).toBe(200);
71
- const json = await res.json();
72
- (0, test_1.expect)(json?.data?.name).toBe("Bengaluru");
73
- (0, test_1.expect)(json?.data?.state).toBeTruthy();
74
- (0, test_1.expect)(json?.data?.state?.name).toBe("Karnataka");
75
- }
76
- finally {
77
- await api.dispose();
78
- }
79
- });
80
- (0, test_1.test)("returns 404 for missing id", async () => {
81
- const api = await test_1.request.newContext({
82
- baseURL,
83
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
84
- });
85
- try {
86
- const res = await api.get(`/api/city-master/99999999`);
87
- (0, test_1.expect)(res.status()).toBe(404);
88
- const json = await res.json();
89
- (0, test_1.expect)(String(json?.message)).toContain("cityMaster");
90
- (0, test_1.expect)(String(json?.message)).toContain("not found");
91
- }
92
- finally {
93
- await api.dispose();
94
- }
95
- });
96
- });
97
- //# sourceMappingURL=crud-service.findOne.cityMaster.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crud-service.findOne.cityMaster.spec.js","sourceRoot":"","sources":["../../tests/api/crud-service.findOne.cityMaster.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getAuthHeaders } from \"../helpers/auth\";\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\n\nasync function getCityByName(name: string) {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`\n );\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n const record = json?.data?.records?.[0];\n expect(record, `Expected city '${name}' in testData`).toBeTruthy();\n expect(record.id, \"Expected record to have id\").toBeTruthy();\n\n return record;\n } finally {\n await api.dispose();\n }\n}\n\ntest.describe(\"CRUDService.findOne (cityMaster)\", () => {\n test(\"returns a city by id\", async () => {\n const city = await getCityByName(\"Mumbai\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Mumbai\");\n expect(json?.data?.description).toBe(\"Mumbai city\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports fields[] selection\", async () => {\n const city = await getCityByName(\"Pune\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master/${city.id}?fields[]=id&fields[]=name`\n );\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Pune\");\n expect(json?.data?.description).toBeUndefined();\n expect(json?.data?.state).toBeUndefined();\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports populate=state\", async () => {\n const city = await getCityByName(\"Bengaluru\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}?populate=state`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.name).toBe(\"Bengaluru\");\n expect(json?.data?.state).toBeTruthy();\n expect(json?.data?.state?.name).toBe(\"Karnataka\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"returns 404 for missing id\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/99999999`);\n expect(res.status()).toBe(404);\n\n const json = await res.json();\n expect(String(json?.message)).toContain(\"cityMaster\");\n expect(String(json?.message)).toContain(\"not found\");\n } finally {\n await api.dispose();\n }\n });\n});\n"]}
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- (0, test_1.test)("GET /api/ping returns pong", async () => {
5
- const baseURL = process.env.BASE_URL || "http://localhost:3000";
6
- if (!baseURL) {
7
- throw new Error("baseURL is not configured. Set API_BASE_URL or use the default.");
8
- }
9
- const api = await test_1.request.newContext({ baseURL });
10
- const res = await api.get("/api/ping");
11
- (0, test_1.expect)(res.status()).toBe(200);
12
- const body = await res.json();
13
- (0, test_1.expect)(body).toEqual({
14
- statusCode: 200,
15
- message: [],
16
- error: "",
17
- data: { pong: "v1.0.2" },
18
- });
19
- await api.dispose();
20
- });
21
- //# sourceMappingURL=ping.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping.spec.js","sourceRoot":"","sources":["../../tests/api/ping.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AAEzD,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAA,aAAM,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\n\ntest(\"GET /api/ping returns pong\", async () => {\n const baseURL = process.env.BASE_URL || \"http://localhost:3000\";\n if (!baseURL) {\n throw new Error(\"baseURL is not configured. Set API_BASE_URL or use the default.\");\n }\n const api = await request.newContext({ baseURL });\n\n const res = await api.get(\"/api/ping\");\n\n expect(res.status()).toBe(200);\n\n const body = await res.json();\n expect(body).toEqual({\n statusCode: 200,\n message: [],\n error: \"\",\n data: { pong: \"v1.0.2\" },\n });\n\n await api.dispose();\n});\n"]}
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAccessToken = getAccessToken;
4
- exports.getAuthHeaders = getAuthHeaders;
5
- const test_1 = require("@playwright/test");
6
- const env_1 = require("./env");
7
- async function getAccessToken(baseURL) {
8
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
9
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
10
- const api = await test_1.request.newContext({
11
- baseURL,
12
- extraHTTPHeaders: {
13
- accept: "*/*",
14
- "content-type": "application/json",
15
- },
16
- });
17
- try {
18
- const res = await api.post("/api/iam/authenticate", {
19
- data: {
20
- email: TEST_USER_EMAIL,
21
- username: "",
22
- password: TEST_USER_PASSWORD,
23
- },
24
- });
25
- (0, test_1.expect)(res.status()).toBe(200);
26
- const json = await res.json();
27
- const token = json?.data?.accessToken;
28
- (0, test_1.expect)(token, "Expected access token from authenticate endpoint.").toBeTruthy();
29
- return token;
30
- }
31
- finally {
32
- await api.dispose();
33
- }
34
- }
35
- async function getAuthHeaders(baseURL) {
36
- const token = await getAccessToken(baseURL);
37
- return {
38
- authorization: `Bearer ${token}`,
39
- };
40
- }
41
- //# sourceMappingURL=auth.js.map