This commit is contained in:
2026-06-11 14:51:14 +03:30
parent 43c52dfec8
commit 5f82f62763
5 changed files with 354 additions and 5 deletions

View File

@@ -5,6 +5,7 @@ import ticketRouter from "../../modules/ticket/routes/ticket.routes";
import userRouter from "../../modules/user/routes/user.router"; import userRouter from "../../modules/user/routes/user.router";
import reportRouter from "../../modules/report/routes/report.router"; import reportRouter from "../../modules/report/routes/report.router";
import authMiddleware from "../middleware/auth.middleware"; import authMiddleware from "../middleware/auth.middleware";
import RoleRouter from "../../modules/roles/role.routes";
const mainRouter = router.Router(); const mainRouter = router.Router();
@@ -13,5 +14,6 @@ mainRouter.use("/department", authMiddleware, departmentRouter);
mainRouter.use("/ticket", authMiddleware, ticketRouter); mainRouter.use("/ticket", authMiddleware, ticketRouter);
mainRouter.use("/user", authMiddleware, userRouter); mainRouter.use("/user", authMiddleware, userRouter);
mainRouter.use("/report", authMiddleware, reportRouter); mainRouter.use("/report", authMiddleware, reportRouter);
mainRouter.use("/role", authMiddleware, RoleRouter);
export default mainRouter; export default mainRouter;

View File

@@ -2,8 +2,16 @@ import {
Model, Model,
DataTypes, DataTypes,
Optional, Optional,
Sequelize Sequelize,
BelongsToManySetAssociationsMixin,
BelongsToManyAddAssociationsMixin,
BelongsToManyRemoveAssociationsMixin,
BelongsToManyGetAssociationsMixin,
HasManyGetAssociationsMixin
} from "sequelize"; } from "sequelize";
import User from "./User";
import Permission from "./Permission";
interface RoleAttributes { interface RoleAttributes {
id: string; id: string;
@@ -27,6 +35,15 @@ class Role
public readonly createdAt!: Date; public readonly createdAt!: Date;
public readonly updatedAt!: 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 { static initModel(sequelize: Sequelize): typeof Role {
Role.init( Role.init(
{ {
@@ -58,21 +75,17 @@ class Role
} }
static associate(models: any) { static associate(models: any) {
// Role -> Users
Role.hasMany(models.User, { Role.hasMany(models.User, {
foreignKey: "roleId", foreignKey: "roleId",
as: "users", as: "users",
}); });
// Role <-> Permission
Role.belongsToMany(models.Permission, { Role.belongsToMany(models.Permission, {
through: models.RolePermission, through: models.RolePermission,
foreignKey: "roleId", foreignKey: "roleId",
otherKey: "permissionId", otherKey: "permissionId",
as: "permissions", as: "permissions",
}); });
} }
} }

View File

@@ -0,0 +1,151 @@
import { NextFunction } from "express";
import { Controller } from "../../core/controller/main.controller";
import RoleService from "./role.service";
import { ServerResponse } from "../../core/types";
class RoleControllerClass extends Controller {
#serivce;
constructor() {
super();
this.#serivce = RoleService;
}
async create(req: any, res: ServerResponse, next: NextFunction) {
try {
const role = await this.#serivce.create(req.body);
return res.status(201).json({
status: 200,
message: "Role created successfully",
data: role,
});
} catch (error) {
next(error);
}
}
async findAll(_req: any, res: ServerResponse, next: NextFunction) {
try {
const roles = await this.#serivce.findAll();
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);
return res.status(200).json({
status: 200,
data: role,
});
} catch (error) {
next(error);
}
};
update = async (req: any, res: ServerResponse, next: NextFunction) => {
try {
const updatedRole = await this.#serivce.update(req.params.id, req.body);
return res.status(200).json({
status: 200,
message: "Role updated statusfully",
data: updatedRole,
});
} catch (error) {
next(error);
}
};
delete = async (req: any, res: ServerResponse, next: NextFunction) => {
try {
const result = await this.#serivce.delete(req.params.id);
return res.status(200).json({
status: 200,
data: { ...result },
});
} catch (error) {
next(error);
}
};
assignPermissions = async (
req: any,
res: ServerResponse,
next: NextFunction,
) => {
try {
const { permissionIds } = req.body;
const role = await this.#serivce.assignPermissions(
req.params.id,
permissionIds,
);
return res.status(200).json({
status: 200,
message: "Permissions assigned statusfully",
data: role,
});
} catch (error) {
next(error);
}
};
addPermissions = async (
req: any,
res: ServerResponse,
next: NextFunction,
) => {
try {
const { permissionIds } = req.body;
const role = await this.#serivce.addPermissions(
req.params.id,
permissionIds,
);
return res.status(200).json({
status: 200,
message: "Permissions added statusfully",
data: role,
});
} catch (error) {
next(error);
}
};
removePermissions = async (
req: any,
res: ServerResponse,
next: NextFunction,
) => {
try {
const { permissionIds } = req.body;
const role = await this.#serivce.removePermissions(
req.params.id,
permissionIds,
);
return res.status(200).json({
status: 200,
message: "Permissions removed statusfully",
data: role,
});
} catch (error) {
next(error);
}
};
}
const RoleController = new RoleControllerClass();
export default RoleController;

View File

@@ -0,0 +1,15 @@
import { Router } from "express";
import RoleController from "./role.controller";
const RoleRouter = Router();
RoleRouter.post("/create", RoleController.create);
RoleRouter.get("/list", RoleController.findAll);
RoleRouter.get("/:id", RoleController.findById);
RoleRouter.put("/update/:id", RoleController.update);
RoleRouter.delete("/remove/:id", RoleController.delete);
RoleRouter.post("/:id/permissions/assign", RoleController.assignPermissions);
RoleRouter.post("/:id/permissions/add", RoleController.addPermissions);
RoleRouter.post("/:id/permissions/remove", RoleController.removePermissions);
export default RoleRouter;

View File

@@ -0,0 +1,168 @@
import Permission from "../../models/Permission";
import Role from "../../models/Role";
import User from "../../models/User";
class RoleServiceClass {
async create(data: { name: string; description?: string }) {
const existingRole = await Role.findOne({
where: { name: data.name },
});
if (existingRole) {
throw new Error("Role already exists");
}
const role = await Role.create({
name: data.name,
description: data.description,
});
return role;
}
async findAll() {
return await Role.findAll({
include: [
{
model: Permission,
as: "permissions",
through: { attributes: [] },
},
{
model: User,
as: "users",
},
],
order: [["createdAt", "DESC"]],
});
}
async findById(id: string) {
const role = await Role.findByPk(id, {
include: [
{
model: Permission,
as: "permissions",
through: { attributes: [] },
},
{
model: User,
as: "users",
},
],
});
if (!role) {
throw new Error("Role not found");
}
return role;
}
async update(id: string, data: { name?: string; description?: string }) {
const role = await Role.findByPk(id);
if (!role) {
throw new Error("Role not found");
}
if (data.name) {
const existingRole = await Role.findOne({
where: { name: data.name },
});
if (existingRole && existingRole.id !== id) {
throw new Error("Another role with this name already exists");
}
}
await role.update({
name: data.name ?? role.name,
description: data.description ?? role.description,
});
return role;
}
async delete(id: string) {
const role = await Role.findByPk(id);
if (!role) {
throw new Error("Role not found");
}
await role.destroy();
return { message: "Role deleted successfully" };
}
async assignPermissions(roleId: string, permissionIds: string[]) {
const role = await Role.findByPk(roleId);
if (!role) {
throw new Error("Role not found");
}
await role.setPermissions(permissionIds);
const updatedRole = await Role.findByPk(roleId, {
include: [
{
model: Permission,
as: "permissions",
through: { attributes: [] },
},
],
});
return updatedRole;
}
async addPermissions(roleId: string, permissionIds: string[]) {
const role = await Role.findByPk(roleId);
if (!role) {
throw new Error("Role not found");
}
await role.addPermissions(permissionIds);
const updatedRole = await Role.findByPk(roleId, {
include: [
{
model: Permission,
as: "permissions",
through: { attributes: [] },
},
],
});
return updatedRole;
}
async removePermissions(roleId: string, permissionIds: string[]) {
const role = await Role.findByPk(roleId);
if (!role) {
throw new Error("Role not found");
}
await role.removePermissions(permissionIds);
const updatedRole = await Role.findByPk(roleId, {
include: [
{
model: Permission,
as: "permissions",
through: { attributes: [] },
},
],
});
return updatedRole;
}
}
const RoleService = new RoleServiceClass();
export default RoleService;