create license management

This commit is contained in:
2026-04-06 13:31:40 +03:30
parent c02e7afca1
commit 1160d81cdb
42 changed files with 1233 additions and 522 deletions
@@ -0,0 +1,33 @@
import {
BadRequestException,
createParamDecorator,
ExecutionContext,
ForbiddenException,
} from '@nestjs/common'
import { Request } from 'express'
import { IConsumerPayload } from '../models'
export const ConsumerInfoInfo = createParamDecorator(
(data: keyof IConsumerPayload | undefined, ctx: ExecutionContext) => {
try {
const request = ctx.switchToHttp().getRequest<Request>()
const info = request.consumerData
if (!info) {
throw new ForbiddenException('شما به این بخش دسترسی ندارید')
}
if (data) {
return info[data]
}
return info
} catch (err) {
if (err) {
throw err
}
throw new BadRequestException('مشکلی در ساختار درخواست شما وجود دارد.')
}
},
)
+1 -1
View File
@@ -14,7 +14,7 @@ export const TokenAccount = createParamDecorator(
const account = request.decodedToken
// ✅ if middleware didn't populate req.account, return undefined or throw
if (!account.user?.id) {
if (!account || !account.user?.id) {
throw new UnauthorizedException('شما به این بخش دسترسی ندارید')
}
+52
View File
@@ -0,0 +1,52 @@
// src/common/guards/permissions.guard.ts
import { ITokenPayload } from '@/modules/auth/auth.utils'
import { PrismaService } from '@/prisma/prisma.service'
import { ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'
import { Request as ExpressRequest } from 'express'
@Injectable()
export class ConsumerGuard {
constructor(private prisma: PrismaService) {}
async canActivate(
tokenPayload: ITokenPayload,
context: ExecutionContext,
): Promise<boolean> {
if (!tokenPayload || tokenPayload.type !== 'CONSUMER')
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
const consumer = await this.prisma.consumer.findFirst({
where: {
accounts: {
some: {
id: tokenPayload.account_id,
},
},
},
select: {
license: {
select: {
expires_at: true,
},
},
},
})
if (!consumer) {
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
}
const req = context.switchToHttp().getRequest<ExpressRequest>()
if (req.method !== 'GET') {
if (!consumer.license?.expires_at) {
throw new ForbiddenException('برای کاربر شما لایسنس ایجاد نشده است.')
}
if (
new Date().toUTCString() > new Date(consumer.license?.expires_at).toUTCString()
) {
throw new ForbiddenException('لایسنس شما منقضی شده است.')
}
}
return true
}
}
@@ -1,54 +1,26 @@
// src/common/guards/permissions.guard.ts
import { ITokenPayload } from '@/modules/auth/auth.utils'
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 { ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'
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
export class PosGuard {
constructor(private prisma: PrismaService) {}
async canActivate(
tokenPayload: ITokenPayload,
context: ExecutionContext,
): Promise<boolean> {
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')
if (!tokenPayload || tokenPayload.type !== 'CONSUMER')
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
const cookie = req.cookies
const { posId, accessToken } = cookie
const { posId } = cookie
if (!posId || !accessToken) {
if (!posId) {
return false
}
@@ -60,7 +32,7 @@ export class PosGuard implements CanActivate {
user: {
accounts: {
some: {
id: account.account_id,
id: tokenPayload.account_id,
},
},
},
@@ -83,7 +55,7 @@ export class PosGuard implements CanActivate {
const foundedAccount = await this.prisma.consumerAccount.findUnique({
where: {
id: account.account_id,
id: tokenPayload.account_id,
},
select: {
role: true,
@@ -96,7 +68,7 @@ export class PosGuard implements CanActivate {
const accountPermissions = await this.prisma.permissionConsumer.findUnique({
where: {
account_id: account.account_id,
account_id: tokenPayload.account_id,
},
select: {
posPermissions: true,
+16 -6
View File
@@ -1,6 +1,8 @@
import { ITokenPayload } from '@/modules/auth/auth.utils'
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
UnauthorizedException,
} from '@nestjs/common'
@@ -8,12 +10,16 @@ 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 { ConsumerGuard } from './auth-consumer.guard'
import { PosGuard } from './auth-pos.guard'
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private jwt: JwtService,
private reflector: Reflector,
private consumerGuard: ConsumerGuard,
private posGuard: PosGuard,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
@@ -42,15 +48,19 @@ export class JwtAuthGuard implements CanActivate {
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
const tokenPayload = this.jwt.decode(token) as ITokenPayload
if (req.url.startsWith('/api/v1/consumer')) {
await this.consumerGuard.canActivate(tokenPayload, context)
} else if (req.url.startsWith('/api/v1/pos')) {
await this.consumerGuard.canActivate(tokenPayload, context)
await this.posGuard.canActivate(tokenPayload, context)
} else if (tokenPayload.type != 'ADMIN') {
throw new ForbiddenException('شما دسترسی لازم را ندارید')
}
return true
} catch (err) {
console.log(err)
if (err) throw err
throw new UnauthorizedException('Invalid or expired token')
}
}
@@ -0,0 +1,4 @@
export interface IConsumerPayload {
user_id: string
account_id: string
}
+3
View File
@@ -0,0 +1,3 @@
export * from './consumerPayload.model'
export * from './posPayload.model'
export * from './tokenPayload.model'