@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.
- package/dist/config/iam.config.d.ts +2 -0
- package/dist/config/iam.config.d.ts.map +1 -1
- package/dist/config/iam.config.js +1 -0
- package/dist/config/iam.config.js.map +1 -1
- package/dist/controllers/chatter-message-details.controller.d.ts +41 -0
- package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -0
- package/dist/controllers/chatter-message-details.controller.js +179 -0
- package/dist/controllers/chatter-message-details.controller.js.map +1 -0
- package/dist/controllers/chatter-message.controller.d.ts +44 -0
- package/dist/controllers/chatter-message.controller.d.ts.map +1 -0
- package/dist/controllers/chatter-message.controller.js +199 -0
- package/dist/controllers/chatter-message.controller.js.map +1 -0
- package/dist/controllers/setting.controller.d.ts +3 -0
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.js +23 -0
- package/dist/controllers/setting.controller.js.map +1 -1
- package/dist/dtos/create-chatter-message-details.dto.d.ts +10 -0
- package/dist/dtos/create-chatter-message-details.dto.d.ts.map +1 -0
- package/dist/dtos/create-chatter-message-details.dto.js +66 -0
- package/dist/dtos/create-chatter-message-details.dto.js.map +1 -0
- package/dist/dtos/create-chatter-message.dto.d.ts +10 -0
- package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -0
- package/dist/dtos/create-chatter-message.dto.js +65 -0
- package/dist/dtos/create-chatter-message.dto.js.map +1 -0
- package/dist/dtos/create-field-metadata.dto.d.ts +1 -0
- package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-field-metadata.dto.js +6 -1
- package/dist/dtos/create-field-metadata.dto.js.map +1 -1
- package/dist/dtos/create-setting.dto.d.ts +2 -15
- package/dist/dtos/create-setting.dto.d.ts.map +1 -1
- package/dist/dtos/create-setting.dto.js +6 -77
- package/dist/dtos/create-setting.dto.js.map +1 -1
- package/dist/dtos/post-chatter-message.dto.d.ts +7 -0
- package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -0
- package/dist/dtos/post-chatter-message.dto.js +41 -0
- package/dist/dtos/post-chatter-message.dto.js.map +1 -0
- package/dist/dtos/update-chatter-message-details.dto.d.ts +11 -0
- package/dist/dtos/update-chatter-message-details.dto.d.ts.map +1 -0
- package/dist/dtos/update-chatter-message-details.dto.js +70 -0
- package/dist/dtos/update-chatter-message-details.dto.js.map +1 -0
- package/dist/dtos/update-chatter-message.dto.d.ts +11 -0
- package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -0
- package/dist/dtos/update-chatter-message.dto.js +74 -0
- package/dist/dtos/update-chatter-message.dto.js.map +1 -0
- package/dist/dtos/update-setting.dto.d.ts +2 -15
- package/dist/dtos/update-setting.dto.d.ts.map +1 -1
- package/dist/dtos/update-setting.dto.js +6 -77
- package/dist/dtos/update-setting.dto.js.map +1 -1
- package/dist/dtos/update-settings.dto.d.ts +4 -0
- package/dist/dtos/update-settings.dto.d.ts.map +1 -0
- package/dist/dtos/update-settings.dto.js +26 -0
- package/dist/dtos/update-settings.dto.js.map +1 -0
- package/dist/entities/chatter-message-details.entity.d.ts +11 -0
- package/dist/entities/chatter-message-details.entity.d.ts.map +1 -0
- package/dist/entities/chatter-message-details.entity.js +52 -0
- package/dist/entities/chatter-message-details.entity.js.map +1 -0
- package/dist/entities/chatter-message.entity.d.ts +11 -0
- package/dist/entities/chatter-message.entity.d.ts.map +1 -0
- package/dist/entities/chatter-message.entity.js +53 -0
- package/dist/entities/chatter-message.entity.js.map +1 -0
- package/dist/entities/field-metadata.entity.d.ts +1 -0
- package/dist/entities/field-metadata.entity.d.ts.map +1 -1
- package/dist/entities/field-metadata.entity.js +5 -1
- package/dist/entities/field-metadata.entity.js.map +1 -1
- package/dist/entities/setting.entity.d.ts +2 -15
- package/dist/entities/setting.entity.d.ts.map +1 -1
- package/dist/entities/setting.entity.js +4 -65
- package/dist/entities/setting.entity.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +1 -18
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +525 -176
- package/dist/services/authentication.service.d.ts +3 -1
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +11 -8
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message-details.service.d.ts +24 -0
- package/dist/services/chatter-message-details.service.d.ts.map +1 -0
- package/dist/services/chatter-message-details.service.js +59 -0
- package/dist/services/chatter-message-details.service.js.map +1 -0
- package/dist/services/chatter-message.service.d.ts +37 -0
- package/dist/services/chatter-message.service.d.ts.map +1 -0
- package/dist/services/chatter-message.service.js +279 -0
- package/dist/services/chatter-message.service.js.map +1 -0
- package/dist/services/crud.service.d.ts +3 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +43 -3
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/setting.service.d.ts +4 -0
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +139 -7
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/user-context.service.d.ts +10 -0
- package/dist/services/user-context.service.d.ts.map +1 -0
- package/dist/services/user-context.service.js +42 -0
- package/dist/services/user-context.service.js.map +1 -0
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +18 -2
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +17 -0
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -0
- package/dist/subscribers/audit.subscriber.js +83 -0
- package/dist/subscribers/audit.subscriber.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config/iam.config.ts +1 -0
- package/src/controllers/chatter-message-details.controller.ts +92 -0
- package/src/controllers/chatter-message.controller.ts +105 -0
- package/src/controllers/setting.controller.ts +13 -1
- package/src/dtos/create-chatter-message-details.dto.ts +40 -0
- package/src/dtos/create-chatter-message.dto.ts +34 -0
- package/src/dtos/create-field-metadata.dto.ts +4 -0
- package/src/dtos/create-setting.dto.ts +10 -59
- package/src/dtos/post-chatter-message.dto.ts +19 -0
- package/src/dtos/update-chatter-message-details.dto.ts +43 -0
- package/src/dtos/update-chatter-message.dto.ts +41 -0
- package/src/dtos/update-setting.dto.ts +10 -60
- package/src/dtos/update-settings.dto.ts +7 -0
- package/src/entities/chatter-message-details.entity.ts +25 -0
- package/src/entities/chatter-message.entity.ts +22 -0
- package/src/entities/field-metadata.entity.ts +3 -0
- package/src/entities/setting.entity.ts +8 -46
- package/src/index.ts +10 -2
- package/src/seeders/module-metadata-seeder.service.ts +1 -19
- package/src/seeders/seed-data/solid-core-metadata.json +526 -177
- package/src/services/authentication.service.ts +8 -6
- package/src/services/chatter-message-details.service.ts +34 -0
- package/src/services/chatter-message.service.ts +301 -0
- package/src/services/crud.service.ts +10 -2
- package/src/services/setting.service.ts +163 -14
- package/src/services/user-context.service.ts +31 -0
- package/src/solid-core.module.ts +18 -3
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
42
|
-
const
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
63
|
-
iamPasswordRegistrationEnabled:
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
+
}
|