@solidstarters/solid-core 1.2.202 → 1.2.204

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 (67) hide show
  1. package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
  2. package/dist/dtos/create-role-metadata.dto.js +1 -0
  3. package/dist/dtos/create-role-metadata.dto.js.map +1 -1
  4. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +3 -0
  5. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  6. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +99 -0
  7. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  8. package/dist/helpers/image-encoding.helper.d.ts +10 -0
  9. package/dist/helpers/image-encoding.helper.d.ts.map +1 -0
  10. package/dist/helpers/image-encoding.helper.js +44 -0
  11. package/dist/helpers/image-encoding.helper.js.map +1 -0
  12. package/dist/helpers/module.helper.d.ts.map +1 -1
  13. package/dist/helpers/module.helper.js +2 -0
  14. package/dist/helpers/module.helper.js.map +1 -1
  15. package/dist/helpers/solid-microservice-adapter.service.d.ts +31 -0
  16. package/dist/helpers/solid-microservice-adapter.service.d.ts.map +1 -0
  17. package/dist/helpers/solid-microservice-adapter.service.js +53 -0
  18. package/dist/helpers/solid-microservice-adapter.service.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/mappers/list-of-values-mapper.d.ts.map +1 -1
  24. package/dist/mappers/list-of-values-mapper.js +1 -1
  25. package/dist/mappers/list-of-values-mapper.js.map +1 -1
  26. package/dist/repository/security-rule.repository.d.ts.map +1 -1
  27. package/dist/repository/security-rule.repository.js +7 -2
  28. package/dist/repository/security-rule.repository.js.map +1 -1
  29. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  30. package/dist/seeders/module-metadata-seeder.service.js +1 -1
  31. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  32. package/dist/seeders/seed-data/solid-core-metadata.json +41 -0
  33. package/dist/services/authentication.service.d.ts.map +1 -1
  34. package/dist/services/authentication.service.js +5 -5
  35. package/dist/services/authentication.service.js.map +1 -1
  36. package/dist/services/crud.service.d.ts.map +1 -1
  37. package/dist/services/crud.service.js +6 -1
  38. package/dist/services/crud.service.js.map +1 -1
  39. package/dist/services/selection-providers/list-of-roles-selectionproviders.service.d.ts +15 -0
  40. package/dist/services/selection-providers/list-of-roles-selectionproviders.service.d.ts.map +1 -0
  41. package/dist/services/selection-providers/list-of-roles-selectionproviders.service.js +59 -0
  42. package/dist/services/selection-providers/list-of-roles-selectionproviders.service.js.map +1 -0
  43. package/dist/services/user.service.d.ts +8 -1
  44. package/dist/services/user.service.d.ts.map +1 -1
  45. package/dist/services/user.service.js +15 -4
  46. package/dist/services/user.service.js.map +1 -1
  47. package/dist/solid-core.module.d.ts.map +1 -1
  48. package/dist/solid-core.module.js +9 -0
  49. package/dist/solid-core.module.js.map +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +1 -1
  52. package/src/dtos/create-role-metadata.dto.ts +3 -0
  53. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +138 -4
  54. package/src/helpers/image-encoding.helper.ts +71 -0
  55. package/src/helpers/module.helper.ts +3 -0
  56. package/src/helpers/solid-microservice-adapter.service.ts +68 -0
  57. package/src/index.ts +2 -0
  58. package/src/mappers/list-of-values-mapper.ts +2 -1
  59. package/src/repository/security-rule.repository.ts +7 -2
  60. package/src/seeders/module-metadata-seeder.service.ts +3 -1
  61. package/src/seeders/seed-data/solid-core-metadata.json +41 -0
  62. package/src/services/1.js +6 -0
  63. package/src/services/authentication.service.ts +7 -5
  64. package/src/services/crud.service.ts +6 -1
  65. package/src/services/selection-providers/list-of-roles-selectionproviders.service.ts +69 -0
  66. package/src/services/user.service.ts +19 -0
  67. package/src/solid-core.module.ts +12 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.202",
3
+ "version": "1.2.204",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -91,6 +91,9 @@ export const INTERNAL_ROLE_PERMISSIONS = [
91
91
  'AuthenticationController.logout',
92
92
  'AuthenticationController.me',
93
93
 
94
+ // Field Metadata permissions
95
+ 'ModelMetadataController.navigation',
96
+
94
97
  // Field Metadata permissions
95
98
  'FieldMetadataController.getSelectionDynamicValues',
96
99
  'FieldMetadataController.getSelectionDynamicValue',
@@ -9,20 +9,122 @@ export interface MediaFieldOptions {
9
9
  type: SolidMediaType;
10
10
  required: boolean | undefined | null;
11
11
  fieldName: string | undefined | null;
12
+ mediaMaxSizeKb: number | undefined | null;
13
+ mediaTypes: string[];
12
14
  }
13
15
 
16
+ type MediaType = 'image' | 'audio' | 'video' | 'file';
17
+
18
+ const MIME_TO_MEDIA_TYPE: Record<string, MediaType> = {
19
+ // Images
20
+ 'image/png': 'image',
21
+ 'image/jpeg': 'image',
22
+ 'image/jpg': 'image',
23
+ 'image/webp': 'image',
24
+ 'image/gif': 'image',
25
+ 'image/bmp': 'image',
26
+ 'image/tiff': 'image',
27
+ 'image/svg+xml': 'image',
28
+ 'image/heic': 'image',
29
+ 'image/heif': 'image',
30
+
31
+ // Audio
32
+ 'audio/mpeg': 'audio', // mp3
33
+ 'audio/mp3': 'audio',
34
+ 'audio/wav': 'audio',
35
+ 'audio/x-wav': 'audio',
36
+ 'audio/webm': 'audio',
37
+ 'audio/ogg': 'audio',
38
+ 'audio/aac': 'audio',
39
+ 'audio/mp4': 'audio', // m4a often shows as audio/mp4
40
+ 'audio/x-m4a': 'audio',
41
+ 'audio/flac': 'audio',
42
+
43
+ // Video
44
+ 'video/mp4': 'video',
45
+ 'video/mpeg': 'video',
46
+ 'video/webm': 'video',
47
+ 'video/ogg': 'video',
48
+ 'video/quicktime': 'video', // mov
49
+ 'video/x-msvideo': 'video', // avi
50
+ 'video/x-matroska': 'video',// mkv
51
+ 'video/3gpp': 'video',
52
+ 'video/3gpp2': 'video',
53
+
54
+ // Documents / files (treat as "file")
55
+ 'application/pdf': 'file',
56
+ 'text/plain': 'file',
57
+ 'text/markdown': 'file',
58
+ 'application/json': 'file',
59
+ 'text/csv': 'file',
60
+
61
+ // Office
62
+ 'application/msword': 'file', // doc
63
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file', // docx
64
+ 'application/vnd.ms-excel': 'file', // xls
65
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file', // xlsx
66
+ 'application/vnd.ms-powerpoint': 'file', // ppt
67
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file', // pptx,
68
+
69
+ // Archives (optional)
70
+ 'application/zip': 'file',
71
+ 'application/x-zip-compressed': 'file',
72
+ 'application/x-rar-compressed': 'file',
73
+ 'application/x-7z-compressed': 'file',
74
+
75
+ // Common binary fallback category
76
+ 'application/octet-stream': 'file',
77
+ };
78
+
79
+ const EXT_TO_MEDIA_TYPE: Record<string, MediaType> = {
80
+ // Images
81
+ png: 'image', jpg: 'image', jpeg: 'image', webp: 'image', gif: 'image', bmp: 'image', tiff: 'image', svg: 'image', heic: 'image', heif: 'image',
82
+
83
+ // Audio
84
+ mp3: 'audio', wav: 'audio', ogg: 'audio', aac: 'audio', m4a: 'audio', flac: 'audio',
85
+
86
+ // Video
87
+ mp4: 'video', mov: 'video', avi: 'video', mkv: 'video', mpeg: 'video', mpg: 'video', '3gp': 'video', '3g2': 'video',
88
+
89
+ // Files
90
+ pdf: 'file', txt: 'file', md: 'file', csv: 'file', json: 'file',
91
+ doc: 'file', docx: 'file', xls: 'file', xlsx: 'file', ppt: 'file', pptx: 'file',
92
+ zip: 'file', rar: 'file', '7z': 'file',
93
+ };
94
+
95
+
14
96
  export class MediaFieldCrudManager implements FieldCrudManager {
15
97
 
16
98
  constructor(private readonly options: MediaFieldOptions) {
17
99
  }
18
100
 
19
- validate(dto: any, files:Array<Express.Multer.File>): ValidationError[] {
101
+ private resolveMediaType(mimetype?: string, filename?: string): MediaType | null {
102
+ const mt = (mimetype || '').toLowerCase().trim();
103
+ if (mt && MIME_TO_MEDIA_TYPE[mt]) {
104
+ return MIME_TO_MEDIA_TYPE[mt];
105
+ }
106
+
107
+ // Some libs may send "image/*" etc. Treat broad families safely.
108
+ if (mt.startsWith('image/')) return 'image';
109
+ if (mt.startsWith('audio/')) return 'audio';
110
+ if (mt.startsWith('video/')) return 'video';
111
+
112
+ // Fallback to extension if provided
113
+ const ext = (filename || '').split('.').pop()?.toLowerCase();
114
+ if (ext && EXT_TO_MEDIA_TYPE[ext]) {
115
+ return EXT_TO_MEDIA_TYPE[ext];
116
+ }
117
+
118
+ return null;
119
+ }
120
+
121
+ validate(dto: any, files: Array<Express.Multer.File>): ValidationError[] {
20
122
  const isValidateForUpdate = dto.id !== undefined; //FIXME: This is a hack, since we are using PUT for update. Once we support PATCH, this will be removed
21
123
  const fieldFiles = files.filter(file => file.fieldname === this.options.fieldName);
22
124
  return this.applyValidations(fieldFiles, isValidateForUpdate);
23
125
  }
24
126
 
25
- private applyValidations(fieldFiles:Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
127
+ private applyValidations(fieldFiles: Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
26
128
  switch (this.options.type) {
27
129
  case SolidMediaType.mediaSingle:
28
130
  return this.validateMediaSingle(fieldFiles, isValidateForUpdate);
@@ -33,7 +135,7 @@ export class MediaFieldCrudManager implements FieldCrudManager {
33
135
  }
34
136
  }
35
137
 
36
- private validateMediaSingle(fieldFiles:Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
138
+ private validateMediaSingle(fieldFiles: Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
37
139
  const errors: ValidationError[] = [];
38
140
  if (!isValidateForUpdate && this.options.required && fieldFiles.length === 0) {
39
141
  errors.push({
@@ -47,10 +149,42 @@ export class MediaFieldCrudManager implements FieldCrudManager {
47
149
  error: `${this.options.fieldName} must be a single file`
48
150
  });
49
151
  }
152
+ // validate size
153
+ if (this.options.mediaMaxSizeKb) {
154
+ for (let i = 0; i < fieldFiles.length; i++) {
155
+ const fieldFile = fieldFiles[i];
156
+ const fieldFileSizeInBytes = Math.ceil(fieldFile.size / 1024);
157
+ if (fieldFileSizeInBytes > this.options.mediaMaxSizeKb) {
158
+ errors.push({
159
+ field: this.options.fieldName,
160
+ error: `${this.options.fieldName} with size ${fieldFileSizeInBytes} KB exceeds max size limit of ${this.options.mediaMaxSizeKb} KB`
161
+ });
162
+ }
163
+ }
164
+ }
165
+ // validate type
166
+ if (this.options.mediaTypes && this.options.mediaTypes.length > 0) {
167
+ const allowedFileTypes = this.options.mediaTypes as MediaType[];
168
+
169
+ for (let i = 0; i < fieldFiles.length; i++) {
170
+ const fieldFile = fieldFiles[i];
171
+
172
+ const resolvedType = this.resolveMediaType(fieldFile.mimetype, fieldFile.originalname ?? fieldFile.filename ?? '');
173
+ if (!resolvedType || !allowedFileTypes.includes(resolvedType)) {
174
+ errors.push({
175
+ field: this.options.fieldName,
176
+ error: `${this.options.fieldName} file type not allowed. ` +
177
+ `Allowed: ${allowedFileTypes.join(', ')}. ` +
178
+ `Received mimetype: ${fieldFile.mimetype}${resolvedType ? ` (mapped to ${resolvedType})` : ''}`
179
+ });
180
+ }
181
+ }
182
+ }
183
+
50
184
  return errors;
51
185
  }
52
186
 
53
- private validateMediaMultiple(fieldFiles:Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
187
+ private validateMediaMultiple(fieldFiles: Array<Express.Multer.File>, isValidateForUpdate: boolean): ValidationError[] {
54
188
  const errors: ValidationError[] = [];
55
189
  if (!isValidateForUpdate && this.options.required && fieldFiles.length === 0) {
56
190
  errors.push({
@@ -0,0 +1,71 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+
3
+ export interface ImageToBase64Result {
4
+ base64: string;
5
+ mediaType: string;
6
+ }
7
+
8
+ @Injectable()
9
+ export class ImageEncodingService {
10
+ private readonly logger = new Logger(ImageEncodingService.name);
11
+
12
+ /**
13
+ * Fetch an image from a URL and return its Base64 representation.
14
+ *
15
+ * @param url - Public or signed image URL
16
+ * @param mimeTypeOverride - Optional MIME type (image/png, image/jpeg)
17
+ * @param maxSizeBytes - Optional max size guard (default 5MB)
18
+ */
19
+ async imageUrlToBase64(
20
+ url: string,
21
+ mimeTypeOverride?: string,
22
+ maxSizeBytes = 5 * 1024 * 1024,
23
+ ): Promise<ImageToBase64Result> {
24
+ this.logger.debug(`Fetching image from URL: ${url}`);
25
+
26
+ const response = await fetch(url);
27
+
28
+ if (!response.ok) {
29
+ throw new Error(
30
+ `Failed to fetch image (${response.status} ${response.statusText})`,
31
+ );
32
+ }
33
+
34
+ const contentType =
35
+ mimeTypeOverride ||
36
+ response.headers.get('content-type') ||
37
+ 'image/jpeg';
38
+
39
+ const arrayBuffer = await response.arrayBuffer();
40
+ const buffer = Buffer.from(arrayBuffer);
41
+
42
+ if (buffer.length > maxSizeBytes) {
43
+ throw new Error(
44
+ `Image size ${buffer.length} bytes exceeds limit of ${maxSizeBytes} bytes`,
45
+ );
46
+ }
47
+
48
+ return {
49
+ base64: buffer.toString('base64'),
50
+ mediaType: contentType,
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Convenience method that returns a data URL
56
+ * (useful for OpenAI Responses API).
57
+ */
58
+ async imageUrlToDataUrl(
59
+ url: string,
60
+ mimeTypeOverride?: string,
61
+ maxSizeBytes?: number,
62
+ ): Promise<string> {
63
+ const { base64, mediaType } = await this.imageUrlToBase64(
64
+ url,
65
+ mimeTypeOverride,
66
+ maxSizeBytes,
67
+ );
68
+
69
+ return `data:${mediaType};base64,${base64}`;
70
+ }
71
+ }
@@ -32,6 +32,8 @@ export const getDynamicModuleNames = (): string[] => {
32
32
  .map(dirent => dirent.name);
33
33
 
34
34
  // logger.debug(`Enabled dynamic modules:`, enabledModules);
35
+ console.log(`Dynamic Modules Are:`, enabledModules);
36
+
35
37
  return enabledModules;
36
38
  }
37
39
 
@@ -65,6 +67,7 @@ export const getDynamicModuleNamesBasedOnMetadata = (): string[] => {
65
67
  .map(dirent => dirent.name);
66
68
 
67
69
  // logger.debug(`Enabled dynamic modules basis src:`, enabledModules);
70
+ console.log(`Src Based Dynamic Modules Are:`, enabledModules);
68
71
  return enabledModules;
69
72
  }
70
73
 
@@ -0,0 +1,68 @@
1
+ import { Injectable, Logger, Scope } from '@nestjs/common';
2
+ import axios from 'axios';
3
+
4
+ export interface SolidxIamAuthRequest {
5
+ email?: string;
6
+ username?: string;
7
+ password: string;
8
+ }
9
+
10
+ export interface SolidxIamUser {
11
+ email: string;
12
+ mobile: string;
13
+ username: string;
14
+ forcePasswordChange: boolean;
15
+ id: number;
16
+ roles: string[];
17
+ }
18
+
19
+ export interface SolidxIamAuthData {
20
+ user: SolidxIamUser;
21
+ accessToken: string;
22
+ refreshToken: string;
23
+ }
24
+
25
+ export interface SolidxIamAuthResponse {
26
+ statusCode: number;
27
+ message: string[];
28
+ error: string;
29
+ data: SolidxIamAuthData;
30
+ }
31
+
32
+ @Injectable({ scope: Scope.TRANSIENT })
33
+ export class SolidMicroserviceAdapter {
34
+ private readonly logger = new Logger(SolidMicroserviceAdapter.name);
35
+ private baseUrl?: string;
36
+
37
+ setBaseUrl(baseUrl: string): this {
38
+ this.baseUrl = baseUrl;
39
+ return this;
40
+ }
41
+
42
+ async authenticate({ email, username, password }: SolidxIamAuthRequest): Promise<SolidxIamAuthResponse> {
43
+ if (!password) {
44
+ throw new Error('password is required for IAM authentication');
45
+ }
46
+ if (!email && !username) {
47
+ throw new Error('email or username is required for IAM authentication');
48
+ }
49
+ if (!this.baseUrl) {
50
+ throw new Error('baseUrl must be set before IAM authentication');
51
+ }
52
+
53
+ const payload: SolidxIamAuthRequest = { password };
54
+ if (email) payload.email = email;
55
+ if (username) payload.username = username;
56
+
57
+ this.logger.debug(`Requesting IAM access token from ${this.baseUrl}/api/iam/authenticate`);
58
+
59
+ const response = await axios.post(`${this.baseUrl}/api/iam/authenticate`, payload, {
60
+ timeout: 10000,
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ });
65
+
66
+ return response.data as SolidxIamAuthResponse;
67
+ }
68
+ }
package/src/index.ts CHANGED
@@ -178,6 +178,8 @@ export * from './helpers/environment.helper'
178
178
  export * from './helpers/cors.helper'
179
179
  export * from './helpers/security.helper'
180
180
  export * from './helpers/model-metadata-helper.service'
181
+ export * from './helpers/image-encoding.helper'
182
+ export * from './helpers/solid-microservice-adapter.service'
181
183
 
182
184
  export * from './services/crud.service'
183
185
  export * from './interceptors/logging.interceptor'
@@ -11,7 +11,8 @@ export class ListOfValuesMapper {
11
11
  description: listOfValue.description,
12
12
  default: listOfValue.default,
13
13
  sequence: listOfValue.sequence,
14
- module: listOfValue.module ? listOfValue.module.id : null
14
+ // module: listOfValue.module ? listOfValue.module.id : null
15
+ moduleUserKey: listOfValue.module ? listOfValue.module.name : null
15
16
  };
16
17
  }
17
18
  }
@@ -43,6 +43,9 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
43
43
  if (rule.securityRuleConfigProvider) {
44
44
  // TODO: Evaluation of the securityRuleConfig Provider should happen outside first...
45
45
  const securityRuleConfigProviderInstance = this.solidRegistry.getSecurityRuleConfigProviderInstance(rule.securityRuleConfigProvider);
46
+ if (!securityRuleConfigProviderInstance) {
47
+ throw new Error(`Unable to resolve instance for security rule config provider: ${rule.securityRuleConfigProvider}`);
48
+ }
46
49
  evaluatedRule = await securityRuleConfigProviderInstance.securityRuleConfig(activeUser, rule);
47
50
  }
48
51
  else {
@@ -54,7 +57,9 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
54
57
  evaluatedRules.push(evaluatedRule);
55
58
 
56
59
  } catch (error) {
57
- this.logger.warn(`Error parsing security rule: ${rule.securityRuleConfig}`, error);
60
+ this.logger.error(`Error parsing security rule: ${rule.securityRuleConfig}`, error);
61
+ this.logger.error(error.stack);
62
+ throw error;
58
63
  }
59
64
  }
60
65
 
@@ -104,7 +109,7 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
104
109
  modelMetadataId: populatedSecurityRule.modelMetadata.id,
105
110
  modelMetadataUserKey: populatedSecurityRule.modelMetadata.singularName,
106
111
  securityRuleConfig: populatedSecurityRule.securityRuleConfig,
107
- securityRuleConfigProvider: ""
112
+ securityRuleConfigProvider: populatedSecurityRule.securityRuleConfigProvider,
108
113
  };
109
114
  }
110
115
 
@@ -196,7 +196,9 @@ export class ModuleMetadataSeederService {
196
196
  // Setup default roles with permissions.
197
197
  await this.setupDefaultRolesWithPermissions();
198
198
 
199
- this.logger.log(`All Seeders finished`);
199
+ // Add a console log indicating seeding is finished. This needs to be console.log so that it looks proper when this code is run via CLI.
200
+ console.log(`Seeding completed.`);
201
+ //this.logger.log(`All Seeders finished`);
200
202
 
201
203
  //FIXME: Handle displaying the created users credentials in a better way.
202
204
  // this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
@@ -8980,6 +8980,13 @@
8980
8980
  "widget": "SolidUserNameAvatarWidget"
8981
8981
  }
8982
8982
  },
8983
+ {
8984
+ "type": "field",
8985
+ "attrs": {
8986
+ "name": "fullName",
8987
+ "isSearchable": true
8988
+ }
8989
+ },
8983
8990
  {
8984
8991
  "type": "field",
8985
8992
  "attrs": {
@@ -11013,6 +11020,22 @@
11013
11020
  "sortable": true,
11014
11021
  "filterable": true
11015
11022
  }
11023
+ },
11024
+ {
11025
+ "type": "field",
11026
+ "attrs": {
11027
+ "name": "event",
11028
+ "sortable": true,
11029
+ "filterable": true
11030
+ }
11031
+ },
11032
+ {
11033
+ "type": "field",
11034
+ "attrs": {
11035
+ "name": "ipAddress",
11036
+ "sortable": true,
11037
+ "filterable": true
11038
+ }
11016
11039
  }
11017
11040
  ]
11018
11041
  }
@@ -11057,6 +11080,24 @@
11057
11080
  "attrs": {
11058
11081
  "name": "user"
11059
11082
  }
11083
+ },
11084
+ {
11085
+ "type": "field",
11086
+ "attrs": {
11087
+ "name": "event"
11088
+ }
11089
+ },
11090
+ {
11091
+ "type": "field",
11092
+ "attrs": {
11093
+ "name": "ipAddress"
11094
+ }
11095
+ },
11096
+ {
11097
+ "type": "field",
11098
+ "attrs": {
11099
+ "name": "userAgent"
11100
+ }
11060
11101
  }
11061
11102
  ]
11062
11103
  },
@@ -0,0 +1,6 @@
1
+ 1. Do i need to create a storeStreams method for aws service too?
2
+ - Handle later
3
+ 2. queues handling -> if queues is enabled by default, i.e triggerExport(exportTransactionEntity.id).
4
+ - startExport should either return the data or return the transaction id
5
+ 3. How to handle scenarios wherein, nested related exist.(do i need to only get the userkey)
6
+ - show the userKey
@@ -176,7 +176,7 @@ export class AuthenticationService {
176
176
  const savedUser = await this.userRepository.save(user);
177
177
  // Also assign a default role to the newly created user.
178
178
  const userRoles = signUpDto.roles ?? [];
179
- if (this.iamConfiguration.defaultRole) {
179
+ if (signUpDto.username !== 'sa' && this.iamConfiguration.defaultRole) {
180
180
  userRoles.push(this.iamConfiguration.defaultRole);
181
181
  }
182
182
  await this.handlePostSignup(savedUser, userRoles, pwd, autoGeneratedPwd);
@@ -842,14 +842,16 @@ export class AuthenticationService {
842
842
  }
843
843
 
844
844
  // Update Password
845
- const newPwd = await this.hashingService.hash(changePasswordDto.newPassword);
845
+ const pwdData = await this.userService.hashPassword(
846
+ changePasswordDto.newPassword,
847
+ );
846
848
  user.password = changePasswordDto.newPassword;
847
- user.passwordScheme = this.hashingService.name(); // e.g. bcrypt
848
- user.passwordSchemeVersion = this.hashingService.currentVersion(); // e.g. 1, 2, 3 ...
849
849
 
850
+ user.password = pwdData.password;
851
+ user.passwordScheme = pwdData.passwordScheme;
852
+ user.passwordSchemeVersion = pwdData.passwordSchemeVersion;
850
853
  // Everytime the user changes the password we reset the forcePasswordChange flag back to false.
851
854
  user.forcePasswordChange = false;
852
- user.password = newPwd;
853
855
 
854
856
  await this.userRepository.save(user);
855
857
 
@@ -339,7 +339,12 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
339
339
  // Use the EntityController to extract uploaded content & pass to the entity service.
340
340
  // If embedded media, then the media uri will saved in the entity table, else the uri will be saved in the media table
341
341
  // Plan the media table schema e.g id, uri, storageProvider, entity_id, entity_name, createdAt, updatedAt
342
- const options = { ...commonOptions, type: fieldMetadata.type as unknown as SolidMediaType };
342
+ const options = {
343
+ ...commonOptions,
344
+ mediaMaxSizeKb: fieldMetadata.mediaMaxSizeKb,
345
+ mediaTypes: fieldMetadata.mediaTypes,
346
+ type: fieldMetadata.type as unknown as SolidMediaType
347
+ };
343
348
  return new MediaFieldCrudManager(options);
344
349
  }
345
350
  case SolidFieldType.relation: {
@@ -0,0 +1,69 @@
1
+ import { Injectable, BadRequestException } from "@nestjs/common";
2
+ import { SelectionProvider } from "src/decorators/selection-provider.decorator";
3
+ import {
4
+ ISelectionProvider,
5
+ ISelectionProviderContext,
6
+ ISelectionProviderValues
7
+ } from "../../interfaces";
8
+ import { BasicFilterDto } from "src/dtos/basic-filters.dto";
9
+ import { RoleMetadataService } from "../role-metadata.service";
10
+
11
+ const DEFAULT_LIMIT = 100;
12
+
13
+ interface ListOfRolesProviderContext extends ISelectionProviderContext {
14
+ filters?: Record<string, any>;
15
+
16
+ }
17
+
18
+ @SelectionProvider()
19
+ @Injectable()
20
+ export class ListOfRolesSelectionProvider implements ISelectionProvider<ListOfRolesProviderContext> {
21
+
22
+ constructor(
23
+ private readonly roleMetadataService: RoleMetadataService
24
+ ) {}
25
+
26
+ name(): string {
27
+ return 'ListOfRolesSelectionProvider';
28
+ }
29
+
30
+ help(): string {
31
+ return '# Simple Role selection provider (search + include/exclude)';
32
+ }
33
+
34
+ async value( optionValue: string, ctxt: ListOfRolesProviderContext ): Promise<ISelectionProviderValues> {
35
+
36
+ const basicFilterQuery = new BasicFilterDto(1, 0);
37
+ basicFilterQuery.filters = {
38
+ name: { $eq: optionValue }
39
+ };
40
+
41
+ const roles = await this.roleMetadataService.find(basicFilterQuery);
42
+
43
+ if (!roles.records || roles.records.length === 0) {
44
+ throw new BadRequestException(
45
+ `Invalid role name: ${optionValue}`
46
+ );
47
+ }
48
+
49
+ const role = roles.records[0];
50
+
51
+ return {
52
+ label: role.name,
53
+ value: role.name
54
+ };
55
+ }
56
+
57
+ async values( query: string, ctxt: ListOfRolesProviderContext ): Promise<readonly ISelectionProviderValues[]> {
58
+
59
+ const basicFilterQuery = new BasicFilterDto(DEFAULT_LIMIT, 0);
60
+ basicFilterQuery.filters = ctxt.filters || {};
61
+
62
+ const roles = await this.roleMetadataService.find(basicFilterQuery);
63
+
64
+ return roles.records.map(role => ({
65
+ label: role.name,
66
+ value: role.name
67
+ }));
68
+ }
69
+ }
@@ -18,6 +18,7 @@ import { iamConfig } from 'src/config/iam.config';
18
18
  import { ERROR_MESSAGES } from 'src/constants/error-messages';
19
19
  import { UserRepository } from 'src/repository/user.repository';
20
20
  import { RoleMetadataRepository } from 'src/repository/role-metadata.repository';
21
+ import { HashingService } from './hashing.service';
21
22
 
22
23
  @Injectable()
23
24
  export class UserService extends CRUDService<User> {
@@ -29,6 +30,8 @@ export class UserService extends CRUDService<User> {
29
30
  readonly fileService: FileService,
30
31
  readonly discoveryService: DiscoveryService,
31
32
  readonly crudHelperService: CrudHelperService,
33
+ readonly hashingService: HashingService,
34
+
32
35
  @InjectEntityManager()
33
36
  readonly entityManager: EntityManager,
34
37
  // @InjectRepository(User, 'default')
@@ -283,4 +286,20 @@ export class UserService extends CRUDService<User> {
283
286
  }
284
287
  }
285
288
 
289
+ async hashPassword(password: string): Promise<{
290
+ password: string;
291
+ passwordScheme: string;
292
+ passwordSchemeVersion: number;
293
+ }> {
294
+ const hashedPassword = await this.hashingService.hash(password);
295
+
296
+ return {
297
+ password: hashedPassword,
298
+ passwordScheme: this.hashingService.name(),
299
+ passwordSchemeVersion: this.hashingService.currentVersion(),
300
+ };
301
+ }
302
+
303
+
286
304
  }
305
+