auth services implmented
This commit is contained in:
65
ServicesApi/src/utils/auth/login_handler.ts
Normal file
65
ServicesApi/src/utils/auth/login_handler.ts
Normal 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 };
|
||||
124
ServicesApi/src/utils/auth/redis_handlers.ts
Normal file
124
ServicesApi/src/utils/auth/redis_handlers.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
0
ServicesApi/src/utils/auth/users_handlers.ts
Normal file
0
ServicesApi/src/utils/auth/users_handlers.ts
Normal file
58
ServicesApi/src/utils/pagination-helper.ts
Normal file
58
ServicesApi/src/utils/pagination-helper.ts
Normal 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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
9
ServicesApi/src/utils/utils.module.ts
Normal file
9
ServicesApi/src/utils/utils.module.ts
Normal 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 {}
|
||||
Reference in New Issue
Block a user