diff --git a/src/main.ts b/src/main.ts index 9f811f2..3a14997 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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: diff --git a/src/modules/application/auth/auth.service.ts b/src/modules/application/auth/auth.service.ts index 75f26f1..d21685f 100644 --- a/src/modules/application/auth/auth.service.ts +++ b/src/modules/application/auth/auth.service.ts @@ -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 } diff --git a/src/modules/application/auth/dto/auth.dto.ts b/src/modules/application/auth/dto/auth.dto.ts index d1b56a3..2d3be28 100644 --- a/src/modules/application/auth/dto/auth.dto.ts +++ b/src/modules/application/auth/dto/auth.dto.ts @@ -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 +} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index a26c49d..eb5a3a3 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -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) } diff --git a/src/modules/auth/dto/login.dto.ts b/src/modules/auth/dto/login.dto.ts index 585ff88..c73d77c 100644 --- a/src/modules/auth/dto/login.dto.ts +++ b/src/modules/auth/dto/login.dto.ts @@ -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 }