Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/backend/src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export class AuthController {
@UseGuards(JwtAuthGuard)
@Get('me')
async me(@Req() req: Request & { user: JwtPayload }): Promise<ApiResponse<UserDto>> {
const user = await this.authService.me(req.user);
const { email } = req.user;
const user = await this.authService.me(email);

return { data: user };
}
Expand Down
25 changes: 14 additions & 11 deletions apps/backend/src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ConflictException, Injectable } from '@nestjs/common';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UserDto } from '../user/dto/user.dto';
import bcrypt from 'bcrypt';
import { CreateUserDto } from './dto/create-user.dto';
Expand All @@ -15,7 +15,7 @@ export class AuthService {
) {}

async validateUser(email: string, password: string): Promise<UserDto | null> {
const user = await this.userService.findByEmail(email);
const user = await this.userService.findByEmailOrNull(email);

if (!user) return null;

Expand All @@ -32,13 +32,7 @@ export class AuthService {
}

async registration(createUserDto: CreateUserDto) {
const existing = await this.userService.findByEmail(createUserDto.email);
if (existing) {
throw new ConflictException('Email in use');
}

const hashedPassword = await hashPassword(createUserDto.password);

const user = await this.userService.create({
...createUserDto,
password: hashedPassword,
Expand All @@ -59,8 +53,17 @@ export class AuthService {
};
}

async me(user: JwtPayload): Promise<UserDto> {
const { sub: id } = user;
return await this.userService.findById(id);
async me(email: string): Promise<UserDto> {
const user = await this.userService.findByEmailOrNull(email);
if (!user) {
throw new UnauthorizedException('Not authorized');
}

return {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
};
}
}
9 changes: 7 additions & 2 deletions apps/backend/src/modules/auth/strategies/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { env } from '../../../env';
import { Injectable } from '@nestjs/common';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtPayload } from '../types/jwt-payload.type';
import { UserService } from '../../user/user.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
constructor(private readonly userService: UserService) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([(req) => req?.cookies?.access_token]),
secretOrKey: env.JWT_SECRET,
});
}

async validate(payload: JwtPayload): Promise<JwtPayload> {
const user = await this.userService.findByEmailOrNull(payload.email);
if (!user) {
throw new UnauthorizedException('Not authorized');
}
return payload;
}
}
38 changes: 18 additions & 20 deletions apps/backend/src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Injectable, NotFoundException } from '@nestjs/common';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from '../../prisma/prisma.service';
import { UserDto } from './dto/user.dto';
import { Prisma } from '@prisma/client';
import { CreateUserDto } from '../auth/dto/create-user.dto';
import { User } from '@prisma/client';
import { handlePrismaError } from '../../shared/helpers/handle-prisma-error.helper';

@Injectable()
export class UserService {
Expand Down Expand Up @@ -39,23 +39,27 @@ export class UserService {
};
}

async findByEmail(email: string): Promise<User | null> {
return await this.prisma.user.findUnique({
async findByEmailOrNull(email: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { email },
});
}

async create(createUserDto: CreateUserDto): Promise<UserDto> {
const user = await this.prisma.user.create({
data: createUserDto,
});
try {
const user = await this.prisma.user.create({
data: createUserDto,
});

return {
id: user.id,
name: user.name,
email: user.email,
role: user.role,
};
return {
id: user.id,
name: user.name,
email: user.email,
role: user.role,
};
} catch (error) {
handlePrismaError(error, { UNIQUE_CONSTRAINT: 'User with this email already exists' });
}
}

async update(id: string, updateUserDto: UpdateUserDto): Promise<UserDto> {
Expand All @@ -72,10 +76,7 @@ export class UserService {
role: updatedUser.role,
};
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025') {
throw new NotFoundException('User not found');
}
throw error;
handlePrismaError(error, { NOT_FOUND: 'User not found' });
}
}

Expand All @@ -85,10 +86,7 @@ export class UserService {
where: { id },
});
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025') {
throw new NotFoundException('User not found');
}
throw error;
handlePrismaError(error, { NOT_FOUND: 'User not found' });
}
}
}
41 changes: 41 additions & 0 deletions apps/backend/src/shared/helpers/handle-prisma-error.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Prisma } from '@prisma/client';
import { BadRequestException, ConflictException, NotFoundException } from '@nestjs/common';

type PrismaErrorMessages = {
NOT_FOUND?: string;
UNIQUE_CONSTRAINT?: string;
FOREIGN_KEY?: string;
VALUE_TOO_LONG?: string;
};

export function handlePrismaError(error: unknown, messages: PrismaErrorMessages): never {
Comment thread
TenzenIga marked this conversation as resolved.
if (error instanceof Prisma.PrismaClientKnownRequestError) {
switch (error.code) {
case 'P2025':
if (messages.NOT_FOUND) {
throw new NotFoundException(messages.NOT_FOUND ?? 'Not found');
}
break;

case 'P2002':
if (messages.UNIQUE_CONSTRAINT) {
throw new ConflictException(messages.UNIQUE_CONSTRAINT ?? 'Unique constraint failed');
}
break;

case 'P2003':
if (messages.FOREIGN_KEY) {
throw new ConflictException(messages.FOREIGN_KEY ?? 'Foreign key constraint failed');
}
break;

case 'P2000':
if (messages.VALUE_TOO_LONG) {
throw new BadRequestException(messages.VALUE_TOO_LONG ?? 'Value too long');
}
break;
}
}

throw error;
}