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,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 {}