update pos consumer module

This commit is contained in:
2026-03-29 18:06:41 +03:30
parent 63fa2bc67e
commit c870a43e35
53 changed files with 2145 additions and 671 deletions
@@ -0,0 +1,29 @@
import {
BadRequestException,
createParamDecorator,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common'
import { Request } from 'express'
import { IPosPayload } from '../models/posPayload.model'
export const PosInfo = createParamDecorator(
(data: keyof IPosPayload | undefined, ctx: ExecutionContext) => {
try {
const request = ctx.switchToHttp().getRequest<Request>()
const posInfo = request.posData
if (!posInfo) {
throw new UnauthorizedException('شما به این بخش دسترسی ندارید')
}
if (data) {
return posInfo[data]
}
return posInfo
} catch (err) {
throw new BadRequestException('مشکلی در ساختار درخواست شما وجود دارد.')
}
},
)
+3 -1
View File
@@ -1,4 +1,4 @@
import { AccessTokenPayload } from '@/modules/auth/models'
import { AccessTokenPayload } from '@/common/models/tokenPayload.model'
import {
BadRequestException,
createParamDecorator,
@@ -29,6 +29,8 @@ export const TokenAccount = createParamDecorator(
// ✅ if called with no param — return full account object
return account
} catch (err) {
console.log(err)
throw new BadRequestException('مشکلی در ساختار درخواست شما وجود دارد.')
}
},
@@ -8,15 +8,11 @@ import { Response } from 'express'
@Catch(BadRequestException)
export class ValidationExceptionFilter implements ExceptionFilter {
constructor() {
console.log('ValidationExceptionFilter initialized')
}
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
const status = exception.getStatus()
const exceptionResponse = exception.getResponse()
console.log(exception)
response.status(status).json({
statusCode: status,
-57
View File
@@ -1,57 +0,0 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { IS_PUBLIC_KEY } from '../decorators/public.decorator'
import { IWithJWTPayloadRequest } from '../models/token-model'
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private jwt: JwtService,
private reflector: Reflector,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
])
if (isPublic) return true
const req = context.switchToHttp().getRequest<IWithJWTPayloadRequest>()
let token: string | undefined = req.cookies?.accessToken
if (!token) {
const authHeader = (req.headers.authorization || req.headers.Authorization) as
| string
| undefined
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.slice(7)
}
}
if (!token) throw new UnauthorizedException('Missing access token')
try {
const payload = this.jwt.verify(token, {
secret: process.env.JWT_SECRET || 'secret',
})
if (payload.type !== 'POS')
throw new UnauthorizedException('Invalid or expired token')
// Set the typed dataPayload to the request
req.dataPayload = payload
return true
} catch (err) {
console.log(err)
throw new UnauthorizedException('Invalid or expired token')
}
}
}
+5 -5
View File
@@ -6,8 +6,8 @@ import {
} from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { Request as ExpressRequest } from 'express'
import { IS_PUBLIC_KEY } from '../decorators/public.decorator'
import { IWithJWTPayloadRequest } from '../models/token-model'
@Injectable()
export class JwtAuthGuard implements CanActivate {
@@ -23,7 +23,7 @@ export class JwtAuthGuard implements CanActivate {
])
if (isPublic) return true
const req = context.switchToHttp().getRequest<IWithJWTPayloadRequest>()
const req = context.switchToHttp().getRequest<ExpressRequest>()
let token: string | undefined = req.cookies?.accessToken
if (!token) {
@@ -38,15 +38,15 @@ export class JwtAuthGuard implements CanActivate {
if (!token) throw new UnauthorizedException('Missing access token')
try {
const payload = this.jwt.verify(token, {
secret: process.env.JWT_SECRET || 'secret',
this.jwt.verify(token, {
secret: process.env.JWT_SECRET,
})
// if (payload.type !== 'POS')
// throw new UnauthorizedException('Invalid or expired token')
// Set the typed dataPayload to the request
req.dataPayload = payload
// req.dataPayload = payload
return true
} catch (err) {
+130
View File
@@ -0,0 +1,130 @@
// src/common/guards/permissions.guard.ts
import { PrismaService } from '@/prisma/prisma.service'
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { Request as ExpressRequest } from 'express'
import { IS_PUBLIC_KEY } from '../decorators/public.decorator'
import { checkAndDecodeJwtToken } from '../utils/jwt-user.util'
@Injectable()
export class PosGuard implements CanActivate {
constructor(
private jwt: JwtService,
private reflector: Reflector,
private prisma: PrismaService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
])
if (isPublic) return true
// const requiredPermissions = this.reflector.get<string[]>(
// 'permissions',
// context.getHandler(),
// )
// if (!requiredPermissions || requiredPermissions.length === 0) return true
const req = context.switchToHttp().getRequest<ExpressRequest>()
const account = checkAndDecodeJwtToken(req, this.jwt)
// const account = req.decodedToken
if (!req.url.startsWith('/api/v1/pos/')) {
return true
}
if (!account || account.type !== 'CONSUMER')
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
const cookie = req.cookies
const { posId, accessToken } = cookie
if (!posId || !accessToken) {
return false
}
const pos = await this.prisma.pos.findUnique({
where: {
id: posId,
complex: {
business_activity: {
user: {
accounts: {
some: {
account_id: account.account_id,
},
},
},
},
},
},
select: {
complex: {
select: {
id: true,
business_activity_id: true,
},
},
},
})
if (!pos) {
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
}
const foundedAccount = await this.prisma.account.findUnique({
where: {
id: account.account_id,
},
select: {
consumer_account: {
select: {
role: true,
},
},
},
})
if (foundedAccount?.consumer_account?.role === 'OWNER') {
return true
}
const accountPermissions = await this.prisma.permissionConsumer.findUnique({
where: {
account_id: account.account_id,
},
select: {
posPermissions: true,
businessPermissions: true,
complexPermissions: true,
},
})
if (accountPermissions?.posPermissions.some(p => p.pos_id === posId)) {
return true
}
if (
accountPermissions?.complexPermissions.some(p => p.complex_id === pos.complex.id)
) {
return true
}
if (
accountPermissions?.businessPermissions.some(
p => p.business_id === pos.complex.business_activity_id,
)
) {
return true
}
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
}
}
@@ -6,3 +6,5 @@ export interface SaleInvoiceGoldTypePayload {
profit: number
}
export interface SaleInvoiceStandardPayload {}
export type SaleInvoiceType = SaleInvoiceGoldTypePayload | SaleInvoiceStandardPayload
+7
View File
@@ -0,0 +1,7 @@
export interface IPosPayload {
pos_id: string
complex_id: string
guild_id: string
business_id: string
consumer_account_id: string
}
-29
View File
@@ -1,29 +0,0 @@
import { Request as ExpressRequest } from 'express'
import { AccountType } from '../enums/enums'
export interface IWithJWTPayloadRequest extends ExpressRequest {
dataPayload?: AccessTokenPayload
}
export interface AccessTokenPayload {
userId: string
mobile_number: string
type: AccountType
username: string
pos_id: number
pos_name: string
complex_id: string
license_id: string
license_expired_at: string
// complex: {
// id: string
// name: string
// }
// license: {
// id: string
// starts_at: string
// expires_at: string
// status: string
// }
}
+15
View File
@@ -0,0 +1,15 @@
import { AccountType } from '../../generated/prisma/enums'
export interface AccessTokenPayload {
account_id: string
type: AccountType
username: string
user?: AccessTokenUser
// license_id?: string
// license_expired_at?: string
}
export interface AccessTokenUser extends Record<string, any> {
id?: string
name?: string
}
+1 -1
View File
@@ -1,8 +1,8 @@
import { AccessTokenPayload } from '@/common/models/tokenPayload.model'
import { ITokenPayload } from '@/modules/auth/auth.utils'
import { UnauthorizedException } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { Request } from 'express'
import { AccessTokenPayload } from 'modules/auth/models'
export function checkAndDecodeJwtToken(
req: Request,