Compare commits

..

2 Commits

Author SHA1 Message Date
parsa aghaei
6ae35efb3e fix(filter): preserve custom Persian error messages in HttpExceptionFilter
All checks were successful
Deploy Canina / deploy (push) Successful in 1m43s
2026-08-16 15:12:18 +03:30
parsa aghaei
0c32985ce2 fix(auth): centralize JWT secret with registerAsync to ensure consistent signing and verification 2026-08-16 15:11:05 +03:30
4 changed files with 27 additions and 19 deletions

View File

@ -0,0 +1,14 @@
export const DEFAULT_JWT_SECRET =
'canina_jwt_fallback_access_secret_32_characters_minimum_len_2026';
export const DEFAULT_JWT_REFRESH_SECRET =
'canina_jwt_fallback_refresh_secret_32_characters_minimum_len_2026';
export const getJwtSecret = (): string =>
process.env.JWT_ACCESS_SECRET ||
process.env.JWT_SECRET ||
DEFAULT_JWT_SECRET;
export const getJwtRefreshSecret = (): string =>
process.env.JWT_REFRESH_SECRET ||
DEFAULT_JWT_REFRESH_SECRET;

View File

@ -5,19 +5,17 @@ import { AuthService } from './auth.service';
import { AuthController } from './auth.controller'; import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy'; import { JwtStrategy } from './jwt.strategy';
import { UsersModule } from '../users/users.module'; import { UsersModule } from '../users/users.module';
import { getJwtSecret } from './auth.constants';
const JWT_SECRET =
process.env.JWT_ACCESS_SECRET ||
process.env.JWT_SECRET ||
'canina_jwt_fallback_secret_32_characters_minimum_len_2026';
@Module({ @Module({
imports: [ imports: [
UsersModule, UsersModule,
PassportModule, PassportModule,
JwtModule.register({ JwtModule.registerAsync({
secret: JWT_SECRET, useFactory: () => ({
signOptions: { expiresIn: '7d' }, secret: getJwtSecret(),
signOptions: { expiresIn: '7d' },
}),
}), }),
], ],
controllers: [AuthController], controllers: [AuthController],

View File

@ -2,6 +2,7 @@ import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service'; import { UsersService } from '../users/users.service';
import { getJwtSecret } from './auth.constants';
export interface JwtPayload { export interface JwtPayload {
sub: string; sub: string;
@ -13,15 +14,10 @@ export interface JwtPayload {
@Injectable() @Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) { export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly usersService: UsersService) { constructor(private readonly usersService: UsersService) {
const secret =
process.env.JWT_ACCESS_SECRET ||
process.env.JWT_SECRET ||
'canina_jwt_fallback_secret_32_characters_minimum_len_2026';
super({ super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false, ignoreExpiration: false,
secretOrKey: secret, secretOrKey: getJwtSecret(),
}); });
} }

View File

@ -92,19 +92,19 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
if (!msg) return 'خطای نامشخص رخ داده است.'; if (!msg) return 'خطای نامشخص رخ داده است.';
const lower = msg.toLowerCase(); const lower = msg.toLowerCase();
if (lower.includes('unauthorized') || status === 401) { if (lower === 'unauthorized' || msg === 'Unauthorized') {
return 'شما دسترسی لازم برای این عملیات را ندارید. لطفاً ابتدا وارد حساب کاربری خود شوید.'; return 'شما دسترسی لازم برای این عملیات را ندارید. لطفاً ابتدا وارد حساب کاربری خود شوید.';
} }
if (lower.includes('forbidden') || status === 403) { if (lower === 'forbidden' || msg === 'Forbidden') {
return 'دسترسی شما به این بخش یا منبع محدود شده است.'; return 'دسترسی شما به این بخش یا منبع محدود شده است.';
} }
if (lower.includes('not found') || status === 404) { if (lower === 'not found' || msg === 'Not Found') {
return 'مورد یا صفحه درخواستی یافت نشد.'; return 'مورد یا صفحه درخواستی یافت نشد.';
} }
if (status === 400) { if (lower === 'bad request' || msg === 'Bad Request') {
return 'درخواست نامعتبر است. لطفاً پارامترهای ورودی را چک کنید.'; return 'درخواست نامعتبر است. لطفاً پارامترهای ورودی را چک کنید.';
} }
if (status === 429) { if (lower === 'too many requests' || msg === 'ThrottlerException: Too Many Requests') {
return 'تعداد درخواست‌های شما بیش از حد مجاز است. لطفاً کمی صبر کرده و مجدداً تلاش کنید.'; return 'تعداد درخواست‌های شما بیش از حد مجاز است. لطفاً کمی صبر کرده و مجدداً تلاش کنید.';
} }