add user registration
This commit is contained in:
1
server/src/controller/index.ts
Normal file
1
server/src/controller/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { UserRoutes } from './users.controller'
|
||||
30
server/src/controller/users.controller.ts
Normal file
30
server/src/controller/users.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { RequestHandler, Router } from 'express'
|
||||
import { UserDto } from '../dto/user.dto';
|
||||
import { UserService } from '../service/user.service';
|
||||
|
||||
const router = Router();
|
||||
|
||||
const basePath = '/users'
|
||||
|
||||
export const post: RequestHandler = (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
UserService.create({
|
||||
email,
|
||||
password
|
||||
}).then(user => {
|
||||
const respose: UserDto = {
|
||||
id: user.id,
|
||||
email: user.email
|
||||
}
|
||||
|
||||
res.json(respose);
|
||||
}).catch(err => {
|
||||
res.status(422).json({
|
||||
error: err.message
|
||||
});
|
||||
})
|
||||
}
|
||||
router.post(basePath, post)
|
||||
|
||||
export const UserRoutes = router;
|
||||
4
server/src/dto/newUser.dto.ts
Normal file
4
server/src/dto/newUser.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type NewUserDto = {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
4
server/src/dto/user.dto.ts
Normal file
4
server/src/dto/user.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type UserDto = {
|
||||
id: number
|
||||
email: string
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IsEmail, IsNotEmpty } from "class-validator"
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
|
||||
|
||||
@Entity()
|
||||
@@ -6,7 +7,9 @@ export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column()
|
||||
@Column({ unique: true })
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string
|
||||
|
||||
@Column()
|
||||
|
||||
@@ -2,18 +2,18 @@ import "reflect-metadata"
|
||||
import * as dotenv from 'dotenv';
|
||||
import * as express from 'express';
|
||||
import { AppDataSource } from "./infra/dataSource";
|
||||
import { UserRoutes } from "./controller";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT;
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!');
|
||||
});
|
||||
app.use(express.json());
|
||||
|
||||
app.use(UserRoutes)
|
||||
|
||||
AppDataSource.initialize().then(() => {
|
||||
const port = process.env.PORT;
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running at ${port} 🚀`);
|
||||
});
|
||||
|
||||
4
server/src/repository/user.repository.ts
Normal file
4
server/src/repository/user.repository.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { User } from "../entity/user.entity";
|
||||
import { AppDataSource } from "../infra/dataSource";
|
||||
|
||||
export const userRepository = AppDataSource.getRepository(User)
|
||||
76
server/src/service/__test__/auth.service.spec.ts
Normal file
76
server/src/service/__test__/auth.service.spec.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { User } from "../../entity/user.entity";
|
||||
import { AuthService, SALT_ROUNDS } from "../auth.service";
|
||||
import * as bcrypt from 'bcrypt'
|
||||
|
||||
describe('AuthService', () => {
|
||||
|
||||
describe('#updateUserPassword', () => {
|
||||
it('should return a change user encryptedPassword', async () => {
|
||||
const user = new User();
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
bcrypt.hash('example', SALT_ROUNDS, function (err, hash) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
user.encryptedPassword = hash
|
||||
|
||||
resolve()
|
||||
});
|
||||
})
|
||||
|
||||
const currentUserEncryptedPassowrd = user.encryptedPassword
|
||||
|
||||
await AuthService.updateUserPassword(user, 'example');
|
||||
|
||||
expect(currentUserEncryptedPassowrd).not.toBe(user.encryptedPassword);
|
||||
});
|
||||
})
|
||||
|
||||
describe('#isUserPasswordValid', () => {
|
||||
it('should return true if password is valid', async () => {
|
||||
const user = new User();
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
bcrypt.hash('example', SALT_ROUNDS, function (err, hash) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
user.encryptedPassword = hash
|
||||
|
||||
resolve()
|
||||
});
|
||||
})
|
||||
|
||||
const currentUserEncryptedPassowrd = user.encryptedPassword
|
||||
|
||||
const result = await AuthService.isUserPasswordValid(user, 'example');
|
||||
|
||||
expect(result).toBeTruthy()
|
||||
});
|
||||
|
||||
it('should return false if password is invalid', async () => {
|
||||
const user = new User();
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
bcrypt.hash('example', SALT_ROUNDS, function (err, hash) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
user.encryptedPassword = hash
|
||||
|
||||
resolve()
|
||||
});
|
||||
})
|
||||
|
||||
const currentUserEncryptedPassowrd = user.encryptedPassword
|
||||
|
||||
const result = await AuthService.isUserPasswordValid(user, 'example2');
|
||||
|
||||
expect(result).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
35
server/src/service/auth.service.ts
Normal file
35
server/src/service/auth.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { User } from "../entity/user.entity";
|
||||
import * as bcrypt from 'bcrypt'
|
||||
|
||||
export const SALT_ROUNDS = 10
|
||||
|
||||
const updateUserPassword = (user: User, password: string) => {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
bcrypt.hash(password, SALT_ROUNDS, function (err, hash) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
user.encryptedPassword = hash
|
||||
|
||||
resolve(true)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const isUserPasswordValid = (user: User, password: string) => {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
bcrypt.compare(password, user.encryptedPassword, function (err, result) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
resolve(result)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export const AuthService = {
|
||||
updateUserPassword,
|
||||
isUserPasswordValid
|
||||
}
|
||||
37
server/src/service/user.service.ts
Normal file
37
server/src/service/user.service.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { validate } from "class-validator"
|
||||
import { NewUserDto } from "../dto/newUser.dto"
|
||||
import { User } from "../entity/user.entity"
|
||||
import { userRepository } from "../repository/user.repository"
|
||||
import { AuthService } from "./auth.service"
|
||||
|
||||
async function create(newUserDto: NewUserDto): Promise<User> {
|
||||
const user = new User()
|
||||
user.email = newUserDto.email
|
||||
|
||||
|
||||
const errors = await validate(user);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error("Invalid user data")
|
||||
}
|
||||
|
||||
|
||||
const result = await findByEmail(user.email)
|
||||
|
||||
if (result) {
|
||||
throw new Error("User already exists")
|
||||
}
|
||||
|
||||
await AuthService.updateUserPassword(user, newUserDto.password)
|
||||
|
||||
return userRepository.save(user)
|
||||
}
|
||||
|
||||
async function findByEmail(email: string): Promise<User | undefined> {
|
||||
return userRepository.findOneBy({ email })
|
||||
}
|
||||
|
||||
export const UserService = {
|
||||
create,
|
||||
findByEmail
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('test', () => {
|
||||
it('true', () => {
|
||||
expect(true).toBeTruthy()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user