feat: add refresh token and global exception filter

This commit is contained in:
2026-05-09 09:00:25 +03:30
parent 06f659101d
commit 73f7af3146
32 changed files with 512 additions and 42 deletions
+11
View File
@@ -0,0 +1,11 @@
import { HttpException, HttpStatus } from '@nestjs/common';
export class AppException extends HttpException {
constructor(
public messageKey: string,
status: HttpStatus,
public params?: Record<string, any>,
) {
super({ message: messageKey, params }, status);
}
}
@@ -0,0 +1,55 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { I18nService } from 'nestjs-i18n';
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
constructor(
/**
* Inject I18n Service
*/
private readonly i18nService: I18nService,
) {}
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
const isHttpException = exception instanceof HttpException;
const status = isHttpException ? exception.getStatus() : 500;
const exceptionResponse = isHttpException
? exception.getResponse()
: 'common.errors.internalServerError';
const messageKey =
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message;
const message = this.i18nService.translate(messageKey as string, {
args: (exceptionResponse as any).params,
});
response.status(status).json({
success: false,
statusCode: status,
message,
path: request.url,
timestamp: new Date().toISOString(),
...(process.env.NODE_ENV !== 'production' && {
raw: exceptionResponse,
stack: exception instanceof Error ? exception.stack : undefined,
}),
});
}
}
@@ -0,0 +1 @@
export const REDIS = 'REDIS_CLIENT';
@@ -0,0 +1,41 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { Inject, Injectable } from '@nestjs/common';
import { REDIS } from '../constants/redis.constants';
import Redis from 'ioredis';
@Injectable()
export class RedisService {
constructor(
/**
* Inject Redis
*/
@Inject(REDIS)
private readonly redis: Redis,
) {}
get client() {
return this.redis;
}
async set(key: string, val: string, ttl?: number) {
if (ttl) {
await this.redis.set(key, val, 'EX', ttl);
} else {
await this.redis.set(key, val);
}
}
async get(key: string) {
return await this.redis.get(key);
}
async del(key: string) {
return await this.redis.del(key);
}
async onModuleDestroy() {
await this.redis.quit();
}
}
+25
View File
@@ -0,0 +1,25 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { Module } from '@nestjs/common';
import { RedisService } from './providers/redis.service';
import { REDIS } from './constants/redis.constants';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';
@Module({
providers: [
{
provide: REDIS,
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
return new Redis({
host: configService.get<string>('redis.host'),
port: configService.get<number>('redis.port'),
});
},
},
RedisService,
],
exports: [RedisService],
})
export class RedisModule {}