Skip to content

Commit

Permalink
feat: updated all other modules to use internalization i18n to return…
Browse files Browse the repository at this point in the history
… translated error messages
  • Loading branch information
rastislavkopal committed Sep 14, 2023
1 parent 2a13591 commit 078e1b4
Show file tree
Hide file tree
Showing 34 changed files with 321 additions and 201 deletions.
7 changes: 5 additions & 2 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ import { CreateUserDto } from '../users/dto/create-user.dto';
import { Public } from './decorators/public-route.decorator';
import { RegisterWithCodeDto } from './dto/register-code.dto';
import { InvitationExistsGuard } from '../common/guards/invitation-exists.guard';
import { BaseController } from '../common/helpers/base-controller';

@ApiTags('Auth')
@Controller({
path: 'auth',
version: '1',
})
export class AuthController {
constructor(private authService: AuthService) {}
export class AuthController extends BaseController {
constructor(private authService: AuthService) {
super();
}

@Public()
@Post('register')
Expand Down
51 changes: 20 additions & 31 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import {
Invitation,
InvitationDocument,
} from '../invitations/schemas/invitation.schema';
import { I18nContext, I18nService } from 'nestjs-i18n';

@Injectable()
export class AuthService {
private jwtExpires: number;

constructor(
private readonly i18nService: I18nService,
private jwtService: JwtService,
private usersService: UsersService,
private configService: ConfigService,
Expand Down Expand Up @@ -69,7 +71,9 @@ export class AuthService {
if (!refreshToken) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: 'User has been logged out.',
message: this.i18nService.t('errors.user_logged_out', {
lang: I18nContext.current()?.lang,
}),
});
}
return refreshToken.refreshToken;
Expand Down Expand Up @@ -108,7 +112,9 @@ export class AuthService {
if (!user) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Email not found',
message: this.i18nService.t('errors.email_not_found', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand All @@ -121,7 +127,9 @@ export class AuthService {
throw new HttpException(
{
statusCode: HttpStatus.UNAUTHORIZED,
message: 'incorrectPassword',
message: this.i18nService.t('errors.incorrect_password', {
lang: I18nContext.current()?.lang,
}),
},
HttpStatus.UNAUTHORIZED,
);
Expand All @@ -145,7 +153,9 @@ export class AuthService {
if (inv?.code !== createDto.code) {
throw new ForbiddenException({
statusCode: HttpStatus.FORBIDDEN,
message: 'Incorrect verification code',
message: this.i18nService.t('errors.incorrect_verification_code', {
lang: I18nContext.current()?.lang,
}),
});
}
await this.invModel.findOneAndDelete({ invitedEmail: createDto.email });
Expand All @@ -166,7 +176,9 @@ export class AuthService {
throw new HttpException(
{
statusCode: HttpStatus.UNAUTHORIZED,
message: 'Invalid refresh token',
message: this.i18nService.t('errors.invalid_refresh_token', {
lang: I18nContext.current()?.lang,
}),
},
HttpStatus.UNAUTHORIZED,
);
Expand All @@ -178,37 +190,14 @@ export class AuthService {
throw new HttpException(
{
statusCode: HttpStatus.UNAUTHORIZED,
message: 'Invalid refresh token',
message: this.i18nService.t('errors.invalid_refresh_token', {
lang: I18nContext.current()?.lang,
}),
},
HttpStatus.UNAUTHORIZED,
);
}

return this.authTokenResponse(user);
}

// ***********************
// ╔╦╗╔═╗╔╦╗╦ ╦╔═╗╔╦╗╔═╗
// ║║║║╣ ║ ╠═╣║ ║ ║║╚═╗
// ╩ ╩╚═╝ ╩ ╩ ╩╚═╝═╩╝╚═╝
// ***********************
// returnJwtExtractor() {
// return this.jwtExtractor;
// }

// getIp(req: Request): string {
// return getClientIp(req);
// }

// getBrowserInfo(req: Request): string {
// return req.header['user-agent'] || 'XX';
// }

// getCountry(req: Request): string {
// return req.header['cf-ipcountry'] ? req.header['cf-ipcountry'] : 'XX';
// }

// encryptText(text: string): string {
// return this.cryptr.encrypt(text);
// }
}
14 changes: 11 additions & 3 deletions src/auth/guards/roles.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import {
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { ROLES_KEY } from '../decorators/roles.decorator';
import { I18nContext, I18nService } from 'nestjs-i18n';

@Injectable()
export class RolesGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
constructor(
private readonly reflector: Reflector,
private readonly i18nService: I18nService,
) {
super();
}

Expand All @@ -29,13 +33,17 @@ export class RolesGuard extends AuthGuard('jwt') {
if (!user) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: 'Unauthorized user',
message: this.i18nService.t('errors.unauthorized', {
lang: I18nContext.current()?.lang,
}),
});
}
if (!(user.roles && hasRole())) {
throw new ForbiddenException({
statusCode: HttpStatus.FORBIDDEN,
message: 'Forbidden',
message: this.i18nService.t('errors.forbidden', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand Down
10 changes: 8 additions & 2 deletions src/auth/strategies/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { JwtPayloadType } from '../../common/types/auth/jwt-payload.type';
import { OrNeverType } from '../../common/types/or-never.type';
import { UsersService } from '../../users/users.service';
import { User } from '../../users/schemas/user.schema';
import { I18nContext, I18nService } from 'nestjs-i18n';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private jwtService: JwtService,
private configService: ConfigService,
private usersService: UsersService,
private readonly i18nService: I18nService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
Expand All @@ -26,7 +28,9 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
if (!payload._id) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: 'Unauthorized',
message: this.i18nService.t('errors.unauthorized', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand All @@ -35,7 +39,9 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
if (!user) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: 'Unauthorized',
message: this.i18nService.t('errors.unauthorized', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand Down
9 changes: 5 additions & 4 deletions src/claims/claims.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { Controller } from '@nestjs/common';
import {
ApiTags,
ApiQuery,
ApiBearerAuth,
ApiParam,
ApiOperation,
ApiBody,
Expand All @@ -33,15 +32,17 @@ import { Public } from '../auth/decorators/public-route.decorator';
import { ClaimResponseType } from './types/claim-response.type';
import { DoesArticleExist } from '../common/guards/article-exists.guard';
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
import { BaseController } from '../common/helpers/base-controller';

@ApiTags('Claims')
@Controller({
version: '1',
})
@ApiBearerAuth()
@UseInterceptors(CacheInterceptor)
export class ClaimsController {
constructor(private readonly claimService: ClaimsService) {}
export class ClaimsController extends BaseController {
constructor(private readonly claimService: ClaimsService) {
super();
}

@Post()
@ApiParam({ name: 'articleId', type: String })
Expand Down
54 changes: 22 additions & 32 deletions src/claims/claims.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,41 @@ import { GameService } from '../game/game.service';
import { GameAtionEnum } from '../game/enums/reputation.enum';
import { Article } from '../articles/schemas/article.schema';
import { ClaimHistoryType } from './types/claim-history.type';
import { I18nContext, I18nService } from 'nestjs-i18n';

@Injectable()
export class ClaimsService {
constructor(
private readonly i18nService: I18nService,
@InjectModel(Article.name) private articleModel: Model<Article>,
@InjectModel(Claim.name) private claimModel: Model<Claim>,
@InjectModel(Review.name) private reviewModel: Model<Review>,
private readonly gameService: GameService,
) {}

private throwClaimNotFoundExcpetion(): never {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: this.i18nService.t('errors.claim_not_found', {
lang: I18nContext.current()?.lang,
}),
});
}

async checkResourceAccess(user: User, _id: Types.ObjectId): Promise<boolean> {
if (_.includes(user.roles, 'admin')) return true;

const claim: Claim | null = await this.claimModel.findOne({ _id });

if (!claim) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Claim not found',
});
}
if (!claim) this.throwClaimNotFoundExcpetion();

if (!_.isEqual(claim.author._id, user._id)) {
throw new HttpException(
{
statusCode: HttpStatus.FORBIDDEN,
message: 'Forbidden',
message: this.i18nService.t('errors.forbidden', {
lang: I18nContext.current()?.lang,
}),
},
HttpStatus.FORBIDDEN,
);
Expand Down Expand Up @@ -79,12 +87,7 @@ export class ClaimsService {

async findByQuery(query: object): Promise<NullableType<Claim>> {
const claim = await this.claimModel.findById(query);
if (!claim) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Claim not found',
});
}
if (!claim) this.throwClaimNotFoundExcpetion();

return claim;
}
Expand Down Expand Up @@ -112,12 +115,7 @@ export class ClaimsService {
.lean(),
]);

if (!claim) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Claim not found',
});
}
if (!claim) this.throwClaimNotFoundExcpetion();

return { ...claim.toObject(), userReview };
}
Expand Down Expand Up @@ -152,16 +150,13 @@ export class ClaimsService {
): Promise<Claim> {
await this.checkResourceAccess(loggedUser, _id);
const currentClaim = await this.claimModel.findById(_id);
if (!currentClaim) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Claim not found',
});
}
if (!currentClaim) this.throwClaimNotFoundExcpetion();
if (currentClaim.history.length >= 10) {
throw new BadRequestException({
statusCode: HttpStatus.BAD_REQUEST,
message: 'Claim can be updated up to 10 times',
message: this.i18nService.t('errors.max_claim_update_requests', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand Down Expand Up @@ -198,12 +193,7 @@ export class ClaimsService {
article: articleId,
_id: claimId,
});
if (!deleterClaim) {
throw new NotFoundException({
statusCode: HttpStatus.NOT_FOUND,
message: 'Claim not found',
});
}
if (!deleterClaim) this.throwClaimNotFoundExcpetion();
return deleterClaim;
}
}
10 changes: 8 additions & 2 deletions src/claims/guards/is-claim-owner.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import {
import { Claim } from '../schemas/claim.schema';
import { ClaimsService } from '../claims.service';
import mongoose from 'mongoose';
import { I18nContext, I18nService } from 'nestjs-i18n';

export const IsClaimOwnerGuard = (paramId: string) => {
@Injectable()
class IsClaimOwnerMixin implements CanActivate {
constructor(@Inject(ClaimsService) public claimService: ClaimsService) {}
constructor(
@Inject(ClaimsService) public claimService: ClaimsService,
readonly i18nService: I18nService,
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
Expand All @@ -24,7 +28,9 @@ export const IsClaimOwnerGuard = (paramId: string) => {
if (!mongoose.Types.ObjectId.isValid(_id)) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: 'Invalid ObjectId',
message: this.i18nService.t('errors.invalid_objectid', {
lang: I18nContext.current()?.lang,
}),
});
}

Expand Down
Loading

0 comments on commit 078e1b4

Please sign in to comment.