chore: add the access log to the ticket

This commit is contained in:
mahyargdz
2025-09-03 13:29:34 +03:30
parent 5e0bd3dce7
commit 76fd4f3435
2 changed files with 132 additions and 26 deletions
+109 -10
View File
@@ -6,8 +6,11 @@ import { DataSource, MoreThanOrEqual, Not, QueryRunner } from "typeorm";
import { TICKET } from "../constant";
import { ReferralService } from "./referral.service";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { ParamDto } from "../../../common/DTO/param.dto";
import { CommonMessage, TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
import { AccessLogService } from "../../access-logs/providers/access-log.service";
import { DanakService } from "../../danak-services/entities/danak-service.entity";
import { NotificationQueue } from "../../notifications/queue/notification.queue";
import { SupportPlanFeatureKey } from "../../support-plans/enums/support-plan-feature-key.enum";
@@ -46,6 +49,7 @@ export class TicketsService implements OnModuleInit {
private readonly referralService: ReferralService,
private readonly dataSource: DataSource,
private readonly supportPlansService: SupportPlansService,
private readonly accessLogService: AccessLogService,
) {}
async onModuleInit() {
@@ -67,7 +71,7 @@ export class TicketsService implements OnModuleInit {
return { categories, count, paginate: true };
}
//******************************** */
async createTicketCategory(createDto: CreateTicketCategoryDto) {
async createTicketCategory(createDto: CreateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
const { userGroup } = await this.usersService.getUserGroupById(createDto.userGroupId);
const existCategory = await this.ticketsCategoryRepository.findCategoryByTitle(createDto.title);
@@ -76,15 +80,29 @@ export class TicketsService implements OnModuleInit {
...createDto,
group: userGroup,
});
await this.ticketsCategoryRepository.save(ticketCategory);
await this.accessLogService.logCreate("TicketCategory", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created ticket category: ${createDto.title}`,
metadata: {
categoryId: ticketCategory.id,
categoryTitle: ticketCategory.title,
},
});
return {
message: TicketMessageEnum.CATEGORY_CREATED,
};
}
//******************************** */
async updateCategory(paramDto: ParamDto, updateCategoryDto: UpdateTicketCategoryDto) {
async updateCategory(paramDto: ParamDto, updateCategoryDto: UpdateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id);
if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
@@ -101,18 +119,47 @@ export class TicketsService implements OnModuleInit {
...updateCategoryDto,
});
await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated ticket category: ${category.title}`,
metadata: {
categoryId: category.id,
categoryTitle: category.title,
},
oldValues: JSON.stringify({ ...category }),
newValues: JSON.stringify({ ...updateCategoryDto }),
});
return {
message: CommonMessage.UPDATE_SUCCESS,
};
}
//******************************** */
async toggleCategoryStatus(paramDto: ParamDto) {
async toggleCategoryStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id);
if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
//
category.isActive = !category.isActive;
await this.ticketsCategoryRepository.save(category);
//
await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin toggled ticket category status: ${category.title}`,
metadata: {
categoryId: category.id,
categoryTitle: category.title,
isActive: category.isActive,
},
});
return {
message: CommonMessage.UPDATE_SUCCESS,
isActive: category.isActive,
@@ -209,7 +256,7 @@ export class TicketsService implements OnModuleInit {
}
//******************************** */
async createTicketByAdmin(createDto: CreateTicketByAdminDto, adminId: string) {
async createTicketByAdmin(createDto: CreateTicketByAdminDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -218,7 +265,7 @@ export class TicketsService implements OnModuleInit {
const [user, admin, ticketCategory] = await Promise.all([
this.usersService.findOneByIdWithQueryRunner(createDto.userId, queryRunner),
this.usersService.findOneByIdWithQueryRunner(adminId, queryRunner),
this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner),
queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }),
]);
@@ -244,6 +291,19 @@ export class TicketsService implements OnModuleInit {
userEmail: user.email,
});
await this.accessLogService.logCreate("Ticket", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created ticket: ${ticket.subject}`,
metadata: {
ticketId: ticket.id,
ticketSubject: ticket.subject,
},
});
await queryRunner.commitTransaction();
return {
@@ -347,13 +407,13 @@ export class TicketsService implements OnModuleInit {
//******************************** */
async closeTicketByUser(ticketId: string, userId: string, isAdmin: boolean) {
async closeTicketByUser(ticketId: string, isAdmin: boolean, userIpAndHeaders: IUserIpAndHeaders) {
let ticket: null | Ticket = null;
if (isAdmin) {
ticket = await this.ticketsRepository.findTicketById(ticketId);
} else {
ticket = await this.ticketsRepository.findTicketById(ticketId, userId);
ticket = await this.ticketsRepository.findTicketById(ticketId, userIpAndHeaders.userId);
}
if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
@@ -361,32 +421,58 @@ export class TicketsService implements OnModuleInit {
ticket.status = TicketStatus.CLOSED;
await this.ticketsRepository.save(ticket);
await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin closed ticket: ${ticket.subject}`,
metadata: {
ticketId: ticket.id,
ticketSubject: ticket.subject,
},
});
return {
message: TicketMessageEnum.CLOSED,
};
}
//******************************** * /
async openTicketByAdmin(ticketId: string) {
async openTicketByAdmin(ticketId: string, userIpAndHeaders: IUserIpAndHeaders) {
const ticket = await this.ticketsRepository.findTicketById(ticketId);
if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
ticket.status = TicketStatus.PENDING;
await this.ticketsRepository.save(ticket);
await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin opened ticket: ${ticket.subject}`,
metadata: {
ticketId: ticket.id,
ticketSubject: ticket.subject,
},
});
return {
message: TicketMessageEnum.OPENED,
};
}
//******************************** */
async referTicket(ticketId: string, userId: string, referDto: ReferTicketDto) {
async referTicket(ticketId: string, referDto: ReferTicketDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
await queryRunner.connect();
await queryRunner.startTransaction();
const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId);
// if (!isSuperAdmin) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER);
console.log("isSuperAdmin", isSuperAdmin);
@@ -408,6 +494,19 @@ export class TicketsService implements OnModuleInit {
});
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/tickets",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin referred ticket: ${ticket.subject}`,
metadata: {
ticketId: ticket.id,
ticketSubject: ticket.subject,
},
});
return {
message: TicketMessageEnum.REFERRED,
ticket,
+23 -16
View File
@@ -12,7 +12,7 @@ import { TicketsService } from "./providers/tickets.service";
import { AdminRoute } from "../../common/decorators/admin.decorator";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { UserDec } from "../../common/decorators/user.decorator";
import { IUserIpAndHeaders, UserDec, UserIpAndHeaders } from "../../common/decorators/user.decorator";
import { ParamDto } from "../../common/DTO/param.dto";
import { PermissionEnum } from "../users/enums/permission.enum";
@Controller("tickets")
@@ -26,20 +26,23 @@ export class TicketsController {
@ApiOperation({ summary: "Create ticket category => admin route" })
@PermissionsDec(PermissionEnum.TICKETS)
@Post("category")
createTicketCategory(@Body() createDto: CreateTicketCategoryDto) {
return this.ticketsService.createTicketCategory(createDto);
createTicketCategory(@Body() createDto: CreateTicketCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.ticketsService.createTicketCategory(createDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Update ticket category => admin route" })
@PermissionsDec(PermissionEnum.TICKETS)
@Patch("category/:id")
updateCategory(@Param() paramDto: ParamDto, @Body() updateCategoryDto: UpdateTicketCategoryDto) {
return this.ticketsService.updateCategory(paramDto, updateCategoryDto);
updateCategory(
@Param() paramDto: ParamDto,
@Body() updateCategoryDto: UpdateTicketCategoryDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.ticketsService.updateCategory(paramDto, updateCategoryDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Get ticket categories " })
@Get("categories")
// @PermissionsDec(PermissionEnum.TICKETS)
getTicketCategories() {
return this.ticketsService.getTicketCategories();
}
@@ -57,8 +60,8 @@ export class TicketsController {
@HttpCode(HttpStatus.OK)
@PermissionsDec(PermissionEnum.TICKETS)
@Post("category/toggle-status/:id")
toggleCategoryStatus(@Param() paramDto: ParamDto) {
return this.ticketsService.toggleCategoryStatus(paramDto);
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders);
}
//----------------- ticket and ticket messages -------------------
@@ -73,8 +76,8 @@ export class TicketsController {
@AdminRoute()
@PermissionsDec(PermissionEnum.TICKETS)
@Post("admin")
createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserDec("id") adminId: string) {
return this.ticketsService.createTicketByAdmin(createDto, adminId);
createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.ticketsService.createTicketByAdmin(createDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Get all tickets of user ==> user route" })
@@ -111,23 +114,27 @@ export class TicketsController {
@ApiOperation({ summary: "close ticket by user or admin" })
@HttpCode(HttpStatus.OK)
@Post(":id/close")
closedTicketByUser(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) {
return this.ticketsService.closeTicketByUser(paramDto.id, userId, isAdmin);
closedTicketByUser(
@Param() paramDto: ParamDto,
@UserDec("isAdmin") isAdmin: boolean,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders);
}
@ApiOperation({ summary: "open ticket by admin" })
@AdminRoute()
@PermissionsDec(PermissionEnum.TICKETS)
@Post(":id/open")
openTicketByAdmin(@Param() paramDto: ParamDto) {
return this.ticketsService.openTicketByAdmin(paramDto.id);
openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders);
}
@ApiOperation({ summary: "referer ticket ==> admin route" })
@AdminRoute()
@PermissionsDec(PermissionEnum.TICKETS)
@Post(":id/refer")
referTicket(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @Body() referDto: ReferTicketDto) {
return this.ticketsService.referTicket(paramDto.id, userId, referDto);
referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders);
}
}