diff --git a/ServicesApi/package-lock.json b/ServicesApi/package-lock.json index 9e03e39..aba49af 100644 --- a/ServicesApi/package-lock.json +++ b/ServicesApi/package-lock.json @@ -20,7 +20,8 @@ "redis": "^5.6.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "zod": "^4.0.10" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -11655,6 +11656,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.10.tgz", + "integrity": "sha512-3vB+UU3/VmLL2lvwcY/4RV2i9z/YU0DTV/tDuYjrwmx5WeJ7hwy+rGEEx8glHp6Yxw7ibRbKSaIFBgReRPe5KA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/ServicesApi/package.json b/ServicesApi/package.json index 165a70e..b5d0a94 100644 --- a/ServicesApi/package.json +++ b/ServicesApi/package.json @@ -31,7 +31,8 @@ "redis": "^5.6.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "zod": "^4.0.10" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/ServicesApi/prisma/schema.prisma b/ServicesApi/prisma/schema.prisma index 3764eae..2f4aa8f 100644 --- a/ServicesApi/prisma/schema.prisma +++ b/ServicesApi/prisma/schema.prisma @@ -3080,6 +3080,7 @@ model occupant_types { occupant_code String @default("") @db.VarChar occupant_category String @default("") @db.VarChar occupant_category_type String @default("") @db.VarChar + function_retriever String @default("") @db.VarChar occupant_is_unique Boolean @default(false) ref_id String? @db.VarChar(100) replication_id Int @default(0) @db.SmallInt @@ -3474,6 +3475,7 @@ model staff { staff_code String @db.VarChar duties_id Int duties_uu_id String @db.VarChar + function_retriever String @default("") @db.VarChar ref_id String? @db.VarChar(100) replication_id Int @default(0) @db.SmallInt cryp_uu_id String? @db.VarChar diff --git a/ServicesApi/src/accounts/accounts.controller.ts b/ServicesApi/src/accounts/accounts.controller.ts index e0c41c4..c4dc5bf 100644 --- a/ServicesApi/src/accounts/accounts.controller.ts +++ b/ServicesApi/src/accounts/accounts.controller.ts @@ -7,8 +7,10 @@ import { Param, Body, HttpCode, + UseGuards, } from '@nestjs/common'; import { AccountsService } from './accounts.service'; +import { AuthControlGuard, EndpointControlGuard } from '../middleware/access-control.guard'; @Controller('accounts') export class AccountsController { @@ -16,6 +18,7 @@ export class AccountsController { @Post('filter') @HttpCode(200) + @UseGuards(AuthControlGuard, EndpointControlGuard) async filterAccounts(@Body() query: any) { const result = await this.accountsService.findWithPagination(query); const { pagination, data } = result; diff --git a/ServicesApi/src/accounts/accounts.module.ts b/ServicesApi/src/accounts/accounts.module.ts index 946d09e..8380666 100644 --- a/ServicesApi/src/accounts/accounts.module.ts +++ b/ServicesApi/src/accounts/accounts.module.ts @@ -4,10 +4,19 @@ import { AccountsController } from './accounts.controller'; import { PrismaModule } from '@/prisma/prisma.module'; import { CacheService } from '../cache.service'; import { UtilsModule } from '../utils/utils.module'; +import { + AuthControlGuard, + EndpointControlGuard, +} from '@/src/middleware/access-control.guard'; @Module({ imports: [PrismaModule, UtilsModule], - providers: [AccountsService, CacheService], + providers: [ + AccountsService, + CacheService, + AuthControlGuard, + EndpointControlGuard, + ], controllers: [AccountsController], }) export class AccountsModule {} diff --git a/ServicesApi/src/app.module.ts b/ServicesApi/src/app.module.ts index 41919f6..e83983b 100644 --- a/ServicesApi/src/app.module.ts +++ b/ServicesApi/src/app.module.ts @@ -13,6 +13,7 @@ import { AuthModule } from './auth/auth.module'; import { RedisModule } from '@liaoliaots/nestjs-redis'; import { CacheService } from './cache.service'; import { LoggerMiddleware } from '@/src/middleware/logger.middleware'; +import { DiscoveryModule } from '@nestjs/core'; const redisConfig = { host: '10.10.2.15', @@ -26,13 +27,14 @@ const serviceModuleList = [ RedisModule.forRoot({ config: redisConfig, }), + DiscoveryModule, ]; const controllersList = [AppController]; const providersList = [AppService, CacheService]; const exportsList = [CacheService]; @Module({ - imports: [...modulesList, ...serviceModuleList], + imports: [...serviceModuleList, ...modulesList], controllers: controllersList, providers: providersList, exports: exportsList, diff --git a/ServicesApi/src/auth/auth.controller.ts b/ServicesApi/src/auth/auth.controller.ts new file mode 100644 index 0000000..8863546 --- /dev/null +++ b/ServicesApi/src/auth/auth.controller.ts @@ -0,0 +1,74 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Param, + Body, + HttpCode, + UseGuards, +} from '@nestjs/common'; +import { AuthService } from './auth.service'; +import { userLoginValidator } from './login/dtoValidator'; +import { userSelectValidator } from './select/dtoValidator'; +import { userLogoutValidator } from './logout/dtoValidator'; +import { AuthControlGuard } from '../middleware/access-control.guard'; + +@Controller('auth') +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Post('login') + @HttpCode(200) + async login(@Body() query: userLoginValidator) { + return await this.authService.login(query); + } + + @Post('select') + @HttpCode(200) + @UseGuards(AuthControlGuard) + async select(@Body() query: userSelectValidator) { + return { message: 'Logout successful' }; + } + + @Post('/password/create') + @HttpCode(200) + async createPassword() { + return { message: 'Password created successfully' }; + } + + @Post('/password/change') + @HttpCode(200) + @UseGuards(AuthControlGuard) + async changePassword() { + return { message: 'Password changed successfully' }; + } + + @Post('/password/reset') + @HttpCode(200) + async resetPassword() { + return { message: 'Password reset successfully' }; + } + + @Post('/password/verify-otp') + @HttpCode(200) + @UseGuards(AuthControlGuard) + async verifyOtp() { + return { message: 'Password verified successfully' }; + } + + @Post('logout') + @HttpCode(200) + @UseGuards(AuthControlGuard) + async logout(@Body() query: userLogoutValidator) { + return { message: 'Logout successful' }; + } + + @Post('disconnect') + @HttpCode(200) + @UseGuards(AuthControlGuard) + async disconnect() { + return { message: 'Disconnect successful' }; + } +} diff --git a/ServicesApi/src/auth/auth.module.ts b/ServicesApi/src/auth/auth.module.ts index f32ceee..d300f6b 100644 --- a/ServicesApi/src/auth/auth.module.ts +++ b/ServicesApi/src/auth/auth.module.ts @@ -1,21 +1,32 @@ import { Module } from '@nestjs/common'; -import { LoginModule } from '@/src/auth/login/login.module'; -import { SelectModule } from '@/src/auth/select/select.module'; -import { PasswordService } from '@/src/auth/password/password.service'; -import { PasswordModule } from '@/src/auth/password/password.module'; -import { LogoutModule } from '@/src/auth/logout/logout.module'; -import { DisconnectModule } from '@/src/auth/disconnect/disconnect.module'; -import { TokenModule } from '@/src/auth/token/token.module'; +import { AuthController } from '@/src/auth/auth.controller'; +import { LoginService } from '@/src/auth/login/login.service'; +import { LogoutService } from '@/src/auth/logout/logout.service'; +import { AuthService } from '@/src/auth/auth.service'; +import { SelectService } from '@/src/auth/select/select.service'; +import { UtilsModule } from '@/src/utils/utils.module'; +import { PrismaService } from '../prisma.service'; +import { CreatePasswordService } from './password/create/create.service'; +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'; @Module({ - imports: [ - LoginModule, - LogoutModule, - SelectModule, - PasswordModule, - DisconnectModule, - TokenModule, + imports: [UtilsModule], + controllers: [AuthController], + providers: [ + AuthService, + LoginService, + LogoutService, + SelectService, + CreatePasswordService, + ResetPasswordService, + ChangePasswordService, + VerifyOtpService, + DisconnectService, + PrismaService, ], - providers: [PasswordService], + exports: [AuthService], }) export class AuthModule {} diff --git a/ServicesApi/src/auth/auth.service.ts b/ServicesApi/src/auth/auth.service.ts new file mode 100644 index 0000000..70e00e1 --- /dev/null +++ b/ServicesApi/src/auth/auth.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@nestjs/common'; +import { LoginService } from './login/login.service'; +import { LogoutService } from './logout/logout.service'; +import { SelectService } from './select/select.service'; +import { userLoginValidator } from '@/src/auth/login/dtoValidator'; +import { userSelectValidator } from './select/dtoValidator'; +import { userLogoutValidator } from './logout/dtoValidator'; +import { CreatePasswordService } from './password/create/create.service'; +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'; + +@Injectable() +export class AuthService { + constructor( + private loginService: LoginService, + private logoutService: LogoutService, + private selectService: SelectService, + private createPasswordService: CreatePasswordService, + private changePasswordService: ChangePasswordService, + private resetPasswordService: ResetPasswordService, + private verifyOtpService: VerifyOtpService, + private disconnectService: DisconnectService, + ) {} + + async login(dto: userLoginValidator) { + return await this.loginService.run(dto); + } + + async logout(dto: userLogoutValidator) { + return await this.logoutService.run(dto); + } + + async select(dto: userSelectValidator) { + return await this.selectService.run(dto); + } + + async createPassword(dto: any) { + return await this.createPasswordService.run(dto); + } + + async changePassword(dto: any) { + return await this.changePasswordService.run(dto); + } + + async resetPassword(dto: any) { + return await this.resetPasswordService.run(dto); + } + + async verifyOtp(dto: any) { + return await this.verifyOtpService.run(dto); + } + async disconnect() { + return await this.disconnectService.run(); + } +} diff --git a/ServicesApi/src/auth/disconnect/disconnect.controller.spec.ts b/ServicesApi/src/auth/disconnect/disconnect.controller.spec.ts deleted file mode 100644 index 13daeed..0000000 --- a/ServicesApi/src/auth/disconnect/disconnect.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { DisconnectController } from './disconnect.controller'; - -describe('DisconnectController', () => { - let controller: DisconnectController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [DisconnectController], - }).compile(); - - controller = module.get(DisconnectController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/disconnect/disconnect.controller.ts b/ServicesApi/src/auth/disconnect/disconnect.controller.ts deleted file mode 100644 index 6765fd3..0000000 --- a/ServicesApi/src/auth/disconnect/disconnect.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('disconnect') -export class DisconnectController {} diff --git a/ServicesApi/src/auth/disconnect/disconnect.module.ts b/ServicesApi/src/auth/disconnect/disconnect.module.ts deleted file mode 100644 index d2d2224..0000000 --- a/ServicesApi/src/auth/disconnect/disconnect.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DisconnectService } from './disconnect.service'; -import { DisconnectController } from './disconnect.controller'; - -@Module({ - providers: [DisconnectService], - controllers: [DisconnectController] -}) -export class DisconnectModule {} diff --git a/ServicesApi/src/auth/disconnect/disconnect.service.ts b/ServicesApi/src/auth/disconnect/disconnect.service.ts index 3b892ab..a80da92 100644 --- a/ServicesApi/src/auth/disconnect/disconnect.service.ts +++ b/ServicesApi/src/auth/disconnect/disconnect.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class DisconnectService {} +export class DisconnectService { + async run() { + return { message: 'Disconnect successful' }; + } +} diff --git a/ServicesApi/src/auth/login/dtoValidator.ts b/ServicesApi/src/auth/login/dtoValidator.ts new file mode 100644 index 0000000..d7bba84 --- /dev/null +++ b/ServicesApi/src/auth/login/dtoValidator.ts @@ -0,0 +1,13 @@ +import { IsObject, IsOptional, IsString, IsBoolean } from 'class-validator'; + +export class userLoginValidator { + @IsString() + accessKey: string; + + @IsString() + password: string; + + @IsBoolean() + @IsOptional() + rememberMe?: boolean; +} diff --git a/ServicesApi/src/auth/login/login.controller.spec.ts b/ServicesApi/src/auth/login/login.controller.spec.ts deleted file mode 100644 index e83e5de..0000000 --- a/ServicesApi/src/auth/login/login.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { LoginController } from './login.controller'; - -describe('LoginController', () => { - let controller: LoginController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [LoginController], - }).compile(); - - controller = module.get(LoginController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/login/login.controller.ts b/ServicesApi/src/auth/login/login.controller.ts deleted file mode 100644 index 7c0cdfd..0000000 --- a/ServicesApi/src/auth/login/login.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('login') -export class LoginController {} diff --git a/ServicesApi/src/auth/login/login.module.ts b/ServicesApi/src/auth/login/login.module.ts deleted file mode 100644 index e5829c7..0000000 --- a/ServicesApi/src/auth/login/login.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { LoginController } from './login.controller'; -import { LoginService } from './login.service'; - -@Module({ - controllers: [LoginController], - providers: [LoginService] -}) -export class LoginModule {} diff --git a/ServicesApi/src/auth/login/login.service.ts b/ServicesApi/src/auth/login/login.service.ts index 6b0cd09..b081b6f 100644 --- a/ServicesApi/src/auth/login/login.service.ts +++ b/ServicesApi/src/auth/login/login.service.ts @@ -1,4 +1,57 @@ import { Injectable } from '@nestjs/common'; +import { userLoginValidator } from '@/src/auth/login/dtoValidator'; +import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; +import { PasswordHandlers } from '@/src/utils/auth/login_handler'; +import { PrismaService } from '@/src/prisma.service'; +import { AuthTokenSchema } from '@/src/types/auth/token'; @Injectable() -export class LoginService {} +export class LoginService { + constructor( + private readonly redis: RedisHandlers, + private readonly passHandlers: PasswordHandlers, + private readonly prisma: PrismaService, + ) {} + + async run(dto: userLoginValidator) { + const foundUser = await this.prisma.users.findFirstOrThrow({ + where: { email: dto.accessKey }, + }); + + // if (foundUser.password_token) { + // throw new Error('Password need to be set first'); + // } + + const isPasswordValid = this.passHandlers.check_password( + foundUser.uu_id, + dto.password, + foundUser.hash_password, + ); + + // if (!isPasswordValid) { + // throw new Error('Invalid password'); + // } + + const foundPerson = await this.prisma.people.findFirstOrThrow({ + where: { id: foundUser.id }, + }); + + const redisData = AuthTokenSchema.parse({ + people: foundPerson, + users: foundUser, + credentials: { + person_id: foundPerson.id, + person_name: foundPerson.firstname, + }, + }); + + const accessToken = await this.redis.setLoginToRedis( + redisData, + foundUser.uu_id, + ); + return { + accessToken, + message: 'Login successful', + }; + } +} diff --git a/ServicesApi/src/auth/logout/dtoValidator.ts b/ServicesApi/src/auth/logout/dtoValidator.ts new file mode 100644 index 0000000..502c26c --- /dev/null +++ b/ServicesApi/src/auth/logout/dtoValidator.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class userLogoutValidator { + @IsString() + selected_uu_id: string; +} diff --git a/ServicesApi/src/auth/logout/logout.controller.spec.ts b/ServicesApi/src/auth/logout/logout.controller.spec.ts deleted file mode 100644 index aee9143..0000000 --- a/ServicesApi/src/auth/logout/logout.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { LogoutController } from './logout.controller'; - -describe('LogoutController', () => { - let controller: LogoutController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [LogoutController], - }).compile(); - - controller = module.get(LogoutController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/logout/logout.controller.ts b/ServicesApi/src/auth/logout/logout.controller.ts deleted file mode 100644 index 3ef0a17..0000000 --- a/ServicesApi/src/auth/logout/logout.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('logout') -export class LogoutController {} diff --git a/ServicesApi/src/auth/logout/logout.module.ts b/ServicesApi/src/auth/logout/logout.module.ts deleted file mode 100644 index 497d216..0000000 --- a/ServicesApi/src/auth/logout/logout.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { LogoutService } from './logout.service'; -import { LogoutController } from './logout.controller'; - -@Module({ - providers: [LogoutService], - controllers: [LogoutController] -}) -export class LogoutModule {} diff --git a/ServicesApi/src/auth/logout/logout.service.ts b/ServicesApi/src/auth/logout/logout.service.ts index 17ff966..0493b62 100644 --- a/ServicesApi/src/auth/logout/logout.service.ts +++ b/ServicesApi/src/auth/logout/logout.service.ts @@ -1,4 +1,9 @@ import { Injectable } from '@nestjs/common'; +import { userLogoutValidator } from '@/src/auth/logout/dtoValidator'; @Injectable() -export class LogoutService {} +export class LogoutService { + async run(dto: userLogoutValidator) { + return dto; + } +} diff --git a/ServicesApi/src/auth/password/change/change.controller.spec.ts b/ServicesApi/src/auth/password/change/change.controller.spec.ts deleted file mode 100644 index 803c511..0000000 --- a/ServicesApi/src/auth/password/change/change.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ChangeController } from './change.controller'; - -describe('ChangeController', () => { - let controller: ChangeController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [ChangeController], - }).compile(); - - controller = module.get(ChangeController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/change/change.controller.ts b/ServicesApi/src/auth/password/change/change.controller.ts deleted file mode 100644 index 3bdbc12..0000000 --- a/ServicesApi/src/auth/password/change/change.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('change') -export class ChangeController {} diff --git a/ServicesApi/src/auth/password/change/change.module.ts b/ServicesApi/src/auth/password/change/change.module.ts deleted file mode 100644 index 9f4b352..0000000 --- a/ServicesApi/src/auth/password/change/change.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ChangeService } from './change.service'; -import { ChangeController } from './change.controller'; - -@Module({ - providers: [ChangeService], - controllers: [ChangeController] -}) -export class ChangeModule {} diff --git a/ServicesApi/src/auth/password/change/change.service.ts b/ServicesApi/src/auth/password/change/change.service.ts index 32fbea6..ee19db4 100644 --- a/ServicesApi/src/auth/password/change/change.service.ts +++ b/ServicesApi/src/auth/password/change/change.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class ChangeService {} +export class ChangePasswordService { + async run(dto: any) { + return dto; + } +} diff --git a/ServicesApi/src/auth/password/create/create.controller.spec.ts b/ServicesApi/src/auth/password/create/create.controller.spec.ts deleted file mode 100644 index 74d64e5..0000000 --- a/ServicesApi/src/auth/password/create/create.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { CreateController } from './create.controller'; - -describe('CreateController', () => { - let controller: CreateController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [CreateController], - }).compile(); - - controller = module.get(CreateController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/create/create.controller.ts b/ServicesApi/src/auth/password/create/create.controller.ts deleted file mode 100644 index 770e81e..0000000 --- a/ServicesApi/src/auth/password/create/create.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('create') -export class CreateController {} diff --git a/ServicesApi/src/auth/password/create/create.module.ts b/ServicesApi/src/auth/password/create/create.module.ts deleted file mode 100644 index ae0db82..0000000 --- a/ServicesApi/src/auth/password/create/create.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CreateService } from './create.service'; -import { CreateController } from './create.controller'; - -@Module({ - providers: [CreateService], - controllers: [CreateController] -}) -export class CreateModule {} diff --git a/ServicesApi/src/auth/password/create/create.service.ts b/ServicesApi/src/auth/password/create/create.service.ts index fe1a048..bf54ff7 100644 --- a/ServicesApi/src/auth/password/create/create.service.ts +++ b/ServicesApi/src/auth/password/create/create.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class CreateService {} +export class CreatePasswordService { + async run(dto: any) { + return { message: 'Password created successfully' }; + } +} diff --git a/ServicesApi/src/auth/password/password.controller.spec.ts b/ServicesApi/src/auth/password/password.controller.spec.ts deleted file mode 100644 index 7b581c5..0000000 --- a/ServicesApi/src/auth/password/password.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { PasswordController } from './password.controller'; - -describe('PasswordController', () => { - let controller: PasswordController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [PasswordController], - }).compile(); - - controller = module.get(PasswordController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/password.controller.ts b/ServicesApi/src/auth/password/password.controller.ts deleted file mode 100644 index 5c23395..0000000 --- a/ServicesApi/src/auth/password/password.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('password') -export class PasswordController {} diff --git a/ServicesApi/src/auth/password/password.module.ts b/ServicesApi/src/auth/password/password.module.ts deleted file mode 100644 index d0c36ff..0000000 --- a/ServicesApi/src/auth/password/password.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PasswordController } from './password.controller'; -import { CreateModule } from './create/create.module'; -import { ChangeModule } from './change/change.module'; -import { ResetModule } from './reset/reset.module'; -import { VerifyOtpModule } from './verify-otp/verify-otp.module'; - -@Module({ - controllers: [PasswordController], - imports: [CreateModule, ChangeModule, ResetModule, VerifyOtpModule] -}) -export class PasswordModule {} diff --git a/ServicesApi/src/auth/password/password.service.spec.ts b/ServicesApi/src/auth/password/password.service.spec.ts deleted file mode 100644 index 730923b..0000000 --- a/ServicesApi/src/auth/password/password.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { PasswordService } from './password.service'; - -describe('PasswordService', () => { - let service: PasswordService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [PasswordService], - }).compile(); - - service = module.get(PasswordService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/password.service.ts b/ServicesApi/src/auth/password/password.service.ts deleted file mode 100644 index 70ffc04..0000000 --- a/ServicesApi/src/auth/password/password.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class PasswordService {} diff --git a/ServicesApi/src/auth/password/reset/reset.controller.spec.ts b/ServicesApi/src/auth/password/reset/reset.controller.spec.ts deleted file mode 100644 index a961f06..0000000 --- a/ServicesApi/src/auth/password/reset/reset.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ResetController } from './reset.controller'; - -describe('ResetController', () => { - let controller: ResetController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [ResetController], - }).compile(); - - controller = module.get(ResetController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/reset/reset.controller.ts b/ServicesApi/src/auth/password/reset/reset.controller.ts deleted file mode 100644 index 72be6f7..0000000 --- a/ServicesApi/src/auth/password/reset/reset.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('reset') -export class ResetController {} diff --git a/ServicesApi/src/auth/password/reset/reset.module.ts b/ServicesApi/src/auth/password/reset/reset.module.ts deleted file mode 100644 index e29af49..0000000 --- a/ServicesApi/src/auth/password/reset/reset.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ResetService } from './reset.service'; -import { ResetController } from './reset.controller'; - -@Module({ - providers: [ResetService], - controllers: [ResetController] -}) -export class ResetModule {} diff --git a/ServicesApi/src/auth/password/reset/reset.service.ts b/ServicesApi/src/auth/password/reset/reset.service.ts index c7c8793..e3c0c00 100644 --- a/ServicesApi/src/auth/password/reset/reset.service.ts +++ b/ServicesApi/src/auth/password/reset/reset.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class ResetService {} +export class ResetPasswordService { + async run(dto: any) { + return dto; + } +} diff --git a/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.spec.ts b/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.spec.ts deleted file mode 100644 index 84a59f8..0000000 --- a/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { VerifyOtpController } from './verify-otp.controller'; - -describe('VerifyOtpController', () => { - let controller: VerifyOtpController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [VerifyOtpController], - }).compile(); - - controller = module.get(VerifyOtpController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.ts b/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.ts deleted file mode 100644 index 3251806..0000000 --- a/ServicesApi/src/auth/password/verify-otp/verify-otp.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('verify-otp') -export class VerifyOtpController {} diff --git a/ServicesApi/src/auth/password/verify-otp/verify-otp.module.ts b/ServicesApi/src/auth/password/verify-otp/verify-otp.module.ts deleted file mode 100644 index 2d15f5a..0000000 --- a/ServicesApi/src/auth/password/verify-otp/verify-otp.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { VerifyOtpService } from './verify-otp.service'; -import { VerifyOtpController } from './verify-otp.controller'; - -@Module({ - providers: [VerifyOtpService], - controllers: [VerifyOtpController] -}) -export class VerifyOtpModule {} diff --git a/ServicesApi/src/auth/password/verify-otp/verify-otp.service.ts b/ServicesApi/src/auth/password/verify-otp/verify-otp.service.ts index 27d1338..30ba47d 100644 --- a/ServicesApi/src/auth/password/verify-otp/verify-otp.service.ts +++ b/ServicesApi/src/auth/password/verify-otp/verify-otp.service.ts @@ -1,4 +1,8 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class VerifyOtpService {} +export class VerifyOtpService { + async run(dto: any) { + return dto; + } +} diff --git a/ServicesApi/src/auth/select/dtoValidator.ts b/ServicesApi/src/auth/select/dtoValidator.ts new file mode 100644 index 0000000..6f9a14f --- /dev/null +++ b/ServicesApi/src/auth/select/dtoValidator.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class userSelectValidator { + @IsString() + selected_uu_id: string; +} diff --git a/ServicesApi/src/auth/select/select.controller.spec.ts b/ServicesApi/src/auth/select/select.controller.spec.ts deleted file mode 100644 index 37b94d8..0000000 --- a/ServicesApi/src/auth/select/select.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SelectController } from './select.controller'; - -describe('SelectController', () => { - let controller: SelectController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SelectController], - }).compile(); - - controller = module.get(SelectController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/ServicesApi/src/auth/select/select.controller.ts b/ServicesApi/src/auth/select/select.controller.ts deleted file mode 100644 index cce3a45..0000000 --- a/ServicesApi/src/auth/select/select.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('select') -export class SelectController {} diff --git a/ServicesApi/src/auth/select/select.module.ts b/ServicesApi/src/auth/select/select.module.ts deleted file mode 100644 index dbf9fe3..0000000 --- a/ServicesApi/src/auth/select/select.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SelectController } from './select.controller'; -import { SelectService } from './select.service'; - -@Module({ - controllers: [SelectController], - providers: [SelectService] -}) -export class SelectModule {} diff --git a/ServicesApi/src/auth/select/select.service.ts b/ServicesApi/src/auth/select/select.service.ts index 84595c0..f0c31ab 100644 --- a/ServicesApi/src/auth/select/select.service.ts +++ b/ServicesApi/src/auth/select/select.service.ts @@ -1,4 +1,9 @@ import { Injectable } from '@nestjs/common'; +import { userSelectValidator } from '@/src/auth/select/dtoValidator'; @Injectable() -export class SelectService {} +export class SelectService { + async run(dto: userSelectValidator) { + return dto; + } +} diff --git a/ServicesApi/src/main.ts b/ServicesApi/src/main.ts index f76bc8d..a5673ef 100644 --- a/ServicesApi/src/main.ts +++ b/ServicesApi/src/main.ts @@ -1,8 +1,13 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import { extractAndPersistRoutes } from '@/src/utils/extract-routes'; +import { PrismaService } from './prisma.service'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(process.env.PORT ?? 3000); + + console.log(`🚀 Uygulama çalışıyor: ${await app.getUrl()}`); + extractAndPersistRoutes(app, app.get(PrismaService)); } bootstrap(); diff --git a/ServicesApi/src/middleware/access-control.guard.ts b/ServicesApi/src/middleware/access-control.guard.ts new file mode 100644 index 0000000..40308d9 --- /dev/null +++ b/ServicesApi/src/middleware/access-control.guard.ts @@ -0,0 +1,66 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + ForbiddenException, +} from '@nestjs/common'; +import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; + +const getAccessTokenFromHeader = (req: Request): string => { + console.log(req.headers); + const token = req.headers['acs']; + if (!token) { + throw new ForbiddenException('Access token header is missing'); + } + return token; +}; + +const getSelectTokenFromHeader = (req: Request): string => { + const token = req.headers['slc']; + if (!token) { + throw new ForbiddenException('Select token header is missing'); + } + return token; +}; + +@Injectable() +export class AuthControlGuard implements CanActivate { + constructor(private cacheService: RedisHandlers) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const accessToken = getAccessTokenFromHeader(req); + console.log('AuthControlGuard', accessToken); + // const hasAccess = accessObject.permissions?.some( + // (p: any) => p.method === method && p.url === path, + // ); + + // if (!hasAccess) { + // throw new ForbiddenException('Access denied to this route'); + // } + + return true; + } +} + +@Injectable() +export class EndpointControlGuard implements CanActivate { + constructor(private cacheService: RedisHandlers) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const selectToken = getSelectTokenFromHeader(req); + const method = req.method; + const path = req.route?.path; + console.log('EndpointControlGuard', selectToken, method, path); + // const hasAccess = accessObject.permissions?.some( + // (p: any) => p.method === method && p.url === path, + // ); + + // if (!hasAccess) { + // throw new ForbiddenException('Access denied to this route'); + // } + + return true; + } +} diff --git a/ServicesApi/src/types/auth/old_token.ts b/ServicesApi/src/types/auth/old_token.ts new file mode 100644 index 0000000..f32d8c8 --- /dev/null +++ b/ServicesApi/src/types/auth/old_token.ts @@ -0,0 +1,90 @@ +// Token type redis base TOKEN object + +enum UserType { + employee = 1, + occupant = 2, +} + +interface Credentials { + person_id: number; + person_name: string; +} + +interface ApplicationToken { + // Application Token Object -> is the main object for the user + user_type: number; + credential_token: string; + user_uu_id: string; + user_id: number; + person_id: number; + person_uu_id: string; + request?: Record; // Request Info of Client + expires_at?: number; // Expiry timestamp + reachable_event_codes?: Record; // ID list of reachable event codes as "endpoint_code": ["UUID", "UUID"] + reachable_app_codes?: Record; // ID list of reachable applications as "page_url": ["UUID", "UUID"] +} + +interface OccupantToken { + // Selection of the occupant type for a build part is made by the user + living_space_id: number; // Internal use + living_space_uu_id: string; // Outer use + occupant_type_id: number; + occupant_type_uu_id: string; + occupant_type: string; + build_id: number; + build_uuid: string; + build_part_id: number; + build_part_uuid: string; + responsible_company_id?: number; + responsible_company_uuid?: string; + responsible_employee_id?: number; + responsible_employee_uuid?: string; +} + +interface CompanyToken { + // Selection of the company for an employee is made by the user + company_id: number; + company_uu_id: string; + department_id: number; // ID list of departments + department_uu_id: string; // UUID list of departments + duty_id: number; + duty_uu_id: string; + staff_id: number; + staff_uu_id: string; + employee_id: number; + employee_uu_id: string; + bulk_duties_id: number; +} + +interface OccupantTokenObject extends ApplicationToken { + // Occupant Token Object -> Requires selection of the occupant type for a specific build part + available_occupants: Record | null; + selected?: Record; // Selected Occupant Type + is_employee: boolean; // Always false + is_occupant: boolean; // Always true +} + +interface EmployeeTokenObject extends ApplicationToken { + // Full hierarchy Employee[staff_id] -> Staff -> Duty -> Department -> Company + companies_id_list: number[]; // List of company objects + companies_uu_id_list: string[]; // UUID list of company objects + duty_id_list: number[]; // List of duty objects + duty_uu_id_list: string[]; // UUID list of duty objects + selected?: Record; // Selected Company Object + is_employee: boolean; // Always true + is_occupant: boolean; // Always false +} + +// Union type for token objects +type TokenDictType = EmployeeTokenObject | OccupantTokenObject; + +export { + UserType, + Credentials, + ApplicationToken, + OccupantToken, + CompanyToken, + OccupantTokenObject, + EmployeeTokenObject, + TokenDictType, +}; diff --git a/ServicesApi/src/types/auth/token.ts b/ServicesApi/src/types/auth/token.ts index f32d8c8..dbf6959 100644 --- a/ServicesApi/src/types/auth/token.ts +++ b/ServicesApi/src/types/auth/token.ts @@ -1,90 +1,122 @@ -// Token type redis base TOKEN object +import { z } from 'zod'; -enum UserType { - employee = 1, - occupant = 2, -} +// ENUM +export const UserType = { + employee: 1, + occupant: 2, +} as const; +export type UserType = (typeof UserType)[keyof typeof UserType]; -interface Credentials { - person_id: number; - person_name: string; -} +// Credentials +export const CredentialsSchema = z.object({ + person_id: z.number(), + person_name: z.string(), +}); +export type Credentials = z.infer; -interface ApplicationToken { - // Application Token Object -> is the main object for the user - user_type: number; - credential_token: string; - user_uu_id: string; - user_id: number; - person_id: number; - person_uu_id: string; - request?: Record; // Request Info of Client - expires_at?: number; // Expiry timestamp - reachable_event_codes?: Record; // ID list of reachable event codes as "endpoint_code": ["UUID", "UUID"] - reachable_app_codes?: Record; // ID list of reachable applications as "page_url": ["UUID", "UUID"] -} +export const AuthTokenSchema = z.object({ + people: z.object({ + firstname: z.string(), + surname: z.string(), + middle_name: z.string(), + birthname: z.string().nullable(), + sex_code: z.string(), + person_ref: z.string().nullable(), + person_tag: z.string(), + father_name: z.string(), + mother_name: z.string(), + country_code: z.string(), + national_identity_id: z.string(), + birth_place: z.string(), + birth_date: z.date(), + tax_no: z.string(), + ref_id: z.string().nullable(), + replication_id: z.number().nullable(), + cryp_uu_id: z.string().nullable(), + created_credentials_token: z.string().nullable(), + updated_credentials_token: z.string().nullable(), + confirmed_credentials_token: z.string().nullable(), + ref_int: z.number().nullable(), + is_confirmed: z.boolean(), + deleted: z.boolean(), + active: z.boolean(), + is_notification_send: z.boolean(), + is_email_send: z.boolean(), + id: z.number(), + uu_id: z.string(), + expiry_starts: z.date(), + expiry_ends: z.date(), + created_at: z.date(), + updated_at: z.date(), + }), + users: z.object({ + user_tag: z.string(), + email: z.string(), + phone_number: z.string(), + via: z.string(), + avatar: z.string(), + hash_password: z.string(), + password_token: z.string(), + remember_me: z.boolean(), + password_expires_day: z.number(), + password_expiry_begins: z.date(), + related_company: z.string(), + person_id: z.number(), + person_uu_id: z.string(), + local_timezone: z.string(), + ref_id: z.string().nullable(), + ref_int: z.number().nullable(), + replication_id: z.number().nullable(), + cryp_uu_id: z.string().nullable(), + created_credentials_token: z.string().nullable(), + updated_credentials_token: z.string().nullable(), + confirmed_credentials_token: z.string().nullable(), + is_confirmed: z.boolean(), + deleted: z.boolean(), + active: z.boolean(), + is_notification_send: z.boolean(), + is_email_send: z.boolean(), + id: z.number(), + uu_id: z.string(), + expiry_starts: z.date(), + expiry_ends: z.date(), + created_at: z.date(), + updated_at: z.date(), + default_language: z.string(), + }), + credentials: CredentialsSchema, +}); -interface OccupantToken { - // Selection of the occupant type for a build part is made by the user - living_space_id: number; // Internal use - living_space_uu_id: string; // Outer use - occupant_type_id: number; - occupant_type_uu_id: string; - occupant_type: string; - build_id: number; - build_uuid: string; - build_part_id: number; - build_part_uuid: string; - responsible_company_id?: number; - responsible_company_uuid?: string; - responsible_employee_id?: number; - responsible_employee_uuid?: string; -} +export type AuthToken = z.infer; -interface CompanyToken { - // Selection of the company for an employee is made by the user - company_id: number; - company_uu_id: string; - department_id: number; // ID list of departments - department_uu_id: string; // UUID list of departments - duty_id: number; - duty_uu_id: string; - staff_id: number; - staff_uu_id: string; - employee_id: number; - employee_uu_id: string; - bulk_duties_id: number; -} +export const EmployeeTokenSchema = z.object({ + functionsRetriever: z.string(), + companies: z.object({}), + department: z.object({}), + duties: z.object({}), + employee: z.object({}), + staffs: z.object({}), + reachable_event_codes: z.array(z.object({})), + reachable_app_codes: z.array(z.object({})), + kind: z.literal(UserType.employee), +}); -interface OccupantTokenObject extends ApplicationToken { - // Occupant Token Object -> Requires selection of the occupant type for a specific build part - available_occupants: Record | null; - selected?: Record; // Selected Occupant Type - is_employee: boolean; // Always false - is_occupant: boolean; // Always true -} +export const OccupantTokenSchema = z.object({ + functionsRetriever: z.string(), + livingSpace: z.object({}), + occupantType: z.object({}), + build: z.object({}), + buildPart: z.object({}), + responsibleCompany: z.object({}).optional(), + responsibleEmployee: z.object({}).optional(), + kind: z.literal(UserType.occupant), + reachable_event_codes: z.array(z.object({})), + reachable_app_codes: z.array(z.object({})), +}); -interface EmployeeTokenObject extends ApplicationToken { - // Full hierarchy Employee[staff_id] -> Staff -> Duty -> Department -> Company - companies_id_list: number[]; // List of company objects - companies_uu_id_list: string[]; // UUID list of company objects - duty_id_list: number[]; // List of duty objects - duty_uu_id_list: string[]; // UUID list of duty objects - selected?: Record; // Selected Company Object - is_employee: boolean; // Always true - is_occupant: boolean; // Always false -} +export const TokenDictTypes = z.discriminatedUnion('kind', [ + EmployeeTokenSchema, + OccupantTokenSchema, +]); -// Union type for token objects -type TokenDictType = EmployeeTokenObject | OccupantTokenObject; - -export { - UserType, - Credentials, - ApplicationToken, - OccupantToken, - CompanyToken, - OccupantTokenObject, - EmployeeTokenObject, - TokenDictType, -}; +export type TokenDictInterface = z.infer; diff --git a/ServicesApi/src/users/users.controller.ts b/ServicesApi/src/users/users.controller.ts index da51c78..e63bfff 100644 --- a/ServicesApi/src/users/users.controller.ts +++ b/ServicesApi/src/users/users.controller.ts @@ -10,6 +10,13 @@ import { } from '@nestjs/common'; import { UsersService } from './users.service'; +/** + * USER TYPE CODE = BM BLD OCC ... + * class Func + * code = "uuid4" + * TYPE = "build_manager" + */ + @Controller('users') export class UsersController { constructor(private usersService: UsersService) {} diff --git a/ServicesApi/src/utils/auth/login_handler.ts b/ServicesApi/src/utils/auth/login_handler.ts index 6533624..81c39b5 100644 --- a/ServicesApi/src/utils/auth/login_handler.ts +++ b/ServicesApi/src/utils/auth/login_handler.ts @@ -1,4 +1,4 @@ -import crypto from 'crypto'; +import * as crypto from 'crypto'; import { v4 as uuidv4 } from 'uuid'; interface TokenConfig { @@ -12,28 +12,29 @@ const tokenConfig: TokenConfig = { }; class PasswordHandlers { - generate_random_uu_id(is_string: boolean = true): string { + generateRandomUUID(is_string: boolean = true): string { return is_string ? uuidv4().toString() : uuidv4(); } - create_hashed_password( - domain: string, - uuid: string, - password: string, - ): string { - const data = `${domain}:${uuid}:${password}`; + create_hashed_password(uuid: string, password: string): string { + const data = `${uuid}:${password}`; + console.log(crypto.createHash('sha256').update(data).digest('hex')); + return crypto.createHash('sha256').update(data).digest('hex'); + } + + createSelectToken(accessToken: string, userUUID: string) { + const data = `${accessToken}:${userUUID}`; return crypto.createHash('sha256').update(data).digest('hex'); } check_password( - domain: string, uuid: string, password: string, hashed_password: string, ): boolean { - return ( - this.create_hashed_password(domain, uuid, password) === hashed_password - ); + const created_hashed_password = this.create_hashed_password(uuid, password); + console.log('created_hashed_password', created_hashed_password); + return created_hashed_password === hashed_password; } generateAccessToken(): string { diff --git a/ServicesApi/src/utils/auth/redis_handlers.ts b/ServicesApi/src/utils/auth/redis_handlers.ts index f341d4f..7ec6d7c 100644 --- a/ServicesApi/src/utils/auth/redis_handlers.ts +++ b/ServicesApi/src/utils/auth/redis_handlers.ts @@ -1,7 +1,7 @@ import { - TokenDictType, - OccupantTokenObject, - EmployeeTokenObject, + TokenDictTypes, + TokenDictInterface, + AuthToken, UserType, } from '@/src/types/auth/token'; import { CacheService } from '@/src/cache.service'; @@ -15,110 +15,50 @@ export class RedisHandlers { constructor( private readonly cacheService: CacheService, private readonly passwordService: PasswordHandlers, - ) { - this.cacheService = cacheService; - this.passwordService = passwordService; + ) {} + + generateSelectToken(accessToken: string, userUUID: string) { + return this.passwordService.createSelectToken(accessToken, userUUID); } - async process_redis_object(redis_object: any): Promise { - if (!redis_object) { - throw new Error('Invalid Redis object: Object is null or undefined'); - } - if (redis_object.user_type === UserType.employee) { - const validateEmployeeToken = (obj: any): obj is EmployeeTokenObject => { - return ( - typeof obj === 'object' && - obj !== null && - typeof obj.user_type === 'number' && - typeof obj.user_uu_id === 'string' && - typeof obj.user_id === 'number' && - typeof obj.person_id === 'number' && - typeof obj.person_uu_id === 'string' && - Array.isArray(obj.companies_id_list) && - Array.isArray(obj.companies_uu_id_list) && - Array.isArray(obj.duty_id_list) && - Array.isArray(obj.duty_uu_id_list) - ); - }; - const empToken: EmployeeTokenObject = { - ...redis_object, - is_employee: true, - is_occupant: false, - user_type: UserType.employee, - credential_token: redis_object.credential_token || '', - }; - if (!validateEmployeeToken(empToken)) { - throw new Error( - 'Invalid Redis object: Does not match EmployeeTokenObject interface', - ); - } - return empToken; - } - - if (redis_object.user_type === UserType.occupant) { - const validateOccupantToken = (obj: any): obj is OccupantTokenObject => { - return ( - typeof obj === 'object' && - obj !== null && - typeof obj.user_type === 'number' && - typeof obj.user_uu_id === 'string' && - typeof obj.user_id === 'number' && - typeof obj.person_id === 'number' && - typeof obj.person_uu_id === 'string' - ); - }; - const occToken: OccupantTokenObject = { - ...redis_object, - is_employee: false, - is_occupant: true, - user_type: UserType.occupant, - credential_token: redis_object.credential_token || '', - available_occupants: redis_object.available_occupants || null, - }; - if (!validateOccupantToken(occToken)) { - throw new Error( - 'Invalid Redis object: Does not match OccupantTokenObject interface', - ); - } - return occToken; - } - throw new Error(`Invalid user_type: ${redis_object.user_type}`); + generateAccessToken() { + return this.passwordService.generateAccessToken(); } - async get_object_from_redis(access_token: string): Promise { - const token = await this.cacheService.get(access_token); - return this.process_redis_object(token); + async getLoginFromRedis(redisKey: string): Promise { + return this.cacheService.get(redisKey); } - async set_login_to_redis(user: users, token: TokenDictType): Promise { - const generated_token = this.passwordService.generateAccessToken(); - const listKeys = [this.AUTH_TOKEN, generated_token, user.uu_id]; - await this.cacheService.set_with_ttl( - this.cacheService.createRegexPattern(listKeys), - token, - 60 * 60 * 24, - ); - return generated_token; + async getSelectFromRedis(redisKey: string): Promise { + return this.cacheService.get(redisKey); } - async update_token_via_token(token: string, additional: any): Promise { - const listKeys = [this.AUTH_TOKEN, token, '*']; - const accessObject = await this.cacheService.get_with_keys(listKeys); - if (!accessObject) throw new Error('Token not found'); - const processedObject: TokenDictType = - await this.process_redis_object(accessObject); - if (processedObject.is_employee) { - processedObject.selected = additional; - } - if (processedObject.is_occupant) { - processedObject.selected = additional; - } - const listKeysNew = [this.AUTH_TOKEN, token, processedObject.user_uu_id]; - await this.cacheService.set_with_ttl( - this.cacheService.createRegexPattern(listKeysNew), - processedObject, - 60 * 60 * 24, - ); - return token; + async renewTtlLoginFromRedis(redisKey: string): Promise { + const token = await this.getLoginFromRedis(redisKey); + return this.cacheService.set_with_ttl(redisKey, token, 60 * 30); + } + + async renewTtlSelectFromRedis(redisKey: string): Promise { + const token = await this.getSelectFromRedis(redisKey); + return this.cacheService.set_with_ttl(redisKey, token, 60 * 30); + } + + async setLoginToRedis(token: AuthToken, userUUID: string): Promise { + const accessToken = this.generateAccessToken(); + const redisKey = `${this.AUTH_TOKEN}:${accessToken}:${accessToken}:${userUUID}:${userUUID}`; + await this.cacheService.set_with_ttl(redisKey, token, 60 * 30); + return accessToken; + } + + async setSelectToRedis( + accessToken: string, + token: TokenDictInterface, + userUUID: string, + livingUUID: string, + ): Promise { + const selectToken = this.generateSelectToken(accessToken, userUUID); + const redisKey = `${this.AUTH_TOKEN}:${accessToken}:${selectToken}:${userUUID}:${livingUUID}`; + await this.cacheService.set_with_ttl(redisKey, token, 60 * 30); + return selectToken; } } diff --git a/ServicesApi/src/utils/extract-routes.ts b/ServicesApi/src/utils/extract-routes.ts new file mode 100644 index 0000000..8c4c7c9 --- /dev/null +++ b/ServicesApi/src/utils/extract-routes.ts @@ -0,0 +1,95 @@ +import { INestApplication, RequestMethod } from '@nestjs/common'; +import { ModulesContainer, Reflector } from '@nestjs/core'; +import { PATH_METADATA, METHOD_METADATA } from '@nestjs/common/constants'; +import { PrismaService } from '@/src/prisma.service'; + +/** + * Helper: Method string'i döndür + */ +function getMethodString(requestMethod: RequestMethod): string { + return RequestMethod[requestMethod]; +} + +/** + * Helper: Path'leri normalize et (iki tane slash varsa düzelt) + */ +function normalizePath(...paths: string[]): string { + const normalized = + '/' + + paths + .filter(Boolean) + .map((p) => p.replace(/^\/|\/$/g, '')) + .filter((p) => p.length > 0) + .join('/'); + + return normalized === '/' ? '' : normalized; // Home route'ı dışla +} + +export async function extractAndPersistRoutes( + app: INestApplication, + prisma: PrismaService, +): Promise<{ method: string; url: string }[]> { + const modulesContainer = app.get(ModulesContainer); + const reflector = app.get(Reflector); + const routes: { method: string; url: string }[] = []; + + modulesContainer.forEach((moduleRef) => { + const controllers = [...moduleRef.controllers.values()]; + controllers.forEach(({ metatype }) => { + if (!metatype || typeof metatype !== 'function') return; + + const controllerPath = + reflector.get(PATH_METADATA, metatype) ?? ''; + const prototype = metatype.prototype; + + const methodNames = Object.getOwnPropertyNames(prototype).filter( + (m) => m !== 'constructor', + ); + + methodNames.forEach((methodName) => { + const methodRef = prototype[methodName]; + const routePath = reflector.get(PATH_METADATA, methodRef); + const requestMethod = reflector.get( + METHOD_METADATA, + methodRef, + ); + + if (routePath !== undefined && requestMethod !== undefined) { + const method = getMethodString(requestMethod); + const fullPath = normalizePath(controllerPath, routePath); + if (fullPath !== '') { + routes.push({ method, url: fullPath }); + } + } + }); + }); + }); + + const existing = await prisma.endpoint_restriction.findMany({ + select: { endpoint_name: true, endpoint_method: true }, + }); + + const existingSet = new Set( + existing.map((r) => `${r.endpoint_method}_${r.endpoint_name}`), + ); + + const newOnes = routes.filter( + (r) => !existingSet.has(`${r.method}_${r.url}`), + ); + + // İsteğe bağlı: veritabanına kaydet + // for (const route of newOnes) { + // await prisma.endpoint_restriction.create({ + // data: { + // endpoint_method: route.method, + // endpoint_name: route.url, + // is_active: true, + // }, + // }); + // } + + console.log('🧭 Route JSON Listesi:'); + console.dir(routes, { depth: null }); + + return routes; +} diff --git a/ServicesApi/src/utils/utils.module.ts b/ServicesApi/src/utils/utils.module.ts index 37b14e2..97f0ab1 100644 --- a/ServicesApi/src/utils/utils.module.ts +++ b/ServicesApi/src/utils/utils.module.ts @@ -1,9 +1,18 @@ import { Module } from '@nestjs/common'; import { PaginationHelper } from './pagination-helper'; import { PrismaService } from '@/src/prisma.service'; +import { RedisHandlers } from './auth/redis_handlers'; +import { PasswordHandlers } from './auth/login_handler'; +import { CacheService } from '@/src/cache.service'; @Module({ - providers: [PaginationHelper, PrismaService], - exports: [PaginationHelper], + providers: [ + PaginationHelper, + PrismaService, + RedisHandlers, + PasswordHandlers, + CacheService, + ], + exports: [PaginationHelper, RedisHandlers, PasswordHandlers, CacheService], }) export class UtilsModule {}