forked from Shared/esg
feat: add Persian error translations and comprehensive test suite
Implement normalized error handling with Persian translations across all exception types, replace legacy NestJS exceptions with AppException, and add unit, integration, and smoke tests. - Add error catalog with gateway-owned codes and Persian messages - Introduce AppException wrapping normalized error envelopes - Add translateError helper for automatic messageFa population - Remove claims module and update provider error normalization - Add unit tests for error contracts and helper functions - Add integration tests for admin and inquiry endpoints - Add smoke tests for real provider connectivity - Add test support utilities (auth mocks, assertions, app factory)
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AuditEvent } from '../common/enums/audit-event.enum';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { UsersService, RequestContext } from '../users/users.service';
|
||||
@@ -14,6 +10,7 @@ import { ProfileDto } from './dto/profile.dto';
|
||||
import { UserMapper } from '../users/mappers/user.mapper';
|
||||
import { JwtPayload } from './interfaces/jwt-payload.interface';
|
||||
import { AuthenticatedUser } from './interfaces/authenticated-user.interface';
|
||||
import { AppException } from '../common/exceptions/app-exception';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
@@ -34,23 +31,23 @@ export class AuthService {
|
||||
userAgent: context.userAgent,
|
||||
metadata: { reason: 'invalid_credentials' },
|
||||
});
|
||||
throw new UnauthorizedException('Invalid credentials');
|
||||
throw new AppException('INVALID_CREDENTIALS');
|
||||
}
|
||||
|
||||
if (!user.isActive) {
|
||||
await this.auditFailedLogin(user._id.toString(), user.username, 'inactive', context);
|
||||
throw new ForbiddenException('Account is inactive');
|
||||
throw new AppException('ACCOUNT_INACTIVE');
|
||||
}
|
||||
|
||||
if (user.isBlocked) {
|
||||
await this.auditFailedLogin(user._id.toString(), user.username, 'blocked', context);
|
||||
throw new ForbiddenException('Account is blocked');
|
||||
throw new AppException('ACCOUNT_BLOCKED');
|
||||
}
|
||||
|
||||
const valid = await this.passwordService.compare(dto.password, user.password);
|
||||
if (!valid) {
|
||||
await this.auditFailedLogin(user._id.toString(), user.username, 'invalid_credentials', context);
|
||||
throw new UnauthorizedException('Invalid credentials');
|
||||
throw new AppException('INVALID_CREDENTIALS');
|
||||
}
|
||||
|
||||
const tokens = await this.issueTokens(user);
|
||||
@@ -72,20 +69,20 @@ export class AuthService {
|
||||
try {
|
||||
payload = this.tokenService.verifyRefreshToken(refreshToken);
|
||||
} catch {
|
||||
throw new UnauthorizedException('Invalid refresh token');
|
||||
throw new AppException('INVALID_REFRESH_TOKEN');
|
||||
}
|
||||
|
||||
const valid = await this.usersService.validateRefreshToken(payload.sub, refreshToken);
|
||||
if (!valid) {
|
||||
throw new UnauthorizedException('Invalid refresh token');
|
||||
throw new AppException('INVALID_REFRESH_TOKEN');
|
||||
}
|
||||
|
||||
const user = await this.usersService.findById(payload.sub);
|
||||
if (!user.isActive) {
|
||||
throw new ForbiddenException('Account is inactive');
|
||||
throw new AppException('ACCOUNT_INACTIVE');
|
||||
}
|
||||
if (user.isBlocked) {
|
||||
throw new ForbiddenException('Account is blocked');
|
||||
throw new AppException('ACCOUNT_BLOCKED');
|
||||
}
|
||||
|
||||
const tokens = await this.issueTokens(user);
|
||||
|
||||
Reference in New Issue
Block a user