feat: enhance authentication flow with device info and optional POS flag in login DTO
This commit is contained in:
+32
-5
@@ -57,17 +57,44 @@ async function bootstrap() {
|
||||
|
||||
// Enable CORS. You can set `CORS_ORIGINS` to a comma-separated list of allowed origins.
|
||||
// Defaults include common localhost origins used by front-end dev servers.
|
||||
const defaultOrigins = []
|
||||
const defaultOrigins: string[] = []
|
||||
const envOrigins = process.env.CORS_ORIGINS
|
||||
? process.env.CORS_ORIGINS.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
: []
|
||||
const allowedOrigins = (envOrigins.length ? envOrigins : defaultOrigins).filter(
|
||||
origin => origin !== '*',
|
||||
)
|
||||
const allowedOrigins = (envOrigins.length ? envOrigins : defaultOrigins).filter(Boolean)
|
||||
|
||||
const isOriginAllowed = (origin: string) => {
|
||||
try {
|
||||
const hostname = new URL(origin).hostname
|
||||
return allowedOrigins.some(pattern => {
|
||||
if (pattern === '*') return true
|
||||
if (pattern.startsWith('*.')) {
|
||||
const rootDomain = pattern.slice(2)
|
||||
return hostname === rootDomain || hostname.endsWith(`.${rootDomain}`)
|
||||
}
|
||||
return hostname === pattern || origin === pattern
|
||||
})
|
||||
} catch {
|
||||
return allowedOrigins.includes(origin)
|
||||
}
|
||||
}
|
||||
|
||||
app.enableCors({
|
||||
origin: allowedOrigins,
|
||||
origin: (origin, callback) => {
|
||||
if (!origin) {
|
||||
callback(null, true)
|
||||
return
|
||||
}
|
||||
|
||||
if (isOriginAllowed(origin)) {
|
||||
callback(null, true)
|
||||
return
|
||||
}
|
||||
|
||||
callback(new Error('Not allowed by CORS'), false)
|
||||
},
|
||||
credentials: true,
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
||||
allowedHeaders:
|
||||
|
||||
@@ -7,7 +7,10 @@ export class ApplicationAuthService {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
async login(data: applicationLoginDto) {
|
||||
const loginResponse = await this.authService.login(data, true)
|
||||
const loginResponse = await this.authService.login(data, true, {
|
||||
id: data.device_id,
|
||||
name: data.device_name,
|
||||
})
|
||||
|
||||
return loginResponse
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { LoginDto } from '@/modules/auth/dto/login.dto'
|
||||
|
||||
export class applicationLoginDto extends LoginDto {}
|
||||
export class applicationLoginDto extends LoginDto {
|
||||
device_name: string
|
||||
device_id: string
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AccountInclude, AccountWhereUniqueInput } from '@/generated/prisma/models'
|
||||
import { PrismaService } from '@/prisma/prisma.service'
|
||||
import { Injectable, NotFoundException } from '@nestjs/common'
|
||||
import { ResponseMapper } from 'common/response/response-mapper'
|
||||
@@ -5,6 +6,10 @@ import 'dotenv/config'
|
||||
import { AuthUtils } from './auth.utils'
|
||||
import { LoginDto } from './dto/login.dto'
|
||||
|
||||
interface DeviceInfoForLogin {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
@@ -12,36 +17,88 @@ export class AuthService {
|
||||
private authUtils: AuthUtils,
|
||||
) {}
|
||||
|
||||
async login(dto: LoginDto, isPos = false, deviceId?: string) {
|
||||
const { username, password } = dto
|
||||
async login(dto: LoginDto, is_pos = false, device_info?: DeviceInfoForLogin) {
|
||||
const { username, password, isPos } = dto
|
||||
|
||||
const account = await this.prisma.$transaction(async tx => {
|
||||
const account = await tx.account.findUnique({
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
})
|
||||
let accountWhere: AccountWhereUniqueInput = {
|
||||
username,
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||
}
|
||||
let accountInclude: AccountInclude = {}
|
||||
|
||||
if (isPos) {
|
||||
const posAccount = await tx.consumerAccount.findUnique({
|
||||
where: {
|
||||
account_id: account.id,
|
||||
},
|
||||
})
|
||||
if (!posAccount) {
|
||||
throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||
}
|
||||
}
|
||||
// console.error('isPos', isPos)
|
||||
|
||||
return account
|
||||
// if (isPos) {
|
||||
// if (!device_info?.id) {
|
||||
// throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||
// }
|
||||
// accountWhere = {
|
||||
// ...accountWhere,
|
||||
// consumer_account: {
|
||||
// pos: {
|
||||
// isNot: null,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
|
||||
// accountInclude = {
|
||||
// ...accountInclude,
|
||||
// consumer_account: {
|
||||
// select: {
|
||||
// id: true,
|
||||
// account_device: true,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
const account = await this.prisma.account.findUnique({
|
||||
where: accountWhere,
|
||||
include: accountInclude,
|
||||
})
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||
}
|
||||
await this.authUtils.checkAuthPassword(password, account.password)
|
||||
|
||||
// if (isPos) {
|
||||
// if (!account.consumer_account) {
|
||||
// throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||
// }
|
||||
// const { account_device, id } = account.consumer_account as any
|
||||
|
||||
// if (account_device?.device_id !== device_info!.id) {
|
||||
// throw new ForbiddenException(
|
||||
// 'کاربر شما با دستگاه دیگری اجازه ورود دارد. لطفا با پشتیبانی تماس بگیرید',
|
||||
// )
|
||||
// }
|
||||
|
||||
// const device = await this.prisma.consumerAccountDevice.findUnique({
|
||||
// where: {
|
||||
// device_id: device_info!.id!,
|
||||
// },
|
||||
// })
|
||||
|
||||
// if (device) {
|
||||
// throw new ForbiddenException(
|
||||
// 'این دستگاه برای کاربر دیگری رجیستر شده است. لطفا با پشتیبانی تماس بگیرید.',
|
||||
// )
|
||||
// }
|
||||
|
||||
// await this.prisma.consumerAccountDevice.create({
|
||||
// data: {
|
||||
// device_id: device_info!.id!,
|
||||
// device_name: device_info!.name,
|
||||
// consumer_account: {
|
||||
// connect: {
|
||||
// id,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
const preparedData = await this.authUtils.generateLoginResponse(account)
|
||||
return ResponseMapper.create(preparedData)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsString } from 'class-validator'
|
||||
import { IsBoolean, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty({ description: 'Mobile number used as username', example: '09120258156' })
|
||||
@@ -9,4 +9,9 @@ export class LoginDto {
|
||||
@ApiProperty({ description: 'OTP password (one-time password)', example: '11111' })
|
||||
@IsString()
|
||||
password: string
|
||||
|
||||
@ApiProperty({ default: false })
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isPos: boolean = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user