updated event controllers and service event mtach tested
This commit is contained in:
parent
aa8f0b8f31
commit
9232da69d3
|
|
@ -17,6 +17,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"ioredis": "^5.6.1",
|
||||
"mongodb": "^6.18.0",
|
||||
"otplib": "^12.0.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"redis": "^5.6.1",
|
||||
|
|
@ -1957,6 +1958,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
|
||||
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/nice": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.4.tgz",
|
||||
|
|
@ -3477,6 +3487,21 @@
|
|||
"integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/webidl-conversions": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/whatwg-url": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
|
||||
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/webidl-conversions": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
||||
|
|
@ -4805,6 +4830,15 @@
|
|||
"node-int64": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bson": {
|
||||
"version": "6.10.4",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
|
||||
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
|
|
@ -8646,6 +8680,12 @@
|
|||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/memory-pager": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||
|
|
@ -8813,6 +8853,62 @@
|
|||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz",
|
||||
"integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.9",
|
||||
"bson": "^6.10.4",
|
||||
"mongodb-connection-string-url": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/credential-providers": "^3.188.0",
|
||||
"@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"kerberos": "^2.0.1",
|
||||
"mongodb-client-encryption": ">=6.0.0 <7",
|
||||
"snappy": "^7.2.2",
|
||||
"socks": "^2.7.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@aws-sdk/credential-providers": {
|
||||
"optional": true
|
||||
},
|
||||
"@mongodb-js/zstd": {
|
||||
"optional": true
|
||||
},
|
||||
"gcp-metadata": {
|
||||
"optional": true
|
||||
},
|
||||
"kerberos": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-client-encryption": {
|
||||
"optional": true
|
||||
},
|
||||
"snappy": {
|
||||
"optional": true
|
||||
},
|
||||
"socks": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/whatwg-url": "^11.0.2",
|
||||
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
@ -9639,7 +9735,6 @@
|
|||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
@ -10613,6 +10708,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sparse-bitfield": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"memory-pager": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
||||
|
|
@ -11340,6 +11444,18 @@
|
|||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-kill": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||
|
|
@ -11867,6 +11983,15 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.100.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz",
|
||||
|
|
@ -12058,6 +12183,19 @@
|
|||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "^5.1.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"ioredis": "^5.6.1",
|
||||
"mongodb": "^6.18.0",
|
||||
"otplib": "^12.0.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"redis": "^5.6.1",
|
||||
|
|
|
|||
|
|
@ -8,53 +8,36 @@ import {
|
|||
Body,
|
||||
HttpCode,
|
||||
UseGuards,
|
||||
ForbiddenException,
|
||||
NotFoundException,
|
||||
Req,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { AccountsService } from './accounts.service';
|
||||
import { AuthControlGuard, EndpointControlGuard } from '../middleware/access-control.guard';
|
||||
import { RedisHandlers } from '../utils/auth/redisHandlers';
|
||||
import { AuthControlGuard, EndpointControlGuard } from '@/src/middleware/access-control.guard';
|
||||
import { Navigator } from '@/src/utils/navigator/navigator';
|
||||
|
||||
@Controller('accounts')
|
||||
export class AccountsController {
|
||||
|
||||
constructor(private accountsService: AccountsService, private redisHandler: RedisHandlers) { }
|
||||
constructor(
|
||||
private accountsService: AccountsService,
|
||||
private navigator: Navigator
|
||||
) { }
|
||||
|
||||
@Get('events')
|
||||
@HttpCode(200)
|
||||
@UseGuards(AuthControlGuard)
|
||||
async getEvents(@Query() query: any) {
|
||||
const { userToken } = query;
|
||||
const events = await this.accountsService.infoEvents(userToken)
|
||||
try {
|
||||
return { events, message: "Events fetched successfully" };
|
||||
} catch (error) {
|
||||
console.error('Error getting events:', error);
|
||||
throw new ForbiddenException(`Error retrieving events. Please contact your system administrator.`);
|
||||
}
|
||||
if (!userToken) { throw new NotFoundException('User token is missing or null') }
|
||||
const events = await this.navigator.getInfos(this.accountsService, userToken)
|
||||
return { events, message: "Events fetched successfully" };
|
||||
}
|
||||
|
||||
@Post('filter')
|
||||
@HttpCode(200)
|
||||
@UseGuards(AuthControlGuard, EndpointControlGuard)
|
||||
async filterAccounts(@Body() query: any, @Req() req: any) {
|
||||
// Get request drive token from acess control guard and retrieve related Service
|
||||
const relatedService = this.accountsService.getService(req)
|
||||
if (!relatedService) { throw new Error(`No service found for drive token: ${req.driveToken}`) }
|
||||
try {
|
||||
// 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);
|
||||
} catch (error) { throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`) }
|
||||
return await this.navigator.getFunction(req, this.accountsService.mapper, query)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,26 @@ import { Module } from '@nestjs/common';
|
|||
import { AccountsService } from './accounts.service';
|
||||
import { AccountsController } from './accounts.controller';
|
||||
import { PrismaModule } from '@/prisma/prisma.module';
|
||||
import { CacheService } from '../cache.service';
|
||||
import { CacheService } from '../database/redis/redis.service';
|
||||
import { UtilsModule } from '../utils/utils.module';
|
||||
import { RedisModule } from '../database/redis/redis.module';
|
||||
import {
|
||||
AuthControlGuard,
|
||||
EndpointControlGuard,
|
||||
} from '@/src/middleware/access-control.guard';
|
||||
import { SuperUsersService } from './superusers/superusers.service';
|
||||
import { UrlHandler } from '../utils/auth/urlHandler';
|
||||
import { UrlHandler } from '../utils/navigator/urlHandler';
|
||||
import { Navigator } from '@/src/utils/navigator/navigator';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, UtilsModule],
|
||||
imports: [PrismaModule, UtilsModule, RedisModule],
|
||||
providers: [
|
||||
AccountsService,
|
||||
CacheService,
|
||||
AuthControlGuard,
|
||||
EndpointControlGuard,
|
||||
SuperUsersService,
|
||||
UrlHandler,
|
||||
Navigator,
|
||||
],
|
||||
controllers: [AccountsController],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { PaginationInfo } from '../utils/pagination-helper';
|
||||
import { SuperUsersService } from './superusers/superusers.service';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -10,24 +9,7 @@ export class AccountsService {
|
|||
private superUsersService: SuperUsersService,
|
||||
) {
|
||||
this.mapper = {
|
||||
"j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": superUsersService,
|
||||
"j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": this.superUsersService,
|
||||
}
|
||||
}
|
||||
|
||||
getService(request: any) {
|
||||
const driveToken = request.driveToken
|
||||
const secondPartOfDriveToken = driveToken.split(":")[1]
|
||||
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[] }> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { PaginationHelper } from '@/src/utils/pagination-helper';
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { PaginationInfo } from '@/src/utils/pagination-helper';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { UrlHandler } from '@/src/utils/auth/urlHandler';
|
||||
import { UrlHandler } from '@/src/utils/navigator/urlHandler';
|
||||
|
||||
@Injectable()
|
||||
export class SuperUsersService {
|
||||
|
|
@ -36,7 +36,6 @@ export class SuperUsersService {
|
|||
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[] }> {
|
||||
console.log("supersServiceFilter query", query)
|
||||
const result = await this.paginationHelper.findWithPagination(query, this.prisma.account_records);
|
||||
const { pagination, data } = result;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,32 +7,26 @@ import {
|
|||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { PrismaModule } from '@/prisma/prisma.module';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { AccountsModule } from './accounts/accounts.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { RedisModule } from '@liaoliaots/nestjs-redis';
|
||||
import { CacheService } from './cache.service';
|
||||
import { RedisModule } from './database/redis/redis.module';
|
||||
import { LoggerMiddleware } from '@/src/middleware/logger.middleware';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
|
||||
const redisConfig = {
|
||||
host: '10.10.2.15',
|
||||
port: 6379,
|
||||
password: 'your_strong_password_here',
|
||||
};
|
||||
import { NavigatorModule } from './navigator/navigator.module';
|
||||
|
||||
const modulesList = [UsersModule, AccountsModule, AuthModule];
|
||||
const serviceModuleList = [
|
||||
PrismaModule,
|
||||
RedisModule.forRoot({ config: redisConfig }),
|
||||
RedisModule.forRootWithConfig(true),
|
||||
DiscoveryModule,
|
||||
];
|
||||
const controllersList = [AppController];
|
||||
const providersList = [AppService, CacheService];
|
||||
const exportsList = [CacheService];
|
||||
const providersList = [AppService];
|
||||
const exportsList = [];
|
||||
|
||||
@Module({
|
||||
imports: [...serviceModuleList, ...modulesList],
|
||||
imports: [...serviceModuleList, ...modulesList, NavigatorModule],
|
||||
controllers: controllersList,
|
||||
providers: providersList,
|
||||
exports: exportsList,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@ import { ResetPasswordService } from './password/reset/reset.service';
|
|||
import { ChangePasswordService } from './password/change/change.service';
|
||||
import { VerifyOtpService } from './password/verify-otp/verify-otp.service';
|
||||
import { DisconnectService } from './disconnect/disconnect.service';
|
||||
import { MongoModule } from '@/src/database/mongo/mongo.module';
|
||||
import { MongoService } from '@/src/database/mongo/mongo.service';
|
||||
import { NavigatorModule } from '../navigator/navigator.module';
|
||||
|
||||
@Module({
|
||||
imports: [UtilsModule],
|
||||
imports: [UtilsModule, MongoModule, NavigatorModule],
|
||||
controllers: [AuthController],
|
||||
providers: [
|
||||
AuthService,
|
||||
|
|
@ -25,8 +28,9 @@ import { DisconnectService } from './disconnect/disconnect.service';
|
|||
ResetPasswordService,
|
||||
VerifyOtpService,
|
||||
DisconnectService,
|
||||
MongoService,
|
||||
PrismaService,
|
||||
],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
export class AuthModule { }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { userLoginValidator } from '@/src/auth/login/dtoValidator';
|
||||
import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
|
||||
import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
|
||||
import { RedisHandlers } from '@/src/utils/store/redisHandlers';
|
||||
import { PasswordHandlers } from '@/src/utils/store/loginHandler';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { AuthTokenSchema } from '@/src/types/auth/token';
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import {
|
|||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { userChangePasswordValidator } from './dtoValidator';
|
||||
import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
|
||||
import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
|
||||
import { RedisHandlers } from '@/src/utils/store/redisHandlers';
|
||||
import { PasswordHandlers } from '@/src/utils/store/loginHandler';
|
||||
|
||||
@Injectable()
|
||||
export class ChangePasswordService {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { userCreatePasswordValidator } from './dtoValidator';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
|
||||
import { PasswordHandlers } from '@/src/utils/store/loginHandler';
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { userResetPasswordValidator } from './dtoValidator';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { PasswordHandlers } from '@/src/utils/auth/loginHandler';
|
||||
import { PasswordHandlers } from '@/src/utils/store/loginHandler';
|
||||
|
||||
@Injectable()
|
||||
export class ResetPasswordService {
|
||||
|
|
|
|||
|
|
@ -1,52 +1,29 @@
|
|||
import {
|
||||
Injectable,
|
||||
BadRequestException,
|
||||
UnauthorizedException,
|
||||
NotAcceptableException,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException, NotAcceptableException } from '@nestjs/common';
|
||||
import { userSelectValidator } from '@/src/auth/select/dtoValidator';
|
||||
import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
|
||||
import {
|
||||
EmployeeTokenSchema,
|
||||
OccupantTokenSchema,
|
||||
TokenDictInterface,
|
||||
UserType,
|
||||
} from '@/src/types/auth/token';
|
||||
import { RedisHandlers } from '@/src/utils/store/redisHandlers';
|
||||
import { EmployeeTokenSchema, OccupantTokenSchema, UserType } from '@/src/types/auth/token';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
// No need to import Prisma client types directly
|
||||
import { MongoService } from '@/src/database/mongo/mongo.service';
|
||||
import { EventsService } from '@/src/navigator/events/events.service';
|
||||
|
||||
@Injectable()
|
||||
export class SelectService {
|
||||
constructor(
|
||||
private readonly redis: RedisHandlers,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly mongoService: MongoService,
|
||||
private readonly eventService: EventsService
|
||||
) { }
|
||||
|
||||
async run(dto: userSelectValidator, req: Request) {
|
||||
const accessObject = await this.redis.getLoginFromRedis(req);
|
||||
if (!accessObject) {
|
||||
throw new UnauthorizedException(
|
||||
'Authorization failed. Please login to continue',
|
||||
);
|
||||
}
|
||||
if (!accessObject) { throw new UnauthorizedException('Authorization failed. Please login to continue') }
|
||||
const accessToken = accessObject.key.split(':')[1];
|
||||
const existingSelectToken = await this.redis.callExistingSelectToken(
|
||||
accessObject.value.users.uu_id,
|
||||
dto.uuid,
|
||||
);
|
||||
if (existingSelectToken) {
|
||||
return {
|
||||
message: 'Select successful',
|
||||
token: existingSelectToken,
|
||||
};
|
||||
}
|
||||
const existingSelectToken = await this.redis.callExistingSelectToken(accessObject.value.users.uu_id, dto.uuid);
|
||||
if (existingSelectToken) { return { message: 'Select successful', token: existingSelectToken } }
|
||||
const userType = accessObject.value.users.user_type;
|
||||
if (userType === 'employee') {
|
||||
const employee = await this.prisma.employees.findFirstOrThrow({
|
||||
where: { uu_id: dto.uuid },
|
||||
omit: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const employee = await this.prisma.employees.findFirstOrThrow({ where: { uu_id: dto.uuid }, omit: { id: true } });
|
||||
const staff = await this.prisma.staff.findFirstOrThrow({
|
||||
where: { uu_id: employee.staff_uu_id },
|
||||
select: {
|
||||
|
|
@ -54,6 +31,8 @@ export class SelectService {
|
|||
staff_code: true,
|
||||
user_type_id: true,
|
||||
duties_id: true,
|
||||
staff_name: true,
|
||||
staff_description: true,
|
||||
duties_uu_id: true,
|
||||
created_credentials_token: true,
|
||||
updated_credentials_token: true,
|
||||
|
|
@ -78,29 +57,20 @@ export class SelectService {
|
|||
});
|
||||
const duties = await this.prisma.duties.findFirstOrThrow({
|
||||
where: { id: staff.duties_id },
|
||||
omit: {
|
||||
id: true,
|
||||
},
|
||||
omit: { id: true },
|
||||
});
|
||||
const department = await this.prisma.departments.findFirstOrThrow({
|
||||
where: { id: duties.department_id },
|
||||
omit: {
|
||||
id: true,
|
||||
},
|
||||
omit: { id: true },
|
||||
});
|
||||
const duty = await this.prisma.duty.findFirstOrThrow({
|
||||
where: { id: duties.duties_id },
|
||||
omit: {
|
||||
id: true,
|
||||
},
|
||||
omit: { id: true },
|
||||
});
|
||||
const company = await this.prisma.companies.findFirstOrThrow({
|
||||
where: { id: duties.company_id },
|
||||
omit: {
|
||||
id: true,
|
||||
},
|
||||
omit: { id: true },
|
||||
});
|
||||
|
||||
const staffUserType = staff.user_type_id ?
|
||||
await this.prisma.user_types.findFirst({
|
||||
where: { id: staff.user_type_id },
|
||||
|
|
@ -109,7 +79,6 @@ export class SelectService {
|
|||
type_token: true
|
||||
}
|
||||
}) : null;
|
||||
|
||||
const employeeToken = EmployeeTokenSchema.parse({
|
||||
uuid: dto.uuid,
|
||||
company: company,
|
||||
|
|
@ -166,6 +135,10 @@ export class SelectService {
|
|||
functionsRetriever: staffUserType?.token,
|
||||
kind: UserType.employee,
|
||||
});
|
||||
// Render page and menu
|
||||
// const collection = this.mongoService.getDb(`Events/${company.uu_id}`)
|
||||
const events = ""
|
||||
|
||||
|
||||
const tokenSelect = await this.redis.setSelectToRedis(
|
||||
accessToken,
|
||||
|
|
@ -173,11 +146,7 @@ export class SelectService {
|
|||
accessObject.value.users.uu_id,
|
||||
dto.uuid,
|
||||
);
|
||||
|
||||
return {
|
||||
message: 'Select successful',
|
||||
token: tokenSelect,
|
||||
};
|
||||
return { message: 'Select successful', token: tokenSelect };
|
||||
} else if (userType === 'occupant') {
|
||||
const livingSpace = await this.prisma.build_living_space.findFirstOrThrow({
|
||||
where: { uu_id: dto.uuid },
|
||||
|
|
@ -187,11 +156,9 @@ export class SelectService {
|
|||
occupant_type_uu_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 },
|
||||
|
|
@ -203,7 +170,6 @@ export class SelectService {
|
|||
token: true
|
||||
}
|
||||
}) : null;
|
||||
|
||||
const part = await this.prisma.build_parts.findFirstOrThrow({
|
||||
where: { uu_id: livingSpace.build_parts_uu_id },
|
||||
select: {
|
||||
|
|
@ -222,7 +188,6 @@ export class SelectService {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const build = await this.prisma.build.findFirstOrThrow({
|
||||
where: { uu_id: part.build_uu_id },
|
||||
select: {
|
||||
|
|
@ -230,7 +195,6 @@ export class SelectService {
|
|||
build_name: true
|
||||
}
|
||||
});
|
||||
|
||||
const company = await this.prisma.companies.findFirstOrThrow({
|
||||
where: { uu_id: accessObject.value.users.related_company },
|
||||
select: {
|
||||
|
|
@ -243,7 +207,6 @@ export class SelectService {
|
|||
ref_int: true
|
||||
}
|
||||
});
|
||||
|
||||
const occupantToken = OccupantTokenSchema.parse({
|
||||
uuid: dto.uuid,
|
||||
livingSpace: livingSpace,
|
||||
|
|
@ -282,16 +245,17 @@ export class SelectService {
|
|||
kind: UserType.occupant
|
||||
});
|
||||
|
||||
// Render page and menu
|
||||
const eventsObject = await this.eventService.getEventsOccupants(livingSpace.uu_id)
|
||||
eventsObject && (occupantToken.events = eventsObject)
|
||||
|
||||
const tokenSelect = await this.redis.setSelectToRedis(
|
||||
accessToken,
|
||||
occupantToken,
|
||||
accessObject.value.users.uu_id,
|
||||
dto.uuid
|
||||
);
|
||||
return {
|
||||
message: 'Select successful',
|
||||
token: tokenSelect
|
||||
};
|
||||
return { message: 'Select successful', token: tokenSelect };
|
||||
} else {
|
||||
throw new NotAcceptableException('Invalid user type');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { MongoService } from './mongo.service';
|
||||
import { MongoProvider } from './mongo.provider';
|
||||
|
||||
@Module({
|
||||
providers: [MongoProvider, MongoService],
|
||||
exports: [MongoService, MongoProvider, 'MONGO_DB']
|
||||
})
|
||||
export class MongoModule {}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MongoClient, Db } from 'mongodb';
|
||||
|
||||
export const MongoProvider = {
|
||||
provide: 'MONGO_DB',
|
||||
useFactory: async (): Promise<Db> => {
|
||||
const uri = 'mongodb://appuser:apppassword@10.10.2.13:27017/appdb';
|
||||
const client = new MongoClient(uri);
|
||||
await client.connect();
|
||||
return client.db('appdb');
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MongoService } from './mongo.service';
|
||||
|
||||
describe('MongoService', () => {
|
||||
let service: MongoService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [MongoService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<MongoService>(MongoService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { Db, Document, Collection, Filter, ObjectId, UpdateResult } from 'mongodb';
|
||||
|
||||
@Injectable()
|
||||
export class MongoService {
|
||||
|
||||
private collection: Collection<Document>;
|
||||
|
||||
constructor(@Inject('MONGO_DB') private readonly db: Db) { this.collection = this.db.collection('mongoCache') }
|
||||
|
||||
async set(collectionName: string) { this.collection = this.db.collection(collectionName) }
|
||||
|
||||
async getDb() { return this.collection }
|
||||
|
||||
/**
|
||||
* Find a document by UUID or create it if it doesn't exist
|
||||
* @param data Document data with UUID field
|
||||
* @returns The found or created document
|
||||
*
|
||||
* @example
|
||||
* Create a new user or retrieve existing one
|
||||
* const userData = { uuid: 'TOKEN:12345:user', name: 'John Doe', email: 'john@example.com' };
|
||||
* const user = await mongoService.findOrCreate(userData);
|
||||
*/
|
||||
async findOrCreate(data: Record<string, any>): Promise<{ data: Document, isCreated: boolean }> {
|
||||
if (!data.uuid) { throw new Error('UUID is required for findOrCreate operation') }
|
||||
// Use direct UUID lookup instead of regex for exact match
|
||||
const existingDoc = await this.collection.findOne({ uuid: data.uuid } as Filter<Document>);
|
||||
if (existingDoc) { return { data: existingDoc, isCreated: false } }
|
||||
const insertResult = await this.collection.insertOne(data);
|
||||
if (!insertResult.acknowledged) { throw new Error('Failed to insert document') }
|
||||
return { data: await this.getOne(insertResult.insertedId), isCreated: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all documents from the collection
|
||||
* @returns Array of all documents
|
||||
*
|
||||
* @example
|
||||
* Get all users in the collection
|
||||
* const allUsers = await mongoService.getAll();
|
||||
*/
|
||||
async getAll(): Promise<Document[]> { return await this.collection.find().toArray() }
|
||||
|
||||
/**
|
||||
* Find a document by ID key using regex pattern
|
||||
* @param idKey ID key to search for
|
||||
* @returns The found document
|
||||
* @throws Error if document is not found
|
||||
*
|
||||
* @example
|
||||
* Find a user by ID key
|
||||
* const user = await mongoService.findOne('12345');
|
||||
* This will search for documents with uuid matching pattern ^TOKEN:12345:
|
||||
*/
|
||||
async findOne(filter: Filter<Document>): Promise<Document> {
|
||||
const result = await this.collection.findOne(filter);
|
||||
if (!result) { throw new Error(`Document with ID key ${filter} not found`) }
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find multiple documents using a filter
|
||||
* @param filter MongoDB filter
|
||||
* @param limit Optional limit of results (default: no limit)
|
||||
* @param skip Optional number of documents to skip (default: 0)
|
||||
* @returns Array of matching documents
|
||||
*
|
||||
* @example
|
||||
* Find active users with pagination
|
||||
* const filter = { active: true } as Filter<Document>;
|
||||
* const activeUsers = await mongoService.findMany(filter, 10, 20); // limit 10, skip 20
|
||||
*
|
||||
* @example
|
||||
* Find users by role
|
||||
* const admins = await mongoService.findMany({ role: 'admin' } as Filter<Document>);
|
||||
*/
|
||||
async findMany(filter: Filter<Document>, limit?: number, skip?: number): Promise<Document[]> {
|
||||
let query = this.collection.find(filter);
|
||||
if (typeof skip === 'number') { query = query.skip(skip) }
|
||||
if (typeof limit === 'number') { query = query.limit(limit) }
|
||||
return await query.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a document by its MongoDB ObjectId
|
||||
* @param id MongoDB ObjectId
|
||||
* @returns The found document
|
||||
* @throws Error if document is not found
|
||||
*
|
||||
* @example
|
||||
* Get a user by ObjectId
|
||||
* const userId = new ObjectId('507f1f77bcf86cd799439011');
|
||||
* const user = await mongoService.getOne(userId);
|
||||
*/
|
||||
async getOne(id: ObjectId): Promise<Document> {
|
||||
const result = await this.collection.findOne({ _id: id });
|
||||
if (!result) { throw new Error(`Document with ID ${id.toString()} not found`) }
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find documents by regex pattern on UUID field
|
||||
* @param idKey ID key to search for
|
||||
* @returns Array of matching documents
|
||||
*
|
||||
* @example
|
||||
* Find all users with a specific ID key pattern
|
||||
* const users = await mongoService.findByRegex('12345');
|
||||
* This will return all documents with uuid matching pattern ^TOKEN:12345:
|
||||
*/
|
||||
async findByRegex(idKey: string): Promise<Document[]> {
|
||||
if (!idKey) { throw new Error('ID key is required for regex search') }
|
||||
const pattern = `^${idKey}`;
|
||||
return await this.collection.find({ uuid: { $regex: pattern } } as Filter<Document>).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single document by its MongoDB ObjectId
|
||||
* @param id MongoDB ObjectId
|
||||
* @param data Data to update
|
||||
* @returns The updated document
|
||||
* @throws Error if document is not found or update fails
|
||||
*
|
||||
* @example
|
||||
* Update a user's profile
|
||||
* const userId = new ObjectId('507f1f77bcf86cd799439011');
|
||||
* const updates = { name: 'Jane Doe', lastLogin: new Date() };
|
||||
* const updatedUser = await mongoService.updateOne(userId, updates);
|
||||
*/
|
||||
async updateOne(id: ObjectId, data: Record<string, any>): Promise<Document> {
|
||||
const updateResult = await this.collection.updateOne(
|
||||
{ _id: id },
|
||||
{ $set: data }
|
||||
);
|
||||
if (!updateResult.acknowledged) { throw new Error('Update operation failed') }
|
||||
if (updateResult.matchedCount === 0) { throw new Error(`Document with ID ${id.toString()} not found`) }
|
||||
return await this.getOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple documents matching a filter
|
||||
* @param filter MongoDB filter
|
||||
* @param data Data to update
|
||||
* @returns Update result with count of modified documents
|
||||
* @throws Error if update fails
|
||||
*
|
||||
* @example
|
||||
* Mark all inactive users as archived
|
||||
* const filter = { active: false } as Filter<Document>;
|
||||
* const updates = { status: 'archived', archivedAt: new Date() };
|
||||
* const result = await mongoService.updateMany(filter, updates);
|
||||
* console.log(`${result.modifiedCount} users archived`);
|
||||
*/
|
||||
async updateMany(filter: Filter<Document>, data: Record<string, any>): Promise<UpdateResult> {
|
||||
const updateResult = await this.collection.updateMany(filter, { $set: data });
|
||||
if (!updateResult.acknowledged) { throw new Error('Update operation failed') }
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a document by its MongoDB ObjectId
|
||||
* @param id MongoDB ObjectId
|
||||
* @returns True if document was deleted, false otherwise
|
||||
*
|
||||
* @example
|
||||
* Delete a user account
|
||||
* const userId = new ObjectId('507f1f77bcf86cd799439011');
|
||||
* const deleted = await mongoService.deleteOne(userId);
|
||||
* if (deleted) console.log('User successfully deleted');
|
||||
*/
|
||||
async deleteOne(id: ObjectId): Promise<boolean> {
|
||||
const deleteResult = await this.collection.deleteOne({ _id: id });
|
||||
return deleteResult.acknowledged && deleteResult.deletedCount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple documents matching a filter
|
||||
* @param filter MongoDB filter
|
||||
* @returns Number of deleted documents
|
||||
*
|
||||
* @example
|
||||
* Delete all expired sessions
|
||||
* const filter = { expiresAt: { $lt: new Date() } } as Filter<Document>;
|
||||
* const count = await mongoService.deleteMany(filter);
|
||||
* console.log(`${count} expired sessions deleted`);
|
||||
*/
|
||||
async deleteMany(filter: Filter<Document>): Promise<number> {
|
||||
const deleteResult = await this.collection.deleteMany(filter);
|
||||
return deleteResult.acknowledged ? deleteResult.deletedCount : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find documents by regex pattern on any specified field
|
||||
* @param field The field name to apply the regex filter on
|
||||
* @param value The value to search for in the field
|
||||
* @param options Optional regex options (e.g., 'i' for case-insensitive)
|
||||
* @param prefix Optional prefix to add before the value (default: '')
|
||||
* @param suffix Optional suffix to add after the value (default: '')
|
||||
* @returns Array of matching documents
|
||||
*
|
||||
* @example
|
||||
* Find users with email from a specific domain (case-insensitive)
|
||||
* const gmailUsers = await mongoService.findByFieldRegex('email', 'gmail.com', 'i');
|
||||
*
|
||||
* @example
|
||||
* Find users with names starting with 'J'
|
||||
* const usersStartingWithJ = await mongoService.findByFieldRegex('name', 'J', 'i', '^');
|
||||
*
|
||||
* @example
|
||||
* Find users with phone numbers ending in specific digits
|
||||
* const specificPhoneUsers = await mongoService.findByFieldRegex('phone', '5555', '', '', '$');
|
||||
*/
|
||||
async findByFieldRegex(field: string, value: string, options?: string, prefix: string = '', suffix: string = ''): Promise<Document[]> {
|
||||
if (!field || !value) { throw new Error('Field name and value are required for regex search') }
|
||||
const pattern = `${prefix}${value}${suffix}`;
|
||||
const query: Record<string, any> = {};
|
||||
query[field] = { $regex: pattern };
|
||||
if (options) { query[field].$options = options; }
|
||||
return await this.collection.find(query as unknown as Filter<Document>).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find documents by regex pattern across all fields (including nested)
|
||||
* @param value The value to search for
|
||||
* @param options Optional regex options (e.g., 'i' for case-insensitive)
|
||||
* @returns Array of matching documents
|
||||
*
|
||||
* @example
|
||||
* Find any document containing a specific value anywhere
|
||||
* const docs = await mongoService.findByRegexAcrossFields('someValue', 'i');
|
||||
*/
|
||||
async findByRegexAcrossFields(value: string, options?: string, searchType: 'value' | 'key' | 'both' = 'value'): Promise<Document[]> {
|
||||
if (!value) { throw new Error('Search value is required') }
|
||||
const query: any = { $or: [] };
|
||||
if (searchType === 'value' || searchType === 'both') {
|
||||
query.$or.push(
|
||||
{ '$expr': { $regexMatch: { input: { $toString: '$$ROOT' }, regex: value, options } } },
|
||||
{ 'data': { $type: 'object', $regex: value, $options: options } }
|
||||
);
|
||||
}
|
||||
|
||||
if (searchType === 'key' || searchType === 'both') {
|
||||
query.$where = function () {
|
||||
const searchRegex = new RegExp(value, options);
|
||||
function checkKeys(obj: Record<string, any>) {
|
||||
for (const key in obj) {
|
||||
if (searchRegex.test(key)) return true; if (obj[key] && typeof obj[key] === 'object') { if (checkKeys(obj[key])) return true }
|
||||
}
|
||||
return false;
|
||||
} return checkKeys(this)
|
||||
}.toString();
|
||||
}
|
||||
|
||||
return await this.collection.find(query as unknown as Filter<Document>).toArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export const REDIS_CLIENT = 'REDIS_CLIENT';
|
||||
export const REDIS_OPTIONS = 'REDIS_OPTIONS';
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { ModuleMetadata, Type } from '@nestjs/common';
|
||||
|
||||
export interface RedisModuleOptions {
|
||||
config?: {
|
||||
host?: string;
|
||||
port?: number;
|
||||
password?: string;
|
||||
db?: number;
|
||||
keyPrefix?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RedisOptionsFactory {
|
||||
createRedisOptions(): Promise<RedisModuleOptions> | RedisModuleOptions;
|
||||
}
|
||||
|
||||
export interface RedisModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
||||
useExisting?: Type<RedisOptionsFactory>;
|
||||
useClass?: Type<RedisOptionsFactory>;
|
||||
useFactory?: (...args: any[]) => Promise<RedisModuleOptions> | RedisModuleOptions;
|
||||
inject?: any[];
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import Redis from 'ioredis';
|
||||
|
||||
import { DynamicModule, Module, OnApplicationShutdown, Provider } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { CacheService } from './redis.service';
|
||||
import { REDIS_CLIENT, REDIS_OPTIONS } from './redis.constants';
|
||||
import { RedisModuleOptions, RedisModuleAsyncOptions } from './redis.interfaces';
|
||||
|
||||
@Module({})
|
||||
export class RedisModule implements OnApplicationShutdown {
|
||||
constructor(private moduleRef: ModuleRef) { }
|
||||
|
||||
/**
|
||||
* Registers the module with default configuration.
|
||||
*
|
||||
* @param isGlobal - Register in the global scope
|
||||
* @returns A DynamicModule
|
||||
*/
|
||||
static forRootWithConfig(isGlobal = false): DynamicModule {
|
||||
const redisConfig = { host: '10.10.2.15', port: 6379, password: 'your_strong_password_here' };
|
||||
return RedisModule.forRoot({ config: redisConfig }, isGlobal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the module synchronously.
|
||||
*
|
||||
* @param options - The module options
|
||||
* @param isGlobal - Register in the global scope
|
||||
* @returns A DynamicModule
|
||||
*/
|
||||
static forRoot(options?: RedisModuleOptions, isGlobal = false): DynamicModule {
|
||||
const redisOptionsProvider: Provider = {
|
||||
provide: REDIS_OPTIONS,
|
||||
useValue: options || {},
|
||||
};
|
||||
|
||||
const redisClientProvider: Provider = {
|
||||
provide: REDIS_CLIENT,
|
||||
useFactory: () => {
|
||||
if (!options || !options.config) { return new Redis() }
|
||||
const { host, port, password } = options.config;
|
||||
return new Redis({ host, port, password });
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
module: RedisModule,
|
||||
providers: [redisOptionsProvider, redisClientProvider, CacheService],
|
||||
exports: [CacheService],
|
||||
global: isGlobal,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the module asynchronously.
|
||||
*
|
||||
* @param options - The async module options
|
||||
* @param isGlobal - Register in the global scope
|
||||
* @returns A DynamicModule
|
||||
*/
|
||||
static forRootAsync(options: RedisModuleAsyncOptions, isGlobal = false): DynamicModule {
|
||||
const redisOptionsProvider: Provider = {
|
||||
provide: REDIS_OPTIONS,
|
||||
useFactory: options.useFactory || (() => ({})),
|
||||
inject: options.inject || [],
|
||||
};
|
||||
|
||||
const redisClientProvider: Provider = {
|
||||
provide: REDIS_CLIENT,
|
||||
useFactory: (redisOptions: RedisModuleOptions) => {
|
||||
if (!redisOptions || !redisOptions.config) { return new Redis() }
|
||||
const { host, port, password } = redisOptions.config;
|
||||
return new Redis({ host, port, password })
|
||||
},
|
||||
inject: [REDIS_OPTIONS],
|
||||
};
|
||||
|
||||
return {
|
||||
module: RedisModule,
|
||||
imports: options.imports || [],
|
||||
providers: [redisOptionsProvider, redisClientProvider, CacheService],
|
||||
exports: [CacheService],
|
||||
global: isGlobal,
|
||||
};
|
||||
}
|
||||
|
||||
async onApplicationShutdown() {
|
||||
const client = this.moduleRef.get(REDIS_CLIENT, { strict: false });
|
||||
if (client && typeof client.quit === 'function') { await client.quit() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CacheService } from './redis.service';
|
||||
import Redis from 'ioredis';
|
||||
import { REDIS_CLIENT } from './redis.constants';
|
||||
|
||||
describe('CacheService', () => {
|
||||
let service: CacheService;
|
||||
let mockRedisClient: Redis;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a mock Redis client
|
||||
mockRedisClient = new Redis();
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CacheService,
|
||||
{
|
||||
provide: REDIS_CLIENT,
|
||||
useValue: mockRedisClient,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CacheService>(CacheService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { RedisService } from '@liaoliaots/nestjs-redis';
|
||||
import Redis from 'ioredis';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { REDIS_CLIENT } from './redis.constants';
|
||||
|
||||
@Injectable()
|
||||
export class CacheService {
|
||||
private client: Redis;
|
||||
|
||||
constructor(private readonly redisService: RedisService) {
|
||||
this.client = this.redisService.getOrThrow();
|
||||
constructor(@Inject(REDIS_CLIENT) private readonly redisClient: Redis) {
|
||||
this.client = redisClient;
|
||||
}
|
||||
|
||||
async set(key: string, value: any) {
|
||||
|
|
@ -16,10 +16,8 @@ export class CacheService {
|
|||
|
||||
async get(key: string): Promise<any | null> {
|
||||
const value = await this.client.get(key);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return { key, value: JSON.parse(value) };
|
||||
if (!value) { return null }
|
||||
return { key, value: JSON.parse(value) }
|
||||
}
|
||||
|
||||
async set_with_ttl(key: string, value: any, ttl: number) {
|
||||
|
|
@ -32,10 +30,7 @@ export class CacheService {
|
|||
|
||||
async delete(key: string): Promise<boolean> {
|
||||
const deleted = await this.client.del(key);
|
||||
if (deleted === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return deleted === 0 ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -4,8 +4,8 @@ import {
|
|||
Injectable,
|
||||
ForbiddenException,
|
||||
} from '@nestjs/common';
|
||||
import { RedisHandlers } from '@/src/utils/auth/redisHandlers';
|
||||
import { UrlHandler } from '@/src/utils/auth/urlHandler';
|
||||
import { RedisHandlers } from '@/src/utils/store/redisHandlers';
|
||||
import { UrlHandler } from '@/src/utils/navigator/urlHandler';
|
||||
|
||||
@Injectable()
|
||||
export class AuthControlGuard implements CanActivate {
|
||||
|
|
@ -14,7 +14,6 @@ export class AuthControlGuard implements CanActivate {
|
|||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
const accessToken = this.cacheService.mergeLoginKey(req);
|
||||
// console.log('AuthControlGuard', accessToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +33,6 @@ export class EndpointControlGuard implements CanActivate {
|
|||
const driveToken = await this.urlHandler.getSecureUrlToken(keyUrl);
|
||||
const accessObject = await this.cacheService.getSelectFromRedis(req);
|
||||
req.driveToken = `${driveToken}:${accessObject?.value.functionsRetriever}`;
|
||||
console.log('EndpointControlGuard driveToken: ', driveToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { IsString, IsObject, IsOptional, IsNumber } from 'class-validator';
|
||||
|
||||
export class mongoSetValidator {
|
||||
@IsString()
|
||||
collectionName: string;
|
||||
|
||||
@IsObject()
|
||||
data: object;
|
||||
}
|
||||
|
||||
export class mongoGetValidator {
|
||||
@IsString()
|
||||
collectionName: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
regexKey: string;
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
filter: object;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
limit: number;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
skip: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { IsString, IsObject, IsOptional, IsNumber, ValidateNested } from 'class-validator';
|
||||
|
||||
// { # collection Events:Build-UUIDv4 | Events:Company-UUIDv4 : "userUUID" : { "userTypeToken" : { "siteUrlToken" : "eventKey" } } }
|
||||
// const jsonData = { 'USER-UUID(V4)': { 'j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA': { 'e6hewIe7YqbQZHO3': 'qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx' } } };
|
||||
|
||||
export class EventsSetterValidator {
|
||||
|
||||
@IsObject()
|
||||
data: Record<string, Record<string, Record<string, string>>>;
|
||||
|
||||
@IsString()
|
||||
dutyUUID: string; // UUID of employee or occupant
|
||||
}
|
||||
|
||||
export class EventsGetterValidator {
|
||||
@IsString()
|
||||
collectionName: string;
|
||||
|
||||
@IsString()
|
||||
dutyUUID: string; // UUID of employee or occupant
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
regexKey?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
searchType?: 'value' | 'key' | 'both';
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
filter?: object = {};
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
limit?: number = 1;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
skip?: number = 0;
|
||||
}
|
||||
|
|
@ -1,4 +1,199 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Body, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { MongoService } from '@/src/database/mongo/mongo.service';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { EventsGetterValidator, EventsSetterValidator } from '@/src/navigator/events/dtoValidator';
|
||||
import { Document } from 'mongodb';
|
||||
|
||||
type SearchType = 'value' | 'key' | 'both';
|
||||
|
||||
@Injectable()
|
||||
export class EventsService {}
|
||||
export class EventsService {
|
||||
|
||||
// const result = await eventsService.getEventsOccupants({
|
||||
// collectionName: 'Events:Build-UUIDv4',
|
||||
// regexKey: 'qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx'
|
||||
// });
|
||||
// const result = await eventsService.getEventsOccupants({
|
||||
// collectionName: 'Events:Build-UUIDv4',
|
||||
// regexKey: 'e6hewIe7YqbQZHO3',
|
||||
// searchType: 'key'
|
||||
// });
|
||||
// const result = await eventsService.getEventsOccupants({
|
||||
// collectionName: 'Events:Build-UUIDv4',
|
||||
// regexKey: 'e6hewIe7YqbQZHO3',
|
||||
// searchType: 'both'
|
||||
// });
|
||||
|
||||
constructor(private mongoService: MongoService, private prisma: PrismaService) { }
|
||||
seperator = "/"
|
||||
|
||||
private async getBuildUUID(uuid: string) {
|
||||
const livingSpace = await this.prisma.build_living_space.findFirstOrThrow({
|
||||
where: { uu_id: uuid },
|
||||
select: {
|
||||
people: {
|
||||
select: {
|
||||
users: {
|
||||
select: {
|
||||
uu_id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
build_parts: {
|
||||
select: {
|
||||
build: {
|
||||
select: {
|
||||
uu_id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const userUUID = livingSpace.people.users[0].uu_id
|
||||
const buildUUID = livingSpace.build_parts.build.uu_id
|
||||
return { userUUID, buildUUID }
|
||||
}
|
||||
|
||||
private async getCompanyUUID(uuid: string) {
|
||||
const employee = await this.prisma.employees.findFirstOrThrow({
|
||||
where: { uu_id: uuid },
|
||||
select: {
|
||||
people: {
|
||||
select: {
|
||||
users: {
|
||||
select: {
|
||||
uu_id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
staff: {
|
||||
select: {
|
||||
duties: {
|
||||
select: {
|
||||
company_uu_id: true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const userUUID = employee.people?.users[0].uu_id
|
||||
const companyUUID = employee.staff?.duties.company_uu_id
|
||||
return { userUUID, companyUUID }
|
||||
}
|
||||
|
||||
private validateCollectionName(collectionName: string) {
|
||||
if (!collectionName) {
|
||||
throw new NotFoundException('Collection name is required')
|
||||
}
|
||||
}
|
||||
|
||||
private async setupMongoCollection(collectionName: string, buildUUID: string) {
|
||||
await this.mongoService.set(collectionName);
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${buildUUID}`);
|
||||
}
|
||||
|
||||
private async findByRegexKey(regexKey: string, searchType: SearchType = 'value'): Promise<Document[]> {
|
||||
const tokens = await this.mongoService.findByRegexAcrossFields(regexKey, 'i', searchType);
|
||||
if (tokens.length === 0) {
|
||||
throw new NotFoundException('Token not found')
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private async findByFilter(filter: object): Promise<Document[]> {
|
||||
const tokens = await this.mongoService.findMany(filter);
|
||||
if (tokens.length === 0) {
|
||||
throw new NotFoundException('Token not found')
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
async getEventsOccupants(livingSpaceUUID: string) {
|
||||
const eventsObject = {}
|
||||
const { userUUID, buildUUID } = await this.getBuildUUID(livingSpaceUUID);
|
||||
const collectionKey = `Events/${buildUUID}`
|
||||
this.validateCollectionName(collectionKey);
|
||||
await this.mongoService.set(collectionKey)
|
||||
const eventsResponse = await this.mongoService.findOne({ [userUUID]: { $exists: true } });
|
||||
if (eventsResponse && typeof eventsResponse === 'object') {
|
||||
const mapOfEvents = eventsResponse[userUUID];
|
||||
if (mapOfEvents && typeof mapOfEvents === 'object') {
|
||||
const userTypeTokenKey = Object.keys(mapOfEvents)[0];
|
||||
const userTypeTokenValue = mapOfEvents[userTypeTokenKey];
|
||||
if (userTypeTokenValue && typeof userTypeTokenValue === 'object') {
|
||||
for (const siteUrlTokenKey of Object.keys(userTypeTokenValue)) {
|
||||
const siteUrlTokenValue = userTypeTokenValue[siteUrlTokenKey];
|
||||
eventsObject[`${siteUrlTokenKey}:${userTypeTokenKey}`] = siteUrlTokenValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return eventsObject
|
||||
}
|
||||
|
||||
async getEventsEmployees(@Body() body: EventsGetterValidator) {
|
||||
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
|
||||
if (!companyUUID) { throw new NotFoundException('Company not found') }
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${companyUUID}`);
|
||||
if (body.regexKey) {
|
||||
// Use the new flattened search for regex with search type
|
||||
const searchType: SearchType = body.searchType as SearchType || 'value';
|
||||
const tokens = await this.mongoService.findByRegexAcrossFields(body.regexKey, 'i', searchType);
|
||||
if (tokens.length === 0) { throw new NotFoundException('Token not found') }
|
||||
return { data: tokens, message: 'Tokens found' };
|
||||
} else if (body.filter) {
|
||||
const tokens = await this.mongoService.findMany(body.filter);
|
||||
if (tokens.length === 0) { throw new NotFoundException('Token not found') }
|
||||
return { data: tokens, message: 'Tokens found' };
|
||||
} else { throw new NotFoundException('Regex key or filter is required') }
|
||||
}
|
||||
|
||||
private async setSavedEventToMapper(data: any, useruuid: string) {
|
||||
await this.mongoService.set(`MAP${this.seperator}EVENTS`);
|
||||
const events = await this.mongoService.findOrCreate({ uuid: `EVENTS:${useruuid}:${data.uuid}`, data });
|
||||
}
|
||||
|
||||
private async deleteSavedEventFromMapper(data: any, useruuid: string) {
|
||||
await this.mongoService.set(`MAP${this.seperator}EVENTS`);
|
||||
const events = await this.mongoService.deleteMany({ uuid: `EVENTS:${useruuid}:${data.uuid}` });
|
||||
return events;
|
||||
}
|
||||
|
||||
async setEventsEmployees(@Body() body: EventsSetterValidator) {
|
||||
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
|
||||
if (!companyUUID) { throw new NotFoundException('Company not found') }
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${companyUUID}`);
|
||||
const events = await this.mongoService.findOrCreate(body.data);
|
||||
// await this.setSavedEventToMapper(events, body.dutyUUID);
|
||||
return events;
|
||||
}
|
||||
|
||||
async setEventsOccupants(@Body() body: EventsSetterValidator) {
|
||||
const buildUUID = await this.getBuildUUID(body.dutyUUID);
|
||||
if (!buildUUID) { throw new NotFoundException('Build not found') }
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${buildUUID}`);
|
||||
const events = await this.mongoService.findOrCreate(body.data);
|
||||
return events;
|
||||
}
|
||||
|
||||
async deleteEventsEmployees(@Body() body: EventsGetterValidator) {
|
||||
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
|
||||
if (!companyUUID) { throw new NotFoundException('Company not found') }
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${companyUUID}`);
|
||||
const events = await this.mongoService.deleteMany({ uuid: { $regex: body.regexKey, $options: 'i' } });
|
||||
return events;
|
||||
}
|
||||
|
||||
async deleteEventsOccupants(@Body() body: EventsGetterValidator) {
|
||||
const buildUUID = await this.getBuildUUID(body.dutyUUID);
|
||||
if (!buildUUID) { throw new NotFoundException('Build not found') }
|
||||
await this.mongoService.set(`EVENTS${this.seperator}${buildUUID}`);
|
||||
const events = await this.mongoService.deleteMany({ uuid: { $regex: body.regexKey, $options: 'i' } });
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { validate } from 'class-validator';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { EventsSetterValidator } from './dtoValidator';
|
||||
|
||||
// Example JSON data to validate
|
||||
const jsonData = {
|
||||
collectionName: 'Events:Build-UUIDv4',
|
||||
data: {
|
||||
'USER-UUID(V4)': {
|
||||
'j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA': {
|
||||
'e6hewIe7YqbQZHO3': 'qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function validateEventData() {
|
||||
const eventData = plainToClass(EventsSetterValidator, jsonData);
|
||||
const errors = await validate(eventData, {
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
validationError: { target: false }
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.log('Validation failed. Errors:', JSON.stringify(errors, null, 2));
|
||||
return false;
|
||||
} else {
|
||||
console.log('Validation successful!');
|
||||
console.log('Validated data:', JSON.stringify(eventData, null, 2));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
validateEventData()
|
||||
.then(isValid => console.log('Is valid:', isValid))
|
||||
.catch(err => console.error('Error during validation:', err));
|
||||
|
|
@ -0,0 +1,394 @@
|
|||
export interface interfaceMenu {
|
||||
"key": string;
|
||||
"icon": string;
|
||||
"text": { "tr": string, "en": string };
|
||||
"page": string | null;
|
||||
"token": string | null;
|
||||
"color": string;
|
||||
"subs": interfaceMenu[] | null;
|
||||
}
|
||||
|
||||
export interface interfaceMapper {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface interfaceMenus {
|
||||
"Menu": interfaceMenu[];
|
||||
"Mapper": interfaceMapper;
|
||||
}
|
||||
|
||||
function generateMapperKey(keys: string[]): string {
|
||||
return keys.join(':') + ':';
|
||||
}
|
||||
|
||||
function generateMapper(menu: interfaceMenu[], parentKeys: string[] = []): interfaceMapper {
|
||||
let mapper: interfaceMapper = {};
|
||||
|
||||
for (const item of menu) {
|
||||
const currentKeys = [...parentKeys, item.key];
|
||||
|
||||
// If this item has a page, add it to the mapper
|
||||
if (item.page) {
|
||||
mapper[item.page] = generateMapperKey(currentKeys);
|
||||
}
|
||||
|
||||
if (item.subs) {
|
||||
const subMapper = generateMapper(item.subs, currentKeys);
|
||||
mapper = { ...mapper, ...subMapper };
|
||||
}
|
||||
}
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
function generateDynamicMapper(menus: interfaceMenu[]): interfaceMapper {
|
||||
return generateMapper(menus);
|
||||
}
|
||||
|
||||
{/*
|
||||
|
||||
*/}
|
||||
const menuForEmployeeDefinition = [
|
||||
{
|
||||
key: "a6EoBlTPSgGbUQELbyRwMA",
|
||||
icon: "",
|
||||
text: { "tr": "Dashboard", "en": "Dashboard" },
|
||||
page: "/dashboard",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
{
|
||||
key: "NV2kI8NERmqrNgIeiUYojQ",
|
||||
icon: "",
|
||||
text: { "tr": "Bireysel", "en": "Individual" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "xnhFAyi3Sp2qVWcVcR6m9w",
|
||||
icon: "",
|
||||
text: { "tr": "Birey", "en": "Person" },
|
||||
page: "/person",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "7wdsqwCQSmXRsRPC9GSgwx",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/person/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "56O8WRP4TyC7F8bc1vjXgx",
|
||||
icon: "",
|
||||
text: { "tr": "Güncelle", "en": "Update" },
|
||||
page: "/person/update",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "RPaESp64SUmjNyEY1WUE8Q",
|
||||
icon: "",
|
||||
text: { "tr": "Sil", "en": "Delete" },
|
||||
page: "/person/delete",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "qcRK3EPQSoLSWkJFhtWOwx",
|
||||
icon: "",
|
||||
text: { "tr": "Kullanıcı", "en": "User" },
|
||||
page: "/users",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "PqNGe0SaQKeyUGyzJoSLwx",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/users/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "ruvQlE7wQzqHqUvCNIoUnA",
|
||||
icon: "",
|
||||
text: { "tr": "Güncelle", "en": "Update" },
|
||||
page: "/users/update",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "DfDStf1dTBCRShNQeb5pZA",
|
||||
icon: "",
|
||||
text: { "tr": "Sil", "en": "Delete" },
|
||||
page: "/users/delete",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "ALV19bQ8S7q8LpOkdRDMwx",
|
||||
icon: "",
|
||||
text: { "tr": "Bina", "en": "Build" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "eToBYS4DTEKseVYMJLNZwx",
|
||||
icon: "",
|
||||
text: { "tr": "Binalar", "en": "Building" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "EkR7p6qmRN2Wb1GLsH5aEQ",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/building/build/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "qcoHwABjSli04D7xeWGOHQ",
|
||||
icon: "",
|
||||
text: { "tr": "Güncelle", "en": "Update" },
|
||||
page: "/building/build/update",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "vC2oPkjRfudvBDlNReeRAx",
|
||||
icon: "",
|
||||
text: { "tr": "Sil", "en": "Delete" },
|
||||
page: "/building/build/delete",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "NFte61RnTHGPWlnoUItHAx",
|
||||
icon: "",
|
||||
text: { "tr": "Daireler", "en": "Parts" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "7o6QNpelSpmxpJxTedEj4w",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/building/parts/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "rP6idRkyToLcxwpalCxgxx",
|
||||
icon: "OBKPalaMQwWhQmQ9Ni0y6Q",
|
||||
text: { "tr": "Güncelle", "en": "Update" },
|
||||
page: "/building/parts/update",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
},
|
||||
{
|
||||
key: "CBNaWzVqRaSpWaPTM54PbA",
|
||||
icon: "",
|
||||
text: { "tr": "Sil", "en": "Delete" },
|
||||
page: "/building/parts/delete",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "NFte61RnTHGPWlnoUItHAx",
|
||||
icon: "",
|
||||
text: { "tr": "Alanlar", "en": "Area" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "yzvyvqMhQ06TdC9paOw4Ax",
|
||||
icon: "",
|
||||
text: { "tr": "Yönetim", "en": "Management" },
|
||||
page: null,
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "DEumSZtaTSKiDsD1VJPQxx",
|
||||
icon: "",
|
||||
text: { "tr": "Bütçe", "en": "Budget" },
|
||||
page: "/management/budget",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "PIPD61aZRveFZ6GGfK3VYw",
|
||||
icon: "",
|
||||
text: { "tr": "Eylemler", "en": "Actions" },
|
||||
page: "/management/budget/actions",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
{
|
||||
key: "",
|
||||
icon: "",
|
||||
text: { "tr": "Durum", "en": "Status" },
|
||||
page: "/management/budget/status",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "RHI0bthYRjWWf4tBaPBdgx",
|
||||
icon: "",
|
||||
text: { "tr": "Toplantılar", "en": "Meetings" },
|
||||
page: "/meetings",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "OESxDOI6S4eNcdeRCrKIjQ",
|
||||
icon: "",
|
||||
text: { "tr": "Yıllık", "en": "Annual" },
|
||||
page: "/meetings/annual",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "MhEHidsRWyHdCqtHJOcvAx",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/meetings/annual/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
{
|
||||
key: "xhnSW4hWSDuJyREMjXOivA",
|
||||
icon: "",
|
||||
text: { "tr": "Kapat", "en": "Close" },
|
||||
page: "/meetings/annual/close",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "A4raUDNFTpZ7mPfqJBGSwx",
|
||||
icon: "",
|
||||
text: { "tr": "Acil", "en": "Emergency" },
|
||||
page: "/meetings/emergency",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [
|
||||
{
|
||||
key: "T3Fd0C5Tf2V1dZhiZuNQxx",
|
||||
icon: "",
|
||||
text: { "tr": "Oluştur", "en": "Create" },
|
||||
page: "/meetings/emergency/create",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
{
|
||||
key: "L1ogOYhSl6BDPstufiSwxx",
|
||||
icon: "",
|
||||
text: { "tr": "Kapat", "en": "Close" },
|
||||
page: "/meetings/emergency/close",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "vwzmxtBoQFW62YHes5OZAg",
|
||||
icon: "",
|
||||
text: { "tr": "Katılımlar", "en": "Participations" },
|
||||
page: "/meetings/participations",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [],
|
||||
}
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
const menuForOccupantDefinition = [
|
||||
{
|
||||
key: "dzFGPzZJRgmft4HrrTeBtQ",
|
||||
icon: "",
|
||||
text: { "tr": "Pano", "en": "Dashboard" },
|
||||
page: "/dashboard",
|
||||
token: null,
|
||||
color: "",
|
||||
subs: [],
|
||||
},
|
||||
]
|
||||
|
||||
const config = {
|
||||
FirstLayerColor: "#ebc334",
|
||||
SecondLayerColor: "#18910d",
|
||||
ThirdLayerColor: "#2825c4",
|
||||
employeePrefix: "/office",
|
||||
occupantPrefix: "/venue"
|
||||
}
|
||||
|
||||
function applyColorsAndPrefixes(menu: interfaceMenu[], isEmployee: boolean, config: any, layer: number = 1): interfaceMenu[] {
|
||||
return menu.map(item => {
|
||||
const newItem = { ...item };
|
||||
if (layer === 1) newItem.color = config.FirstLayerColor;
|
||||
else if (layer === 2) newItem.color = config.SecondLayerColor;
|
||||
else if (layer >= 3) newItem.color = config.ThirdLayerColor;
|
||||
if (newItem.page) { newItem.page = `${isEmployee ? config.employeePrefix : config.occupantPrefix}${newItem.page}` }
|
||||
if (newItem.subs) { newItem.subs = applyColorsAndPrefixes(newItem.subs, isEmployee, layer + 1) }
|
||||
return newItem;
|
||||
});
|
||||
}
|
||||
|
||||
export const occupantMenus: interfaceMenus = {
|
||||
Menu: applyColorsAndPrefixes(menuForOccupantDefinition, false, config),
|
||||
Mapper: generateDynamicMapper(applyColorsAndPrefixes(menuForOccupantDefinition, false, config))
|
||||
};
|
||||
|
||||
export const employeeMenus: interfaceMenus = {
|
||||
Menu: applyColorsAndPrefixes(menuForEmployeeDefinition, true, config),
|
||||
Mapper: generateDynamicMapper(applyColorsAndPrefixes(menuForEmployeeDefinition, true, config))
|
||||
};
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class ServicesService {}
|
||||
export class MenusService { }
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { employeeMenus, occupantMenus } from './main';
|
||||
|
||||
console.log('Employee Menu Mapper:');
|
||||
console.log(JSON.stringify(employeeMenus.Mapper, null, 2));
|
||||
|
||||
console.log('\nEmployee Menu Structure (with colors):');
|
||||
console.log(JSON.stringify(employeeMenus.Menu, null, 2));
|
||||
|
||||
console.log('\nOccupant Menu Mapper:');
|
||||
console.log(JSON.stringify(occupantMenus.Mapper, null, 2));
|
||||
|
||||
console.log('\nOccupant Menu Structure (with colors):');
|
||||
console.log(JSON.stringify(occupantMenus.Menu, null, 2));
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { NavigatorController } from './navigator.controller';
|
||||
|
||||
describe('NavigatorController', () => {
|
||||
let controller: NavigatorController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [NavigatorController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<NavigatorController>(NavigatorController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { Controller, Post, NotFoundException, Body } from '@nestjs/common';
|
||||
import { MongoService } from '@/src/database/mongo/mongo.service';
|
||||
import { mongoSetValidator, mongoGetValidator } from '@/src/navigator/dtoValidator';
|
||||
|
||||
@Controller('navigator')
|
||||
export class NavigatorController {
|
||||
constructor(private mongoService: MongoService) { }
|
||||
|
||||
@Post('event/set')
|
||||
async setEvent(@Body() body: any) { }
|
||||
|
||||
@Post('event/get')
|
||||
async getEvent(@Body() body: any) {
|
||||
// Get all events from backend statics & Get users registered event from mongo service
|
||||
}
|
||||
|
||||
@Post('page/set')
|
||||
async setPage(@Body() body: any) { }
|
||||
|
||||
@Post('page/get')
|
||||
async getPage(@Body() body: any) {
|
||||
// Get all pages from Frontend & Get users registered page from mongo service
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { MongoModule } from '@/src/database/mongo/mongo.module';
|
||||
import { MenusService } from '@/src/navigator/menus/services.service';
|
||||
import { NavigatorController } from '@/src/navigator/navigator.controller';
|
||||
import { EventsService } from './events/events.service';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [NavigatorController],
|
||||
imports: [MongoModule],
|
||||
providers: [MenusService, EventsService, PrismaService],
|
||||
exports: [MenusService, EventsService, PrismaService]
|
||||
})
|
||||
export class NavigatorModule {
|
||||
constructor() { }
|
||||
}
|
||||
|
|
@ -240,7 +240,7 @@ export const EmployeeTokenSchema = z.object({
|
|||
|
||||
menu: z.array(z.object({})).nullable(),
|
||||
pages: z.array(z.string()).nullable(),
|
||||
events: z.array(z.string()).nullable(),
|
||||
events: z.record(z.string(), z.string()).nullable(),
|
||||
|
||||
selection: z.record(z.string(), z.unknown()).nullable(),
|
||||
typeToken: z.string(),
|
||||
|
|
@ -258,7 +258,7 @@ export const OccupantTokenSchema = z.object({
|
|||
|
||||
menu: z.array(z.object({})).nullable(),
|
||||
pages: z.array(z.string()).nullable(),
|
||||
events: z.array(z.string()).nullable(),
|
||||
events: z.record(z.string(), z.string()).nullable(),
|
||||
|
||||
selection: z.record(z.string(), z.unknown()).nullable(),
|
||||
typeToken: z.string(),
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@ import { Module } from '@nestjs/common';
|
|||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { PrismaModule } from '@/prisma/prisma.module';
|
||||
import { CacheService } from '../cache.service';
|
||||
import { CacheService } from '../database/redis/redis.service';
|
||||
import { UtilsModule } from '../utils/utils.module';
|
||||
import { RedisModule } from '../database/redis/redis.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, UtilsModule],
|
||||
providers: [UsersService, CacheService],
|
||||
imports: [PrismaModule, UtilsModule, RedisModule],
|
||||
providers: [UsersService],
|
||||
controllers: [UsersController],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
export class UsersModule { }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { Prisma, users } from '@prisma/client';
|
||||
import { CacheService } from '../cache.service';
|
||||
import { CacheService } from '../database/redis/redis.service';
|
||||
import { PaginationHelper, PaginationInfo } from '../utils/pagination-helper';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -10,7 +10,7 @@ export class UsersService {
|
|||
private prisma: PrismaService,
|
||||
private cacheService: CacheService,
|
||||
private paginationHelper: PaginationHelper,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async findAll(filter: any): Promise<Partial<users>[]> {
|
||||
return this.prisma.users.findMany({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
import { ForbiddenException, MisdirectedException, Injectable } from "@nestjs/common";
|
||||
import { RedisHandlers } from "../store/redisHandlers";
|
||||
|
||||
@Injectable()
|
||||
export class Navigator {
|
||||
|
||||
constructor(private redisHandler: RedisHandlers) { }
|
||||
|
||||
async getInfos(mainService: any, userToken: string) {
|
||||
// Get asked service by userToken
|
||||
const mainServiceMapper = mainService?.mapper
|
||||
if (!mainServiceMapper) { throw new ForbiddenException(`Mapper in ${mainService.constructor.name} is missing or null`) }
|
||||
// Get related events from mainServiceMapper by userToken
|
||||
const relatedService = mainServiceMapper?.[userToken]
|
||||
if (!relatedService) { throw new MisdirectedException(`No service found for drive token: ${userToken}`) }
|
||||
// Call event infos from relatedService
|
||||
return await relatedService.infoEvents(userToken);
|
||||
}
|
||||
|
||||
async getService(request: any, mapper: any) {
|
||||
// Get request drive token from acess control guard and retrieve related Service
|
||||
const driveToken = request.driveToken
|
||||
if (!driveToken) { throw new Error('Drive token is missing or null') }
|
||||
// Get second part of drive token which is user type token
|
||||
const secondPartOfDriveToken = driveToken.split(":")[1]
|
||||
if (!secondPartOfDriveToken) { throw new Error('Drive token is missing or null') }
|
||||
// Get related service from mapper which function maps registered events to functions
|
||||
return mapper[secondPartOfDriveToken];
|
||||
}
|
||||
|
||||
async getFunction(request: any, mapper: any, query: any) {
|
||||
const relatedService = await this.getService(request, mapper)
|
||||
if (!relatedService) { throw new Error(`No service found for drive token: ${request.driveToken}`) }
|
||||
try {
|
||||
// Get function mapper from related service
|
||||
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(request);
|
||||
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(request.driveToken))[0]
|
||||
if (!eventKey) { throw new Error(`No event is registered for this user ${request.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: ${request.driveToken}`);
|
||||
}
|
||||
return await functionToCall(query);
|
||||
} catch (error) { throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`) }
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,6 @@ export class PaginationHelper {
|
|||
query: any & { page?: number; pageSize?: number },
|
||||
service: ModelDelegate,
|
||||
): Promise<{ data: any[]; pagination: PaginationInfo }> {
|
||||
console.log("findWithPagination query", query)
|
||||
return this.paginate(service, query);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class MongoHandler {
|
||||
constructor() { }
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ import {
|
|||
TokenDictInterface,
|
||||
AuthToken,
|
||||
AuthTokenSchema,
|
||||
} from '@/src/types/auth/token';
|
||||
import { CacheService } from '@/src/cache.service';
|
||||
} from '../../types/auth/token';
|
||||
import { CacheService } from '../../database/redis/redis.service';
|
||||
import { PasswordHandlers } from './loginHandler';
|
||||
import { Injectable, ForbiddenException } from '@nestjs/common';
|
||||
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { PaginationHelper } from './pagination-helper';
|
||||
import { PrismaService } from '@/src/prisma.service';
|
||||
import { RedisHandlers } from './auth/redisHandlers';
|
||||
import { PasswordHandlers } from './auth/loginHandler';
|
||||
import { CacheService } from '@/src/cache.service';
|
||||
import { RedisHandlers } from './store/redisHandlers';
|
||||
import { PasswordHandlers } from './store/loginHandler';
|
||||
import { RedisModule } from '../database/redis/redis.module';
|
||||
|
||||
@Module({
|
||||
imports: [RedisModule],
|
||||
providers: [
|
||||
PaginationHelper,
|
||||
PrismaService,
|
||||
RedisHandlers,
|
||||
PasswordHandlers,
|
||||
CacheService,
|
||||
],
|
||||
exports: [PaginationHelper, RedisHandlers, PasswordHandlers, CacheService],
|
||||
exports: [PaginationHelper, RedisHandlers, PasswordHandlers],
|
||||
})
|
||||
export class UtilsModule { }
|
||||
export class UtilsModule { }
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"types": ["node", "jest"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
// "baseUrl": "./src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,6 @@ export default async function ProtectedLayout({
|
|||
const headersList = await headers();
|
||||
// const locale = getLocaleFromPath(removeSubStringFromPath(headersList));
|
||||
const removedLocaleRoute = removeSubStringFromPath(headersList);
|
||||
console.log('Removed locale route:', removedLocaleRoute);
|
||||
// console.log('Removed locale route:', removedLocaleRoute);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
'use server';
|
||||
import React from 'react';
|
||||
import { dashboardPages } from '@/pages/office/dashboard/mapper';
|
||||
import { renderPage } from '@/lib/page';
|
||||
import { getSelectToken } from '@/fetchers/token/select';
|
||||
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const pageUrl = "/office/dashboard";
|
||||
const selectToken = await getSelectToken();
|
||||
const RenderPage = renderPage(selectToken, pageUrl, dashboardPages);
|
||||
if (RenderPage) {
|
||||
|
||||
return <>
|
||||
<div>Dashboard Page</div>
|
||||
<div className='flex align-center justify-center h-screen w-screen mt-10 text-2xl'>
|
||||
<RenderPage />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
return <>
|
||||
<div>Dashboard Page</div>
|
||||
<div>You are not allowed to reach any page under {pageUrl}. Please contact your administrator.</div>
|
||||
</>;
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
'use server';
|
||||
import React from 'react';
|
||||
import { dashboardPages } from '@/pages/venue/dashboard/mapper';
|
||||
import { renderPage } from '@/lib/page';
|
||||
import { getSelectTokenObject } from '@/fetchers/token/select';
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const pageUrl = "/venue/dashboard";
|
||||
const selectToken = await getSelectTokenObject();
|
||||
if (!selectToken) {
|
||||
const RenderPage = renderPage(selectToken, pageUrl, dashboardPages);
|
||||
if (RenderPage) {
|
||||
return <>
|
||||
<div>Dashboard Page</div>
|
||||
<div className='flex align-center justify-center h-screen w-screen mt-10 text-2xl'>
|
||||
<RenderPage />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
return <>
|
||||
<div>Dashboard Page</div>
|
||||
<div>You are not allowed to reach any page under {pageUrl}. Please contact your administrator.</div>
|
||||
</>;
|
||||
};
|
||||
|
|
@ -32,6 +32,7 @@ interface UpdateFieldParams<T> {
|
|||
}
|
||||
|
||||
type RScan = Promise<string | null>;
|
||||
type RScanToken = Promise<any | null>;
|
||||
type RExists = Promise<boolean>;
|
||||
type RUpdate = Promise<void>;
|
||||
type RDelete = Promise<void>;
|
||||
|
|
@ -122,3 +123,38 @@ export async function scanByRKeyDouble(params: DScanParams): RScan {
|
|||
} while (cursor !== "0");
|
||||
return keys.length > 0 ? keys[0] : null;
|
||||
}
|
||||
|
||||
export async function scanByRKeySingleToken(params: SScanParams): RScan {
|
||||
const pattern = redisScanAccess(params.rKey);
|
||||
const keys: string[] = [];
|
||||
let cursor = "0";
|
||||
do {
|
||||
const [nextCursor, matchedKeys] = await redis.scan(
|
||||
cursor,
|
||||
"MATCH",
|
||||
pattern
|
||||
);
|
||||
cursor = nextCursor;
|
||||
keys.push(...matchedKeys);
|
||||
} while (cursor !== "0");
|
||||
if (keys.length === 0) return null;
|
||||
return await getJSON({ key: keys[0] });
|
||||
}
|
||||
|
||||
export async function scanByRKeyDoubleToken(params: DScanParams): RScan {
|
||||
const pattern = redisScanSelect(params.rKey, params.sKey);
|
||||
console.log("pattern", pattern);
|
||||
const keys: string[] = [];
|
||||
let cursor = "0";
|
||||
do {
|
||||
const [nextCursor, matchedKeys] = await redis.scan(
|
||||
cursor,
|
||||
"MATCH",
|
||||
pattern
|
||||
);
|
||||
cursor = nextCursor;
|
||||
keys.push(...matchedKeys);
|
||||
} while (cursor !== "0");
|
||||
if (keys.length === 0) return null;
|
||||
return await getJSON({ key: keys[0] });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use server';
|
||||
import { AuthError } from "@/fetchers/types/base";
|
||||
import { scanByRKeyDouble } from "@/fetchers/redis/redisService";
|
||||
import { scanByRKeyDouble, scanByRKeyDoubleToken, scanByRKeySingleToken } from "@/fetchers/redis/redisService";
|
||||
import { getPlainAccessToken } from "./access";
|
||||
import { nextCrypto } from "@/fetchers/base";
|
||||
import { getCookieSelectToken, removeCookieTokens, setCookieSelectToken } from "./cookies";
|
||||
|
|
@ -54,6 +54,30 @@ async function getSelectToken() {
|
|||
catch (error) { throw new AuthError("No select token found in headers") }
|
||||
}
|
||||
|
||||
async function getSelectTokenObject() {
|
||||
try {
|
||||
const plainAccessToken = await getPlainAccessToken();
|
||||
const plainSelectToken = await getPlainSelectToken();
|
||||
console.log('plainAccessToken', plainAccessToken);
|
||||
console.log('plainSelectToken', plainSelectToken);
|
||||
const scanToken = await scanByRKeyDoubleToken({ rKey: plainAccessToken, sKey: plainSelectToken });
|
||||
if (!scanToken) throw new AuthError("Select token is invalid");
|
||||
return scanToken;
|
||||
}
|
||||
catch (error) { throw new AuthError("No select token found in headers") }
|
||||
}
|
||||
|
||||
async function getAccessTokenObject() {
|
||||
try {
|
||||
const plainAccessToken = await getPlainAccessToken();
|
||||
console.log('plainAccessToken', plainAccessToken);
|
||||
const scanToken = await scanByRKeySingleToken({ rKey: plainAccessToken });
|
||||
if (!scanToken) throw new AuthError("Access token is invalid");
|
||||
return scanToken;
|
||||
}
|
||||
catch (error) { throw new AuthError("No access token found in headers") }
|
||||
}
|
||||
|
||||
async function setSelectToken(token: string) {
|
||||
console.log('setSelectToken is triggered...');
|
||||
await setCookieSelectToken(token);
|
||||
|
|
@ -75,4 +99,4 @@ async function removeSelectToken() {
|
|||
await removeCookieTokens();
|
||||
}
|
||||
|
||||
export { getSelectToken, setSelectToken, removeSelectToken, getPlainSelectToken, isSelectTokenValid };
|
||||
export { getSelectToken, setSelectToken, removeSelectToken, getPlainSelectToken, isSelectTokenValid, getSelectTokenObject, getAccessTokenObject };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
function renderPage(selectToken: any, pageUrl: string, dashboardPages: any) {
|
||||
const subPageKey = selectToken.pages[pageUrl];
|
||||
if (Object.keys(dashboardPages).includes(subPageKey)) {
|
||||
const subPage = dashboardPages[subPageKey as keyof typeof dashboardPages];
|
||||
if (subPage) { if (subPage.page) { return subPage.page } }
|
||||
} return null;
|
||||
}
|
||||
|
||||
export { renderPage };
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
'use client';
|
||||
|
||||
const DashboardU0QncONSk22PFxZ5xefmgx: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>DashboardU0QncONSk22PFxZ5xefmgx</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardU0QncONSk22PFxZ5xefmgx;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Page } from "@/pages/types/page";
|
||||
import DashboardU0QncONSk22PFxZ5xefmgx from "./U0QncONSk22PFxZ5xefmgx";
|
||||
|
||||
const dashboardPages: Page = {
|
||||
"qY56XMEr08wJkNvOR6EYQZKMVdTQEfHdLXGzzxcKU24E:U0QncONSk22PFxZ5xefmgx": {
|
||||
name: "DashboardU0QncONSk22PFxZ5xefmgx",
|
||||
key: "U0QncONSk22PFxZ5xefmgx",
|
||||
url: "/venue/dashboard",
|
||||
page: DashboardU0QncONSk22PFxZ5xefmgx,
|
||||
description: "Dashboard",
|
||||
isDefault: true,
|
||||
params: {},
|
||||
events: ["Aevent", "Aevent", "Aevent"],
|
||||
tokens: ["TOKEN", "TOKEN", "TOKEN", "TOKEN"]
|
||||
}
|
||||
};
|
||||
|
||||
export { dashboardPages };
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
export interface Page {
|
||||
[key: string]: {
|
||||
name: string;
|
||||
key: string;
|
||||
url: string;
|
||||
page: React.FC;
|
||||
description: string;
|
||||
isDefault: boolean;
|
||||
params: Record<string, boolean>;
|
||||
events?: string[];
|
||||
tokens?: string[];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
'use client';
|
||||
|
||||
const DashboardIdTch3qS9aJXkvqXodAxxx: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>DashboardIdTch3qS9aJXkvqXodAxxx</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardIdTch3qS9aJXkvqXodAxxx;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Page } from "@/pages/types/page";
|
||||
import DashboardIdTch3qS9aJXkvqXodAxxx from "./IdTch3qS9aJXkvqXodAxxx";
|
||||
|
||||
const dashboardPages: Page = {
|
||||
"IbGpchaw3muiY7y9rnV0EJYoPy5XoOOrITT9JlfIbqwE:IdTch3qS9aJXkvqXodAxxx": {
|
||||
name: "DashboardIdTch3qS9aJXkvqXodAxxx",
|
||||
key: "IdTch3qS9aJXkvqXodAxxx",
|
||||
url: "/venue/dashboard",
|
||||
page: DashboardIdTch3qS9aJXkvqXodAxxx,
|
||||
description: "Dashboard",
|
||||
isDefault: true,
|
||||
params: {},
|
||||
events: ["Aevent", "Aevent", "Aevent"],
|
||||
tokens: ["TOKEN", "TOKEN", "TOKEN", "TOKEN"]
|
||||
}
|
||||
};
|
||||
|
||||
export { dashboardPages };
|
||||
|
|
@ -5,6 +5,7 @@ MONGO_PORT=27017
|
|||
MONGO_USER=appuser
|
||||
MONGO_AUTH_DB=appdb
|
||||
MONGO_PASSWORD=apppassword
|
||||
MONGO_URL=mongodb://appuser:apppassword@10.10.2.13:27017/appdb
|
||||
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=password
|
||||
|
|
|
|||
Loading…
Reference in New Issue