auth services implmented

This commit is contained in:
2025-07-25 22:46:37 +03:00
parent 8ca2d34dc6
commit b4b752ca3a
92 changed files with 5282 additions and 296 deletions

View File

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

View File

@@ -0,0 +1,39 @@
import {
Controller,
Get,
Post,
Put,
Delete,
Param,
Body,
HttpCode,
} from '@nestjs/common';
import { AccountsService } from './accounts.service';
@Controller('accounts')
export class AccountsController {
constructor(private accountsService: AccountsService) {}
@Post('filter')
@HttpCode(200)
async filterAccounts(@Body() query: any) {
const result = await this.accountsService.findWithPagination(query);
const { pagination, data } = result;
if (data.length === 0) {
return { pagination, data: [] };
}
const resultRefined = data.map((rec: any) => ({
...rec,
build_decision_book_payments: rec.build_decision_book_payments?.map(
(pmt: any) => ({
...pmt,
ratePercent:
((pmt.payment_amount / rec.currency_value) * 100).toFixed(2) + '%',
}),
),
}));
return { pagination, data: resultRefined };
}
}

View File

@@ -0,0 +1,13 @@
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 { UtilsModule } from '../utils/utils.module';
@Module({
imports: [PrismaModule, UtilsModule],
providers: [AccountsService, CacheService],
controllers: [AccountsController],
})
export class AccountsModule {}

View File

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

View File

@@ -0,0 +1,42 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/src/prisma.service';
import { Prisma, account_records } from '@prisma/client';
import { CacheService } from '../cache.service';
import { PaginationHelper, PaginationInfo } from '../utils/pagination-helper';
@Injectable()
export class AccountsService {
constructor(
private prisma: PrismaService,
private cacheService: CacheService,
private paginationHelper: PaginationHelper,
) {}
async findAll(filter: any): Promise<Partial<account_records>[]> {
return this.prisma.account_records.findMany({
where: { ...filter },
});
}
async findDynamic(
query: Prisma.account_recordsFindManyArgs,
): Promise<{ totalCount: number; result: Partial<account_records>[] }> {
const totalCount = await this.prisma.account_records.count({
where: query.where,
});
const result = await this.prisma.account_records.findMany(query);
return { totalCount, result };
}
async findWithPagination(
query: any & { page?: number; pageSize?: number },
): Promise<{ data: any[]; pagination: PaginationInfo }> {
return this.paginationHelper.paginate(this.prisma.account_records, query);
}
async findOne(uuid: string): Promise<Partial<account_records> | null> {
return this.prisma.account_records.findUnique({
where: { uu_id: uuid },
});
}
}

View File

@@ -0,0 +1,36 @@
http://localhost:3000/accounts/filter
{
"where": {
"build_parts": {
"part_code": {
"contains": "10",
"mode": "insensitive"
}
}
},
"select": {
"process_comment": true,
"bank_date": true,
"currency_value": true,
"build_parts": {
"select": {
"part_code": true
}
},
"build_decision_book_payments": {
"select": {
"payment_amount": true,
"process_date": true,
"build_decision_book_items": {
"select": {
"item_order": true,
"item_comment": true
}
}
}
}
},
"page": 2,
"pageSize": 5
}

View File

@@ -1,12 +1,50 @@
import { Module } from '@nestjs/common';
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.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 { LoggerMiddleware } from '@/src/middleware/logger.middleware';
const redisConfig = {
host: '10.10.2.15',
port: 6379,
password: 'your_strong_password_here',
};
const modulesList = [UsersModule, AccountsModule, AuthModule];
const serviceModuleList = [
PrismaModule,
RedisModule.forRoot({
config: redisConfig,
}),
];
const controllersList = [AppController];
const providersList = [AppService, CacheService];
const exportsList = [CacheService];
@Module({
imports: [PrismaModule, UsersModule],
controllers: [AppController],
providers: [AppService],
imports: [...modulesList, ...serviceModuleList],
controllers: controllersList,
providers: providersList,
exports: exportsList,
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'accounts', method: RequestMethod.ALL },
{ path: 'users/*path', method: RequestMethod.ALL },
)
.forRoutes('*');
}
}

View File

@@ -0,0 +1,21 @@
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';
@Module({
imports: [
LoginModule,
LogoutModule,
SelectModule,
PasswordModule,
DisconnectModule,
TokenModule,
],
providers: [PasswordService],
})
export class AuthModule {}

View File

@@ -0,0 +1,18 @@
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>(DisconnectController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { DisconnectService } from './disconnect.service';
import { DisconnectController } from './disconnect.controller';
@Module({
providers: [DisconnectService],
controllers: [DisconnectController]
})
export class DisconnectModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(LoginController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';
import { LoginService } from './login.service';
@Module({
controllers: [LoginController],
providers: [LoginService]
})
export class LoginModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(LogoutController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { LogoutService } from './logout.service';
import { LogoutController } from './logout.controller';
@Module({
providers: [LogoutService],
controllers: [LogoutController]
})
export class LogoutModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(ChangeController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { ChangeService } from './change.service';
import { ChangeController } from './change.controller';
@Module({
providers: [ChangeService],
controllers: [ChangeController]
})
export class ChangeModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(CreateController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { CreateService } from './create.service';
import { CreateController } from './create.controller';
@Module({
providers: [CreateService],
controllers: [CreateController]
})
export class CreateModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(PasswordController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,12 @@
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 {}

View File

@@ -0,0 +1,18 @@
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>(PasswordService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,18 @@
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>(ResetController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { ResetService } from './reset.service';
import { ResetController } from './reset.controller';
@Module({
providers: [ResetService],
controllers: [ResetController]
})
export class ResetModule {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(VerifyOtpController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common';
@Controller('verify-otp')
export class VerifyOtpController {}

View File

@@ -0,0 +1,9 @@
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 {}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
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>(SelectController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { SelectController } from './select.controller';
import { SelectService } from './select.service';
@Module({
controllers: [SelectController],
providers: [SelectService]
})
export class SelectModule {}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { CheckService } from './check.service';
import { CheckController } from './check.controller';
@Module({
providers: [CheckService],
controllers: [CheckController]
})
export class CheckModule {}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { RefreshService } from './refresh.service';
import { RefreshController } from './refresh.controller';
@Module({
providers: [RefreshService],
controllers: [RefreshController]
})
export class RefreshModule {}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { TokenService } from './token.service';
import { TokenController } from './token.controller';
import { CheckModule } from './check/check.module';
import { RefreshModule } from './refresh/refresh.module';
@Module({
providers: [TokenService],
controllers: [TokenController],
imports: [CheckModule, RefreshModule],
})
export class TokenModule {}

View File

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

View File

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

View File

@@ -0,0 +1,102 @@
import { Injectable } from '@nestjs/common';
import { RedisService } from '@liaoliaots/nestjs-redis';
import Redis from 'ioredis';
@Injectable()
export class CacheService {
private client: Redis;
constructor(private readonly redisService: RedisService) {
this.client = this.redisService.getOrThrow();
}
async set(key: string, value: any) {
await this.client.set(key, JSON.stringify(value));
}
async get(key: string): Promise<any | null> {
const value = await this.client.get(key);
if (!value) {
return null;
}
return JSON.parse(value);
}
async get_with_keys(listKeys: (string | null)[]): Promise<any | null> {
const joinKeys = this.createRegexPattern(listKeys);
const value = await this.client.get(joinKeys);
if (!value) {
return null;
}
return JSON.parse(value);
}
async set_with_ttl(key: string, value: any, ttl: number) {
await this.client.set(key, JSON.stringify(value), 'EX', ttl);
}
async check_ttl(key: string): Promise<number> {
return this.client.ttl(key);
}
async delete(key: string): Promise<boolean> {
const deleted = await this.client.del(key);
if (deleted === 0) {
return false;
}
return true;
}
/**
* Delete multiple keys matching a pattern.
* Simplified version that just returns the count of deleted keys.
*
* @param listKeys - List of key components to form pattern for deletion.
* @returns Number of deleted keys
*/
async deleteMultiple(listKeys: (string | null)[]): Promise<number> {
const regex = this.createRegexPattern(listKeys);
const keys: string[] = [];
let cursor = '0';
do {
const [nextCursor, matchedKeys] = await this.client.scan(
cursor,
'MATCH',
regex,
);
cursor = nextCursor;
keys.push(...matchedKeys);
} while (cursor !== '0');
if (keys.length === 0) {
return 0;
}
let deletedCount = 0;
for (const key of keys) {
const result = await this.client.del(key);
deletedCount += result;
}
return deletedCount;
}
/**
* Create a regex pattern from list of keys
* This is a simplified implementation - adjust according to your needs
*/
/**
* Create a regex pattern from list of keys
* Replaces null/undefined values with '*' wildcards
* @param listKeys Array of key components, can contain null/undefined values
* @returns Redis pattern string with wildcards
*/
createRegexPattern(listKeys: (string | null)[]): string {
return listKeys.map((key) => (key === null ? '*' : key)).join(':') + '*';
}
async check(key: string): Promise<boolean> {
const exists = await this.client.exists(key);
return exists === 1;
}
}

View File

@@ -0,0 +1,10 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`[LoggerMiddleware] ${req.method} ${req.originalUrl}`);
next();
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}

View File

@@ -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<string, any>; // Request Info of Client
expires_at?: number; // Expiry timestamp
reachable_event_codes?: Record<string, any>; // ID list of reachable event codes as "endpoint_code": ["UUID", "UUID"]
reachable_app_codes?: Record<string, any>; // 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<string, any> | null;
selected?: Record<string, any>; // 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<string, any>; // 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,
};

View File

@@ -0,0 +1,3 @@
export function processUsers(): void {
console.log('Processing users New...');
}

View File

@@ -6,39 +6,17 @@ import {
Delete,
Param,
Body,
HttpCode,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from '@prisma/client';
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get()
async findAll(): Promise<User[]> {
return this.usersService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string): Promise<User | null> {
return this.usersService.findOne(Number(id));
}
@Post()
async create(@Body() data: { name: string; email: string }): Promise<User> {
return this.usersService.create(data);
}
@Put(':id')
async update(
@Param('id') id: string,
@Body() data: Partial<{ name: string; email: string }>,
): Promise<User> {
return this.usersService.update(Number(id), data);
}
@Delete(':id')
async remove(@Param('id') id: string): Promise<User> {
return this.usersService.remove(Number(id));
@Post('filter')
@HttpCode(200)
async filterUsers(@Body() query: any) {
return this.usersService.findWithPagination(query);
}
}

View File

@@ -2,10 +2,12 @@ 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 { UtilsModule } from '../utils/utils.module';
@Module({
imports: [PrismaModule],
providers: [UsersService],
imports: [PrismaModule, UtilsModule],
providers: [UsersService, CacheService],
controllers: [UsersController],
exports: [UsersService],
})

View File

@@ -1,31 +1,28 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/prisma.service';
import { User } from '@prisma/client';
import { PrismaService } from '@/src/prisma.service';
import { users } from '@prisma/client';
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async findAll(): Promise<User[]> {
return this.prisma.user.findMany();
async findAll(): Promise<users[]> {
return this.prisma.users.findMany();
}
async findOne(id: number): Promise<User | null> {
return this.prisma.user.findUnique({ where: { id } });
async findOne(id: number): Promise<users | null> {
return this.prisma.users.findUnique({ where: { id } });
}
async create(data: { name: string; email: string }): Promise<User> {
return this.prisma.user.create({ data });
async create(data: any): Promise<users> {
return this.prisma.users.create({ data });
}
async update(
id: number,
data: Partial<{ name: string; email: string }>,
): Promise<User> {
return this.prisma.user.update({ where: { id }, data });
async update(id: number, data: any): Promise<users> {
return this.prisma.users.update({ where: { id }, data });
}
async remove(id: number): Promise<User> {
return this.prisma.user.delete({ where: { id } });
async remove(id: number): Promise<users> {
return this.prisma.users.delete({ where: { id } });
}
}

View File

@@ -1,31 +1,42 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/prisma.service';
import { User } from '@prisma/client';
import { PrismaService } from '@/src/prisma.service';
import { Prisma, users } from '@prisma/client';
import { CacheService } from '../cache.service';
import { PaginationHelper, PaginationInfo } from '../utils/pagination-helper';
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
constructor(
private prisma: PrismaService,
private cacheService: CacheService,
private paginationHelper: PaginationHelper,
) {}
async findAll(): Promise<User[]> {
return this.prisma.user.findMany();
async findAll(filter: any): Promise<Partial<users>[]> {
return this.prisma.users.findMany({
where: { ...filter },
});
}
async findOne(id: number): Promise<User | null> {
return this.prisma.user.findUnique({ where: { id } });
async findDynamic(
query: Prisma.usersFindManyArgs,
): Promise<{ totalCount: number; result: Partial<users>[] }> {
const totalCount = await this.prisma.users.count({
where: query.where,
});
const result = await this.prisma.users.findMany(query);
return { totalCount, result };
}
async create(data: { name: string; email: string }): Promise<User> {
return this.prisma.user.create({ data });
async findWithPagination(
query: any & { page?: number; pageSize?: number },
): Promise<{ data: any[]; pagination: PaginationInfo }> {
return this.paginationHelper.paginate(this.prisma.users, query);
}
async update(
id: number,
data: Partial<{ name: string; email: string }>,
): Promise<User> {
return this.prisma.user.update({ where: { id }, data });
}
async remove(id: number): Promise<User> {
return this.prisma.user.delete({ where: { id } });
async findOne(uuid: string): Promise<Partial<users> | null> {
return this.prisma.users.findUnique({
where: { uu_id: uuid },
});
}
}

View File

@@ -0,0 +1,65 @@
import crypto from 'crypto';
import { v4 as uuidv4 } from 'uuid';
interface TokenConfig {
ACCESS_TOKEN_LENGTH: number;
REFRESHER_TOKEN_LENGTH: number;
}
const tokenConfig: TokenConfig = {
ACCESS_TOKEN_LENGTH: 64,
REFRESHER_TOKEN_LENGTH: 128,
};
class PasswordHandlers {
generate_random_uu_id(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}`;
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
);
}
generateAccessToken(): string {
return this.generateToken(tokenConfig.ACCESS_TOKEN_LENGTH);
}
generateRefreshToken(): string {
return this.generateToken(tokenConfig.REFRESHER_TOKEN_LENGTH);
}
generateToken(length: number): string {
const letters = 'abcdefghijklmnopqrstuvwxyz';
const mergedLetters = [...letters, ...letters.toUpperCase().split('')];
let token = crypto.randomBytes(length).toString('base64url');
token = token
.split('')
.map((char) =>
mergedLetters.includes(char)
? char
: mergedLetters[Math.floor(Math.random() * mergedLetters.length)],
)
.join('');
return token;
}
}
export { PasswordHandlers };

View File

@@ -0,0 +1,124 @@
import {
TokenDictType,
OccupantTokenObject,
EmployeeTokenObject,
UserType,
} from '@/src/types/auth/token';
import { CacheService } from '@/src/cache.service';
import { users } from '@prisma/client';
import { PasswordHandlers } from './login_handler';
import { Injectable } from '@nestjs/common';
@Injectable()
export class RedisHandlers {
AUTH_TOKEN = 'AUTH_TOKEN';
constructor(
private readonly cacheService: CacheService,
private readonly passwordService: PasswordHandlers,
) {
this.cacheService = cacheService;
this.passwordService = passwordService;
}
async process_redis_object(redis_object: any): Promise<TokenDictType> {
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}`);
}
async get_object_from_redis(access_token: string): Promise<TokenDictType> {
const token = await this.cacheService.get(access_token);
return this.process_redis_object(token);
}
async set_login_to_redis(user: users, token: TokenDictType): Promise<any> {
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 update_token_via_token(token: string, additional: any): Promise<any> {
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;
}
}

View File

@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/src/prisma.service';
export interface PaginationInfo {
totalCount: number;
page: number;
pageSize: number;
totalPages: number;
hasNextPage: boolean;
hasPreviousPage: boolean;
pageCount: number;
}
type ModelDelegate = {
count: (args: any) => Promise<number>;
findMany: (args: any) => Promise<any[]>;
};
@Injectable()
export class PaginationHelper {
constructor(private prisma: PrismaService) {}
/**
* Sayfalama destekli sorgu yapar
*
* @param modelDelegate Prisma model delegesi (ör. prisma.users)
* @param query Prisma findMany argümanları + opsiyonel page, pageSize
* @returns { data, pagination } sonuç ve sayfalama bilgisi
*/
async paginate(
modelDelegate: ModelDelegate,
query: any & { page?: number; pageSize?: number },
): Promise<{ data: any[]; pagination: PaginationInfo }> {
const { page = 1, pageSize = 10, ...prismaQuery } = query;
const totalCount = await modelDelegate.count({ where: prismaQuery.where });
const totalPages = Math.max(Math.ceil(totalCount / pageSize), 1);
const pageNumber = page < 1 ? 1 : page > totalPages ? totalPages : page;
const pageSizeNumber = pageSize > 0 ? pageSize : 10;
const data = await modelDelegate.findMany({
...prismaQuery,
skip: (pageNumber - 1) * pageSizeNumber,
take: pageSizeNumber,
});
const pageCount = data.length;
return {
data,
pagination: {
totalCount,
page: pageNumber,
pageSize: pageSizeNumber,
totalPages,
hasNextPage: pageNumber < totalPages,
hasPreviousPage: pageNumber > 1,
pageCount,
},
};
}
}

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { PaginationHelper } from './pagination-helper';
import { PrismaService } from '@/src/prisma.service';
@Module({
providers: [PaginationHelper, PrismaService],
exports: [PaginationHelper],
})
export class UtilsModule {}