update
This commit is contained in:
@@ -6,6 +6,7 @@ import userRouter from "../../modules/user/routes/user.router";
|
||||
import reportRouter from "../../modules/report/routes/report.router";
|
||||
import authMiddleware from "../middleware/auth.middleware";
|
||||
import RoleRouter from "../../modules/roles/role.routes";
|
||||
import PermissionRouter from "../../modules/permission/permission.routes";
|
||||
|
||||
const mainRouter = router.Router();
|
||||
|
||||
@@ -15,5 +16,6 @@ mainRouter.use("/ticket", authMiddleware, ticketRouter);
|
||||
mainRouter.use("/user", authMiddleware, userRouter);
|
||||
mainRouter.use("/report", authMiddleware, reportRouter);
|
||||
mainRouter.use("/role", authMiddleware, RoleRouter);
|
||||
mainRouter.use("/permission", authMiddleware, PermissionRouter);
|
||||
|
||||
export default mainRouter;
|
||||
|
||||
@@ -1,92 +1,92 @@
|
||||
import {
|
||||
Model,
|
||||
DataTypes,
|
||||
Optional,
|
||||
Sequelize,
|
||||
BelongsToManySetAssociationsMixin,
|
||||
BelongsToManyAddAssociationsMixin,
|
||||
BelongsToManyRemoveAssociationsMixin,
|
||||
BelongsToManyGetAssociationsMixin,
|
||||
HasManyGetAssociationsMixin
|
||||
} from "sequelize";
|
||||
import User from "./User";
|
||||
import Permission from "./Permission";
|
||||
import {
|
||||
Model,
|
||||
DataTypes,
|
||||
Optional,
|
||||
Sequelize,
|
||||
BelongsToManySetAssociationsMixin,
|
||||
BelongsToManyAddAssociationsMixin,
|
||||
BelongsToManyRemoveAssociationsMixin,
|
||||
BelongsToManyGetAssociationsMixin,
|
||||
HasManyGetAssociationsMixin
|
||||
} from "sequelize";
|
||||
import User from "./User";
|
||||
import Permission from "./Permission";
|
||||
|
||||
|
||||
interface RoleAttributes {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
interface RoleCreationAttributes
|
||||
extends Optional<RoleAttributes, "id" | "description"> {}
|
||||
|
||||
class Role
|
||||
extends Model<RoleAttributes, RoleCreationAttributes>
|
||||
implements RoleAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public name!: string;
|
||||
public description?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
// Users relation
|
||||
public getUsers!: HasManyGetAssociationsMixin<User>;
|
||||
|
||||
// Permissions relation
|
||||
public getPermissions!: BelongsToManyGetAssociationsMixin<Permission>;
|
||||
public setPermissions!: BelongsToManySetAssociationsMixin<Permission, string>;
|
||||
public addPermissions!: BelongsToManyAddAssociationsMixin<Permission, string>;
|
||||
public removePermissions!: BelongsToManyRemoveAssociationsMixin<Permission, string>;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Role {
|
||||
Role.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
allowNull: false,
|
||||
},
|
||||
|
||||
description: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: "Role",
|
||||
tableName: "roles",
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
return Role;
|
||||
interface RoleAttributes {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
static associate(models: any) {
|
||||
Role.hasMany(models.User, {
|
||||
foreignKey: "roleId",
|
||||
as: "users",
|
||||
});
|
||||
interface RoleCreationAttributes
|
||||
extends Optional<RoleAttributes, "id" | "description"> {}
|
||||
|
||||
Role.belongsToMany(models.Permission, {
|
||||
through: models.RolePermission,
|
||||
foreignKey: "roleId",
|
||||
otherKey: "permissionId",
|
||||
as: "permissions",
|
||||
});
|
||||
class Role
|
||||
extends Model<RoleAttributes, RoleCreationAttributes>
|
||||
implements RoleAttributes
|
||||
{
|
||||
public id!: string;
|
||||
public name!: string;
|
||||
public description?: string;
|
||||
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
// Users relation
|
||||
public getUsers!: HasManyGetAssociationsMixin<User>;
|
||||
|
||||
// Permissions relation
|
||||
public getPermissions!: BelongsToManyGetAssociationsMixin<Permission>;
|
||||
public setPermissions!: BelongsToManySetAssociationsMixin<Permission, string>;
|
||||
public addPermissions!: BelongsToManyAddAssociationsMixin<Permission, string>;
|
||||
public removePermissions!: BelongsToManyRemoveAssociationsMixin<Permission, string>;
|
||||
|
||||
static initModel(sequelize: Sequelize): typeof Role {
|
||||
Role.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
allowNull: false,
|
||||
},
|
||||
|
||||
description: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: "Role",
|
||||
tableName: "roles",
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
return Role;
|
||||
}
|
||||
|
||||
static associate(models: any) {
|
||||
Role.hasMany(models.User, {
|
||||
foreignKey: "roleId",
|
||||
as: "users",
|
||||
});
|
||||
|
||||
Role.belongsToMany(models.Permission, {
|
||||
through: models.RolePermission,
|
||||
foreignKey: "roleId",
|
||||
otherKey: "permissionId",
|
||||
as: "permissions",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Role;
|
||||
export default Role;
|
||||
|
||||
75
src/modules/permission/permission.controller.ts
Normal file
75
src/modules/permission/permission.controller.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { Controller } from "../../core/controller/main.controller";
|
||||
import PermissionService from "./permission.service";
|
||||
import { ServerResponse } from "../../core/types";
|
||||
|
||||
class PermissionControllerClass extends Controller {
|
||||
#service;
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = PermissionService;
|
||||
}
|
||||
|
||||
// ایجاد دسترسی جدید
|
||||
create = async (req:any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const result = await this.#service.create(req.body);
|
||||
return res.status(201).json({status:201,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// دریافت لیست دسترسیها (با فیلتر و صفحهبندی)
|
||||
getAll = async (req:any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const result = await this.#service.getAll(req.query);
|
||||
return res.status(200).json({status:200,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// دریافت یک دسترسی خاص
|
||||
getById = async (req:any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.getById(id);
|
||||
return res.status(200).json({status:200,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// ویرایش دسترسی
|
||||
update = async (req:any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.update(id, req.body);
|
||||
return res.status(200).json({status:200,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
getList = async(req:any,res:ServerResponse,next:NextFunction) => {
|
||||
try {
|
||||
const result = await this.#service.getList();
|
||||
return res.status(200).json({status:200,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
// حذف دسترسی
|
||||
delete = async (req:any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const result = await this.#service.delete(id);
|
||||
return res.status(200).json({status:200,data:result});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const PermissionController = new PermissionControllerClass();
|
||||
export default PermissionController;
|
||||
19
src/modules/permission/permission.routes.ts
Normal file
19
src/modules/permission/permission.routes.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Router } from "express";
|
||||
import PermissionController from "./permission.controller";
|
||||
|
||||
const PermissionRouter = Router();
|
||||
|
||||
PermissionRouter.post("/create", PermissionController.create);
|
||||
|
||||
|
||||
PermissionRouter.get("/all", PermissionController.getAll);
|
||||
PermissionRouter.get("/list", PermissionController.getList);
|
||||
|
||||
PermissionRouter.get("/get/:id", PermissionController.getById);
|
||||
|
||||
|
||||
PermissionRouter.put("/update/:id", PermissionController.update);
|
||||
|
||||
PermissionRouter.delete("/remove/:id", PermissionController.delete);
|
||||
|
||||
export default PermissionRouter;
|
||||
81
src/modules/permission/permission.service.ts
Normal file
81
src/modules/permission/permission.service.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Op } from "sequelize";
|
||||
import createHttpError from "http-errors";
|
||||
import Permission from "../../models/Permission";
|
||||
|
||||
class PermissionServiceClass {
|
||||
async create(data: { name: string }) {
|
||||
const existing = await Permission.findOne({ where: { name: data.name } });
|
||||
if (existing) {
|
||||
throw createHttpError.BadRequest("این دسترسی قبلاً ثبت شده است.");
|
||||
}
|
||||
return await Permission.create(data);
|
||||
}
|
||||
|
||||
async getAll(query: any) {
|
||||
const { page = 1, limit = 10, search, startDate, endDate } = query;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
const whereClause: any = {};
|
||||
|
||||
if (search) {
|
||||
whereClause.name = { [Op.iLike]: `%${search}%` };
|
||||
}
|
||||
|
||||
if (startDate || endDate) {
|
||||
whereClause.createdAt = {};
|
||||
if (startDate) whereClause.createdAt[Op.gte] = new Date(startDate);
|
||||
if (endDate) {
|
||||
const end = new Date(endDate);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
whereClause.createdAt[Op.lte] = end;
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await Permission.findAndCountAll({
|
||||
where: whereClause,
|
||||
order: [["createdAt", "DESC"]],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset.toString()),
|
||||
distinct: true,
|
||||
});
|
||||
|
||||
return {
|
||||
data: rows,
|
||||
totalItems: count,
|
||||
totalPages: Math.ceil(count / limit),
|
||||
currentPage: parseInt(page),
|
||||
};
|
||||
}
|
||||
async getList() {
|
||||
const data = await Permission.findAll({});
|
||||
|
||||
return data;
|
||||
}
|
||||
async getById(id: string) {
|
||||
const permission = await Permission.findByPk(id);
|
||||
if (!permission) {
|
||||
throw createHttpError.NotFound("دسترسی مورد نظر یافت نشد.");
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
|
||||
async update(id: string, data: { name: string }) {
|
||||
const permission = await this.getById(id);
|
||||
|
||||
if (data.name && data.name !== permission.name) {
|
||||
const existing = await Permission.findOne({ where: { name: data.name } });
|
||||
if (existing) throw createHttpError.BadRequest("نام دسترسی تکراری است.");
|
||||
}
|
||||
|
||||
return await permission.update(data);
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const permission = await this.getById(id);
|
||||
await permission.destroy();
|
||||
return { message: "دسترسی با موفقیت حذف شد." };
|
||||
}
|
||||
}
|
||||
|
||||
const PermissionService = new PermissionServiceClass();
|
||||
export default PermissionService;
|
||||
@@ -23,9 +23,9 @@ class RoleControllerClass extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
async findAll(_req: any, res: ServerResponse, next: NextFunction) {
|
||||
async findAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const roles = await this.#serivce.findAll();
|
||||
const roles = await this.#serivce.findAll(req);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
@@ -36,6 +36,18 @@ class RoleControllerClass extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
async findList(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const roles = await this.#serivce.findList();
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: roles,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
findById = async (req: any, res: ServerResponse, next: NextFunction) => {
|
||||
try {
|
||||
const role = await this.#serivce.findById(req.params.id);
|
||||
|
||||
@@ -3,7 +3,8 @@ import RoleController from "./role.controller";
|
||||
|
||||
const RoleRouter = Router();
|
||||
RoleRouter.post("/create", RoleController.create);
|
||||
RoleRouter.get("/list", RoleController.findAll);
|
||||
RoleRouter.get("/list", RoleController.findList);
|
||||
RoleRouter.get("/all", RoleController.findAll);
|
||||
RoleRouter.get("/:id", RoleController.findById);
|
||||
RoleRouter.put("/update/:id", RoleController.update);
|
||||
RoleRouter.delete("/remove/:id", RoleController.delete);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import createHttpError from "http-errors";
|
||||
import Permission from "../../models/Permission";
|
||||
import Role from "../../models/Role";
|
||||
import User from "../../models/User";
|
||||
import { GlobalMessages } from "../../core/messages";
|
||||
import { Op } from "sequelize";
|
||||
|
||||
class RoleServiceClass {
|
||||
async create(data: { name: string; description?: string }) {
|
||||
async create(data: { name: string; description?: string,permissions:string[] }) {
|
||||
const existingRole = await Role.findOne({
|
||||
where: { name: data.name },
|
||||
});
|
||||
@@ -12,15 +15,28 @@ class RoleServiceClass {
|
||||
throw new Error("Role already exists");
|
||||
}
|
||||
|
||||
// 1️⃣ ساخت Role
|
||||
const role = await Role.create({
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
});
|
||||
|
||||
// 2️⃣ اگر permission ارسال شده بود
|
||||
if (data.permissions && data.permissions.length) {
|
||||
const permissions = await Permission.findAll({
|
||||
where: {
|
||||
id: data.permissions,
|
||||
},
|
||||
});
|
||||
|
||||
// 3️⃣ اتصال permissions به role
|
||||
await role.setPermissions(permissions);
|
||||
}
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
async findList() {
|
||||
return await Role.findAll({
|
||||
include: [
|
||||
{
|
||||
@@ -37,6 +53,77 @@ class RoleServiceClass {
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(req: any) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
name, // فیلتر نام نقش (اختیاری)
|
||||
startDate,
|
||||
endDate,
|
||||
} = req.query;
|
||||
|
||||
// ۱. محاسبه offset برای صفحهبندی
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// ۲. ساخت آبجکت فیلترها به صورت داینامیک
|
||||
const whereClause: any = {};
|
||||
|
||||
if (name) {
|
||||
// استفاده از Op.iLike برای جستجوی متنی (Case-Insensitive) در دیتابیسهای PostgreSQL
|
||||
// اگر از MySQL استفاده میکنید، از Op.like استفاده کنید
|
||||
whereClause.name = { [Op.iLike]: `%${name}%` };
|
||||
}
|
||||
|
||||
if (startDate || endDate) {
|
||||
whereClause.createdAt = {};
|
||||
if (startDate) {
|
||||
whereClause.createdAt[Op.gte] = new Date(startDate); // تاریخ شروع (بزرگتر مساوی)
|
||||
}
|
||||
if (endDate) {
|
||||
// تنظیم ساعت روی پایان روز برای شامل شدن کلِ روز آخر
|
||||
const end = new Date(endDate);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
whereClause.createdAt[Op.lte] = end; // تاریخ پایان (کوچکتر مساوی)
|
||||
}
|
||||
}
|
||||
|
||||
// ۳. استفاده از findAndCountAll برای دریافت همزمان دیتا و تعداد کل رکوردها
|
||||
const { count, rows } = await Role.findAndCountAll({
|
||||
where: whereClause,
|
||||
distinct: true, // اضافه کردن این خط مشکل شمارش تکراری را حل میکند
|
||||
col: "id", // به صورت اختیاری مشخص کنید روی کدام ستون distinct انجام شود
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
as: "permissions",
|
||||
through: { attributes: [] },
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: "users",
|
||||
},
|
||||
],
|
||||
order: [["createdAt", "DESC"]],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset.toString()),
|
||||
});
|
||||
|
||||
// ۴. خروجی استاندارد صفحهبندی شده
|
||||
return {
|
||||
data: rows,
|
||||
totalItems: count,
|
||||
totalPages: Math.ceil(count / limit),
|
||||
currentPage: parseInt(page),
|
||||
};
|
||||
} catch (error) {
|
||||
// مدیریت خطا مشابه متد Ticket
|
||||
throw new createHttpError.InternalServerError(
|
||||
GlobalMessages.errors.server,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
const role = await Role.findByPk(id, {
|
||||
include: [
|
||||
|
||||
Reference in New Issue
Block a user