updated accounts service navigator

This commit is contained in:
Berkay 2025-08-03 18:23:26 +03:00
parent 1b87dee60d
commit aa8f0b8f31
24 changed files with 398 additions and 211 deletions

View File

@ -3094,7 +3094,7 @@ model occupant_types {
occupant_code String @default("") @db.VarChar occupant_code String @default("") @db.VarChar
occupant_category String @default("") @db.VarChar occupant_category String @default("") @db.VarChar
occupant_category_type String @default("") @db.VarChar occupant_category_type String @default("") @db.VarChar
function_retriever String @default("") @db.VarChar // function_retriever String @default("") @db.VarChar
user_type_id Int? user_type_id Int?
user_type_uu_id String? @db.VarChar user_type_uu_id String? @db.VarChar
occupant_is_unique Boolean @default(false) occupant_is_unique Boolean @default(false)
@ -3516,7 +3516,7 @@ model staff {
employee_history employee_history[] employee_history employee_history[]
employees employees[] employees employees[]
duties duties @relation(fields: [duties_id], references: [id], onDelete: NoAction, onUpdate: NoAction) duties duties @relation(fields: [duties_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
user_type user_types? @relation(fields: [user_type_id], references: [id], onDelete: NoAction, onUpdate: NoAction) user_types user_types? @relation(fields: [user_type_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
@@index([created_at], map: "ix_staff_created_at") @@index([created_at], map: "ix_staff_created_at")
@@index([cryp_uu_id], map: "ix_staff_cryp_uu_id") @@index([cryp_uu_id], map: "ix_staff_cryp_uu_id")

View File

@ -14,38 +14,47 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { AccountsService } from './accounts.service'; import { AccountsService } from './accounts.service';
import { AuthControlGuard, EndpointControlGuard } from '../middleware/access-control.guard'; import { AuthControlGuard, EndpointControlGuard } from '../middleware/access-control.guard';
import { RedisHandlers } from '../utils/auth/redisHandlers';
@Controller('accounts') @Controller('accounts')
export class AccountsController { export class AccountsController {
constructor(private accountsService: AccountsService) { }
constructor(private accountsService: AccountsService, private redisHandler: RedisHandlers) { }
@Get('events') @Get('events')
@HttpCode(200) @HttpCode(200)
@UseGuards(AuthControlGuard, EndpointControlGuard) @UseGuards(AuthControlGuard)
async getEvents(@Query() query: any) { async getEvents(@Query() query: any) {
const { url, func } = query; const { userToken } = query;
const events = await this.accountsService.infoEvents(url, func); const events = await this.accountsService.infoEvents(userToken)
return { try {
events, return { events, message: "Events fetched successfully" };
message: "Events fetched successfully", } catch (error) {
console.error('Error getting events:', error);
throw new ForbiddenException(`Error retrieving events. Please contact your system administrator.`);
} }
} }
@Post('filter') @Post('filter')
@HttpCode(200) @HttpCode(200)
@UseGuards(AuthControlGuard, EndpointControlGuard) @UseGuards(AuthControlGuard, EndpointControlGuard)
async filterAccounts(@Body() query: any, @Req() req: any) { async filterAccounts(@Body() query: any, @Req() req: any) {
const driveToken = req.driveToken // Get request drive token from acess control guard and retrieve related Service
const redirectToService = await this.accountsService.getEvents(); const relatedService = this.accountsService.getService(req)
console.log('redirectToService', redirectToService); if (!relatedService) { throw new Error(`No service found for drive token: ${req.driveToken}`) }
try { try {
const functionToCall = redirectToService[driveToken]; // Get function mapper from related
if (!relatedService.mapper) { throw new Error(`Mapper in ${relatedService.constructor.name} is missing or null`) }
// Get redis select token object from redis
const selectObject = await this.redisHandler.getSelectFromRedis(req);
if (!selectObject) { throw new Error(`Select object is missing or null`) }
if (!selectObject.value.events) { throw new Error(`Events in select object is missing or null`) }
const eventKey = Object.entries(selectObject.value.events).filter((key) => key.includes(req.driveToken))[0]
if (!eventKey) { throw new Error(`No event is registered for this user ${req.driveToken}`) }
// Get function to call from related service mapper
const functionToCall = relatedService.mapper[eventKey.join(":")];
if (!functionToCall || typeof functionToCall !== 'function') { throw new Error(`No function found for drive token: ${req.driveToken}`); }
return await functionToCall(query); return await functionToCall(query);
} catch (error) { } catch (error) { throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`) }
console.error('Error redirecting to service:', error);
throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`);
}
} }
} }

View File

@ -9,6 +9,7 @@ import {
EndpointControlGuard, EndpointControlGuard,
} from '@/src/middleware/access-control.guard'; } from '@/src/middleware/access-control.guard';
import { SuperUsersService } from './superusers/superusers.service'; import { SuperUsersService } from './superusers/superusers.service';
import { UrlHandler } from '../utils/auth/urlHandler';
@Module({ @Module({
imports: [PrismaModule, UtilsModule], imports: [PrismaModule, UtilsModule],
@ -18,16 +19,11 @@ import { SuperUsersService } from './superusers/superusers.service';
AuthControlGuard, AuthControlGuard,
EndpointControlGuard, EndpointControlGuard,
SuperUsersService, SuperUsersService,
UrlHandler,
], ],
controllers: [AccountsController], controllers: [AccountsController],
}) })
export class AccountsModule { export class AccountsModule {
constructor( constructor() { }
private accountsService: AccountsService,
) { }
async onModuleInit() {
const accountEvents = await this.accountsService.infoEvents();
console.dir(accountEvents, { depth: null });
}
} }

View File

@ -1,80 +1,33 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PaginationInfo } from '../utils/pagination-helper'; import { PaginationInfo } from '../utils/pagination-helper';
import { SuperUsersService } from './superusers/superusers.service'; import { SuperUsersService } from './superusers/superusers.service';
import crypto from 'crypto';
@Injectable() @Injectable()
export class AccountsService { export class AccountsService {
mapper: any
constructor( constructor(
private superUsersService: SuperUsersService, private superUsersService: SuperUsersService,
) { } ) {
events = { this.mapper = {
"/accounts/filter:GQKQshahQhGm8HYy4O4Tgx": [ "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": superUsersService,
{
"key": "s8OnSnHoQfyfuDk7A1XRww",
"description": "Super Users Filter",
"isDefault": true,
"query": { "query": true, "page": false, "pageSize": false },
"token": "GQKQshahQhGm8HYy4O4Tgx",
"pages": ["accounts"]
// "type": "EMP",
// "fr": "SuperUserEmployee",
}
],
"/accounts/read:GQKQshahQhGm8HYy4O4Tgx": [
{
"key": "s8OnSnHoQfyfuDk7A1XRww",
"description": "Super Users Read",
"isDefault": true,
"query": { "query": true, "page": false, "pageSize": false },
"token": "GQKQshahQhGm8HYy4O4Tgx",
"pages": ["accounts"]
// "type": "EMP",
// "fr": "SuperUserEmployee",
}
]
};
createSecureKeyWithoutLib(url: string) {
const subString = crypto.createHash('sha256').update(url).digest().toString('base64').substring(0, 16)
return subString.replace(/=/g, 'E').replace(/-/g, 'M').replace(/_/g, 'N').replace(/\+/g, 'P').replace(/\//g, 'Q')
}
async infoEvents(urlRetriever: string | null = null, functionRetriever: string | null = null) {
const events = this.events;
if (urlRetriever && !functionRetriever) {
if (events[urlRetriever]) {
return [[urlRetriever, events[urlRetriever]]];
}
return [];
} else if (urlRetriever && functionRetriever) {
if (events[urlRetriever] && events[urlRetriever][functionRetriever]) {
return [[urlRetriever, { [functionRetriever]: events[urlRetriever][functionRetriever] }]];
}
return [];
} else if (!urlRetriever && functionRetriever) {
const filteredEvents: [string, any][] = [];
Object.entries(events).forEach(([url, urlEvents]) => {
if (urlEvents[functionRetriever]) {
filteredEvents.push([url, { [functionRetriever]: urlEvents[functionRetriever] }]);
}
});
return filteredEvents;
} else {
return Object.entries(events);
} }
} }
async getEvents() { getService(request: any) {
return { const driveToken = request.driveToken
"/accounts/filter:GQKQshahQhGm8HYy4O4Tgx:s8OnSnHoQfyfuDk7A1XRww": (query: any) => this.supersUserFilter(query), const secondPartOfDriveToken = driveToken.split(":")[1]
"/accounts/read:a5b6d9c716f409a7004a:tcc116f409a7004a": (query: any) => this.supersUserFilter(query) if (!secondPartOfDriveToken) { throw new Error('Drive token is missing or null') }
}; return this.mapper[secondPartOfDriveToken];
} }
async supersUserFilter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> { async supersUserFilter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> {
return this.superUsersService.filter(query); return this.superUsersService.filter(query);
} }
async infoEvents(userToken: string) {
const relatedMapper = this.getService(userToken)
if (!relatedMapper) { throw new Error(`No service found for user token: ${userToken}`) }
return relatedMapper.infoEvents(userToken);
}
} }

View File

@ -2,14 +2,39 @@ import { PaginationHelper } from '@/src/utils/pagination-helper';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PaginationInfo } from '@/src/utils/pagination-helper'; import { PaginationInfo } from '@/src/utils/pagination-helper';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
import { UrlHandler } from '@/src/utils/auth/urlHandler';
@Injectable() @Injectable()
export class SuperUsersService { export class SuperUsersService {
userToken: string = "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA"
constructor( constructor(
private paginationHelper: PaginationHelper, private paginationHelper: PaginationHelper,
private prisma: PrismaService, private prisma: PrismaService,
private urlHandler: UrlHandler,
) { } ) { }
events = {
"e6hewIe7YqbQZHO3:j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": [
{
"key": "qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx",
"endpoint": "/accounts/filter:POST",
"eToken": "e6hewIe7YqbQZHO3",
"token": "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA",
"description": "Super Users Account Filter",
"isDefault": true,
"query": { "query": true, "page": false, "pageSize": false },
"pages": []
}
]
};
mapper = {
"e6hewIe7YqbQZHO3:j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA:qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx": (query: any) => this.filter(query),
}
async getEvents() { return this.urlHandler.getEvents(this.events, this.mapper) }
async infoEvents(userToken: string) { return Object.entries(this.events).filter(([key]) => key.endsWith(userToken)) }
async filter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> { async filter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> {
console.log("supersServiceFilter query", query) console.log("supersServiceFilter query", query)
const result = await this.paginationHelper.findWithPagination(query, this.prisma.account_records); const result = await this.paginationHelper.findWithPagination(query, this.prisma.account_records);

View File

@ -24,9 +24,7 @@ const redisConfig = {
const modulesList = [UsersModule, AccountsModule, AuthModule]; const modulesList = [UsersModule, AccountsModule, AuthModule];
const serviceModuleList = [ const serviceModuleList = [
PrismaModule, PrismaModule,
RedisModule.forRoot({ RedisModule.forRoot({ config: redisConfig }),
config: redisConfig,
}),
DiscoveryModule, DiscoveryModule,
]; ];
const controllersList = [AppController]; const controllersList = [AppController];

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { userLoginValidator } from '@/src/auth/login/dtoValidator'; import { userLoginValidator } from '@/src/auth/login/dtoValidator';
import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
import { PasswordHandlers } from '@/src/utils/auth/login_handler'; import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
import { AuthTokenSchema } from '@/src/types/auth/token'; import { AuthTokenSchema } from '@/src/types/auth/token';
@ -58,7 +58,7 @@ export class LoginService {
uu_id: true, uu_id: true,
occupant_code: true, occupant_code: true,
occupant_type: true, occupant_type: true,
function_retriever: true, // function_retriever: true,
}, },
}, },
build_parts: { build_parts: {
@ -106,7 +106,7 @@ export class LoginService {
select: { select: {
uu_id: true, uu_id: true,
staff_code: true, staff_code: true,
function_retriever: true, // function_retriever: true,
duties: { duties: {
select: { select: {
uu_id: true, uu_id: true,

View File

@ -5,8 +5,8 @@ import {
UnauthorizedException, UnauthorizedException,
} from '@nestjs/common'; } from '@nestjs/common';
import { userChangePasswordValidator } from './dtoValidator'; import { userChangePasswordValidator } from './dtoValidator';
import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
import { PasswordHandlers } from '@/src/utils/auth/login_handler'; import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
@Injectable() @Injectable()
export class ChangePasswordService { export class ChangePasswordService {
@ -14,7 +14,7 @@ export class ChangePasswordService {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly redis: RedisHandlers, private readonly redis: RedisHandlers,
private readonly passHandlers: PasswordHandlers, private readonly passHandlers: PasswordHandlers,
) {} ) { }
private async syncPasswordHistory( private async syncPasswordHistory(
foundUser: any, foundUser: any,

View File

@ -1,6 +1,6 @@
import { userCreatePasswordValidator } from './dtoValidator'; import { userCreatePasswordValidator } from './dtoValidator';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
import { PasswordHandlers } from '@/src/utils/auth/login_handler'; import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
import { Injectable, BadRequestException } from '@nestjs/common'; import { Injectable, BadRequestException } from '@nestjs/common';
@Injectable() @Injectable()
@ -8,7 +8,7 @@ export class CreatePasswordService {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly passHandlers: PasswordHandlers, private readonly passHandlers: PasswordHandlers,
) {} ) { }
async run(dto: userCreatePasswordValidator) { async run(dto: userCreatePasswordValidator) {
if (dto.password !== dto.rePassword) { if (dto.password !== dto.rePassword) {

View File

@ -1,14 +1,14 @@
import { Injectable, BadRequestException } from '@nestjs/common'; import { Injectable, BadRequestException } from '@nestjs/common';
import { userResetPasswordValidator } from './dtoValidator'; import { userResetPasswordValidator } from './dtoValidator';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
import { PasswordHandlers } from '@/src/utils/auth/login_handler'; import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
@Injectable() @Injectable()
export class ResetPasswordService { export class ResetPasswordService {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly passHandlers: PasswordHandlers, private readonly passHandlers: PasswordHandlers,
) {} ) { }
async run(dto: userResetPasswordValidator) { async run(dto: userResetPasswordValidator) {
const foundUser = await this.prisma.users.findFirstOrThrow({ const foundUser = await this.prisma.users.findFirstOrThrow({
where: { where: {

View File

@ -5,7 +5,7 @@ import {
NotAcceptableException, NotAcceptableException,
} from '@nestjs/common'; } from '@nestjs/common';
import { userSelectValidator } from '@/src/auth/select/dtoValidator'; import { userSelectValidator } from '@/src/auth/select/dtoValidator';
import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
import { import {
EmployeeTokenSchema, EmployeeTokenSchema,
OccupantTokenSchema, OccupantTokenSchema,
@ -13,6 +13,7 @@ import {
UserType, UserType,
} from '@/src/types/auth/token'; } from '@/src/types/auth/token';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
// No need to import Prisma client types directly
@Injectable() @Injectable()
export class SelectService { export class SelectService {
@ -47,9 +48,32 @@ export class SelectService {
}, },
}); });
const staff = await this.prisma.staff.findFirstOrThrow({ const staff = await this.prisma.staff.findFirstOrThrow({
where: { id: employee.staff_id }, where: { uu_id: employee.staff_uu_id },
omit: { select: {
id: true, uu_id: true,
staff_code: true,
user_type_id: true,
duties_id: true,
duties_uu_id: true,
created_credentials_token: true,
updated_credentials_token: true,
confirmed_credentials_token: true,
is_confirmed: true,
deleted: true,
active: true,
is_notification_send: true,
is_email_send: true,
expiry_starts: true,
expiry_ends: true,
created_at: true,
updated_at: true,
ref_int: true,
user_types: {
select: {
token: true,
type_token: true
}
}
}, },
}); });
const duties = await this.prisma.duties.findFirstOrThrow({ const duties = await this.prisma.duties.findFirstOrThrow({
@ -77,7 +101,17 @@ export class SelectService {
}, },
}); });
const staffUserType = staff.user_type_id ?
await this.prisma.user_types.findFirst({
where: { id: staff.user_type_id },
select: {
token: true,
type_token: true
}
}) : null;
const employeeToken = EmployeeTokenSchema.parse({ const employeeToken = EmployeeTokenSchema.parse({
uuid: dto.uuid,
company: company, company: company,
department: department, department: department,
duty: duty, duty: duty,
@ -85,6 +119,7 @@ export class SelectService {
staff: staff, staff: staff,
menu: null, menu: null,
pages: null, pages: null,
events: null,
selection: await this.prisma.employees.findFirstOrThrow({ selection: await this.prisma.employees.findFirstOrThrow({
where: { uu_id: dto.uuid }, where: { uu_id: dto.uuid },
select: { select: {
@ -93,7 +128,12 @@ export class SelectService {
select: { select: {
uu_id: true, uu_id: true,
staff_code: true, staff_code: true,
function_retriever: true, user_types: {
select: {
uu_id: true,
token: true,
},
},
duties: { duties: {
select: { select: {
uu_id: true, uu_id: true,
@ -122,7 +162,8 @@ export class SelectService {
}, },
}, },
}), }),
functionsRetriever: staff.function_retriever, typeToken: staffUserType?.type_token,
functionsRetriever: staffUserType?.token,
kind: UserType.employee, kind: UserType.employee,
}); });
@ -138,57 +179,73 @@ export class SelectService {
token: tokenSelect, token: tokenSelect,
}; };
} else if (userType === 'occupant') { } else if (userType === 'occupant') {
const livingSpace = await this.prisma.build_living_space.findFirstOrThrow( const livingSpace = await this.prisma.build_living_space.findFirstOrThrow({
{
where: { uu_id: dto.uuid }, where: { uu_id: dto.uuid },
omit: { select: {
id: true, uu_id: true,
person_id: true, build_parts_uu_id: true,
build_parts_id: true, occupant_type_uu_id: true
occupant_type_id: true, }
ref_id: true,
replication_id: true,
cryp_uu_id: true,
},
},
);
const occupantType = await this.prisma.occupant_types.findFirstOrThrow({
where: { uu_id: livingSpace.occupant_type_uu_id },
omit: {
id: true,
cryp_uu_id: true,
ref_id: true,
replication_id: true,
},
}); });
const occupantType = await this.prisma.occupant_types.findFirstOrThrow({
where: { uu_id: livingSpace.occupant_type_uu_id }
});
const userTypeInfo = occupantType.user_type_uu_id ?
await this.prisma.user_types.findFirst({
where: { uu_id: occupantType.user_type_uu_id },
select: {
uu_id: true,
type: true,
description: true,
type_token: true,
token: true
}
}) : null;
const part = await this.prisma.build_parts.findFirstOrThrow({ const part = await this.prisma.build_parts.findFirstOrThrow({
where: { uu_id: livingSpace.build_parts_uu_id }, where: { uu_id: livingSpace.build_parts_uu_id },
omit: { select: {
id: true, uu_id: true,
cryp_uu_id: true, part_code: true,
ref_id: true, part_no: true,
replication_id: true, part_level: true,
}, human_livable: true,
build_uu_id: true,
api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: {
select: {
uu_id: true,
enum_class: true,
value: true
}
}
}
}); });
const build = await this.prisma.build.findFirstOrThrow({ const build = await this.prisma.build.findFirstOrThrow({
where: { uu_id: part.build_uu_id }, where: { uu_id: part.build_uu_id },
omit: { select: {
id: true, uu_id: true,
cryp_uu_id: true, build_name: true
ref_id: true, }
replication_id: true,
},
}); });
const company = await this.prisma.companies.findFirstOrThrow({ const company = await this.prisma.companies.findFirstOrThrow({
where: { uu_id: accessObject.value.users.related_company }, where: { uu_id: accessObject.value.users.related_company },
omit: { select: {
id: true, uu_id: true,
cryp_uu_id: true, is_confirmed: true,
ref_id: true, deleted: true,
replication_id: true, active: true,
}, created_at: true,
updated_at: true,
ref_int: true
}
}); });
const occupantToken = OccupantTokenSchema.parse({ const occupantToken = OccupantTokenSchema.parse({
uuid: dto.uuid,
livingSpace: livingSpace, livingSpace: livingSpace,
occupant: occupantType, occupant: occupantType,
build: build, build: build,
@ -196,54 +253,44 @@ export class SelectService {
company: company, company: company,
menu: null, menu: null,
pages: null, pages: null,
selection: await this.prisma.build_living_space.findFirstOrThrow({ events: null,
where: { uu_id: dto.uuid }, selection: {
select: {
uu_id: true,
occupant_types: { occupant_types: {
select: { uu_id: occupantType.uu_id,
uu_id: true, occupant_code: occupantType.occupant_code,
occupant_code: true, occupant_type: occupantType.occupant_type
occupant_type: true,
function_retriever: true,
},
}, },
build_parts: { build_parts: {
select: { uu_id: part.uu_id,
uu_id: true, part_code: part.part_code,
part_code: true, part_no: part.part_no,
part_no: true, part_level: part.part_level,
part_level: true, human_livable: part.human_livable,
human_livable: true,
api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: { api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: {
select: { uu_id: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.uu_id,
uu_id: true, enum_class: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.enum_class,
enum_class: true, value: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.value
value: true,
},
}, },
build: { build: {
select: { uu_id: build.uu_id,
uu_id: true, build_name: build.build_name
build_name: true, }
}
}, },
}, typeToken: userTypeInfo?.type_token,
}, functionsRetriever: userTypeInfo?.token,
}, kind: UserType.occupant
},
}),
functionsRetriever: occupantType.function_retriever,
kind: UserType.occupant,
}); });
const tokenSelect = await this.redis.setSelectToRedis( const tokenSelect = await this.redis.setSelectToRedis(
accessToken, accessToken,
occupantToken, occupantToken,
accessObject.value.users.uu_id, accessObject.value.users.uu_id,
dto.uuid, dto.uuid
); );
return { return {
message: 'Select successful', message: 'Select successful',
token: tokenSelect, token: tokenSelect
}; };
} else { } else {
throw new NotAcceptableException('Invalid user type'); throw new NotAcceptableException('Invalid user type');

View File

@ -4,7 +4,8 @@ import {
Injectable, Injectable,
ForbiddenException, ForbiddenException,
} from '@nestjs/common'; } from '@nestjs/common';
import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
import { UrlHandler } from '@/src/utils/auth/urlHandler';
@Injectable() @Injectable()
export class AuthControlGuard implements CanActivate { export class AuthControlGuard implements CanActivate {
@ -13,25 +14,27 @@ export class AuthControlGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest(); const req = context.switchToHttp().getRequest();
const accessToken = this.cacheService.mergeLoginKey(req); const accessToken = this.cacheService.mergeLoginKey(req);
console.log('AuthControlGuard', accessToken); // console.log('AuthControlGuard', accessToken);
return true; return true;
} }
} }
@Injectable() @Injectable()
export class EndpointControlGuard implements CanActivate { export class EndpointControlGuard implements CanActivate {
constructor(private cacheService: RedisHandlers) { } constructor(
private cacheService: RedisHandlers,
private urlHandler: UrlHandler,
) { }
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest(); const req = context.switchToHttp().getRequest();
// const selectToken = this.cacheService.mergeSelectKey(req);
const method = req.method; const method = req.method;
const path = req.route?.path; const path = req.route?.path;
console.log('EndpointControlGuard', method, 'path', path); const keyUrl = `${path}:${method.toUpperCase()}`;
// const accessObject = await this.cacheService.getSelectFromRedis(req); const driveToken = await this.urlHandler.getSecureUrlToken(keyUrl);
// console.log('EndpointControlGuard', accessObject); const accessObject = await this.cacheService.getSelectFromRedis(req);
req.driveToken = "c5b6d9c7-9115-4825-bcc1-16f409a7004a" req.driveToken = `${driveToken}:${accessObject?.value.functionsRetriever}`;
// console.log('EndpointControlGuard', req.driveToken); console.log('EndpointControlGuard driveToken: ', driveToken);
return true; return true;
} }
} }

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EventsService } from './events.service';
describe('EventsService', () => {
let service: EventsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EventsService],
}).compile();
service = module.get<EventsService>(EventsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,4 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class EventsService {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PagesService } from './pages.service';
describe('PagesService', () => {
let service: PagesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PagesService],
}).compile();
service = module.get<PagesService>(PagesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,4 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class PagesService {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ServicesService } from './services.service';
describe('ServicesService', () => {
let service: ServicesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ServicesService],
}).compile();
service = module.get<ServicesService>(ServicesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,4 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class ServicesService {}

View File

@ -1,4 +1,4 @@
import { z } from 'zod'; import { uuid, z } from 'zod';
// ENUM // ENUM
export const UserType = { export const UserType = {
@ -101,6 +101,7 @@ export const AuthTokenSchema = z.object({
export type AuthToken = z.infer<typeof AuthTokenSchema>; export type AuthToken = z.infer<typeof AuthTokenSchema>;
export const EmployeeTokenSchema = z.object({ export const EmployeeTokenSchema = z.object({
uuid: z.string(),
company: z.object({ company: z.object({
// id: z.number(), // id: z.number(),
uu_id: z.string(), uu_id: z.string(),
@ -218,7 +219,7 @@ export const EmployeeTokenSchema = z.object({
staff_code: z.string(), staff_code: z.string(),
// duties_id: z.number(), // duties_id: z.number(),
duties_uu_id: z.string(), duties_uu_id: z.string(),
function_retriever: z.string().nullable(), // function_retriever: z.string().nullable(),
// ref_id: z.string().nullable(), // ref_id: z.string().nullable(),
// replication_id: z.number(), // replication_id: z.number(),
// cryp_uu_id: z.string().nullable(), // cryp_uu_id: z.string().nullable(),
@ -239,13 +240,16 @@ export const EmployeeTokenSchema = z.object({
menu: z.array(z.object({})).nullable(), menu: z.array(z.object({})).nullable(),
pages: z.array(z.string()).nullable(), pages: z.array(z.string()).nullable(),
events: z.array(z.string()).nullable(),
selection: z.record(z.string(), z.unknown()).nullable(), selection: z.record(z.string(), z.unknown()).nullable(),
typeToken: z.string(),
functionsRetriever: z.string(), functionsRetriever: z.string(),
kind: z.literal(UserType.employee), kind: z.literal(UserType.employee),
}); });
export const OccupantTokenSchema = z.object({ export const OccupantTokenSchema = z.object({
uuid: z.string(),
livingSpace: z.object({}), livingSpace: z.object({}),
occupant: z.object({}), occupant: z.object({}),
build: z.object({}), build: z.object({}),
@ -254,8 +258,10 @@ export const OccupantTokenSchema = z.object({
menu: z.array(z.object({})).nullable(), menu: z.array(z.object({})).nullable(),
pages: z.array(z.string()).nullable(), pages: z.array(z.string()).nullable(),
events: z.array(z.string()).nullable(),
selection: z.record(z.string(), z.unknown()).nullable(), selection: z.record(z.string(), z.unknown()).nullable(),
typeToken: z.string(),
functionsRetriever: z.string(), functionsRetriever: z.string(),
kind: z.literal(UserType.occupant), kind: z.literal(UserType.occupant),
}); });

View File

@ -5,7 +5,7 @@ import {
AuthTokenSchema, AuthTokenSchema,
} from '@/src/types/auth/token'; } from '@/src/types/auth/token';
import { CacheService } from '@/src/cache.service'; import { CacheService } from '@/src/cache.service';
import { PasswordHandlers } from './login_handler'; import { PasswordHandlers } from './loginHandler';
import { Injectable, ForbiddenException } from '@nestjs/common'; import { Injectable, ForbiddenException } from '@nestjs/common';
interface LoginFromRedis { interface LoginFromRedis {
@ -152,7 +152,7 @@ export class RedisHandlers {
for (const key of keys) { for (const key of keys) {
const value = await this.cacheService.get(key); const value = await this.cacheService.get(key);
if (value) { if (value) {
return { key, value }; return { key: value.key, value: value.value };
} }
} }
throw new ForbiddenException( throw new ForbiddenException(

View File

@ -0,0 +1,68 @@
import { Injectable } from "@nestjs/common";
import { Events, Mapper } from "@/src/utils/types/url";
import { createHash } from 'crypto';
@Injectable()
export class UrlHandler {
private createSecureKeyWithoutLib(url: string): string {
const subString = createHash('sha256').update(url).digest().toString('base64').substring(0, 16)
return subString.replace(/=/g, 'E').replace(/-/g, 'M').replace(/_/g, 'N').replace(/\+/g, 'P').replace(/\//g, 'Q')
}
async getSecureUrlToken(url: string): Promise<string> {
return this.createSecureKeyWithoutLib(url);
}
async getEvents(events: Events, mapper: Mapper) {
for (const keyUrl of Object.keys(mapper)) {
const splittedMapper = keyUrl.split(':')
const eToken = splittedMapper[0]
const token = splittedMapper[1]
const key = splittedMapper[2]
const eventKey = `${eToken}:${token}`
if (Object.keys(events).includes(eventKey)) {
// Check if the event contains an item with the matching key
const eventArray = events[eventKey]
const foundEvent = eventArray.find(item => item.key === key)
if (!foundEvent) {
throw new Error(`Event key ${key} not found in event ${eventKey}`)
}
} else {
throw new Error(`Event ${eventKey} not found in events`)
}
}
return mapper;
}
async infoEvents(events: Events, urlRetriever: string | null = null, functionRetriever: string | null = null) {
if (urlRetriever && !functionRetriever) {
console.log("urlRetriever", urlRetriever)
if (events[urlRetriever]) {
return [[urlRetriever, events[urlRetriever]]];
}
return [];
} else if (urlRetriever && functionRetriever) {
if (events[urlRetriever]) {
const eventItem = events[urlRetriever].find(item => item.key === functionRetriever);
if (eventItem) {
return [[urlRetriever, { [functionRetriever]: eventItem }]];
}
}
return [];
} else if (!urlRetriever && functionRetriever) {
const filteredEvents: [string, any][] = [];
Object.entries(events).forEach(([url, eventArray]) => {
const eventItem = eventArray.find(item => item.key === functionRetriever);
if (eventItem) {
filteredEvents.push([url, { [functionRetriever]: eventItem }]);
}
});
return filteredEvents;
} else {
return Object.entries(events);
}
}
}

View File

@ -0,0 +1,16 @@
export interface Events {
[key: string]: Array<{
endpoint: string;
eToken: string;
token: string;
key: string;
description: string;
isDefault: boolean;
query: Record<string, boolean>;
pages: string[];
}>;
}
export interface Mapper {
[key: string]: (query: any) => any;
}

View File

@ -1,8 +1,8 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { PaginationHelper } from './pagination-helper'; import { PaginationHelper } from './pagination-helper';
import { PrismaService } from '@/src/prisma.service'; import { PrismaService } from '@/src/prisma.service';
import { RedisHandlers } from './auth/redis_handlers'; import { RedisHandlers } from './auth/redisHandlers';
import { PasswordHandlers } from './auth/login_handler'; import { PasswordHandlers } from './auth/loginHandler';
import { CacheService } from '@/src/cache.service'; import { CacheService } from '@/src/cache.service';
@Module({ @Module({
@ -15,4 +15,4 @@ import { CacheService } from '@/src/cache.service';
], ],
exports: [PaginationHelper, RedisHandlers, PasswordHandlers, CacheService], exports: [PaginationHelper, RedisHandlers, PasswordHandlers, CacheService],
}) })
export class UtilsModule {} export class UtilsModule { }