add auth

parent 6edad83d
......@@ -27,6 +27,7 @@
"@nestjs/platform-express": "^11.0.1",
"@nestjs/serve-static": "^5.0.3",
"@nestjs/typeorm": "^11.0.0",
"bcrypt": "^6.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"multer": "^2.0.2",
......@@ -43,6 +44,7 @@
"@nestjs/testing": "^11.0.1",
"@swc/cli": "^0.6.0",
"@swc/core": "^1.10.7",
"@types/bcrypt": "^6.0.0",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.14",
"@types/multer": "^2.0.0",
......
......@@ -29,6 +29,9 @@ importers:
'@nestjs/typeorm':
specifier: ^11.0.0
version: 11.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.26(pg@8.16.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@swc/core@1.13.3)(@types/node@22.17.1)(typescript@5.9.2)))
bcrypt:
specifier: ^6.0.0
version: 6.0.0
class-transformer:
specifier: ^0.5.1
version: 0.5.1
......@@ -72,6 +75,9 @@ importers:
'@swc/core':
specifier: ^1.10.7
version: 1.13.3
'@types/bcrypt':
specifier: ^6.0.0
version: 6.0.0
'@types/express':
specifier: ^5.0.0
version: 5.0.3
......@@ -1014,6 +1020,9 @@ packages:
'@types/babel__traverse@7.28.0':
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
'@types/bcrypt@6.0.0':
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
'@types/body-parser@1.19.6':
resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
......@@ -1419,6 +1428,10 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
bcrypt@6.0.0:
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
engines: {node: '>= 18'}
bin-version-check@5.1.0:
resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==}
engines: {node: '>=12'}
......@@ -2681,9 +2694,17 @@ packages:
node-abort-controller@3.1.1:
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
node-addon-api@8.5.0:
resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==}
engines: {node: ^18 || ^20 || >= 21}
node-emoji@1.11.0:
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
......@@ -4622,6 +4643,10 @@ snapshots:
dependencies:
'@babel/types': 7.28.2
'@types/bcrypt@6.0.0':
dependencies:
'@types/node': 22.17.1
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
......@@ -5153,6 +5178,11 @@ snapshots:
base64-js@1.5.1: {}
bcrypt@6.0.0:
dependencies:
node-addon-api: 8.5.0
node-gyp-build: 4.8.4
bin-version-check@5.1.0:
dependencies:
bin-version: 6.0.0
......@@ -6595,10 +6625,14 @@ snapshots:
node-abort-controller@3.1.1: {}
node-addon-api@8.5.0: {}
node-emoji@1.11.0:
dependencies:
lodash: 4.17.21
node-gyp-build@4.8.4: {}
node-int64@0.4.0: {}
node-releases@2.0.19: {}
......
onlyBuiltDependencies:
- '@nestjs/core'
- '@swc/core'
- bcrypt
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProductsModule } from './products/products.module';
import { AuthModule } from './auth/auth.module';
import { CategoriesModule } from './categories/categories.module';
import { ProductsModule } from './products/products.module';
import { UserModule } from './user/user.module';
@Module({
imports: [
......@@ -26,6 +28,8 @@ import { CategoriesModule } from './categories/categories.module';
}),
ProductsModule,
CategoriesModule,
UserModule,
AuthModule,
],
controllers: [],
providers: [],
......
import { Body, Controller, Get, Headers, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginAuthDto } from './dto/login.dto';
import { RegisterAuthDto } from './dto/register.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
register(@Body() registerAuthDto: RegisterAuthDto) {
return this.authService.register(registerAuthDto);
}
@Post('login')
login(@Body() loginAuthDto: LoginAuthDto) {
return this.authService.login(loginAuthDto);
}
@Get('secret')
secret(@Headers('Authorization') token: string) {
return this.authService.secret(token);
}
}
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/user/entities/user.entity';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcrypt';
import { User } from 'src/user/entities/user.entity';
import { Repository } from 'typeorm';
import { LoginAuthDto } from './dto/login.dto';
import { RegisterAuthDto } from './dto/register.dto';
const SALT_WORK_FACTOR = 10;
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async register(registerAuthDto: RegisterAuthDto) {
const salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
registerAuthDto.password = await bcrypt.hash(
registerAuthDto.password,
salt,
);
const user = this.userRepo.create(registerAuthDto);
user.generateToken();
return await this.userRepo.save(user);
}
async login(loginAuthDto: LoginAuthDto) {
const user = await this.userRepo.findOneBy({
userName: loginAuthDto.userName,
});
if (!user) throw new NotFoundException('User not found');
const isMatch = await user.comparePassword(loginAuthDto.password);
if (!isMatch) throw new Error('Invalid userName or password');
return user;
}
async secret(token: string) {
const user = await this.userRepo.findOneBy({ token });
return user;
}
}
import { IsNotEmpty, IsString } from 'class-validator';
export class LoginAuthDto {
@IsNotEmpty()
@IsString()
userName: string;
@IsNotEmpty()
@IsString()
password: string;
}
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
export class RegisterAuthDto {
@IsNotEmpty()
@IsString()
userName: string;
@IsOptional()
@IsString()
displayName?: string;
@IsNotEmpty()
@IsString()
password: string;
}
import { IsOptional, IsString } from 'class-validator';
export class CreateUserDto {
@IsString()
userName: string;
@IsOptional()
@IsString()
displayName: string;
@IsString()
password: string;
}
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}
import * as bcrypt from 'bcrypt';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity({ name: 'users' })
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
userName: string;
@Column({ nullable: true })
displayName?: string;
@Column()
password: string;
@Column({ nullable: true })
token: string;
async comparePassword(password: string): Promise<boolean> {
return await bcrypt.compare(password, this.password);
}
generateToken() {
this.token = crypto.randomUUID();
}
}
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
}
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async create(createUserDto: CreateUserDto) {
const user = this.userRepo.create(createUserDto);
return await this.userRepo.save(user);
}
async findAll() {
return await this.userRepo.find();
}
async findOne(id: number) {
return await this.userRepo.findOneBy({ id });
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment