diff --git a/apps/sample/src/listener/listener.service.ts b/apps/sample/src/listener/listener.service.ts index f1738aa..4867af9 100755 --- a/apps/sample/src/listener/listener.service.ts +++ b/apps/sample/src/listener/listener.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import { EVENTS } from '@rumsan/user'; +import { EVENTS } from '@rumsan/extensions/constants'; import { DevService } from '../utils/develop.service'; import { EmailService } from '../utils/email.service'; diff --git a/apps/sample/src/user/user.controller.ts b/apps/sample/src/user/user.controller.ts index 06c96f9..064413c 100755 --- a/apps/sample/src/user/user.controller.ts +++ b/apps/sample/src/user/user.controller.ts @@ -1,12 +1,8 @@ import { Controller, Get, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { - ACTIONS, - AbilitiesGuard, - CheckAbilities, - JwtGuard, - SUBJECTS, -} from '@rumsan/user'; +import { ACTIONS, SUBJECTS } from '@rumsan/extensions/constants'; +import { JwtGuard } from '@rumsan/extensions/guards'; +import { AbilitiesGuard, CheckAbilities } from '@rumsan/user'; import { APP } from '../constants'; import { AppUsersService } from './user.service'; diff --git a/libs/user/src/lib/constants/errors.ts b/libs/extensions/src/constants/errors.ts similarity index 83% rename from libs/user/src/lib/constants/errors.ts rename to libs/extensions/src/constants/errors.ts index 6d10664..c882823 100755 --- a/libs/user/src/lib/constants/errors.ts +++ b/libs/extensions/src/constants/errors.ts @@ -1,15 +1,15 @@ -import { RSError } from '@rumsan/extensions/exceptions'; +import { RSError } from '../exceptions'; export function RSE( message: string, - name: string = 'UNKNOWN', - httpCode: number = 500, + name = 'UNKNOWN', + httpCode = 500, meta?: any, ) { return new RSError({ message, name, httpCode, srcModule: 'RS_USER', meta }); } -export const ERRORS = { +export const RSERRORS = { ROLE_NAME_INVALID: RSE( 'Invalid characters in role name.', 'ROLE_NAME_INVALID', diff --git a/libs/user/src/lib/constants/events.ts b/libs/extensions/src/constants/events.ts similarity index 100% rename from libs/user/src/lib/constants/events.ts rename to libs/extensions/src/constants/events.ts diff --git a/libs/extensions/src/constants/index.ts b/libs/extensions/src/constants/index.ts index fe0ec6f..893e4cd 100644 --- a/libs/extensions/src/constants/index.ts +++ b/libs/extensions/src/constants/index.ts @@ -6,3 +6,33 @@ export const ConstantControllers: { [key: string]: ControllerFunction } = { }; export const PROTECTED_SETTINGS = 'PROTECTED'; + +export { RSE, RSERRORS } from './errors'; +export * from './events'; + +//For Ability Guard +export const ACTIONS = { + MANAGE: 'manage', + CREATE: 'create', + UPDATE: 'update', + DELETE: 'delete', + READ: 'read', +}; + +// For Ability Guard +export const SUBJECTS = { + ALL: 'all', + PUBLIC: 'public', + USER: 'user', + ROLE: 'role', +}; + +export const APP = { + JWT_BEARER: 'JWT', +}; + +export const CLIENT_TOKEN_LIFETIME = 600; + +export const IS_PUBLIC_KEY = 'isPublic'; + +export const NOT_AVAILABLE = 'N/A'; diff --git a/libs/user/src/lib/auths/decorator/current-user.decorator.ts b/libs/extensions/src/decorators/currentUser.decorator.ts old mode 100755 new mode 100644 similarity index 84% rename from libs/user/src/lib/auths/decorator/current-user.decorator.ts rename to libs/extensions/src/decorators/currentUser.decorator.ts index e89ec71..6ad110a --- a/libs/user/src/lib/auths/decorator/current-user.decorator.ts +++ b/libs/extensions/src/decorators/currentUser.decorator.ts @@ -1,5 +1,5 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; -import { CurrentUserInterface } from '../interfaces/current-user.interface'; +import { CurrentUserInterface } from '@rumsan/sdk/interfaces'; export const CurrentUser = createParamDecorator( (data: undefined, ctx: ExecutionContext): CurrentUserInterface => { diff --git a/libs/extensions/src/decorators/index.ts b/libs/extensions/src/decorators/index.ts index ba0d526..9d51029 100644 --- a/libs/extensions/src/decorators/index.ts +++ b/libs/extensions/src/decorators/index.ts @@ -1,2 +1,4 @@ +export * from './currentUser.decorator'; export * from './param.uuid.decorator'; +export * from './public.decorator'; export * from './request.decorator'; diff --git a/libs/extensions/src/decorators/public.decorator.ts b/libs/extensions/src/decorators/public.decorator.ts new file mode 100644 index 0000000..215a524 --- /dev/null +++ b/libs/extensions/src/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; +import { IS_PUBLIC_KEY } from '../constants'; + +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/libs/extensions/src/dtos/settingDto/createSetting.dto.ts b/libs/extensions/src/dtos/settingDto/createSetting.dto.ts index d462cd4..d3b6f67 100644 --- a/libs/extensions/src/dtos/settingDto/createSetting.dto.ts +++ b/libs/extensions/src/dtos/settingDto/createSetting.dto.ts @@ -55,6 +55,19 @@ export class CreateSettingDto { @IsOptional() @IsBoolean() isPrivate?: boolean; + + @IsString() + @IsOptional() + createdBy?: string; + + @IsString() + @IsOptional() + updatedBy?: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + sessionId: string; } export class UpdateSettingDto extends PickType(CreateSettingDto, ['value']) {} diff --git a/libs/extensions/src/dtos/settingDto/updateSetting.dto.ts b/libs/extensions/src/dtos/settingDto/updateSetting.dto.ts index 3099926..b8bf013 100644 --- a/libs/extensions/src/dtos/settingDto/updateSetting.dto.ts +++ b/libs/extensions/src/dtos/settingDto/updateSetting.dto.ts @@ -45,4 +45,12 @@ export class UpdateSettngsDto { }) @IsBoolean() isReadOnly!: false; + + @IsOptional() + @IsNotEmpty() + sessionId?: string; + + @IsOptional() + @IsNotEmpty() + updatedBy?: string; } diff --git a/libs/user/src/lib/auths/guard/index.ts b/libs/extensions/src/guards/index.ts old mode 100755 new mode 100644 similarity index 100% rename from libs/user/src/lib/auths/guard/index.ts rename to libs/extensions/src/guards/index.ts diff --git a/libs/extensions/src/guards/jwt.guard.ts b/libs/extensions/src/guards/jwt.guard.ts new file mode 100644 index 0000000..4dc198c --- /dev/null +++ b/libs/extensions/src/guards/jwt.guard.ts @@ -0,0 +1,23 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { Observable } from 'rxjs'; +import { IS_PUBLIC_KEY } from '../constants'; + +@Injectable() +export class JwtGuard extends AuthGuard('jwt') implements CanActivate { + constructor(private reflector: Reflector) { + super(); + } + override canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const isPublic = this.reflector.get( + IS_PUBLIC_KEY, + context.getHandler(), + ); + + if (isPublic) return true; + return super.canActivate(context); + } +} diff --git a/libs/extensions/src/settings/settings.controller.ts b/libs/extensions/src/settings/settings.controller.ts index ef9543e..d9b6dcb 100644 --- a/libs/extensions/src/settings/settings.controller.ts +++ b/libs/extensions/src/settings/settings.controller.ts @@ -6,33 +6,53 @@ import { Patch, Post, Query, + UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { CUI } from '@rumsan/sdk/interfaces'; +import { APP } from '../constants'; +import { Public } from '../decorators'; +import { CurrentUser } from '../decorators/currentUser.decorator'; import { CreateSettingDto, ListSettingDto, UpdateSettngsDto } from '../dtos'; +import { JwtGuard } from '../guards'; import { SettingsService } from './settings.service'; @Controller('settings') @ApiTags('Settings') +@ApiBearerAuth(APP.JWT_BEARER) +@UseGuards(JwtGuard) export class SettingsController { constructor(private readonly settingsService: SettingsService) {} @Get('') + @Public() list(@Query() query: ListSettingDto) { return this.settingsService.list(query); } @Post('') - create(@Body() createSettingDto: CreateSettingDto) { + create(@Body() createSettingDto: CreateSettingDto, @CurrentUser() cu: CUI) { + if (cu.name) { + createSettingDto.createdBy = cu.name; + } + createSettingDto.sessionId = cu.sessionId; return this.settingsService.create(createSettingDto); } @Get(':name') + @Public() get(@Param('name') name: string) { return this.settingsService.getByName(name); } @Patch(':name') - udpdate(@Param('name') name: string, @Body() @Body() dto: UpdateSettngsDto) { + update( + @Param('name') name: string, + @Body() dto: UpdateSettngsDto, + @CurrentUser() cu: CUI, + ) { + dto.sessionId = cu.sessionId; + if (cu.name) dto.updatedBy = cu?.name; return this.settingsService.update(name, dto); } } diff --git a/libs/extensions/src/settings/settings.service.spec.ts b/libs/extensions/src/settings/settings.service.spec.ts index 9f5bca4..b80781d 100644 --- a/libs/extensions/src/settings/settings.service.spec.ts +++ b/libs/extensions/src/settings/settings.service.spec.ts @@ -1,63 +1,101 @@ import { SettingsService } from './settings.service'; describe('SettingsService', () => { - let service: SettingsService; - let prismaMock: any; - - beforeEach(() => { - prismaMock = { - setting: { - findMany: jest.fn(), - findUnique: jest.fn(), - }, - }; - service = new SettingsService(prismaMock); - }); + let service: SettingsService; + let prismaMock: any; - it('should return public settings', async () => { - const mockSettings = [ - { name: 'SMTP', value: { "HOST": "smtp.gmail.com", "PORT": 465, "SECURE": true, "PASSWORD": "test", "USERNAME": "test" }, isPrivate: false }, - { name: 'SMTP1', value: { "HOST": "smtp.gmail.com", "PORT": 465, "SECURE": true, "PASSWORD": "test", "USERNAME": "test" }, isPrivate: false }, - ]; + beforeEach(() => { + prismaMock = { + setting: { + findMany: jest.fn(), + findUnique: jest.fn(), + }, + }; + service = new SettingsService(prismaMock); + }); - prismaMock.setting.findMany.mockResolvedValue(mockSettings); + it('should return public settings', async () => { + const mockSettings = [ + { + name: 'SMTP', + value: { + HOST: 'smtp.gmail.com', + PORT: 465, + SECURE: true, + PASSWORD: 'test', + USERNAME: 'test', + }, + isPrivate: false, + }, + { + name: 'SMTP1', + value: { + HOST: 'smtp.gmail.com', + PORT: 465, + SECURE: true, + PASSWORD: 'test', + USERNAME: 'test', + }, + isPrivate: false, + }, + ]; - const result = await service.listPublic(); - console.log(result) + prismaMock.setting.findMany.mockResolvedValue(mockSettings); - expect(result).toEqual({ - SMTP: { "HOST": "smtp.gmail.com", "PORT": 465, "SECURE": true, "PASSWORD": "test", "USERNAME": "test" }, - SMTP1: { "HOST": "smtp.gmail.com", "PORT": 465, "SECURE": true, "PASSWORD": "test", "USERNAME": "test" }, - }); - expect(prismaMock.setting.findMany).toHaveBeenCalledWith({ - where: { isPrivate: false }, - }); - }); + const result = await service.listPublic(); + expect(result).toEqual({ + SMTP: { + HOST: 'smtp.gmail.com', + PORT: 465, + SECURE: true, + PASSWORD: 'test', + USERNAME: 'test', + }, + SMTP1: { + HOST: 'smtp.gmail.com', + PORT: 465, + SECURE: true, + PASSWORD: 'test', + USERNAME: 'test', + }, + }); + expect(prismaMock.setting.findMany).toHaveBeenCalledWith({ + where: { isPrivate: false }, + }); + }); - it('should return a public setting by name', async () => { - const mockSetting = { name: 'SMTP', value: { "HOST": "smtp.gmail.com", "PORT": 465, "SECURE": true, "PASSWORD": "test", "USERNAME": "test" }, isPrivate: false }; + it('should return a public setting by name', async () => { + const mockSetting = { + name: 'SMTP', + value: { + HOST: 'smtp.gmail.com', + PORT: 465, + SECURE: true, + PASSWORD: 'test', + USERNAME: 'test', + }, + isPrivate: false, + }; - prismaMock.setting.findUnique.mockResolvedValue(mockSetting); + prismaMock.setting.findUnique.mockResolvedValue(mockSetting); - const result = await service.getPublic('SMTP'); + const result = await service.getPublic('SMTP'); - expect(result).toEqual(mockSetting); - expect(prismaMock.setting.findUnique).toHaveBeenCalledWith({ - where: { name: 'SMTP', isPrivate: false }, - }); + expect(result).toEqual(mockSetting); + expect(prismaMock.setting.findUnique).toHaveBeenCalledWith({ + where: { name: 'SMTP', isPrivate: false }, }); + }); - it('should throw an error if a public setting is not found', async () => { - prismaMock.setting.findUnique.mockResolvedValue(null); - - + it('should throw an error if a public setting is not found', async () => { + prismaMock.setting.findUnique.mockResolvedValue(null); - await expect(service.getPublic('NONEXISTENT')).rejects.toThrow( - "Public setting 'NONEXISTENT' not found" - ); - expect(prismaMock.setting.findUnique).toHaveBeenCalledWith({ - where: { name: 'NONEXISTENT', isPrivate: false }, - }); + await expect(service.getPublic('NONEXISTENT')).rejects.toThrow( + "Public setting 'NONEXISTENT' not found", + ); + expect(prismaMock.setting.findUnique).toHaveBeenCalledWith({ + where: { name: 'NONEXISTENT', isPrivate: false }, }); -}); \ No newline at end of file + }); +}); diff --git a/libs/extensions/src/settings/settings.service.ts b/libs/extensions/src/settings/settings.service.ts index 6c0fcdf..aba6a23 100644 --- a/libs/extensions/src/settings/settings.service.ts +++ b/libs/extensions/src/settings/settings.service.ts @@ -86,7 +86,6 @@ export class SettingsService { } async list(query: ListSettingDto) { - // console.log(query); const AND_CONDITIONS = []; let conditions = {}; @@ -189,6 +188,8 @@ export class SettingsService { requiredFields: dto.requiredFields, isPrivate: dto.isPrivate, isReadOnly: dto.isReadOnly, + sessionId: dto.sessionId, + updatedBy: dto.updatedBy, }, }); this.load(); @@ -218,6 +219,8 @@ export class SettingsService { requiredFields, isReadOnly, isPrivate, + sessionId, + createdBy, } = createSettingDto; let value: any = dtoValue; @@ -280,6 +283,8 @@ export class SettingsService { requiredFields: requiredFieldsArray, isReadOnly, isPrivate, + sessionId, + createdBy, }, }); diff --git a/libs/user/src/lib/auths/strategy/index.ts b/libs/extensions/src/strategy/index.ts old mode 100755 new mode 100644 similarity index 100% rename from libs/user/src/lib/auths/strategy/index.ts rename to libs/extensions/src/strategy/index.ts diff --git a/libs/user/src/lib/auths/strategy/jwt.strategy.ts b/libs/extensions/src/strategy/jwt.strategy.ts old mode 100755 new mode 100644 similarity index 84% rename from libs/user/src/lib/auths/strategy/jwt.strategy.ts rename to libs/extensions/src/strategy/jwt.strategy.ts index 982bc45..84964e4 --- a/libs/user/src/lib/auths/strategy/jwt.strategy.ts +++ b/libs/extensions/src/strategy/jwt.strategy.ts @@ -2,12 +2,15 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { PrismaService } from '@rumsan/prisma'; +import { getSecret } from '@rumsan/sdk/utils'; import { ExtractJwt, Strategy } from 'passport-jwt'; -import { getSecret } from '../../utils/config.utils'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor(config: ConfigService, private prisma: PrismaService) { + constructor( + config: ConfigService, + private prisma: PrismaService, + ) { // Extrac Bearer Token from Authorization header request super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), diff --git a/libs/user/src/lib/auths/interfaces/auth.interface.ts b/libs/sdk/src/interfaces/auth.interface.ts similarity index 51% rename from libs/user/src/lib/auths/interfaces/auth.interface.ts rename to libs/sdk/src/interfaces/auth.interface.ts index 8bdf659..4f77bb9 100755 --- a/libs/user/src/lib/auths/interfaces/auth.interface.ts +++ b/libs/sdk/src/interfaces/auth.interface.ts @@ -1,3 +1,3 @@ -import { CurrentUserInterface } from './current-user.interface'; +import { CurrentUserInterface } from './currentUser.interface'; export interface TokenDataInterface extends CurrentUserInterface {} diff --git a/libs/user/src/lib/auths/interfaces/current-user.interface.ts b/libs/sdk/src/interfaces/currentUser.interface.ts similarity index 100% rename from libs/user/src/lib/auths/interfaces/current-user.interface.ts rename to libs/sdk/src/interfaces/currentUser.interface.ts diff --git a/libs/sdk/src/interfaces/index.ts b/libs/sdk/src/interfaces/index.ts new file mode 100644 index 0000000..05e519d --- /dev/null +++ b/libs/sdk/src/interfaces/index.ts @@ -0,0 +1,2 @@ +export * from './auth.interface'; +export * from './currentUser.interface'; diff --git a/libs/sdk/src/utils/config.utils.ts b/libs/sdk/src/utils/config.utils.ts new file mode 100644 index 0000000..4ea911b --- /dev/null +++ b/libs/sdk/src/utils/config.utils.ts @@ -0,0 +1,14 @@ +import { ConfigService } from '@nestjs/config'; +import * as crypto from 'crypto'; +const configService = new ConfigService(); + +export const getSecret = () => { + const privateKey = configService.get('PRIVATE_KEY'); + if (!privateKey) { + throw new Error('No PRIVATE_KEY found in config file'); + } + + const hash = crypto.createHash('sha256'); + hash.update(privateKey); + return hash.digest('hex'); +}; diff --git a/libs/sdk/src/utils/index.ts b/libs/sdk/src/utils/index.ts index bba84a7..24ff34b 100644 --- a/libs/sdk/src/utils/index.ts +++ b/libs/sdk/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from './config.utils'; export * from './enum.utils'; export * from './formatResponse.utils'; export * from './string.utils'; diff --git a/libs/user/src/index.ts b/libs/user/src/index.ts index 0d0ad59..82a5512 100755 --- a/libs/user/src/index.ts +++ b/libs/user/src/index.ts @@ -4,8 +4,6 @@ export * from './lib/ability/ability.module'; export * from './lib/ability/ability.subjects'; export * from './lib/auths/auths.module'; export * from './lib/auths/auths.service'; -export * from './lib/auths/guard'; -export { ACTIONS, ERRORS, EVENTS, SUBJECTS } from './lib/constants'; export * from './lib/roles/roles.module'; export * from './lib/roles/roles.service'; export * from './lib/rsuser-module'; diff --git a/libs/user/src/lib/ability/ability.guard.ts b/libs/user/src/lib/ability/ability.guard.ts index 0050e38..960936a 100755 --- a/libs/user/src/lib/ability/ability.guard.ts +++ b/libs/user/src/lib/ability/ability.guard.ts @@ -7,9 +7,9 @@ import { import { Reflector } from '@nestjs/core'; import { AbilityBuilder, createMongoAbility } from '@casl/ability'; -import { CurrentUserInterface } from '../auths/interfaces/current-user.interface'; -import { ACTIONS } from '../constants'; -import { RSE } from '../constants/errors'; +import { CurrentUserInterface } from '@rumsan/sdk/interfaces'; + +import { ACTIONS, RSE } from '@rumsan/extensions/constants'; import { CHECK_ABILITY, RequiredRule } from './ability.decorator'; const createForUser = (user: CurrentUserInterface) => { diff --git a/libs/user/src/lib/ability/ability.module.ts b/libs/user/src/lib/ability/ability.module.ts index 8a06f43..3e6a748 100755 --- a/libs/user/src/lib/ability/ability.module.ts +++ b/libs/user/src/lib/ability/ability.module.ts @@ -1,6 +1,6 @@ import { DynamicModule, Global, Module } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { SUBJECTS } from '../constants'; +import { SUBJECTS } from '@rumsan/extensions/constants'; import { AbilitiesGuard } from './ability.guard'; import { AbilitySubject } from './ability.subjects'; diff --git a/libs/user/src/lib/ability/ability.subjects.ts b/libs/user/src/lib/ability/ability.subjects.ts index 2ce105d..948dee6 100755 --- a/libs/user/src/lib/ability/ability.subjects.ts +++ b/libs/user/src/lib/ability/ability.subjects.ts @@ -1,10 +1,10 @@ -import { SUBJECTS } from '../constants'; +import { SUBJECTS } from '@rumsan/extensions/constants'; //TODO: register source of subjects const subjectsList: any = SUBJECTS; export const AbilitySubject = { - add: (subjects: { [key: string]: string }, source: string = '') => { + add: (subjects: { [key: string]: string }, source = '') => { Object.keys(subjects).forEach((key) => { subjectsList[key] = subjects[key]; }); diff --git a/libs/user/src/lib/auths/auths.module.ts b/libs/user/src/lib/auths/auths.module.ts index 76f2974..62427d9 100755 --- a/libs/user/src/lib/auths/auths.module.ts +++ b/libs/user/src/lib/auths/auths.module.ts @@ -2,10 +2,10 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; +import { JwtStrategy } from '@rumsan/extensions/strategy'; import { PrismaModule } from '@rumsan/prisma'; import { AuthsController } from './auths.controller'; import { AuthsService } from './auths.service'; -import { JwtStrategy } from './strategy'; @Module({ imports: [JwtModule.register({}), PrismaModule, PassportModule, ConfigModule], diff --git a/libs/user/src/lib/auths/auths.service.ts b/libs/user/src/lib/auths/auths.service.ts index 12bddda..50b093c 100755 --- a/libs/user/src/lib/auths/auths.service.ts +++ b/libs/user/src/lib/auths/auths.service.ts @@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { JwtService } from '@nestjs/jwt'; import { AuthSession, User } from '@prisma/client'; +import { EVENTS } from '@rumsan/extensions/constants'; import { ChallengeDto, OtpDto, @@ -13,13 +14,12 @@ import { ERRORS } from '@rumsan/extensions/exceptions'; import { PrismaService } from '@rumsan/prisma'; import { CONSTANTS } from '@rumsan/sdk/constants'; import { Service } from '@rumsan/sdk/enums'; +import { TokenDataInterface } from '@rumsan/sdk/interfaces'; import { Request } from '@rumsan/sdk/types'; import { hashMessage, recoverAddress } from 'viem'; -import { EVENTS } from '../constants'; import { createChallenge, decryptChallenge } from '../utils/challenge.utils'; import { getSecret } from '../utils/config.utils'; import { getServiceTypeByAddress } from '../utils/service.utils'; -import { TokenDataInterface } from './interfaces/auth.interface'; @Injectable() export class AuthsService { diff --git a/libs/user/src/lib/auths/decorator/index.ts b/libs/user/src/lib/auths/decorator/index.ts deleted file mode 100755 index 67e74a5..0000000 --- a/libs/user/src/lib/auths/decorator/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './current-user.decorator'; diff --git a/libs/user/src/lib/auths/guard/jwt.guard.ts b/libs/user/src/lib/auths/guard/jwt.guard.ts deleted file mode 100755 index f65f845..0000000 --- a/libs/user/src/lib/auths/guard/jwt.guard.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class JwtGuard extends AuthGuard('jwt') {} diff --git a/libs/user/src/lib/constants/index.ts b/libs/user/src/lib/constants/index.ts deleted file mode 100755 index 825f3f4..0000000 --- a/libs/user/src/lib/constants/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export { ERRORS } from './errors'; -export * from './events'; - -// For Ability Guard -export const ACTIONS = { - MANAGE: 'manage', - CREATE: 'create', - UPDATE: 'update', - DELETE: 'delete', - READ: 'read', -}; - -// For Ability Guard -export const SUBJECTS = { - ALL: 'all', - PUBLIC: 'public', - USER: 'user', - ROLE: 'role', -}; - -export const APP = { - JWT_BEARER: 'JWT', -}; - -export const CLIENT_TOKEN_LIFETIME = 600; diff --git a/libs/user/src/lib/roles/roles.controller.ts b/libs/user/src/lib/roles/roles.controller.ts index 61b3031..f60e3dc 100755 --- a/libs/user/src/lib/roles/roles.controller.ts +++ b/libs/user/src/lib/roles/roles.controller.ts @@ -11,16 +11,16 @@ import { ValidationPipe, } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { ACTIONS, APP, SUBJECTS } from '@rumsan/extensions/constants'; import { CreateRoleDto, EditRoleDto, ListRoleDto, SearchPermissionDto, } from '@rumsan/extensions/dtos'; +import { JwtGuard } from '@rumsan/extensions/guards'; import { CheckAbilities } from '../ability/ability.decorator'; import { AbilitiesGuard } from '../ability/ability.guard'; -import { JwtGuard } from '../auths/guard'; -import { ACTIONS, APP, SUBJECTS } from '../constants'; import { RolesService } from './roles.service'; @Controller('roles') diff --git a/libs/user/src/lib/roles/roles.service.ts b/libs/user/src/lib/roles/roles.service.ts index 6b55448..bd6f36f 100755 --- a/libs/user/src/lib/roles/roles.service.ts +++ b/libs/user/src/lib/roles/roles.service.ts @@ -2,14 +2,13 @@ import { Injectable } from '@nestjs/common'; import { Permission, Prisma, PrismaClient, Role } from '@prisma/client'; import { DefaultArgs } from '@prisma/client/runtime/library'; import { StringUtils } from '@rumsan/core'; +import { RSE, RSERRORS } from '@rumsan/extensions/constants'; import { CreateRoleDto, EditRoleDto, ListRoleDto, } from '@rumsan/extensions/dtos'; import { PaginatorTypes, PrismaService, paginator } from '@rumsan/prisma'; -import { ERRORS } from '../constants'; -import { RSE } from '../constants/errors'; import { PermissionSet } from '../interfaces'; import { checkPermissionSet, @@ -27,7 +26,7 @@ export class RolesService { constructor(private prisma: PrismaService) {} async create(dto: CreateRoleDto) { - if (!StringUtils.isValidString(dto.name)) throw ERRORS.ROLE_NAME_INVALID; + if (!StringUtils.isValidString(dto.name)) throw RSERRORS.ROLE_NAME_INVALID; const { permissions, ...data } = dto; const { isValid, validSubjects } = checkPermissionSet(permissions); if (!isValid) @@ -40,7 +39,6 @@ export class RolesService { return this.prisma.$transaction(async (prisma) => { const role = await prisma.role.create({ data }); - console.log(role); await this._addPermissionsToRole(role.id, permissions, prisma); return role; diff --git a/libs/user/src/lib/rsuser-module.ts b/libs/user/src/lib/rsuser-module.ts index 9241b99..3072b41 100755 --- a/libs/user/src/lib/rsuser-module.ts +++ b/libs/user/src/lib/rsuser-module.ts @@ -1,8 +1,8 @@ import { DynamicModule, Global, Module } from '@nestjs/common'; import { RumsanAppModule } from '@rumsan/extensions/apps'; +import { RSERRORS } from '@rumsan/extensions/constants'; import { RSExceptionModule } from '@rumsan/extensions/exceptions'; import { AbilitySubject } from './ability/ability.subjects'; -import { ERRORS } from './constants'; @Global() @Module({}) @@ -34,7 +34,7 @@ export class RSUserModule { module: RSUserModule, imports: [ //SignupModule.forRoot({ autoApprove: false }), - RSExceptionModule.forRoot({ errorSet: ERRORS }), + RSExceptionModule.forRoot({ errorSet: RSERRORS }), RumsanAppModule.forRoot({ controllers: { subjects: AbilitySubject.list, diff --git a/libs/user/src/lib/signups/signups.controller.ts b/libs/user/src/lib/signups/signups.controller.ts index 77c10d9..109d9b4 100755 --- a/libs/user/src/lib/signups/signups.controller.ts +++ b/libs/user/src/lib/signups/signups.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { ACTIONS, APP, SUBJECTS } from '@rumsan/extensions/constants'; +import { JwtGuard } from '@rumsan/extensions/guards'; import { CheckAbilities } from '../ability/ability.decorator'; import { AbilitiesGuard } from '../ability/ability.guard'; -import { JwtGuard } from '../auths/guard'; -import { ACTIONS, APP, SUBJECTS } from '../constants'; import { SignupEmailDto, SignupListDto } from './dto'; import { SignupApproveDto } from './dto/signup-approve.dto'; import { SignupPhoneDto } from './dto/signup-phone.dto'; diff --git a/libs/user/src/lib/users/users.controller.ts b/libs/user/src/lib/users/users.controller.ts index 0f45808..3b77979 100755 --- a/libs/user/src/lib/users/users.controller.ts +++ b/libs/user/src/lib/users/users.controller.ts @@ -11,21 +11,25 @@ import { UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiBody, ApiTags } from '@nestjs/swagger'; -import { ApiUuidParam, RequestDetails } from '@rumsan/extensions/decorators'; +import { ACTIONS, APP, SUBJECTS } from '@rumsan/extensions/constants'; +import { + ApiUuidParam, + CU, + CurrentUser, + RequestDetails, +} from '@rumsan/extensions/decorators'; import { CreateUserDto, ListUserDto, UpdateUserDto, } from '@rumsan/extensions/dtos'; import { ERRORS } from '@rumsan/extensions/exceptions'; +import { JwtGuard } from '@rumsan/extensions/guards'; +import { CUI } from '@rumsan/sdk/interfaces'; import { Request } from '@rumsan/sdk/types'; import { UUID } from 'crypto'; import { CheckAbilities } from '../ability/ability.decorator'; import { AbilitiesGuard } from '../ability/ability.guard'; -import { CU, CurrentUser } from '../auths/decorator'; -import { JwtGuard } from '../auths/guard'; -import { CUI } from '../auths/interfaces/current-user.interface'; -import { ACTIONS, APP, SUBJECTS } from '../constants'; import { UsersService } from './users.service'; @Controller('users') @@ -63,6 +67,8 @@ export class UsersController { @Body() dto: UpdateUserDto, @RequestDetails() rdetails: any, ) { + dto.updatedBy = cu.uuid; + dto.sessionId = cu.sessionId; return this.userService.updateMe(cu.userId, dto, rdetails); } diff --git a/libs/user/src/lib/users/users.service.ts b/libs/user/src/lib/users/users.service.ts index 2c64b39..09e1450 100755 --- a/libs/user/src/lib/users/users.service.ts +++ b/libs/user/src/lib/users/users.service.ts @@ -2,16 +2,16 @@ import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { Prisma, PrismaClient, Service, User } from '@prisma/client'; import { DefaultArgs } from '@prisma/client/runtime/library'; +import { EVENTS, NOT_AVAILABLE, RSERRORS } from '@rumsan/extensions/constants'; import { CreateUserDto, ListUserDto, UpdateUserDto, } from '@rumsan/extensions/dtos'; import { paginator, PaginatorTypes, PrismaService } from '@rumsan/prisma'; +import { CUI } from '@rumsan/sdk/interfaces'; import { Request, UserRole } from '@rumsan/sdk/types'; import { UUID } from 'crypto'; -import { CUI } from '../auths/interfaces/current-user.interface'; -import { ERRORS, EVENTS } from '../constants'; import { createChallenge, decryptChallenge } from '../utils/challenge.utils'; import { getSecret } from '../utils/config.utils'; import { @@ -56,9 +56,30 @@ export class UsersService { // await this.addRoles(user.uuid as UUID, dto.roles, tx); await Promise.all([ - this._createAuth(user.id, Service.EMAIL, user.email, tx), - this._createAuth(user.id, Service.PHONE, user.phone, tx), - this._createAuth(user.id, Service.WALLET, user.wallet, tx), + this._createAuth( + user.id, + Service.EMAIL, + user.email, + user.sessionId ?? NOT_AVAILABLE, + user.createdBy ?? NOT_AVAILABLE, + tx, + ), + this._createAuth( + user.id, + Service.PHONE, + user.phone, + user.sessionId ?? NOT_AVAILABLE, + user.createdBy ?? NOT_AVAILABLE, + tx, + ), + this._createAuth( + user.id, + Service.WALLET, + user.wallet, + user.sessionId ?? NOT_AVAILABLE, + user.createdBy ?? NOT_AVAILABLE, + tx, + ), ]); if (callback) { @@ -81,12 +102,14 @@ export class UsersService { userId: number, service: Service, serviceId: string | null, + sessionId: string, + createdBy: string, prisma: PrismaClientType, ): Promise { if (!prisma) prisma = this.prisma; if (serviceId) { await prisma.auth.create({ - data: { userId, service, serviceId }, + data: { userId, service, serviceId, sessionId, createdBy }, }); } } @@ -136,12 +159,12 @@ export class UsersService { where: { uuid, deletedAt: null }, }); - if (!user) throw ERRORS.USER_NOT_FOUND; + if (!user) throw RSERRORS.USER_NOT_FOUND; // Update user details const updatedUser = await tx.user.update({ where: { id: user.id }, - data: { ...dto }, + data: { ...dto, updatedAt: new Date() }, }); // Update authentication details only if corresponding DTO field is provided @@ -196,7 +219,7 @@ export class UsersService { const updatedUser = await tx.user.update({ where: { id: user.id }, - data, + data: { ...data, updatedAt: new Date() }, }); // Helper function to create a verification challenge and emit an event @@ -282,7 +305,7 @@ export class UsersService { async listRoles(uuid: UUID, prisma?: PrismaClientType): Promise { if (!prisma) prisma = this.prisma; const user = await this.get(uuid, prisma); - if (!user) throw ERRORS.USER_NOT_FOUND; + if (!user) throw RSERRORS.USER_NOT_FOUND; const roles = await prisma.userRole.findMany({ where: { userId: user?.id }, include: { Role: true }, @@ -309,7 +332,7 @@ export class UsersService { const user = await prisma.user.findUnique({ where: { uuid }, }); - if (!user) throw ERRORS.USER_NOT_FOUND; + if (!user) throw RSERRORS.USER_NOT_FOUND; await prisma.userRole.createMany({ data: getValidRoles.map((role) => ({ @@ -330,7 +353,7 @@ export class UsersService { const user = await prisma.user.findUnique({ where: { uuid }, }); - if (!user) throw ERRORS.USER_NOT_FOUND; + if (!user) throw RSERRORS.USER_NOT_FOUND; await prisma.userRole.deleteMany({ where: { diff --git a/libs/user/src/lib/utils/permission.utils.ts b/libs/user/src/lib/utils/permission.utils.ts index 89389c1..b38a293 100755 --- a/libs/user/src/lib/utils/permission.utils.ts +++ b/libs/user/src/lib/utils/permission.utils.ts @@ -1,6 +1,6 @@ import { Permission } from '@prisma/client'; +import { RSERRORS } from '@rumsan/extensions/constants'; import { AbilitySubject } from '../ability/ability.subjects'; -import { ERRORS } from '../constants'; import { PermissionSet } from '../interfaces'; export function isPermissionSet(variable: any): variable is PermissionSet { @@ -29,7 +29,7 @@ export function checkPermissionSet(permissions: PermissionSet) { validSubjects: AbilitySubject.listArray(), }; } - if (!isPermissionSet(permissions)) throw ERRORS.PERMISSION_SET_INVALID; + if (!isPermissionSet(permissions)) throw RSERRORS.PERMISSION_SET_INVALID; return AbilitySubject.checkForValidSubjects(Object.keys(permissions)); } diff --git a/libs/user/src/lib/utils/service.utils.ts b/libs/user/src/lib/utils/service.utils.ts index 0f51206..30c4abc 100755 --- a/libs/user/src/lib/utils/service.utils.ts +++ b/libs/user/src/lib/utils/service.utils.ts @@ -1,5 +1,5 @@ import { Service } from '@prisma/client'; -import { ERRORS, EVENTS } from '../constants'; +import { EVENTS, RSERRORS } from '@rumsan/extensions/constants'; export function getServiceTypeByAddress(input: string): Service | null { // Regular expressions for email, Ethereum wallet address, and phone number @@ -14,7 +14,7 @@ export function getServiceTypeByAddress(input: string): Service | null { } else if (phoneRegex.test(input)) { return Service.PHONE; } else { - throw ERRORS.SERVICE_TYPE_INVALID; + throw RSERRORS.SERVICE_TYPE_INVALID; } } diff --git a/prisma/migrations/20240702072110_update_rs/migration.sql b/prisma/migrations/20240702072110_update_rs/migration.sql deleted file mode 100644 index e3437c3..0000000 --- a/prisma/migrations/20240702072110_update_rs/migration.sql +++ /dev/null @@ -1,173 +0,0 @@ --- CreateEnum -CREATE TYPE "Gender" AS ENUM ('MALE', 'FEMALE', 'OTHER', 'UNKNOWN'); - --- CreateEnum -CREATE TYPE "Service" AS ENUM ('API', 'EMAIL', 'PHONE', 'WALLET', 'GOOGLE', 'APPLE', 'FACEBOOK', 'TWITTER', 'GITHUB', 'LINKEDIN'); - --- CreateEnum -CREATE TYPE "SignupStatus" AS ENUM ('PENDING', 'APPROVED', 'FAILED', 'REJECTED'); - --- CreateEnum -CREATE TYPE "SettingDataType" AS ENUM ('STRING', 'NUMBER', 'BOOLEAN', 'OBJECT'); - --- CreateTable -CREATE TABLE "tbl_users" ( - "id" SERIAL NOT NULL, - "uuid" TEXT NOT NULL, - "name" TEXT NOT NULL, - "gender" "Gender" NOT NULL DEFAULT 'UNKNOWN', - "email" TEXT, - "phone" TEXT, - "wallet" TEXT, - "extras" JSONB, - "notes" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3), - "deletedAt" TIMESTAMP(3), - "createdBy" INTEGER, - "updatedBy" INTEGER, - - CONSTRAINT "tbl_users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tbl_auth_roles" ( - "id" SERIAL NOT NULL, - "name" VARCHAR NOT NULL, - "isSystem" BOOLEAN NOT NULL DEFAULT false, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3), - "createdBy" INTEGER, - "updatedBy" INTEGER -); - --- CreateTable -CREATE TABLE "tbl_auth_permissions" ( - "id" SERIAL NOT NULL, - "roleId" INTEGER NOT NULL, - "action" VARCHAR NOT NULL, - "subject" VARCHAR NOT NULL, - "inverted" BOOLEAN NOT NULL DEFAULT false, - "conditions" JSONB, - "reason" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) -); - --- CreateTable -CREATE TABLE "tbl_users_roles" ( - "id" SERIAL NOT NULL, - "userId" INTEGER NOT NULL, - "roleId" INTEGER NOT NULL, - "expiry" TIMESTAMP(3), - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "createdBy" INTEGER, - - CONSTRAINT "tbl_users_roles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tbl_auth" ( - "id" SERIAL NOT NULL, - "userId" INTEGER NOT NULL, - "service" "Service" NOT NULL, - "serviceId" TEXT NOT NULL, - "details" JSONB, - "challenge" TEXT, - "falseAttempts" INTEGER NOT NULL DEFAULT 0, - "isLocked" BOOLEAN NOT NULL DEFAULT false, - "lockedOnAt" TIMESTAMP(3), - "lastLoginAt" TIMESTAMP(3), - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3), - "deletedAt" TIMESTAMP(3), - - CONSTRAINT "tbl_auth_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tbl_auth_sessions" ( - "id" SERIAL NOT NULL, - "clientId" TEXT NOT NULL, - "sessionId" TEXT NOT NULL, - "authId" INTEGER NOT NULL, - "ip" TEXT, - "details" JSONB, - "userAgent" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "tbl_auth_sessions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tbl_users_signups" ( - "id" SERIAL NOT NULL, - "uuid" TEXT NOT NULL, - "userIdentifier" TEXT, - "data" JSONB, - "status" "SignupStatus" NOT NULL DEFAULT 'PENDING', - "rejectedReason" TEXT, - "approvedBy" INTEGER, - "approvedAt" TIMESTAMP(3), - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3), - - CONSTRAINT "tbl_users_signups_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tbl_settings" ( - "name" TEXT NOT NULL, - "value" JSONB NOT NULL, - "dataType" "SettingDataType" NOT NULL, - "requiredFields" TEXT[], - "isReadOnly" BOOLEAN NOT NULL DEFAULT false, - "isPrivate" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "tbl_settings_pkey" PRIMARY KEY ("name") -); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_users_uuid_key" ON "tbl_users"("uuid"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_auth_roles_id_key" ON "tbl_auth_roles"("id"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_auth_roles_name_key" ON "tbl_auth_roles"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_auth_permissions_id_key" ON "tbl_auth_permissions"("id"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_users_roles_userId_roleId_key" ON "tbl_users_roles"("userId", "roleId"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_auth_service_serviceId_key" ON "tbl_auth"("service", "serviceId"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_auth_sessions_sessionId_key" ON "tbl_auth_sessions"("sessionId"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_users_signups_uuid_key" ON "tbl_users_signups"("uuid"); - --- CreateIndex -CREATE UNIQUE INDEX "tbl_settings_name_key" ON "tbl_settings"("name"); - --- AddForeignKey -ALTER TABLE "tbl_auth_permissions" ADD CONSTRAINT "tbl_auth_permissions_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "tbl_auth_roles"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tbl_users_roles" ADD CONSTRAINT "tbl_users_roles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "tbl_users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tbl_users_roles" ADD CONSTRAINT "tbl_users_roles_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "tbl_auth_roles"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tbl_auth" ADD CONSTRAINT "tbl_auth_userId_fkey" FOREIGN KEY ("userId") REFERENCES "tbl_users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tbl_auth_sessions" ADD CONSTRAINT "tbl_auth_sessions_authId_fkey" FOREIGN KEY ("authId") REFERENCES "tbl_auth"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tbl_users_signups" ADD CONSTRAINT "tbl_users_signups_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "tbl_users"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20240802110910_users/migration.sql b/prisma/migrations/20240802110910_users/migration.sql deleted file mode 100644 index f31ec7e..0000000 --- a/prisma/migrations/20240802110910_users/migration.sql +++ /dev/null @@ -1,4 +0,0 @@ --- AlterTable -ALTER TABLE "tbl_users" ADD COLUMN "sessionId" TEXT, -ALTER COLUMN "createdBy" SET DATA TYPE TEXT, -ALTER COLUMN "updatedBy" SET DATA TYPE TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e5d062b..51c6051 100755 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -30,7 +30,7 @@ model User { Signup Signup[] createdAt DateTime @default(now()) - updatedAt DateTime? @updatedAt() + updatedAt DateTime? deletedAt DateTime? createdBy String? updatedBy String? @@ -50,17 +50,19 @@ enum Gender { // ===================Role Model================== model Role { - id Int @unique @default(autoincrement()) - name String @unique @db.VarChar() - isSystem Boolean @default(false) + id Int @unique @default(autoincrement()) + name String @unique @db.VarChar() + isSystem Boolean @default(false) + sessionId String? Permission Permission[] UserRole UserRole[] createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt() - createdBy Int? - updatedBy Int? + deletedAt DateTime? + createdBy String? + updatedBy String? @@map("tbl_auth_roles") } @@ -74,11 +76,15 @@ model Permission { inverted Boolean @default(false) conditions Json? @db.JsonB() reason String? @db.Text() + sessionId String? Role Role @relation(fields: [roleId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt() + deletedAt DateTime? + createdBy String? + updatedBy String? @@map("tbl_auth_permissions") } @@ -110,6 +116,7 @@ model Auth { isLocked Boolean @default(false) lockedOnAt DateTime? lastLoginAt DateTime? + sessionId String? User User @relation(fields: [userId], references: [id]) AuthLog AuthSession[] @@ -117,6 +124,8 @@ model Auth { createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt() deletedAt DateTime? + createdBy String? + updatedBy String? @@unique([service, serviceId], name: "authIdentifier") @@map("tbl_auth") @@ -190,6 +199,13 @@ model Setting { requiredFields String[] isReadOnly Boolean @default(false) isPrivate Boolean @default(true) + sessionId String? + + createdAt DateTime @default(now()) + updatedAt DateTime? @updatedAt() + + createdBy String? + updatedBy String? @@map("tbl_settings") }