Commit 84aa15f2 authored by Kulpybaev Ilyas's avatar Kulpybaev Ilyas

lesson 89

parent fcdb6611
This diff is collapsed.
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"cors": "^2.8.5", "cors": "^2.8.5",
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
"typescript": "^5.1.6" "typescript": "^5.1.6"
}, },
"devDependencies": { "devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/multer": "^1.4.11", "@types/multer": "^1.4.11",
......
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { Product } from '@/entities/product.entity'; import { Product } from '@/entities/product.entity';
import { Category } from '@/entities/category.entity'; import { Category } from '@/entities/category.entity';
import { User } from '@/entities/user.entity';
export const appDataSource = new DataSource({ export const appDataSource = new DataSource({
type: 'mysql', type: 'mysql',
...@@ -11,5 +12,5 @@ export const appDataSource = new DataSource({ ...@@ -11,5 +12,5 @@ export const appDataSource = new DataSource({
database: 'test', database: 'test',
synchronize: true, synchronize: true,
logging: true, logging: true,
entities: [Product, Category], entities: [Product, Category, User],
}); });
import { AuthService } from '@/services/auth.service';
import { RequestHandler } from 'express';
import { plainToInstance } from 'class-transformer';
import { RegisterUserDto } from '@/dto/register-user.dto';
import { SignInUserDto } from '@/dto/sign-in-user.dto';
export class AuthController {
private service: AuthService;
constructor() {
this.service = new AuthService();
}
signIn: RequestHandler = async (req, res): Promise<void> => {
try {
const signInUserDto = plainToInstance(SignInUserDto, req.body);
const user = await this.service.signIn(signInUserDto);
res.send(user);
} catch (e) {
res.send((e as Error).message);
}
};
register: RequestHandler = async (req, res): Promise<void> => {
try {
const registerUserDto = plainToInstance(RegisterUserDto, req.body);
const user = await this.service.register(registerUserDto);
res.send(user);
} catch (e) {
if ((e as { code: string }).code === 'ER_DUP_ENTRY') {
res.send({ error: { message: 'User already exists' } });
} else {
res.status(500).send({ error: { message: 'Oops something went wrong' } });
}
}
};
secret: RequestHandler = async (req, res) => {
try {
const token = req.header('Authorization');
if (!token) return res.status(401).send({ error: { message: 'No token present' } });
const user = await this.service.getUserByToken(token);
if (!user) return res.status(401).send({ error: { message: 'Wrong token' } });
return res.send({ message: 'some secret message' });
} catch (e) {
return res.status(500).send({ error: { message: 'Internal server error' } });
}
};
}
import { Expose } from 'class-transformer';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
export class RegisterUserDto {
@Expose()
@IsString()
@IsNotEmpty()
password!: string;
@Expose()
@IsString()
@IsNotEmpty()
username!: string;
@Expose()
@IsString()
@IsOptional()
displayName!: string;
}
import { Expose } from 'class-transformer';
import { IsNotEmpty, IsString } from 'class-validator';
export class SignInUserDto {
@Expose()
@IsString()
@IsNotEmpty()
password!: string;
@Expose()
@IsString()
@IsNotEmpty()
username!: string;
}
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm';
import bcrypt from 'bcrypt';
@Entity('users')
@Unique(['username'])
export class User {
@PrimaryGeneratedColumn()
id!: number;
@Column()
username!: string;
@Column()
password?: string;
@Column()
displayName!: string;
@Column()
token!: string;
async comparePassword(password: string): Promise<boolean> {
if (this.password) return await bcrypt.compare(password, this.password);
return false;
}
generateToken() {
this.token = crypto.randomUUID();
}
}
...@@ -4,11 +4,12 @@ import { ArticleRoute } from './routes/article.route'; ...@@ -4,11 +4,12 @@ import { ArticleRoute } from './routes/article.route';
import { ProductRoute } from '@/routes/product.route'; import { ProductRoute } from '@/routes/product.route';
import cors from 'cors'; import cors from 'cors';
import { CategoryRoute } from '@/routes/category.route'; import { CategoryRoute } from '@/routes/category.route';
import { AuthRoute } from '@/routes/auth.route';
const app = new App({ const app = new App({
port: 8000, port: 8000,
middlewares: [logger(), cors()], middlewares: [logger(), cors()],
controllers: [new ArticleRoute(), new ProductRoute(), new CategoryRoute()], controllers: [new ArticleRoute(), new ProductRoute(), new CategoryRoute(), new AuthRoute()],
}); });
app.listen(); app.listen();
export interface IUser {
id: number;
username: string;
password?: string;
displayName: string;
}
import { Repository } from 'typeorm';
import { User } from '@/entities/user.entity';
import { appDataSource } from '@/config/dataSource';
import { SignInUserDto } from '@/dto/sign-in-user.dto';
import { RegisterUserDto } from '@/dto/register-user.dto';
import { IUser } from '@/interfaces/IUser.interface';
import bcrypt from 'bcrypt';
const SALT_WORK_FACTORY = 10;
export class UserRepository extends Repository<User> {
constructor() {
super(User, appDataSource.createEntityManager());
}
async signIn(signInUserDto: SignInUserDto): Promise<User> {
const user = await this.findOne({
select: ['username', 'displayName', 'id', 'password'],
where: { username: signInUserDto.username },
});
if (!user) throw new Error('User with this username not exists');
const isMatch = await user.comparePassword(signInUserDto.password);
if (!isMatch) throw new Error('Password is wrong');
user.generateToken();
const userWithToken = await this.save(user);
delete userWithToken.password;
return userWithToken;
}
async register(registerUserDto: RegisterUserDto): Promise<IUser> {
const salt = await bcrypt.genSalt(SALT_WORK_FACTORY);
const userData = await this.create(registerUserDto);
userData.password = await bcrypt.hash(registerUserDto.password, salt);
userData.generateToken();
const user = await this.save(userData);
delete user.password;
return user;
}
async getUserByToken(token: string): Promise<IUser | null> {
return await this.findOneBy({ token });
}
}
import { IRoute } from '@/interfaces/IRoute.interface';
import { Router } from 'express';
import { AuthController } from '@/controllers/auth.controller';
export class AuthRoute implements IRoute {
public path = '/auth';
public router = Router();
private controller: AuthController;
constructor() {
this.controller = new AuthController();
this.init();
}
private init() {
this.router.post('/register', this.controller.register);
this.router.post('/sign-in', this.controller.signIn);
this.router.get('/secret', this.controller.secret);
}
}
import { UserRepository } from '@/repositories/user.repository';
import { RegisterUserDto } from '@/dto/register-user.dto';
import { IUser } from '@/interfaces/IUser.interface';
import { SignInUserDto } from '@/dto/sign-in-user.dto';
export class AuthService {
private repository: UserRepository;
constructor() {
this.repository = new UserRepository();
}
signIn = async (signInUserDto: SignInUserDto): Promise<IUser> => {
return await this.repository.signIn(signInUserDto);
};
register = async (registerUserDto: RegisterUserDto): Promise<IUser> => {
return await this.repository.register(registerUserDto);
};
getUserByToken = async (token: string): Promise<IUser | null> => {
return await this.repository.getUserByToken(token);
};
}
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