feat: add auth module
This commit is contained in:
parent
d3b4cd0f00
commit
67f60f1137
@ -25,6 +25,8 @@
|
|||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
"@nestjs/swagger": "^11.2.5",
|
"@nestjs/swagger": "^11.2.5",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
|
"class-validator": "^0.14.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { Module } from '@nestjs/common'
|
import { Module } from '@nestjs/common'
|
||||||
import { ConfigModule } from '@nestjs/config'
|
import { ConfigModule } from '@nestjs/config'
|
||||||
|
|
||||||
|
import { AuthModule } from '../modules/auth/auth.module'
|
||||||
|
|
||||||
import { AppController } from './app.controller'
|
import { AppController } from './app.controller'
|
||||||
import { AppService } from './app.service'
|
import { AppService } from './app.service'
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule.forRoot({ isGlobal: true })],
|
imports: [ConfigModule.forRoot({ isGlobal: true }), AuthModule],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService]
|
providers: [AppService]
|
||||||
})
|
})
|
||||||
|
|||||||
9
src/core/config/cors.config.ts
Normal file
9
src/core/config/cors.config.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface'
|
||||||
|
import { ConfigService } from '@nestjs/config'
|
||||||
|
|
||||||
|
export function getCorsConfig(configService: ConfigService): CorsOptions {
|
||||||
|
return {
|
||||||
|
origin: configService.getOrThrow<string>('HTTP_CORS').split(','),
|
||||||
|
credentials: true
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/core/config/index.ts
Normal file
3
src/core/config/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './cors.config'
|
||||||
|
export * from './swagger.config'
|
||||||
|
export * from './validation-pipe.config'
|
||||||
8
src/core/config/swagger.config.ts
Normal file
8
src/core/config/swagger.config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { DocumentBuilder } from '@nestjs/swagger'
|
||||||
|
|
||||||
|
export const swaggerConfig = new DocumentBuilder()
|
||||||
|
.setTitle('Teacinema API')
|
||||||
|
.setDescription('API Gateway for Teacinema microservices')
|
||||||
|
.setVersion('1.0.0')
|
||||||
|
.addBearerAuth()
|
||||||
|
.build()
|
||||||
6
src/core/config/validation-pipe.config.ts
Normal file
6
src/core/config/validation-pipe.config.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { ValidationPipe } from '@nestjs/common'
|
||||||
|
|
||||||
|
export const validationPipe = new ValidationPipe({
|
||||||
|
transform: true,
|
||||||
|
whitelist: true
|
||||||
|
})
|
||||||
15
src/main.ts
15
src/main.ts
@ -1,9 +1,10 @@
|
|||||||
import { Logger } from '@nestjs/common'
|
import { Logger } from '@nestjs/common'
|
||||||
import { ConfigService } from '@nestjs/config'
|
import { ConfigService } from '@nestjs/config'
|
||||||
import { NestFactory } from '@nestjs/core'
|
import { NestFactory } from '@nestjs/core'
|
||||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
import { SwaggerModule } from '@nestjs/swagger'
|
||||||
|
|
||||||
import { AppModule } from './core/app.module'
|
import { AppModule } from './core/app.module'
|
||||||
|
import { getCorsConfig, swaggerConfig, validationPipe } from './core/config'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule)
|
const app = await NestFactory.create(AppModule)
|
||||||
@ -11,17 +12,9 @@ async function bootstrap() {
|
|||||||
const logger = new Logger()
|
const logger = new Logger()
|
||||||
const config = app.get(ConfigService)
|
const config = app.get(ConfigService)
|
||||||
|
|
||||||
app.enableCors({
|
app.useGlobalPipes(validationPipe)
|
||||||
origin: config.getOrThrow<string>('HTTP_CORS').split(','),
|
|
||||||
credentials: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const swaggerConfig = new DocumentBuilder()
|
app.enableCors(getCorsConfig(config))
|
||||||
.setTitle('Teacinema API')
|
|
||||||
.setDescription('API Gateway for Teacinema microservices')
|
|
||||||
.setVersion('1.0.0')
|
|
||||||
.addBearerAuth()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig)
|
const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig)
|
||||||
SwaggerModule.setup('docs', app, swaggerDocument, {
|
SwaggerModule.setup('docs', app, swaggerDocument, {
|
||||||
|
|||||||
19
src/modules/auth/auth.controller.ts
Normal file
19
src/modules/auth/auth.controller.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'
|
||||||
|
import { ApiOperation } from '@nestjs/swagger'
|
||||||
|
|
||||||
|
import { SendOtpRequestDto } from './dto'
|
||||||
|
|
||||||
|
@Controller('auth')
|
||||||
|
export class AuthController {
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Send OTP',
|
||||||
|
description: 'Send verification code to the user phone or email'
|
||||||
|
})
|
||||||
|
@Post('otp/send')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
public sendOtp(@Body() dto: SendOtpRequestDto) {
|
||||||
|
console.log('data', dto)
|
||||||
|
|
||||||
|
return { ok: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/modules/auth/auth.module.ts
Normal file
7
src/modules/auth/auth.module.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [AuthController]
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
||||||
1
src/modules/auth/dto/index.ts
Normal file
1
src/modules/auth/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './rq'
|
||||||
1
src/modules/auth/dto/rq/index.ts
Normal file
1
src/modules/auth/dto/rq/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './send-otp.request.dto'
|
||||||
22
src/modules/auth/dto/rq/send-otp.request.dto.ts
Normal file
22
src/modules/auth/dto/rq/send-otp.request.dto.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
|
import { IsEnum, IsString, Validate } from 'class-validator'
|
||||||
|
|
||||||
|
import { IdentifierValidator } from '../../../../shared/validators'
|
||||||
|
|
||||||
|
export class SendOtpRequestDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'User phone or email',
|
||||||
|
example: '+791234567890'
|
||||||
|
})
|
||||||
|
@Validate(IdentifierValidator)
|
||||||
|
@IsString()
|
||||||
|
public identifier: string
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Type of identifier',
|
||||||
|
example: 'phone',
|
||||||
|
enum: ['phone', 'email']
|
||||||
|
})
|
||||||
|
@IsEnum(['phone', 'email'])
|
||||||
|
public type: 'phone' | 'email'
|
||||||
|
}
|
||||||
35
src/shared/validators/identifier.validator.ts
Normal file
35
src/shared/validators/identifier.validator.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
isEmail,
|
||||||
|
isPhoneNumber,
|
||||||
|
ValidationArguments,
|
||||||
|
ValidatorConstraint,
|
||||||
|
ValidatorConstraintInterface
|
||||||
|
} from 'class-validator'
|
||||||
|
|
||||||
|
import { SendOtpRequestDto } from '../../modules/auth/dto'
|
||||||
|
|
||||||
|
@ValidatorConstraint({ name: 'IdentifierValidator', async: false })
|
||||||
|
export class IdentifierValidator implements ValidatorConstraintInterface {
|
||||||
|
public validate(value: string, args: ValidationArguments) {
|
||||||
|
const object = args.object as SendOtpRequestDto
|
||||||
|
if (object.type === 'email') {
|
||||||
|
return typeof value === 'string' && isEmail(value)
|
||||||
|
} else if (object.type === 'phone') {
|
||||||
|
return typeof value === 'string' && isPhoneNumber(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
public defaultMessage(args: ValidationArguments) {
|
||||||
|
const object = args.object as SendOtpRequestDto
|
||||||
|
|
||||||
|
if (object.type === 'email') {
|
||||||
|
return 'identifier must be a valid email'
|
||||||
|
} else if (object.type === 'phone') {
|
||||||
|
return 'identifier must be a valid phone number'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Invalid identifier'
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/shared/validators/index.ts
Normal file
1
src/shared/validators/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './identifier.validator'
|
||||||
29
yarn.lock
29
yarn.lock
@ -1359,6 +1359,11 @@
|
|||||||
"@types/methods" "^1.1.4"
|
"@types/methods" "^1.1.4"
|
||||||
"@types/superagent" "^8.1.0"
|
"@types/superagent" "^8.1.0"
|
||||||
|
|
||||||
|
"@types/validator@^13.15.3":
|
||||||
|
version "13.15.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.15.10.tgz#742b77ec34d58554b94a76a14cef30d59e3c16b9"
|
||||||
|
integrity sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==
|
||||||
|
|
||||||
"@types/yargs-parser@*":
|
"@types/yargs-parser@*":
|
||||||
version "21.0.3"
|
version "21.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
|
||||||
@ -2112,6 +2117,20 @@ cjs-module-lexer@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca"
|
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca"
|
||||||
integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==
|
integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==
|
||||||
|
|
||||||
|
class-transformer@^0.5.1:
|
||||||
|
version "0.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336"
|
||||||
|
integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==
|
||||||
|
|
||||||
|
class-validator@^0.14.3:
|
||||||
|
version "0.14.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.3.tgz#834a4caafa8359aed73d7708badb4cf271be50fe"
|
||||||
|
integrity sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==
|
||||||
|
dependencies:
|
||||||
|
"@types/validator" "^13.15.3"
|
||||||
|
libphonenumber-js "^1.11.1"
|
||||||
|
validator "^13.15.20"
|
||||||
|
|
||||||
cli-cursor@^3.1.0:
|
cli-cursor@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
|
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
|
||||||
@ -3666,6 +3685,11 @@ levn@^0.4.1:
|
|||||||
prelude-ls "^1.2.1"
|
prelude-ls "^1.2.1"
|
||||||
type-check "~0.4.0"
|
type-check "~0.4.0"
|
||||||
|
|
||||||
|
libphonenumber-js@^1.11.1:
|
||||||
|
version "1.12.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.34.tgz#20ed9da39e930e98eff4b6e24a8d8480303658af"
|
||||||
|
integrity sha512-v/Ip8k8eYdp7bINpzqDh46V/PaQ8sK+qi97nMQgjZzFlb166YFqlR/HVI+MzsI9JqcyyVWCOipmmretiaSyQyw==
|
||||||
|
|
||||||
lines-and-columns@^1.1.6:
|
lines-and-columns@^1.1.6:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||||
@ -4953,6 +4977,11 @@ v8-to-istanbul@^9.0.1:
|
|||||||
"@types/istanbul-lib-coverage" "^2.0.1"
|
"@types/istanbul-lib-coverage" "^2.0.1"
|
||||||
convert-source-map "^2.0.0"
|
convert-source-map "^2.0.0"
|
||||||
|
|
||||||
|
validator@^13.15.20:
|
||||||
|
version "13.15.26"
|
||||||
|
resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944"
|
||||||
|
integrity sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==
|
||||||
|
|
||||||
vary@^1, vary@^1.1.2:
|
vary@^1, vary@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user