diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..354551a --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './controllers/auth.controller'; +import { UserDataService } from './data/user.dataservice'; +import { AuthService } from './domain/services/auth.service'; + +@Module({ + providers: [AuthService, UserDataService], + controllers: [AuthController], +}) +export class AuthModule {} diff --git a/src/auth/constants.ts b/src/auth/constants.ts new file mode 100644 index 0000000..f569990 --- /dev/null +++ b/src/auth/constants.ts @@ -0,0 +1,3 @@ +export const JWT_SECRET = + process.env.JWT_SECRET ?? 'B9A8Y92wZwbGBHOcUaHykeQ6mNNKeTFt'; +export const JWT_EXPIRED = process.env.JWT_EXPIRED ?? '12h'; diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts new file mode 100644 index 0000000..6bf7ba8 --- /dev/null +++ b/src/auth/controllers/auth.controller.ts @@ -0,0 +1,32 @@ +import { Body, Controller, Get, Post } from '@nestjs/common'; +import { Unprotected } from 'src/core/guards'; +import { Pagination } from 'src/core/response'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { LoginRequest } from '../domain/entities/request.interface'; +import { User } from '../domain/entities/user.interface'; +import { AuthService } from '../domain/services/auth.service'; + +@Controller('auth') +export class AuthController { + constructor(private readonly service: AuthService) {} + + @Unprotected() + @Post() + login(@Body() body: LoginRequest) { + return this.service.createAccessToken(body); + } + + @Get() + user() { + return this.service.getUser(); + } + + @Pagination() + @Get('/all') + async users(): Promise> { + return { + data: await this.service.getUsers(), + total: 101, + }; + } +} diff --git a/src/auth/data/user.dataservice.ts b/src/auth/data/user.dataservice.ts new file mode 100644 index 0000000..8af7586 --- /dev/null +++ b/src/auth/data/user.dataservice.ts @@ -0,0 +1,54 @@ +import { LoginRequest } from '../domain/entities/request.interface'; +import { User } from '../domain/entities/user.interface'; + +const mockUsers: User[] = [ + { + id: 1, + name: 'John Doe', + username: 'johndoe', + password: 'password1', + roles: ['admin'], + }, + { + id: 2, + name: 'Jane Doe', + username: 'janedoe', + password: 'password2', + roles: ['user'], + }, + { + id: 3, + name: 'Jim Brown', + username: 'jimbrown', + password: 'password3', + roles: ['user', 'admin'], + }, + { + id: 4, + name: 'Jane Smith', + username: 'janesmith', + password: 'password4', + roles: ['user'], + }, + { + id: 5, + name: 'John Smith', + username: 'johnsmith', + password: 'password5', + roles: ['admin'], + }, +]; + +export class UserDataService { + async login({ username, password }: LoginRequest): Promise { + const user = mockUsers.find((user) => { + return user.username == username && user.password == password; + }); + + return user; + } + + async users(): Promise { + return mockUsers; + } +} diff --git a/src/auth/domain/entities/request.interface.ts b/src/auth/domain/entities/request.interface.ts new file mode 100644 index 0000000..c7aa400 --- /dev/null +++ b/src/auth/domain/entities/request.interface.ts @@ -0,0 +1,4 @@ +export interface LoginRequest { + username: string; + password: string; +} diff --git a/src/auth/domain/entities/user.interface.ts b/src/auth/domain/entities/user.interface.ts new file mode 100644 index 0000000..8c9d3bc --- /dev/null +++ b/src/auth/domain/entities/user.interface.ts @@ -0,0 +1,7 @@ +export interface User { + id: number; + name: string; + username: string; + password: string; + roles: string[]; +} diff --git a/src/auth/domain/services/auth.service.spec.ts b/src/auth/domain/services/auth.service.spec.ts new file mode 100644 index 0000000..800ab66 --- /dev/null +++ b/src/auth/domain/services/auth.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AuthService], + }).compile(); + + service = module.get(AuthService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/auth/domain/services/auth.service.ts b/src/auth/domain/services/auth.service.ts new file mode 100644 index 0000000..35eeec8 --- /dev/null +++ b/src/auth/domain/services/auth.service.ts @@ -0,0 +1,37 @@ +import { Injectable, UnprocessableEntityException } from '@nestjs/common'; +import { SessionService, UserProvider, UsersSession } from 'src/core/sessions'; +import { UserDataService } from '../../data/user.dataservice'; +import { LoginRequest } from '../entities/request.interface'; +import { User } from '../entities/user.interface'; + +@Injectable() +export class AuthService { + constructor( + private readonly userDataService: UserDataService, + private readonly session: SessionService, + private readonly user: UserProvider, + ) {} + async createAccessToken(payload: LoginRequest): Promise { + const user = await this.userDataService.login(payload); + + if (!user) + throw new UnprocessableEntityException(`Username or Password not match`); + + const token = this.session.createAccessToken({ + id: user.id, + username: user.username, + name: user.name, + roles: user.roles, + }); + + return token; + } + + getUser(): UsersSession { + return this.user.user; + } + + async getUsers(): Promise { + return this.userDataService.users(); + } +} diff --git a/src/auth/index.ts b/src/auth/index.ts new file mode 100644 index 0000000..c94f80f --- /dev/null +++ b/src/auth/index.ts @@ -0,0 +1 @@ +export * from './constants';