@solidstarters/solid-core 1.2.57 → 1.2.59

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 (137) hide show
  1. package/dist/config/iam.config.d.ts +2 -0
  2. package/dist/config/iam.config.d.ts.map +1 -1
  3. package/dist/config/iam.config.js +1 -0
  4. package/dist/config/iam.config.js.map +1 -1
  5. package/dist/controllers/chatter-message-details.controller.d.ts +41 -0
  6. package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -0
  7. package/dist/controllers/chatter-message-details.controller.js +179 -0
  8. package/dist/controllers/chatter-message-details.controller.js.map +1 -0
  9. package/dist/controllers/chatter-message.controller.d.ts +44 -0
  10. package/dist/controllers/chatter-message.controller.d.ts.map +1 -0
  11. package/dist/controllers/chatter-message.controller.js +199 -0
  12. package/dist/controllers/chatter-message.controller.js.map +1 -0
  13. package/dist/controllers/setting.controller.d.ts +3 -0
  14. package/dist/controllers/setting.controller.d.ts.map +1 -1
  15. package/dist/controllers/setting.controller.js +23 -0
  16. package/dist/controllers/setting.controller.js.map +1 -1
  17. package/dist/dtos/create-chatter-message-details.dto.d.ts +10 -0
  18. package/dist/dtos/create-chatter-message-details.dto.d.ts.map +1 -0
  19. package/dist/dtos/create-chatter-message-details.dto.js +66 -0
  20. package/dist/dtos/create-chatter-message-details.dto.js.map +1 -0
  21. package/dist/dtos/create-chatter-message.dto.d.ts +10 -0
  22. package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -0
  23. package/dist/dtos/create-chatter-message.dto.js +65 -0
  24. package/dist/dtos/create-chatter-message.dto.js.map +1 -0
  25. package/dist/dtos/create-field-metadata.dto.d.ts +1 -0
  26. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  27. package/dist/dtos/create-field-metadata.dto.js +6 -1
  28. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  29. package/dist/dtos/create-setting.dto.d.ts +2 -15
  30. package/dist/dtos/create-setting.dto.d.ts.map +1 -1
  31. package/dist/dtos/create-setting.dto.js +6 -77
  32. package/dist/dtos/create-setting.dto.js.map +1 -1
  33. package/dist/dtos/post-chatter-message.dto.d.ts +7 -0
  34. package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -0
  35. package/dist/dtos/post-chatter-message.dto.js +41 -0
  36. package/dist/dtos/post-chatter-message.dto.js.map +1 -0
  37. package/dist/dtos/update-chatter-message-details.dto.d.ts +11 -0
  38. package/dist/dtos/update-chatter-message-details.dto.d.ts.map +1 -0
  39. package/dist/dtos/update-chatter-message-details.dto.js +70 -0
  40. package/dist/dtos/update-chatter-message-details.dto.js.map +1 -0
  41. package/dist/dtos/update-chatter-message.dto.d.ts +11 -0
  42. package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -0
  43. package/dist/dtos/update-chatter-message.dto.js +74 -0
  44. package/dist/dtos/update-chatter-message.dto.js.map +1 -0
  45. package/dist/dtos/update-setting.dto.d.ts +2 -15
  46. package/dist/dtos/update-setting.dto.d.ts.map +1 -1
  47. package/dist/dtos/update-setting.dto.js +6 -77
  48. package/dist/dtos/update-setting.dto.js.map +1 -1
  49. package/dist/dtos/update-settings.dto.d.ts +4 -0
  50. package/dist/dtos/update-settings.dto.d.ts.map +1 -0
  51. package/dist/dtos/update-settings.dto.js +26 -0
  52. package/dist/dtos/update-settings.dto.js.map +1 -0
  53. package/dist/entities/chatter-message-details.entity.d.ts +11 -0
  54. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -0
  55. package/dist/entities/chatter-message-details.entity.js +52 -0
  56. package/dist/entities/chatter-message-details.entity.js.map +1 -0
  57. package/dist/entities/chatter-message.entity.d.ts +11 -0
  58. package/dist/entities/chatter-message.entity.d.ts.map +1 -0
  59. package/dist/entities/chatter-message.entity.js +53 -0
  60. package/dist/entities/chatter-message.entity.js.map +1 -0
  61. package/dist/entities/field-metadata.entity.d.ts +1 -0
  62. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  63. package/dist/entities/field-metadata.entity.js +5 -1
  64. package/dist/entities/field-metadata.entity.js.map +1 -1
  65. package/dist/entities/setting.entity.d.ts +2 -15
  66. package/dist/entities/setting.entity.d.ts.map +1 -1
  67. package/dist/entities/setting.entity.js +4 -65
  68. package/dist/entities/setting.entity.js.map +1 -1
  69. package/dist/index.d.ts +10 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +10 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  74. package/dist/seeders/module-metadata-seeder.service.js +1 -18
  75. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  76. package/dist/seeders/seed-data/solid-core-metadata.json +525 -176
  77. package/dist/services/authentication.service.d.ts +3 -1
  78. package/dist/services/authentication.service.d.ts.map +1 -1
  79. package/dist/services/authentication.service.js +11 -8
  80. package/dist/services/authentication.service.js.map +1 -1
  81. package/dist/services/chatter-message-details.service.d.ts +24 -0
  82. package/dist/services/chatter-message-details.service.d.ts.map +1 -0
  83. package/dist/services/chatter-message-details.service.js +59 -0
  84. package/dist/services/chatter-message-details.service.js.map +1 -0
  85. package/dist/services/chatter-message.service.d.ts +37 -0
  86. package/dist/services/chatter-message.service.d.ts.map +1 -0
  87. package/dist/services/chatter-message.service.js +279 -0
  88. package/dist/services/chatter-message.service.js.map +1 -0
  89. package/dist/services/crud.service.d.ts +3 -1
  90. package/dist/services/crud.service.d.ts.map +1 -1
  91. package/dist/services/crud.service.js +43 -3
  92. package/dist/services/crud.service.js.map +1 -1
  93. package/dist/services/setting.service.d.ts +4 -0
  94. package/dist/services/setting.service.d.ts.map +1 -1
  95. package/dist/services/setting.service.js +139 -7
  96. package/dist/services/setting.service.js.map +1 -1
  97. package/dist/services/user-context.service.d.ts +10 -0
  98. package/dist/services/user-context.service.d.ts.map +1 -0
  99. package/dist/services/user-context.service.js +42 -0
  100. package/dist/services/user-context.service.js.map +1 -0
  101. package/dist/solid-core.module.d.ts.map +1 -1
  102. package/dist/solid-core.module.js +18 -2
  103. package/dist/solid-core.module.js.map +1 -1
  104. package/dist/subscribers/audit.subscriber.d.ts +17 -0
  105. package/dist/subscribers/audit.subscriber.d.ts.map +1 -0
  106. package/dist/subscribers/audit.subscriber.js +83 -0
  107. package/dist/subscribers/audit.subscriber.js.map +1 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/package.json +1 -1
  110. package/src/config/iam.config.ts +1 -0
  111. package/src/controllers/chatter-message-details.controller.ts +92 -0
  112. package/src/controllers/chatter-message.controller.ts +105 -0
  113. package/src/controllers/setting.controller.ts +13 -1
  114. package/src/dtos/create-chatter-message-details.dto.ts +40 -0
  115. package/src/dtos/create-chatter-message.dto.ts +34 -0
  116. package/src/dtos/create-field-metadata.dto.ts +4 -0
  117. package/src/dtos/create-setting.dto.ts +10 -59
  118. package/src/dtos/post-chatter-message.dto.ts +19 -0
  119. package/src/dtos/update-chatter-message-details.dto.ts +43 -0
  120. package/src/dtos/update-chatter-message.dto.ts +41 -0
  121. package/src/dtos/update-setting.dto.ts +10 -60
  122. package/src/dtos/update-settings.dto.ts +7 -0
  123. package/src/entities/chatter-message-details.entity.ts +25 -0
  124. package/src/entities/chatter-message.entity.ts +22 -0
  125. package/src/entities/field-metadata.entity.ts +3 -0
  126. package/src/entities/setting.entity.ts +8 -46
  127. package/src/index.ts +10 -2
  128. package/src/seeders/module-metadata-seeder.service.ts +1 -19
  129. package/src/seeders/seed-data/solid-core-metadata.json +526 -177
  130. package/src/services/authentication.service.ts +8 -6
  131. package/src/services/chatter-message-details.service.ts +34 -0
  132. package/src/services/chatter-message.service.ts +301 -0
  133. package/src/services/crud.service.ts +10 -2
  134. package/src/services/setting.service.ts +163 -14
  135. package/src/services/user-context.service.ts +31 -0
  136. package/src/solid-core.module.ts +18 -3
  137. package/src/subscribers/audit.subscriber.ts +73 -0
@@ -39,6 +39,7 @@ import {
39
39
  RegistrationValidationSource,
40
40
  TransactionalRegistrationValidationSource
41
41
  } from "../constants";
42
+ import { SettingService } from './setting.service';
42
43
  import { CreateUserDto } from 'src/dtos/create-user.dto';
43
44
 
44
45
  enum LoginProvider {
@@ -71,6 +72,7 @@ export class AuthenticationService {
71
72
  private readonly mailService: SMTPEMailService,
72
73
  private readonly smsService: Msg91OTPService,
73
74
  private readonly eventEmitter: EventEmitter2,
75
+ private readonly settingService: SettingService,
74
76
  ) { }
75
77
 
76
78
  async resolveUser(username: string, email: string) {
@@ -108,7 +110,7 @@ export class AuthenticationService {
108
110
 
109
111
  async signUp(signUpDto: SignUpDto, activeUser: ActiveUserData = null): Promise<User> {
110
112
  // If public registrations are disabled and no activeUser is present when invoking signUp then we throw an exception.
111
- if (!this.iamConfiguration.allowPublicRegistration && !activeUser) {
113
+ if (!(await this.settingService.getConfigValue('allowPublicRegistration')) && !activeUser) {
112
114
  throw new BadRequestException('Public registrations are disabled.');
113
115
  }
114
116
 
@@ -278,7 +280,7 @@ export class AuthenticationService {
278
280
  user = this.createUser(signUpDto);
279
281
  this.populateVerificationTokens(finalRegistrationVerificationSources, user);
280
282
  await this.userRepository.save(user);
281
- await this.userService.addRoleToUser(user.username, this.iamConfiguration.defaultRole);
283
+ await this.userService.addRoleToUser(user.username, await this.settingService.getConfigValue('defaultRole'));
282
284
  }
283
285
  else {
284
286
  this.populateVerificationTokens(finalRegistrationVerificationSources, user);
@@ -397,7 +399,7 @@ export class AuthenticationService {
397
399
  user.emailVerifiedOnRegistrationAt = new Date();
398
400
  user.emailVerificationTokenOnRegistration = null;
399
401
  user.emailVerificationTokenOnRegistrationExpiresAt = null;
400
- user.active = this.iamConfiguration.activateUserOnRegistration && this.areRegistrationValidationSourcesVerified(user);
402
+ user.active = await this.settingService.getConfigValue('activateUserOnRegistration') && this.areRegistrationValidationSourcesVerified(user);
401
403
  const savedUser: User = await this.userRepository.save(user);
402
404
  this.triggerRegistrationEvent(savedUser);
403
405
  return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
@@ -419,7 +421,7 @@ export class AuthenticationService {
419
421
  user.mobileVerifiedOnRegistrationAt = new Date();
420
422
  user.mobileVerificationTokenOnRegistration = null;
421
423
  user.mobileVerificationTokenOnRegistrationExpiresAt = null;
422
- user.active = this.iamConfiguration.activateUserOnRegistration && this.areRegistrationValidationSourcesVerified(user);
424
+ user.active = await this.settingService.getConfigValue('activateUserOnRegistration') && this.areRegistrationValidationSourcesVerified(user);
423
425
  const savedUser: User = await this.userRepository.save(user);
424
426
  this.triggerRegistrationEvent(savedUser);
425
427
  return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
@@ -976,8 +978,8 @@ export class AuthenticationService {
976
978
 
977
979
  }
978
980
 
979
- private isPasswordlessRegistrationEnabled() {
980
- return this.iamConfiguration.passwordlessRegistration;
981
+ private async isPasswordlessRegistrationEnabled() {
982
+ return this.settingService.getConfigValue('passwordlessRegistration');
981
983
  }
982
984
 
983
985
  //FIXME - Pending implementation
@@ -0,0 +1,34 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
+ import { EntityManager, Repository, In } from 'typeorm';
5
+
6
+ import { CRUDService } from 'src/services/crud.service';
7
+ import { ModelMetadataService } from 'src/services/model-metadata.service';
8
+ import { ModuleMetadataService } from 'src/services/module-metadata.service';
9
+ import { ConfigService } from '@nestjs/config';
10
+ import { FileService } from 'src/services/file.service';
11
+ import { CrudHelperService } from 'src/services/crud-helper.service';
12
+
13
+ import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
14
+ import { UserContextService } from './user-context.service';
15
+
16
+ @Injectable()
17
+ export class ChatterMessageDetailsService extends CRUDService<ChatterMessageDetails>{
18
+ constructor(
19
+ readonly modelMetadataService: ModelMetadataService,
20
+ readonly moduleMetadataService: ModuleMetadataService,
21
+ readonly configService: ConfigService,
22
+ readonly fileService: FileService,
23
+ readonly discoveryService: DiscoveryService,
24
+ readonly crudHelperService: CrudHelperService,
25
+ @InjectEntityManager()
26
+ readonly entityManager: EntityManager,
27
+ @InjectRepository(ChatterMessageDetails, 'default')
28
+ readonly repo: Repository<ChatterMessageDetails>,
29
+ readonly moduleRef: ModuleRef,
30
+ readonly userContextService: UserContextService
31
+ ) {
32
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'chatterMessageDetails', 'solid-core', moduleRef, userContextService);
33
+ }
34
+ }
@@ -0,0 +1,301 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
+ import { EntityManager, Repository, EntityMetadata } from 'typeorm';
5
+
6
+ import { CRUDService } from 'src/services/crud.service';
7
+ import { ModelMetadataService } from 'src/services/model-metadata.service';
8
+ import { ModuleMetadataService } from 'src/services/module-metadata.service';
9
+ import { ConfigService } from '@nestjs/config';
10
+ import { FileService } from 'src/services/file.service';
11
+ import { CrudHelperService } from 'src/services/crud-helper.service';
12
+ import { PostChatterMessageDto } from 'src/dtos/post-chatter-message.dto';
13
+ import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
14
+ import { ChatterMessage } from '../entities/chatter-message.entity';
15
+ import { getMediaStorageProvider } from './mediaStorageProviders';
16
+ import { MediaStorageProviderType } from '../dtos/create-media-storage-provider-metadata.dto';
17
+ import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
18
+ import { ModelMetadata } from 'src/entities/model-metadata.entity';
19
+ import { UserContextService } from './user-context.service';
20
+ @Injectable()
21
+ export class ChatterMessageService extends CRUDService<ChatterMessage>{
22
+ constructor(
23
+ readonly modelMetadataService: ModelMetadataService,
24
+ readonly moduleMetadataService: ModuleMetadataService,
25
+ readonly configService: ConfigService,
26
+ readonly fileService: FileService,
27
+ readonly discoveryService: DiscoveryService,
28
+ readonly crudHelperService: CrudHelperService,
29
+ @InjectEntityManager()
30
+ readonly entityManager: EntityManager,
31
+ @InjectRepository(ChatterMessage, 'default')
32
+ readonly repo: Repository<ChatterMessage>,
33
+ @InjectRepository(ChatterMessageDetails, 'default')
34
+ readonly chatterMessageDetailsRepo: Repository<ChatterMessageDetails>,
35
+ readonly moduleRef: ModuleRef,
36
+ @InjectRepository(ModelMetadata)
37
+ private readonly modelMetadataRepo: Repository<ModelMetadata>,
38
+ readonly userContextService: UserContextService
39
+ ) {
40
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'chatterMessage', 'solid-core', moduleRef, userContextService);
41
+ }
42
+
43
+ async postMessage(postDto: PostChatterMessageDto, solidRequestContext: SolidRequestContextDto, files: Express.Multer.File[] = []) {
44
+ const chatterMessage = new ChatterMessage();
45
+ chatterMessage.messageType = 'custom';
46
+ chatterMessage.messageSubType = postDto.messageSubType || 'general';
47
+ chatterMessage.messageBody = postDto.messageBody;
48
+ chatterMessage.coModelEntityId = postDto.coModelEntityId;
49
+ chatterMessage.coModelName = postDto.coModelName;
50
+
51
+ const userId = typeof solidRequestContext.activeUser === 'object'
52
+ ? solidRequestContext.activeUser.sub
53
+ : solidRequestContext.activeUser;
54
+
55
+ chatterMessage.user = { id: userId } as any;
56
+
57
+ const savedMessage = await this.repo.save(chatterMessage);
58
+
59
+ if (files && files.length > 0) {
60
+ const model = await this.modelMetadataService.findOneBySingularName('chatterMessage', {
61
+ fields: {
62
+ model: true,
63
+ mediaStorageProvider: true,
64
+ },
65
+ module: true,
66
+ });
67
+
68
+ const mediaFields = model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple');
69
+
70
+ for (const mediaField of mediaFields) {
71
+ const media = files.filter(multerFile => multerFile.fieldname === mediaField.name);
72
+ if (media.length > 0) {
73
+ const storageProviderMetadata = mediaField.mediaStorageProvider;
74
+ const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
75
+ const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
76
+ await storageProvider.store(media, savedMessage, mediaField);
77
+ }
78
+ }
79
+ }
80
+
81
+ return savedMessage;
82
+ }
83
+
84
+ async postAuditMessageOnInsert(entity: any, metadata: EntityMetadata, activeUser: any, messageQueue: boolean = false) {
85
+ const model = await this.modelMetadataRepo.findOne({
86
+ where: {
87
+ displayName: metadata.name
88
+ },
89
+ relations: {
90
+ fields: true,
91
+ module: true
92
+ }
93
+ });
94
+
95
+ if (!model || !model.enableAuditTracking) {
96
+ return;
97
+ }
98
+
99
+ const auditFields = model.fields.filter(field =>
100
+ field.enableAuditTracking &&
101
+ !['oneToMany', 'mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type)
102
+ );
103
+
104
+ const chatterMessage = new ChatterMessage();
105
+ chatterMessage.messageType = 'audit';
106
+ chatterMessage.messageSubType = 'insert';
107
+ chatterMessage.coModelEntityId = entity.id;
108
+ chatterMessage.coModelName = model.singularName;
109
+ chatterMessage.messageBody = `New ${model.displayName} created`;
110
+
111
+ if (activeUser) {
112
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
113
+ chatterMessage.user = { id: userId } as any;
114
+ } else {
115
+ chatterMessage.user = null;
116
+ }
117
+
118
+ const savedMessage = await this.repo.save(chatterMessage);
119
+
120
+ for (const field of auditFields) {
121
+ const fieldValue = entity[field.name];
122
+ if (fieldValue !== undefined && fieldValue !== null) {
123
+ const messageDetail = new ChatterMessageDetails();
124
+ messageDetail.chatterMessage = savedMessage;
125
+ messageDetail.fieldName = field.name;
126
+ messageDetail.oldValue = null;
127
+ messageDetail.oldValueDisplay = null;
128
+ messageDetail.newValue = this.formatFieldValue(field, fieldValue);
129
+ messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, fieldValue);
130
+ await this.chatterMessageDetailsRepo.save(messageDetail);
131
+ }
132
+ }
133
+ }
134
+
135
+ async postAuditMessageOnUpdate(entity: any, metadata: EntityMetadata, databaseEntity: any, activeUser: any, messageQueue: boolean = false) {
136
+ const model = await this.modelMetadataRepo.findOne({
137
+ where: {
138
+ displayName: metadata.name
139
+ },
140
+ relations: {
141
+ fields: true,
142
+ module: true
143
+ }
144
+ });
145
+
146
+ if (!model || !model.enableAuditTracking) {
147
+ return;
148
+ }
149
+
150
+ const auditFields = model.fields.filter(field =>
151
+ field.enableAuditTracking &&
152
+ !['oneToMany', 'mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type)
153
+ );
154
+
155
+ const relationFields = auditFields.filter(field =>
156
+ field.type === 'relation'
157
+ );
158
+ if (relationFields.length > 0) {
159
+ const populatedEntity = await this.entityManager.findOne(metadata.target, {
160
+ where: { id: databaseEntity.id },
161
+ relations: relationFields.map(field => field.name)
162
+ });
163
+ if (populatedEntity) {
164
+ databaseEntity = populatedEntity;
165
+ }
166
+ }
167
+
168
+ const changedFields = auditFields.filter(field => {
169
+ const newValue = entity[field.name];
170
+ const oldValue = databaseEntity[field.name];
171
+ return this.hasValueChanged(newValue, oldValue);
172
+ });
173
+
174
+ if (changedFields.length === 0) {
175
+ return;
176
+ }
177
+
178
+ const chatterMessage = new ChatterMessage();
179
+ chatterMessage.messageType = 'audit';
180
+ chatterMessage.messageSubType = 'update';
181
+ chatterMessage.coModelEntityId = entity.id;
182
+ chatterMessage.coModelName = model.singularName;
183
+ chatterMessage.messageBody = `${model.displayName} updated`;
184
+
185
+ if (activeUser) {
186
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
187
+ chatterMessage.user = { id: userId } as any;
188
+ } else {
189
+ chatterMessage.user = null;
190
+ }
191
+
192
+ const savedMessage = await this.repo.save(chatterMessage);
193
+
194
+ for (const field of changedFields) {
195
+ const messageDetail = new ChatterMessageDetails();
196
+ messageDetail.chatterMessage = savedMessage;
197
+ messageDetail.fieldName = field.name;
198
+ messageDetail.oldValue = this.formatFieldValue(field, databaseEntity[field.name]);
199
+ messageDetail.newValue = this.formatFieldValue(field, entity[field.name]);
200
+ messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, databaseEntity[field.name]);
201
+ messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, entity[field.name]);
202
+ await this.chatterMessageDetailsRepo.save(messageDetail);
203
+ }
204
+ }
205
+
206
+ async postAuditMessageOnDelete(entity: any, metadata: EntityMetadata, databaseEntity: any, activeUser: any, messageQueue: boolean = false) {
207
+ const model = await this.modelMetadataRepo.findOne({
208
+ where: {
209
+ displayName: metadata.name
210
+ },
211
+ relations: {
212
+ fields: true,
213
+ module: true
214
+ }
215
+ });
216
+
217
+ if (!model || !model.enableAuditTracking) {
218
+ return;
219
+ }
220
+
221
+ const chatterMessage = new ChatterMessage();
222
+ chatterMessage.messageType = 'audit';
223
+ chatterMessage.messageSubType = 'delete';
224
+ chatterMessage.coModelEntityId = databaseEntity.id;
225
+ chatterMessage.coModelName = model.singularName;
226
+ chatterMessage.messageBody = `${model.displayName} deleted`;
227
+
228
+ if (activeUser) {
229
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
230
+ chatterMessage.user = { id: userId } as any;
231
+ } else {
232
+ chatterMessage.user = null;
233
+ }
234
+
235
+ await this.repo.save(chatterMessage);
236
+ }
237
+
238
+ private formatFieldValue(field: any, value: any): string {
239
+ if (value === null || value === undefined) {
240
+ return '';
241
+ }
242
+
243
+ if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
244
+ return `${value}`;
245
+ }
246
+
247
+ if (field.type === 'relation') {
248
+ if (field.relationType === "many-to-one") {
249
+ return value.id;
250
+ }
251
+ if (field.relationType === 'manyToMany') {
252
+ return value.map(item => item.id).join(', ');
253
+ }
254
+ }
255
+
256
+
257
+ return value.toString();
258
+ }
259
+
260
+ private formatFieldValueDisplay(field: any, value: any): string {
261
+ if (value === null || value === undefined) {
262
+ return '';
263
+ }
264
+
265
+ if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
266
+ return `${value}`;
267
+ }
268
+
269
+ if (field.type === 'relation') {
270
+ if (field.relationType === "many-to-one") {
271
+ return value.name;
272
+ }
273
+ if (field.relationType === 'many-toMany') {
274
+ return value.map(item => item.name).join(', ');
275
+ }
276
+ }
277
+
278
+
279
+ return value.toString();
280
+ }
281
+
282
+ private hasValueChanged(newValue: any, oldValue: any): boolean {
283
+ if (newValue === oldValue) {
284
+ return false;
285
+ }
286
+
287
+ if (newValue === null && oldValue === null) {
288
+ return false;
289
+ }
290
+
291
+ if (newValue === undefined && oldValue === undefined) {
292
+ return false;
293
+ }
294
+
295
+ if (Array.isArray(newValue) && Array.isArray(oldValue)) {
296
+ return JSON.stringify(newValue) !== JSON.stringify(oldValue);
297
+ }
298
+
299
+ return true;
300
+ }
301
+ }
@@ -34,7 +34,8 @@ import { FileService } from "./file.service";
34
34
  import { getMediaStorageProvider } from "./mediaStorageProviders";
35
35
  import { ModelMetadataService } from "./model-metadata.service";
36
36
  import { ModuleMetadataService } from "./module-metadata.service";
37
-
37
+ import { UserContextService } from "./user-context.service";
38
+ import { Optional } from "@nestjs/common";
38
39
  const DEFAULT_LIMIT = 10;
39
40
  const DEFAULT_OFFSET = 0;
40
41
  export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
@@ -50,7 +51,8 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
50
51
  readonly repo: Repository<T>,
51
52
  readonly modelName: string,
52
53
  readonly moduleName: string,
53
- readonly moduleRef: ModuleRef
54
+ readonly moduleRef: ModuleRef,
55
+ @Optional() readonly userContextService?: UserContextService
54
56
  //We can just have the Model Entity here
55
57
  ) { }
56
58
 
@@ -66,6 +68,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
66
68
  // Check wheather user has create permission for model
67
69
  if (solidRequestContext.activeUser) {
68
70
  const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
71
+ if (this.userContextService) {
72
+ this.userContextService.setUser(solidRequestContext.activeUser);
73
+ }
69
74
  if (!hasPermission) {
70
75
  throw new BadRequestException('Forbidden');
71
76
  }
@@ -160,6 +165,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
160
165
  // Check wheather user has update permission for model
161
166
  if (solidRequestContext.activeUser) {
162
167
  const hasPermission = this.crudHelperService.hasUpdatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
168
+ if (this.userContextService) {
169
+ this.userContextService.setUser(solidRequestContext.activeUser);
170
+ }
163
171
  if (!hasPermission) {
164
172
  throw new BadRequestException('Forbidden');
165
173
  }
@@ -38,31 +38,88 @@ export class SettingService extends CRUDService<Setting> {
38
38
  );
39
39
  }
40
40
 
41
- async wrapSettings(): Promise<Record<string, any>> {
42
- const settingsArray: any[] = await this.repo.find();
41
+ async seedDefaultSettings(): Promise<void> {
42
+ const settingsSeederData = {
43
+ allowPublicRegistration: this.iamConfiguration.allowPublicRegistration,
44
+ iamPasswordRegistrationEnabled: this.iamConfiguration.iamPasswordRegistrationEnabled,
45
+ passwordlessRegistration: this.iamConfiguration.passwordlessRegistration,
46
+ activateUserOnRegistration: this.iamConfiguration.activateUserOnRegistration,
47
+ iamGoogleOAuthEnabled: false,
48
+ authPagesLayout: "center",
49
+ authPagesTheme: "light",
50
+ appTitle: process.env.SOLID_APP_NAME || "Solid App",
51
+ appLogo: "",
52
+ appDescription: "",
53
+ appTnc: "",
54
+ appPrivacyPolicy: "",
55
+ defaultRole: this.iamConfiguration.defaultRole,
56
+ shouldQueueEmails: this.commonConfiguration.shouldQueueEmails,
57
+ shouldQueueSms: this.commonConfiguration.shouldQueueSms
58
+ };
43
59
 
44
- if (!settingsArray || settingsArray.length === 0) {
45
- return this.getDefaultSettings();
60
+ const existingSettings = await this.repo.find();
61
+ const existingKeys = new Set(existingSettings.map(s => s.key));
62
+
63
+ const settingsToInsert: Setting[] = [];
64
+ for (const [key, value] of Object.entries(settingsSeederData)) {
65
+ if (!existingKeys.has(key)) {
66
+ const setting = new Setting();
67
+ setting.key = key;
68
+ setting.value = typeof value === 'boolean' ? value.toString() :
69
+ Array.isArray(value) ? value.join(',') :
70
+ value === null || value === undefined ? '' : String(value);
71
+ settingsToInsert.push(setting);
72
+ }
46
73
  }
47
74
 
48
- const settings = settingsArray[0];
75
+ if (settingsToInsert.length > 0) {
76
+ await this.repo.save(settingsToInsert);
77
+ }
78
+ }
49
79
 
80
+ async wrapSettings(): Promise<Record<string, any>> {
81
+ const settingsArray: Setting[] = await this.repo.find();
82
+
83
+ if (!settingsArray || settingsArray.length === 0) {
84
+ return this.getDefaultSettings();
85
+ }
86
+
87
+ const settingsMap: Record<string, any> = {};
88
+ for (const setting of settingsArray) {
89
+ if (setting.key && setting.value !== undefined && setting.value !== null) {
90
+ let value = setting.value;
91
+
92
+ if (value === 'true' || value === 'false') {
93
+ settingsMap[setting.key] = value === 'true';
94
+ }
95
+ else if (!isNaN(Number(value)) && value.trim() !== '') {
96
+ settingsMap[setting.key] = Number(value);
97
+ }
98
+ else if (value.includes(',')) {
99
+ settingsMap[setting.key] = value.split(',').map(item => item.trim());
100
+ }
101
+ else {
102
+ settingsMap[setting.key] = value;
103
+ }
104
+ }
105
+ }
106
+
50
107
  const defaultSettings = this.getDefaultSettings();
51
-
108
+
52
109
  const mergedSettings = Object.keys(defaultSettings).reduce((acc, key) => {
53
- acc[key] = settings[key] !== null && settings[key] !== undefined ? settings[key] : defaultSettings[key];
110
+ acc[key] = settingsMap[key] !== undefined ? settingsMap[key] : defaultSettings[key];
54
111
  return acc;
55
112
  }, {} as Record<string, any>);
56
-
113
+
57
114
  return mergedSettings;
58
115
  }
59
-
116
+
60
117
  private getDefaultSettings(): Record<string, any> {
61
118
  return {
62
- iamAllowPublicRegistration: this.iamConfiguration.allowPublicRegistration,
63
- iamPasswordRegistrationEnabled: false,
64
- iamPasswordLessRegistrationEnabled: this.iamConfiguration.passwordlessRegistration,
65
- iamActivateUserOnRegistration: this.iamConfiguration.activateUserOnRegistration,
119
+ allowPublicRegistration: this.iamConfiguration.allowPublicRegistration,
120
+ iamPasswordRegistrationEnabled: this.iamConfiguration.iamPasswordRegistrationEnabled,
121
+ passwordlessRegistration: this.iamConfiguration.passwordlessRegistration,
122
+ activateUserOnRegistration: this.iamConfiguration.activateUserOnRegistration,
66
123
  iamGoogleOAuthEnabled: false,
67
124
  authPagesLayout: "center",
68
125
  authPagesTheme: "light",
@@ -71,10 +128,102 @@ export class SettingService extends CRUDService<Setting> {
71
128
  appDescription: "",
72
129
  appTnc: "",
73
130
  appPrivacyPolicy: "",
74
- iamDefaultRole: this.iamConfiguration.defaultRole,
131
+ defaultRole: this.iamConfiguration.defaultRole,
75
132
  shouldQueueEmails: this.commonConfiguration.shouldQueueEmails,
76
133
  shouldQueueSms: this.commonConfiguration.shouldQueueSms
77
134
  };
78
135
  }
79
136
 
137
+ async getConfigValue(settingKey: string) {
138
+ try {
139
+ const settingsArray: Setting[] = await this.repo.find();
140
+ const settingEntry = settingsArray.find(setting => setting.key === settingKey);
141
+
142
+ if (settingEntry && settingEntry.value !== null && settingEntry.value !== undefined) {
143
+ const value = settingEntry.value;
144
+
145
+ if (value === 'true' || value === 'false') {
146
+ return value === 'true';
147
+ }
148
+ else if (!isNaN(Number(value)) && value.trim() !== '') {
149
+ return Number(value);
150
+ }
151
+ else if (value.includes(',')) {
152
+ return value.split(',').map(item => item.trim());
153
+ }
154
+ else {
155
+ return value;
156
+ }
157
+ }
158
+
159
+ const defaultSettings = this.getDefaultSettings();
160
+ return defaultSettings[settingKey];
161
+ } catch (error) {
162
+ const defaultSettings = this.getDefaultSettings();
163
+ return defaultSettings[settingKey];
164
+ }
165
+ }
166
+
167
+ async updateSettings(settings: Record<string, any>): Promise<Setting[]> {
168
+ const existingSettings = await this.repo.find();
169
+ const existingKeys = new Set(existingSettings.map(s => s.key));
170
+
171
+ const settingsToUpdate: Setting[] = [];
172
+ const settingsToCreate: Setting[] = [];
173
+
174
+ for (const [key, value] of Object.entries(settings)) {
175
+ const stringValue = typeof value === 'boolean' ? value.toString() :
176
+ Array.isArray(value) ? value.join(',') :
177
+ value === null || value === undefined ? '' : String(value);
178
+
179
+ if (existingKeys.has(key)) {
180
+ const existingSetting = existingSettings.find(s => s.key === key);
181
+ if (existingSetting) {
182
+ existingSetting.value = stringValue;
183
+ settingsToUpdate.push(existingSetting);
184
+ }
185
+ } else {
186
+ const newSetting = new Setting();
187
+ newSetting.key = key;
188
+ newSetting.value = stringValue;
189
+ settingsToCreate.push(newSetting);
190
+ }
191
+ }
192
+
193
+ if (settingsToUpdate.length > 0) {
194
+ await this.repo.save(settingsToUpdate);
195
+ }
196
+
197
+ if (settingsToCreate.length > 0) {
198
+ await this.repo.save(settingsToCreate);
199
+ }
200
+
201
+ return [...settingsToUpdate, ...settingsToCreate];
202
+ }
203
+
204
+ async getAllSettings(): Promise<Record<string, any>> {
205
+ const settingsArray = await this.repo.find();
206
+ const settingsMap: Record<string, any> = {};
207
+
208
+ for (const setting of settingsArray) {
209
+ if (setting.key && setting.value !== undefined && setting.value !== null) {
210
+ const value = setting.value;
211
+
212
+ if (value === 'true' || value === 'false') {
213
+ settingsMap[setting.key] = value === 'true';
214
+ }
215
+ else if (!isNaN(Number(value)) && value.trim() !== '') {
216
+ settingsMap[setting.key] = Number(value);
217
+ }
218
+ else if (value.includes(',')) {
219
+ settingsMap[setting.key] = value.split(',').map(item => item.trim());
220
+ }
221
+ else {
222
+ settingsMap[setting.key] = value;
223
+ }
224
+ }
225
+ }
226
+
227
+ return settingsMap;
228
+ }
80
229
  }
@@ -0,0 +1,31 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { ActiveUserData } from '../interfaces/active-user-data.interface';
3
+
4
+ @Injectable()
5
+ export class UserContextService {
6
+ private readonly logger = new Logger(UserContextService.name);
7
+ private static currentUser: any = null;
8
+
9
+ setUser(user: any) {
10
+ this.logger.debug(`Setting user: ${JSON.stringify(user)}`);
11
+ UserContextService.currentUser = user;
12
+ }
13
+
14
+ getUser() {
15
+ return UserContextService.currentUser;
16
+ }
17
+
18
+ runWithUser<T>(user: any, callback: () => T): T {
19
+ const previousUser = this.getUser();
20
+ try {
21
+ this.setUser(user);
22
+ return callback();
23
+ } finally {
24
+ this.setUser(previousUser);
25
+ }
26
+ }
27
+
28
+ getCurrentUser(): ActiveUserData | null {
29
+ return this.getUser();
30
+ }
31
+ }