after claude code

This commit is contained in:
Phuoc Nguyen
2025-10-10 16:04:10 +07:00
parent cc53f60bea
commit 6203e8c2ec
109 changed files with 10109 additions and 150 deletions

View File

@@ -0,0 +1,138 @@
import {
Injectable,
UnauthorizedException,
BadRequestException,
ConflictException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { UsersService } from '../users/users.service';
import { LoginDto, RegisterDto, AuthResponseDto } from './dto';
import { JwtPayload } from './interfaces/jwt-payload.interface';
import { UserRole } from '../users/entities/user.entity';
@Injectable()
export class AuthService {
private readonly BCRYPT_ROUNDS = 10;
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
) {}
/**
* Validate user credentials (used by LocalStrategy)
*/
async validateUser(email: string, password: string): Promise<any> {
const user = await this.usersService.findByEmail(email);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
if (!user.isActive) {
throw new UnauthorizedException('User account is inactive');
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw new UnauthorizedException('Invalid credentials');
}
// Return user without password
const { password: _, ...result } = user;
return result;
}
/**
* Register new user
*/
async register(registerDto: RegisterDto): Promise<AuthResponseDto> {
// Check if user already exists
const existingUser = await this.usersService.findByEmail(registerDto.email);
if (existingUser) {
throw new ConflictException('Email already registered');
}
// Hash password
const hashedPassword = await this.hashPassword(registerDto.password);
// Create user with default role if not provided
const user = await this.usersService.create({
...registerDto,
password: hashedPassword,
roles: registerDto.roles || [UserRole.USER],
});
// Generate JWT and return
return this.login(user);
}
/**
* Login user and generate JWT
*/
async login(user: any): Promise<AuthResponseDto> {
const payload: JwtPayload = {
sub: user.id,
email: user.email,
roles: user.roles || [],
};
return {
access_token: this.jwtService.sign(payload),
user: {
id: user.id,
email: user.email,
name: user.name,
roles: user.roles,
isActive: user.isActive,
createdAt: user.createdAt,
},
};
}
/**
* Validate JWT token
*/
async validateToken(token: string): Promise<any> {
try {
const payload = this.jwtService.verify(token);
return payload;
} catch (error) {
throw new UnauthorizedException('Invalid or expired token');
}
}
/**
* Refresh access token
*/
async refreshToken(userId: string): Promise<AuthResponseDto> {
const user = await this.usersService.findOne(userId);
if (!user) {
throw new UnauthorizedException('User not found');
}
if (!user.isActive) {
throw new UnauthorizedException('User account is inactive');
}
return this.login(user);
}
/**
* Hash password using bcrypt
*/
private async hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, this.BCRYPT_ROUNDS);
}
/**
* Verify password hash
*/
async verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
}