This commit is contained in:
2026-06-16 11:32:07 +03:30
parent fdda306e85
commit b5bfd13ea2
17 changed files with 626 additions and 358 deletions

View File

@@ -1,4 +1,149 @@
export const JWT_SECRET = process.env.JWT_SECRET || "secret";
export const TOKEN_NAME = 'userToken'
export const TOKEN_NAME = "userToken";
export const requestType = ['software','hardware','his','rahkaran','taradod','general']
export const requestType = [
"software",
"hardware",
"his",
"rahkaran",
"taradod",
"general",
];
export const permissions = [
"filter.view",
/* ================================
✅ TICKETS
================================= */
"ticket.create",
"ticket.view",
"ticket.view_all",
"ticket.update",
"ticket.delete",
"ticket.assign",
"ticket.change_status",
"ticket.close",
"ticket.reopen",
"ticket.comment",
"ticket.internal_note",
"ticket.upload_file",
"ticket.merge",
"ticket.export",
/* ================================
✅ USERS
================================= */
"user.view",
"user.view_all",
"user.create",
"user.update",
"user.delete",
"user.activate",
"user.deactivate",
"user.assign_role",
/* ================================
✅ ROLES
================================= */
"role.view",
"role.create",
"role.update",
"role.delete",
/* ================================
✅ PERMISSIONS
================================= */
"permission.view",
"permission.assign_to_role",
"permission.assign_to_user",
/* ================================
✅ DEPARTMENTS
================================= */
"department.view",
"department.create",
"department.update",
"department.delete",
/* ================================
✅ REPORTS - SYSTEM LEVEL
================================= */
"report.system.view",
"report.system.export",
"report.department.view",
"report.department.export",
"report.operator.view",
"report.operator.export",
"report.sla.view",
"report.sla.export",
"report.kpi.view",
"report.kpi.export",
"report.trend.view",
"report.closure_rate.view",
"report.critical.view",
"report.avg_resolution.view",
"report.aging.view",
"report.agent_efficiency.view",
"report.department_load.view",
"report.prediction.view",
"report.export_excel",
/* ================================
✅ DASHBOARD
================================= */
"dashboard.view",
"dashboard.view_advanced",
/* ================================
✅ SLA MANAGEMENT
================================= */
"sla.view",
"sla.create",
"sla.update",
"sla.delete",
/* ================================
✅ REQUEST TYPES
================================= */
"request_type.view",
"request_type.create",
"request_type.update",
"request_type.delete",
/* ================================
✅ TAGS
================================= */
"tag.view",
"tag.create",
"tag.update",
"tag.delete",
/* ================================
✅ SETTINGS
================================= */
"settings.view",
"settings.update",
/* ================================
✅ LOGS
================================= */
"log.view",
"audit.view",
/* ================================
✅ OPERATOR / PERFORMANCE
================================= */
"operator.view",
"operator.performance.view",
"operator.performance.export",
"permission.view_all",
"permission.create",
"permission.update",
"permission.delete",
] as const;

View File

@@ -2,28 +2,66 @@ import { NextFunction } from "express";
import { ServerResponse } from "../types";
import { JWT_SECRET, TOKEN_NAME } from "../constants";
import createHttpError from "http-errors";
import User from "../../models/User";
import Permission from "../../models/Permission";
import Role from "../../models/Role";
const jwt = require("jsonwebtoken");
const authMiddleware = (req:any, res:ServerResponse, next:NextFunction) => {
// ۱. خواندن توکن از کوکی‌ها
const token = req.cookies[TOKEN_NAME]; // TOKEN_NAME همان متغیری است که در زمان ست کردن کوکی داشتید
const authMiddleware = async (
req: any,
res: ServerResponse,
next: NextFunction,
) => {
const token = req.cookies[TOKEN_NAME];
if (!token) {
throw new createHttpError.Unauthorized("لطفا وارد حساب كاربري خود شويد")
return next(
new createHttpError.Unauthorized("لطفا وارد حساب كاربري خود شويد"),
);
}
try {
// ۲. اعتبارسنجی توکن با استفاده از همان SECRET
const decoded = jwt.verify(token, JWT_SECRET);
const decoded = jwt.verify(token, JWT_SECRET) as {
id: number;
roleId: number;
};
// ۳. قرار دادن اطلاعات کاربر در آبجکت درخواست برای استفاده در مسیرهای بعدی
req.user = decoded;
const user = await User.findByPk(decoded.id, {
attributes: ["id", "roleId"],
include: [
{
model: Role,
as: "role",
attributes: ["id", "name"],
include: [
{
model: Permission,
as: "permissions",
attributes: ["name"],
through: { attributes: [] },
},
],
},
],
});
next(); // ادامه به مسیر اصلی
} catch (error) {
throw new createHttpError.Forbidden("حساب كاربري شما نامعتبر است")
if (!user) {
return next(new createHttpError.Unauthorized("کاربر یافت نشد"));
}
const permissions = user?.role?.permissions?.map((p: any) => p.name) || [];
req.user = {
id: user.id,
roleId: user.roleId,
permissions,
};
next();
} catch (err) {
return next(new createHttpError.Forbidden("حساب كاربري شما نامعتبر است"));
}
};
export default authMiddleware
export default authMiddleware;

View File

@@ -1,46 +1,65 @@
import Permission from "../../models/Permission";
import RolePermission from "../../models/RolePermission";
import UserPermission from "../../models/UserPermission";
import { NextFunction } from "express";
import { PermissionType } from "../types";
import createHttpError from "http-errors";
// export function checkPermission(permissionName: string) {
// return async (req: any, res: any, next: any) => {
// const user = req.user;
export function checkPermission(permissionName: string) {
return async (req: any, res: any, next: any) => {
// const permission = await Permission.findOne({
// where: { name: permissionName },
// });
// if (!permission) {
// return res.status(403).json({ message: "Permission not found" });
// }
// // ✅ 1- چک سطح کاربر
// const userPermission = await UserPermission.findOne({
// where: {
// userId: user.id,
// permissionId: permission.id,
// },
// });
// if (userPermission) {
// return next();
// }
// // ✅ 2- چک سطح رول
// const rolePermission = await RolePermission.findOne({
// where: {
// roleId: user.roleId,
// permissionId: permission.id,
// },
// });
// if (rolePermission) {
// return next();
// }
// return res.status(403).json({
// message: "Access Denied",
// });
// };
// }
export function requirePermission(permission: PermissionType[]) {
return (req: any, res: any, next: NextFunction) => {
const user = req.user;
const permission = await Permission.findOne({
where: { name: permissionName },
});
if (!permission) {
return res.status(403).json({ message: "Permission not found" });
if (!user) {
throw new createHttpError.Unauthorized("وارد حساب كاربري خود شويد");
}
// ✅ 1- چک سطح کاربر
const userPermission = await UserPermission.findOne({
where: {
userId: user.id,
permissionId: permission.id,
},
});
const userPermissions: PermissionType[] = user.permissions || [];
if (userPermission) {
return next();
const hasPermission = permission.some((p) => userPermissions.includes(p));
if (!hasPermission) {
throw new createHttpError.Forbidden("به اين قسمت دسترسي نداريد");
}
// ✅ 2- چک سطح رول
const rolePermission = await RolePermission.findOne({
where: {
roleId: user.roleId,
permissionId: permission.id,
},
});
if (rolePermission) {
return next();
}
return res.status(403).json({
message: "Access Denied",
});
next();
};
}

View File

@@ -1,4 +1,5 @@
import { Response } from "express";
import { permissions } from "../constants";
export interface ServerResponseObject {
status: number;
@@ -69,30 +70,29 @@ export interface ticketRequestTypeDataType {
updatedAt?: Date;
}
// reports
export interface TicketStats {
total: number
open: number
inProgress: number
resolved: number
closed: number
total: number;
open: number;
inProgress: number;
resolved: number;
closed: number;
}
export interface DepartmentReport {
departmentId: string
departmentName: string
totalTickets: number
openTickets: number
resolvedTickets: number
departmentId: string;
departmentName: string;
totalTickets: number;
openTickets: number;
resolvedTickets: number;
}
export interface AgentReport {
userId: string
fullname: string
totalAssigned: number
resolved: number
open: number
userId: string;
fullname: string;
totalAssigned: number;
resolved: number;
open: number;
}
export type PermissionType = (typeof permissions)[number];