Skip to content

Commit

Permalink
feat: 馃幐 add login api
Browse files Browse the repository at this point in the history
  • Loading branch information
yeukfei02 committed Apr 21, 2022
1 parent ad976f7 commit 9c6616f
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 6 deletions.
5 changes: 4 additions & 1 deletion .env.sample
@@ -1,2 +1,5 @@
# postgres
DATABASE_URL_PROD=
DATABASE_URL_PROD=

# jwt secret
JWT_SECRET=
4 changes: 4 additions & 0 deletions package.json
Expand Up @@ -11,9 +11,11 @@
"@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.2.1",
"@prisma/client": "^3.12.0",
"bcryptjs": "^2.4.3",
"compression": "^1.7.4",
"dayjs": "^1.11.1",
"helmet": "^5.0.2",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.21",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
Expand All @@ -25,10 +27,12 @@
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@types/bcryptjs": "^2.4.2",
"@types/compression": "^1.7.2",
"@types/express": "^4.17.13",
"@types/helmet": "^4.0.0",
"@types/jest": "27.4.1",
"@types/jsonwebtoken": "^8.5.8",
"@types/lodash": "^4.14.182",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
Expand Down
@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[email]` on the table `users` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "users" ADD COLUMN "password" TEXT;

-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
3 changes: 2 additions & 1 deletion prisma/schema.prisma
Expand Up @@ -13,7 +13,8 @@ model users {
first_name String? @db.VarChar(50)
last_name String? @db.VarChar(50)
gender Gender
email String?
email String? @unique
password String?
date_of_birth DateTime?
register_date DateTime?
phone String?
Expand Down
6 changes: 6 additions & 0 deletions prisma/seed.ts
@@ -1,6 +1,7 @@
import { PrismaClient } from '@prisma/client';
import _ from 'lodash';
import { faker } from '@faker-js/faker';
import bcrypt from 'bcryptjs';

const prisma = new PrismaClient();

Expand All @@ -15,14 +16,19 @@ const prisma = new PrismaClient();
async function createUsers() {
const titleList = ['mr', 'ms', 'mrs', 'miss', 'dr'];

const salt = bcrypt.genSaltSync(10);

const usersDataList = [];
for (let index = 0; index < 100; index++) {
const hashedPassword = bcrypt.hashSync(faker.internet.password(), salt);

const usersData = {
title: _.sample(titleList),
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
gender: index % 2 === 0 ? 'male' : 'female',
email: faker.internet.email(),
password: hashedPassword,
date_of_birth: faker.date.recent(),
register_date: faker.date.recent(),
phone: faker.phone.phoneNumber(),
Expand Down
10 changes: 9 additions & 1 deletion src/app.module.ts
@@ -1,14 +1,22 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoginModule } from './login/login.module';
import { UserModule } from './user/user.module';
import { LocationModule } from './location/location.module';
import { PostModule } from './post/post.module';
import { TagModule } from './tag/tag.module';
import { CommentModule } from './comment/comment.module';

@Module({
imports: [UserModule, LocationModule, PostModule, TagModule, CommentModule],
imports: [
LoginModule,
UserModule,
LocationModule,
PostModule,
TagModule,
CommentModule,
],
controllers: [AppController],
providers: [AppService],
})
Expand Down
9 changes: 9 additions & 0 deletions src/login/dto/login.dto.ts
@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';

export class LoginDto {
@ApiProperty()
email: string;

@ApiProperty()
password: string;
}
18 changes: 18 additions & 0 deletions src/login/login.controller.spec.ts
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LoginController } from './login.controller';

describe('LoginController', () => {
let controller: LoginController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [LoginController],
}).compile();

controller = module.get<LoginController>(LoginController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
47 changes: 47 additions & 0 deletions src/login/login.controller.ts
@@ -0,0 +1,47 @@
import { Controller, Post, Body } from '@nestjs/common';
import { LoginService } from './login.service';
import { LoginDto } from './dto/login.dto';

import { ApiResponse } from '@nestjs/swagger';
import { LoginResponse } from './response/login.response';
import jwt from 'jsonwebtoken';

@Controller('login')
export class LoginController {
constructor(private readonly loginService: LoginService) {}

@Post()
@ApiResponse({
status: 201,
description: 'Successful response',
type: LoginResponse,
})
async login(@Body() loginDto: LoginDto): Promise<any> {
let response = {};

const users = await this.loginService.login(loginDto);
if (users) {
const token = jwt.sign(
{ id: users.id, email: users.email },
process.env.JWT_SECRET,
{ expiresIn: '1d' },
);

response = {
message: 'login',
users: {
id: users.id,
first_name: users.first_name,
last_name: users.last_name,
gender: users.gender,
email: users.email,
created_at: users.created_at,
updated_at: users.updated_at,
},
token: token,
};
}

return response;
}
}
11 changes: 11 additions & 0 deletions src/login/login.module.ts
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';
import { LoginService } from './login.service';
import { PrismaService } from '../prisma.service';

@Module({
imports: [],
controllers: [LoginController],
providers: [LoginService, PrismaService],
})
export class LoginModule {}
18 changes: 18 additions & 0 deletions src/login/login.service.spec.ts
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LoginService } from './login.service';

describe('LoginService', () => {
let service: LoginService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LoginService],
}).compile();

service = module.get<LoginService>(LoginService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
31 changes: 31 additions & 0 deletions src/login/login.service.ts
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { LoginDto } from './dto/login.dto';
import { PrismaService } from '../prisma.service';
import { users } from '@prisma/client';
import bcrypt from 'bcryptjs';

@Injectable()
export class LoginService {
constructor(private readonly prisma: PrismaService) {}

async login(loginDto: LoginDto): Promise<users> {
let result = null;

const users = await this.prisma.users.findFirst({
where: {
email: loginDto.email,
},
});

const isValidPassword = bcrypt.compareSync(
loginDto.password,
users.password,
);
console.log('isValidPassword = ', isValidPassword);
if (isValidPassword) {
result = users;
}

return result;
}
}
35 changes: 35 additions & 0 deletions src/login/response/login.response.ts
@@ -0,0 +1,35 @@
import { ApiProperty } from '@nestjs/swagger';

export class User {
@ApiProperty()
id: string;

@ApiProperty()
first_name: string;

@ApiProperty()
last_name: string;

@ApiProperty()
gender: string;

@ApiProperty()
email: string;

@ApiProperty()
created_at: string;

@ApiProperty()
updated_at: string;
}

export class LoginResponse {
@ApiProperty()
message: string;

@ApiProperty()
user: User;

@ApiProperty()
token: string;
}
3 changes: 3 additions & 0 deletions src/user/dto/createUser.dto.ts
Expand Up @@ -17,6 +17,9 @@ export class CreateUserDto {
@ApiProperty()
email: string;

@ApiProperty()
password: string;

@ApiProperty()
date_of_birth: string;

Expand Down
5 changes: 5 additions & 0 deletions src/user/user.service.ts
Expand Up @@ -6,6 +6,7 @@ import { users } from '@prisma/client';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import bcrypt from 'bcryptjs';

dayjs.extend(utc);
dayjs.extend(timezone);
Expand All @@ -15,13 +16,17 @@ export class UserService {
constructor(private readonly prisma: PrismaService) {}

async createUser(createUserDto: CreateUserDto): Promise<users> {
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(createUserDto.password, salt);

const users = await this.prisma.users.create({
data: {
title: createUserDto.title,
first_name: createUserDto.first_name,
last_name: createUserDto.last_name,
gender: createUserDto.gender,
email: createUserDto.email,
password: hashedPassword,
date_of_birth: dayjs(createUserDto.date_of_birth)
.tz('Asia/Hong_Kong')
.toISOString(),
Expand Down

0 comments on commit 9c6616f

Please sign in to comment.