update: remove the blog from the response of the update or create blog

This commit is contained in:
mahyargdz
2025-09-03 08:59:17 +03:30
parent 672fe9b783
commit 5e0bd3dce7
24 changed files with 526 additions and 385 deletions
+13
View File
@@ -21,3 +21,16 @@ export const UserDec = createParamDecorator((data: keyof ITokenPayload | undefin
// const req = ctx.switchToHttp().getRequest<FastifyRequest>();
// return req.user;
// });
export const UserIpAndHeaders = createParamDecorator((_data: unknown, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
const user = req.user;
return { ip: req.ip, headers: req.headers, userId: user?.id };
});
export interface IUserIpAndHeaders {
ip: string;
headers: Record<string, string>;
userId: string;
}
+1
View File
@@ -151,6 +151,7 @@ export const enum CategoryMessage {
ORDER_SHOULD_BE_INT = "ترتیب دسته بندی باید یک عدد صحیح باشد",
CREATION_FAILED = "ایجاد دسته بندی موفیقیت آمیز نبود",
UPDATE_FAILED = "به روز رسانی دسته بندی موفیقیت آمیز نبود",
CATEGORY_HAS_SERVICES = "دسته بندی دارای سرویس می‌باشد",
}
export const enum ServiceMessage {
+1 -1
View File
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
username: configService.getOrThrow<string>("DB_USER"),
password: configService.getOrThrow<string>("DB_PASS"),
autoLoadEntities: true,
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
migrationsTableName: "typeorm_migrations",
migrationsRun: false,
@@ -1,64 +0,0 @@
import { IsEnum, IsNumber, IsObject, IsOptional, IsString, IsUUID } from "class-validator";
import { User } from "../../users/entities/user.entity";
import { OperationType } from "../enums/operation-type.enum";
import { UserType } from "../enums/user-type.enum";
export class CreateAccessLogDto {
@IsEnum(OperationType)
operationType: OperationType;
@IsEnum(UserType)
userType: UserType;
@IsString()
entityName: string;
@IsOptional()
@IsString()
entityId?: string;
@IsOptional()
@IsString()
actionDescription?: string;
@IsOptional()
@IsString()
oldValues?: string;
@IsOptional()
@IsString()
newValues?: string;
@IsOptional()
@IsString()
ipAddress?: string;
@IsOptional()
@IsString()
userAgent?: string;
@IsOptional()
@IsString()
endpoint?: string;
@IsOptional()
@IsNumber()
statusCode?: number;
@IsOptional()
@IsString()
errorMessage?: string;
@IsOptional()
@IsObject()
metadata?: Record<string, any>;
@IsOptional()
@IsUUID()
user?: Pick<User, "id">;
@IsOptional()
@IsString()
requestId?: string;
}
@@ -13,42 +13,42 @@ export class AccessLog extends BaseEntity {
@Column({ type: "enum", enum: UserType })
userType: UserType;
@Column({ type: "varchar", length: 255 })
@Column({ type: "varchar", length: 255, nullable: false })
entityName: string;
@Column({ type: "varchar", length: 255, nullable: true })
entityId: string;
entityId?: string;
@Column({ type: "varchar", length: 255, nullable: true })
actionDescription: string;
actionDescription?: string;
@Column({ type: "text", nullable: true })
oldValues: string;
oldValues?: string;
@Column({ type: "text", nullable: true })
newValues: string;
newValues?: string;
@Column({ type: "varchar", length: 45, nullable: true })
ipAddress: string;
ipAddress?: string;
@Column({ type: "varchar", length: 500, nullable: true })
userAgent: string;
userAgent?: string;
@Column({ type: "varchar", length: 255, nullable: true })
endpoint: string;
endpoint?: string;
@Column({ type: "integer", nullable: true })
statusCode: number;
statusCode?: number;
@Column({ type: "text", nullable: true })
errorMessage: string;
errorMessage?: string;
@Column({ type: "jsonb", nullable: true })
metadata: Record<string, any>;
@ManyToOne(() => User, { nullable: true })
user: User;
metadata?: Record<string, unknown>;
@Column({ type: "varchar", length: 255, nullable: true })
requestId: string;
requestId?: string;
@ManyToOne(() => User, { nullable: true })
user?: User;
}
@@ -0,0 +1,6 @@
import { User } from "../../users/entities/user.entity";
import { AccessLog } from "../entities/access-log.entity";
export interface IAccessLogData extends Partial<Omit<AccessLog, "id" | "createdAt" | "updatedAt" | "user">> {
user?: Pick<User, "id">;
}
@@ -1,20 +0,0 @@
import { FastifyRequest } from "fastify";
import { User } from "../../users/entities/user.entity";
import { OperationType } from "../enums/operation-type.enum";
import { UserType } from "../enums/user-type.enum";
export interface ILogContext {
operationType: OperationType;
entityName: string;
entityId?: string;
actionDescription?: string;
oldValues?: any;
newValues?: any;
user?: Pick<User, "id">;
userType?: UserType;
metadata?: Record<string, any>;
request?: FastifyRequest;
statusCode?: number;
errorMessage?: string;
}
@@ -1,41 +1,18 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { FastifyRequest } from "fastify";
import { AccessLogMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity";
import { AccessLogQueryDto, AccessLogStatsQueryDto, EntityIdQueryDto } from "../DTO/access-log-query.dto";
import { CreateAccessLogDto } from "../DTO/create-access-log.dto";
import { OperationType } from "../enums/operation-type.enum";
import { UserType } from "../enums/user-type.enum";
import { ILogContext } from "../interfaces/log-context.interface";
import { IAccessLogData } from "../interfaces/access-log-data.interface";
import { AccessLogRepository } from "../repositories/access-log.repository";
@Injectable()
export class AccessLogService {
constructor(private readonly accessLogRepository: AccessLogRepository) {}
async logOperation(context: ILogContext) {
const logData: CreateAccessLogDto = {
operationType: context.operationType,
userType: context.userType || UserType.ADMIN,
entityName: context.entityName,
entityId: context.entityId,
actionDescription: context.actionDescription,
oldValues: context.oldValues ? JSON.stringify(context.oldValues) : undefined,
newValues: context.newValues ? JSON.stringify(context.newValues) : undefined,
user: context.user,
metadata: context.metadata,
statusCode: context.statusCode,
errorMessage: context.errorMessage,
};
// Extract request information if available
if (context.request) {
logData.ipAddress = this.getClientIp(context.request);
logData.userAgent = context.request.headers["user-agent"];
logData.endpoint = context.request.url;
logData.requestId = context.request.headers["x-request-id"] as string;
}
async logOperation(logData: IAccessLogData) {
const accessLog = this.accessLogRepository.create({ ...logData });
await this.accessLogRepository.save(accessLog);
@@ -43,81 +20,74 @@ export class AccessLogService {
}
//########################################################################################
async logCreate(entityName: string, entityId: string, newValues: Record<string, any>, context: Partial<ILogContext> = {}) {
async logCreate(entityName: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.CREATE,
entityName,
entityId,
newValues,
actionDescription: `Created ${entityName}`,
...context,
entityName,
userType,
});
}
//########################################################################################
async logUpdate(
entityName: string,
entityId: string,
oldValues: Record<string, any>,
newValues: Record<string, any>,
context: Partial<ILogContext> = {},
) {
async logUpdate(entityName: string, entityId: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.UPDATE,
entityName,
entityId,
oldValues,
newValues,
actionDescription: `Updated ${entityName}`,
...context,
userType,
});
}
//########################################################################################
async logDelete(entityName: string, entityId: string, oldValues: Record<string, any>, context: Partial<ILogContext> = {}) {
async logDelete(entityName: string, entityId: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.DELETE,
entityName,
entityId,
oldValues,
actionDescription: `Deleted ${entityName}`,
...context,
userType,
});
}
//########################################################################################
async logRead(entityName: string, entityId?: string, context: Partial<ILogContext> = {}) {
async logRead(entityName: string, entityId: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.READ,
entityName,
entityId,
actionDescription: `Read ${entityName}`,
...context,
userType,
});
}
//########################################################################################
async logLogin(userId: string, context: Partial<ILogContext> = {}) {
return await this.logOperation({
async logLogin(userId: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.LOGIN,
entityName: "User",
entityId: userId,
user: { id: userId },
userType: UserType.USER,
user: { id: userId } as User,
userType,
actionDescription: "User login",
...context,
});
}
//########################################################################################
async logLogout(userId: string, context: Partial<ILogContext> = {}) {
return await this.logOperation({
async logLogout(userId: string, userType: UserType, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType: OperationType.LOGOUT,
entityName: "User",
entityId: userId,
user: { id: userId },
userType: UserType.USER,
user: { id: userId } as User,
userType,
actionDescription: "User logout",
...context,
});
}
@@ -127,15 +97,15 @@ export class AccessLogService {
entityName: string,
entityId: string,
actionDescription: string,
context: Partial<ILogContext> = {},
logData: IAccessLogData = {},
) {
return await this.logOperation({
return this.logOperation({
...logData,
operationType,
entityName,
entityId,
actionDescription,
userType: UserType.ADMIN,
...context,
});
}
@@ -146,15 +116,15 @@ export class AccessLogService {
entityName: string,
entityId: string,
actionDescription: string,
context: Partial<ILogContext> = {},
logData: IAccessLogData = {},
) {
return this.logOperation({
...logData,
operationType,
entityName,
entityId,
actionDescription,
userType: UserType.USER,
...context,
});
}
@@ -164,27 +134,27 @@ export class AccessLogService {
entityName: string,
entityId: string,
actionDescription: string,
context: Partial<ILogContext> = {},
logData: IAccessLogData = {},
) {
return this.logOperation({
...logData,
operationType,
entityName,
entityId,
actionDescription,
userType: UserType.SYSTEM,
...context,
});
}
//########################################################################################
async logError(operationType: OperationType, entityName: string, errorMessage: string, context: Partial<ILogContext> = {}) {
async logError(operationType: OperationType, entityName: string, errorMessage: string, logData: IAccessLogData = {}) {
return this.logOperation({
...logData,
operationType,
entityName,
errorMessage,
actionDescription: `Error in ${operationType} operation on ${entityName}`,
statusCode: 500,
...context,
});
}
//########################################################################################
@@ -230,11 +200,4 @@ export class AccessLogService {
if (!accessLog) throw new BadRequestException(AccessLogMessage.ACCESS_LOG_NOT_FOUND);
return { accessLog };
}
//########################################################################################
private getClientIp(request: FastifyRequest): string {
return (
(request.headers["x-forwarded-for"] as string)?.split(",")[0] || (request.headers["x-real-ip"] as string) || request.ip || "unknown"
);
}
}
@@ -7,7 +7,7 @@ import { UpdateAnnouncementDto } from "./DTO/update-announcement.dto";
import { AnnouncementService } from "./providers/announcement.service";
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";
@@ -19,22 +19,26 @@ export class AnnouncementController {
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
@ApiProperty({ description: "Create a new announcement (admin route)" })
@Post()
create(@Body() createAnnouncementDto: CreateAnnouncementDto, @UserDec("id") userId: string) {
return this.announcementService.createAnnouncement(createAnnouncementDto, userId);
create(@Body() createAnnouncementDto: CreateAnnouncementDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.announcementService.createAnnouncement(createAnnouncementDto, userIpAndHeaders);
}
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
@ApiProperty({ description: "Update an announcement (admin route)" })
@Patch(":id")
update(@Param() paramDto: ParamDto, @Body() updateAnnouncementDto: UpdateAnnouncementDto, @UserDec("id") userId: string) {
return this.announcementService.updateAnnouncement(paramDto.id, updateAnnouncementDto, userId);
update(
@Param() paramDto: ParamDto,
@Body() updateAnnouncementDto: UpdateAnnouncementDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.announcementService.updateAnnouncement(paramDto.id, updateAnnouncementDto, userIpAndHeaders);
}
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
@ApiProperty({ description: "Delete an announcement (admin route)" })
@Delete(":id")
delete(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.announcementService.deleteAnnouncement(paramDto.id, userId);
delete(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.announcementService.deleteAnnouncement(paramDto.id, userIpAndHeaders);
}
@ApiProperty({ description: "Get all announcements (admin route)" })
@@ -3,6 +3,7 @@ import { BadRequestException, Injectable } from "@nestjs/common";
import { Queue } from "bullmq";
import dayjs from "dayjs";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { AnnouncementMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
import { AccessLogService } from "../../access-logs/providers/access-log.service";
@@ -27,7 +28,7 @@ export class AnnouncementService {
/***************************************** */
async createAnnouncement(createAnnouncementDto: CreateAnnouncementDto, userId: string) {
async createAnnouncement(createAnnouncementDto: CreateAnnouncementDto, userIpAndHeaders: IUserIpAndHeaders) {
const { serviceId, userIds } = createAnnouncementDto;
let isPublic = true;
@@ -65,12 +66,19 @@ export class AnnouncementService {
},
);
await this.accessLogService.logCreate("Announcement", announcement.id, announcement, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logCreate("Announcement", UserType.ADMIN, {
entityId: announcement.id,
user: { id: userIpAndHeaders.userId },
endpoint: "/announcements",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created announcement: ${announcement.title}`,
metadata: {
serviceId: serviceId,
userIp: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
},
});
@@ -210,7 +218,7 @@ export class AnnouncementService {
}
//************************************ */
async updateAnnouncement(id: string, updateAnnouncementDto: UpdateAnnouncementDto, userId: string) {
async updateAnnouncement(id: string, updateAnnouncementDto: UpdateAnnouncementDto, userIpAndHeaders: IUserIpAndHeaders) {
const announcement = await this.announcementRepository.findOneBy({ id });
if (!announcement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND);
@@ -255,12 +263,19 @@ export class AnnouncementService {
);
}
await this.accessLogService.logUpdate("Announcement", announcement.id, announcement, {
user: { id: userId },
await this.accessLogService.logUpdate("Announcement", announcement.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/announcements",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
userType: UserType.ADMIN,
actionDescription: `Admin updated announcement: ${announcement.title}`,
metadata: {
serviceId: serviceId,
userIp: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
},
});
@@ -272,7 +287,7 @@ export class AnnouncementService {
//************************************ */
async deleteAnnouncement(id: string, userId: string) {
async deleteAnnouncement(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const announcement = await this.announcementRepository.findOneBy({ id });
if (!announcement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND);
@@ -284,12 +299,19 @@ export class AnnouncementService {
await this.announcementRepository.delete(id);
await this.accessLogService.logDelete("Announcement", announcement.id, announcement, {
user: { id: userId },
await this.accessLogService.logDelete("Announcement", announcement.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/announcements",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
userType: UserType.ADMIN,
actionDescription: `Admin deleted announcement: ${announcement.title}`,
metadata: {
serviceId: announcement.targetService.id,
userIp: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
},
});
+25 -12
View File
@@ -1,4 +1,5 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common";
import { CacheInterceptor, CacheTTL } from "@nestjs/cache-manager";
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query, UseInterceptors } from "@nestjs/common";
import { ApiOperation } from "@nestjs/swagger";
import { BlogCommentQueryDto } from "./DTO/blog-comment-query.dto";
@@ -18,7 +19,7 @@ import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { Pagination } from "../../common/decorators/pagination.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { SkipAuth } from "../../common/decorators/skip-auth.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 { CategoryListSearchQueryDto } from "../danak-services/DTO/category-search-query.dto";
import { PermissionEnum } from "../users/enums/permission.enum";
@@ -31,6 +32,8 @@ export class BlogsController {
) {}
@ApiOperation({ summary: "Get all public and active blogs with category id" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get()
getBlogsPublic(@Query() queryDto: BlogSearchQueryDto) {
@@ -38,6 +41,8 @@ export class BlogsController {
}
@ApiOperation({ summary: "Get most visited and pinned blogs" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get("combined")
getCombinedBlogs() {
@@ -53,6 +58,8 @@ export class BlogsController {
}
@ApiOperation({ summary: "Get a blog by id" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get(":id")
getBlog(@Param() paramDto: ParamDto, @UserDec("isAdmin") isAdmin: boolean) {
@@ -60,6 +67,8 @@ export class BlogsController {
}
@ApiOperation({ summary: "Get a blog by slug" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get("slug/:slug")
getBlogBySlug(@Param() paramDto: BlogSlugDto, @UserDec("isAdmin") isAdmin: boolean) {
@@ -67,6 +76,8 @@ export class BlogsController {
}
@ApiOperation({ summary: "Get most visited blogs" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get("most-visited")
getMostVisitedBlogs() {
@@ -82,6 +93,8 @@ export class BlogsController {
}
@ApiOperation({ summary: "Get most pinned blogs" })
@UseInterceptors(CacheInterceptor)
@CacheTTL(120 * 1000)
@SkipAuth()
@Get("pinned")
getPinnedBlogs() {
@@ -100,24 +113,24 @@ export class BlogsController {
@Post()
@AdminRoute()
@PermissionsDec(PermissionEnum.BLOGS)
createBlog(@Body() createBlogDto: CreateBlogDto, @UserDec("id") userId: string) {
return this.blogsService.createBlog(createBlogDto, userId);
createBlog(@Body() createBlogDto: CreateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.blogsService.createBlog(createBlogDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Update a blog (admin route)" })
@Patch(":id")
@AdminRoute()
@PermissionsDec(PermissionEnum.BLOGS)
updateBlog(@Param() paramDto: ParamDto, @Body() updateBlogDto: UpdateBlogDto, @UserDec("id") userId: string) {
return this.blogsService.updateBlog(paramDto.id, updateBlogDto, userId);
updateBlog(@Param() paramDto: ParamDto, @Body() updateBlogDto: UpdateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.blogsService.updateBlog(paramDto.id, updateBlogDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Delete a blog (admin route) " })
@Delete(":id")
@AdminRoute()
@PermissionsDec(PermissionEnum.BLOGS)
deleteBlog(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.blogsService.deleteBlog(paramDto.id, userId);
deleteBlog(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.blogsService.deleteBlog(paramDto.id, userIpAndHeaders);
}
//-------------- comments --------------
@@ -200,15 +213,15 @@ export class BlogsController {
@Delete("categories/:id")
@AdminRoute()
@PermissionsDec(PermissionEnum.BLOGS)
deleteCategory(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.blogsService.deleteCategory(paramDto.id, userId);
deleteCategory(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.blogsService.deleteCategory(paramDto.id, userIpAndHeaders);
}
@ApiOperation({ summary: "toggle status of categories (admin route)" })
@PermissionsDec(PermissionEnum.BLOGS)
@HttpCode(HttpStatus.OK)
@Post("categories/toggle-status/:id")
toggleCategoryStatus(@Param() paramDto: ParamDto) {
return this.blogsService.toggleCategoryStatus(paramDto.id);
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.blogsService.toggleCategoryStatus(paramDto.id, userIpAndHeaders);
}
}
+72 -52
View File
@@ -1,6 +1,7 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { Not } from "typeorm";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { AdminMessage, BlogMessage, CommonMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
import { AccessLogService } from "../../access-logs/providers/access-log.service";
@@ -49,13 +50,13 @@ export class BlogsService {
return {
message: CommonMessage.UPDATE_SUCCESS,
category,
categoryId: category.id,
};
}
//*********************************** */
async deleteCategory(id: string, userId: string) {
const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
async deleteCategory(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId);
if (!isSuperAdmin) throw new BadRequestException(AdminMessage.NOT_ALLOWED);
const category = await this.blogCategoriesRepository.findOneBy({ id });
@@ -65,23 +66,45 @@ export class BlogsService {
if (blogs.length > 0) throw new BadRequestException(BlogMessage.CATEGORY_HAS_BLOGS);
await this.blogCategoriesRepository.remove(category);
await this.accessLogService.logDelete("BlogCategory", category.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/blogs/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted blog category: ${category.title}`,
metadata: {
categoryId: category.id,
},
});
return {
message: CommonMessage.DELETED,
};
}
//*********************************** */
async toggleCategoryStatus(id: string) {
async toggleCategoryStatus(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.findCategoryById(id);
//
category.isActive = !category.isActive;
await this.blogCategoriesRepository.save(category);
//
await this.accessLogService.logUpdate("BlogCategory", category.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/blogs/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated blog category status: ${category.title}`,
metadata: {
categoryId: category.id,
},
});
return {
message: CommonMessage.UPDATE_SUCCESS,
category,
categoryId: category.id,
};
}
//*********************************** */
@@ -104,7 +127,7 @@ export class BlogsService {
}
//*********************************** */
async createBlog(createBlogDto: CreateBlogDto, authorId: string) {
async createBlog(createBlogDto: CreateBlogDto, userIpAndHeaders: IUserIpAndHeaders) {
const existingBlog = await this.blogsRepository.findOne({ where: { title: createBlogDto.title } });
if (existingBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS);
@@ -117,38 +140,36 @@ export class BlogsService {
const blog = this.blogsRepository.create({
...createBlogDto,
author: { id: authorId },
author: { id: userIpAndHeaders.userId },
category,
slug,
});
await this.blogsRepository.save(blog);
await this.accessLogService.logCreate(
"Blog",
blog.id,
{ blog: createBlogDto.title },
{
user: { id: authorId },
userType: UserType.ADMIN,
actionDescription: `Admin created blog: ${createBlogDto.title}`,
metadata: {
categoryId: createBlogDto.categoryId,
slug: slug,
authorId: authorId,
},
await this.accessLogService.logCreate("Blog", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/blogs",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created blog: ${createBlogDto.title}`,
metadata: {
categoryId: createBlogDto.categoryId,
slug: slug,
authorId: userIpAndHeaders.userId,
},
);
});
return {
message: CommonMessage.CREATED,
blog,
blogId: blog.id,
};
}
//*********************************** */
async updateBlog(id: string, updateBlogDto: UpdateBlogDto, userId: string) {
async updateBlog(id: string, updateBlogDto: UpdateBlogDto, userIpAndHeaders: IUserIpAndHeaders) {
const blog = await this.findBlogById(id);
if (updateBlogDto.title) {
@@ -172,30 +193,29 @@ export class BlogsService {
slug: this.generateSlug(updateBlogDto.slug ?? blog.slug),
});
await this.accessLogService.logUpdate(
"Blog",
blog.id,
{ blog: blog.title },
{ blog: updateBlogDto.title },
{
user: { id: userId },
userType: UserType.ADMIN,
actionDescription: `Admin updated blog: ${blog.title}`,
metadata: {
categoryId: updateBlogDto.categoryId,
},
await this.accessLogService.logUpdate("Blog", blog.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/blogs",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated blog: ${blog.title}`,
metadata: {
categoryId: updateBlogDto.categoryId,
},
);
oldValues: JSON.stringify({ ...blog }),
newValues: JSON.stringify({ ...updateBlogDto }),
});
return {
message: CommonMessage.UPDATE_SUCCESS,
blog,
blogId: blog.id,
};
}
//*********************************** */
async deleteBlog(id: string, userId: string) {
const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
async deleteBlog(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId);
if (!isSuperAdmin) throw new BadRequestException(AdminMessage.NOT_ALLOWED);
const blog = await this.blogsRepository.findOneBy({ id });
@@ -203,19 +223,19 @@ export class BlogsService {
await this.blogsRepository.softDelete({ id });
await this.accessLogService.logDelete(
"Blog",
blog.id,
{ blog: blog.title },
{
user: { id: userId },
userType: UserType.ADMIN,
actionDescription: `Admin deleted blog: ${blog.title}`,
metadata: {
categoryId: blog.category.id,
},
await this.accessLogService.logDelete("Blog", blog.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/blogs",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted blog: ${blog.title}`,
metadata: {
categoryId: blog.category.id,
},
);
oldValues: JSON.stringify({ ...blog }),
newValues: JSON.stringify({ ...blog }),
});
return {
message: CommonMessage.DELETED,
};
@@ -23,7 +23,7 @@ import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { Pagination } from "../../common/decorators/pagination.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { StrictRateLimit } from "../../common/decorators/rate-limit.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";
@@ -40,8 +40,8 @@ export class DanakServicesController {
@PermissionsDec(PermissionEnum.SERVICES)
@ApiOperation({ summary: "Create a new service category => admin route" })
@Post("categories")
createCategory(@Body() createDto: CreateDanakServiceCategoryDto) {
return this.danakServicesService.createCategory(createDto);
createCategory(@Body() createDto: CreateDanakServiceCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.danakServicesService.createCategory(createDto, userIpAndHeaders);
}
@AuthGuards()
@@ -80,16 +80,20 @@ export class DanakServicesController {
@PermissionsDec(PermissionEnum.SERVICES)
@ApiOperation({ summary: "update category by id => admin route" })
@Patch("categories/:id")
updateCategory(@Param() paramDto: ParamDto, @Body() updateDto: UpdateDanakServiceCategoryDto) {
return this.danakServicesService.updateCategory(paramDto.id, updateDto);
updateCategory(
@Param() paramDto: ParamDto,
@Body() updateDto: UpdateDanakServiceCategoryDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.danakServicesService.updateCategory(paramDto.id, updateDto, userIpAndHeaders);
}
@AuthGuards()
@PermissionsDec(PermissionEnum.SERVICES)
@ApiOperation({ summary: "delete category by id => admin route" })
@Delete("categories/:id")
deleteCategoryById(@Param() paramDto: ParamDto) {
return this.danakServicesService.deleteCategoryById(paramDto.id);
deleteCategoryById(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.danakServicesService.deleteCategoryById(paramDto.id, userIpAndHeaders);
}
@AuthGuards()
@@ -97,16 +101,16 @@ export class DanakServicesController {
@ApiOperation({ summary: "toggle status of categories => admin route" })
@HttpCode(HttpStatus.OK)
@Post("categories/toggle-status/:id")
toggleCategoryStatus(@Param() paramDto: ParamDto) {
return this.danakServicesService.toggleCategoryStatus(paramDto);
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.danakServicesService.toggleCategoryStatus(paramDto, userIpAndHeaders);
}
//-------------------- service management --------------------------
@AuthGuards()
@PermissionsDec(PermissionEnum.SERVICES)
@ApiOperation({ summary: "create new danak services => admin route" })
@Post()
createService(@Body() createDto: CreateServiceDto, @UserDec("id") userId: string) {
return this.danakServicesService.createService(createDto, userId);
createService(@Body() createDto: CreateServiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.danakServicesService.createService(createDto, userIpAndHeaders);
}
@AuthGuards()
@@ -136,8 +140,8 @@ export class DanakServicesController {
@ApiOperation({ summary: "toggle status of danak service => admin route" })
@HttpCode(HttpStatus.OK)
@Post("toggle-status/:id")
toggleServiceStatus(@Param() paramDto: ParamDto) {
return this.danakServicesService.toggleServiceStatus(paramDto);
toggleServiceStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.danakServicesService.toggleServiceStatus(paramDto, userIpAndHeaders);
}
@AuthGuards()
@@ -190,8 +194,12 @@ export class DanakServicesController {
@AuthGuards()
@PermissionsDec(PermissionEnum.SERVICES)
@Patch(":id")
updateDanakService(@Param() paramDto: ParamDto, @Body() updateDto: UpdateServiceDto, @UserDec("id") userId: string) {
return this.danakServicesService.updateDanakService(paramDto.id, updateDto, userId);
updateDanakService(
@Param() paramDto: ParamDto,
@Body() updateDto: UpdateServiceDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.danakServicesService.updateDanakService(paramDto.id, updateDto, userIpAndHeaders);
}
@ApiOperation({ summary: "delete danak service" })
@@ -1,6 +1,7 @@
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { DataSource, FindOptionsWhere, In, IsNull, LessThan, MoreThan, Not, QueryRunner } from "typeorm";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { ParamDto } from "../../../common/DTO/param.dto";
import { CategoryMessage, CommonMessage, ServiceMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
@@ -48,7 +49,7 @@ export class DanakServicesService {
) {}
/******************************************** */
async createCategory(createDto: CreateDanakServiceCategoryDto) {
async createCategory(createDto: CreateDanakServiceCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
@@ -68,6 +69,31 @@ export class DanakServicesService {
await this.handleOrderCollision(queryRunner, category);
await queryRunner.commitTransaction();
await this.accessLogService.logCreate("DanakServiceCategory", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created category: ${createDto.title}`,
metadata: {
userIp: userIpAndHeaders.ip,
referer: userIpAndHeaders.headers["referer"] || userIpAndHeaders.headers["referrer"],
userAgent: userIpAndHeaders.headers["user-agent"],
acceptLanguage: userIpAndHeaders.headers["accept-language"],
acceptEncoding: userIpAndHeaders.headers["accept-encoding"],
contentType: userIpAndHeaders.headers["content-type"],
origin: userIpAndHeaders.headers["origin"],
host: userIpAndHeaders.headers["host"],
xForwardedFor: userIpAndHeaders.headers["x-forwarded-for"],
xRealIp: userIpAndHeaders.headers["x-real-ip"],
timestamp: new Date().toISOString(),
platform: userIpAndHeaders.headers["sec-ch-ua-platform"],
mobile: userIpAndHeaders.headers["sec-ch-ua-mobile"],
},
});
return {
message: CommonMessage.CREATED,
category,
@@ -112,7 +138,7 @@ export class DanakServicesService {
/******************************************** */
async updateCategory(categoryId: string, updateDto: UpdateDanakServiceCategoryDto) {
async updateCategory(categoryId: string, updateDto: UpdateDanakServiceCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -135,8 +161,35 @@ export class DanakServicesService {
}
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate("DanakServiceCategory", category.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services/categories",
metadata: {
userIp: userIpAndHeaders.ip,
referer: userIpAndHeaders.headers["referer"] || userIpAndHeaders.headers["referrer"],
userAgent: userIpAndHeaders.headers["user-agent"],
acceptLanguage: userIpAndHeaders.headers["accept-language"],
acceptEncoding: userIpAndHeaders.headers["accept-encoding"],
contentType: userIpAndHeaders.headers["content-type"],
origin: userIpAndHeaders.headers["origin"],
host: userIpAndHeaders.headers["host"],
xForwardedFor: userIpAndHeaders.headers["x-forwarded-for"],
xRealIp: userIpAndHeaders.headers["x-real-ip"],
timestamp: new Date().toISOString(),
platform: userIpAndHeaders.headers["sec-ch-ua-platform"],
mobile: userIpAndHeaders.headers["sec-ch-ua-mobile"],
},
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated category: ${updateDto.title}`,
oldValues: JSON.stringify({ ...category }),
newValues: JSON.stringify({ ...updateDto }),
});
return {
message: CommonMessage.UPDATE_SUCCESS,
categoryId: category.id,
};
} catch (error) {
await queryRunner.rollbackTransaction();
@@ -149,11 +202,37 @@ export class DanakServicesService {
/******************************************** */
async deleteCategoryById(categoryId: string) {
async deleteCategoryById(categoryId: string, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.danakServicesCategoryRepository.findOneById(categoryId);
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
if (category.danakServices.length > 0) throw new BadRequestException(CategoryMessage.CATEGORY_HAS_SERVICES);
//
await this.danakServicesCategoryRepository.softDelete(categoryId);
await this.accessLogService.logDelete("DanakServiceCategory", categoryId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted category: ${category.title}`,
metadata: {
userIp: userIpAndHeaders.ip,
referer: userIpAndHeaders.headers["referer"] || userIpAndHeaders.headers["referrer"],
userAgent: userIpAndHeaders.headers["user-agent"],
acceptLanguage: userIpAndHeaders.headers["accept-language"],
acceptEncoding: userIpAndHeaders.headers["accept-encoding"],
contentType: userIpAndHeaders.headers["content-type"],
origin: userIpAndHeaders.headers["origin"],
host: userIpAndHeaders.headers["host"],
xForwardedFor: userIpAndHeaders.headers["x-forwarded-for"],
xRealIp: userIpAndHeaders.headers["x-real-ip"],
timestamp: new Date().toISOString(),
platform: userIpAndHeaders.headers["sec-ch-ua-platform"],
mobile: userIpAndHeaders.headers["sec-ch-ua-mobile"],
},
});
return {
message: CommonMessage.DELETED,
};
@@ -235,7 +314,7 @@ export class DanakServicesService {
/******************************************** */
async toggleCategoryStatus(paramDto: ParamDto) {
async toggleCategoryStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.danakServicesCategoryRepository.findOneById(paramDto.id);
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
//
@@ -243,14 +322,23 @@ export class DanakServicesService {
//
await this.danakServicesCategoryRepository.save(category);
//
await this.accessLogService.logUpdate("DanakServiceCategory", category.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services/categories",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
});
//
return {
categoryId: category.id,
message: CommonMessage.UPDATE_SUCCESS,
isActive: category.isActive,
};
}
/******************************************** */
async toggleServiceStatus(paramDto: ParamDto) {
async toggleServiceStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) {
const service = await this.danakServicesRepository.findServiceById(paramDto.id);
if (!service) throw new BadRequestException(ServiceMessage.SERVICE_NOT_EXIST);
//
@@ -258,6 +346,16 @@ export class DanakServicesService {
//
await this.danakServicesRepository.save(service);
//
await this.accessLogService.logUpdate("DanakService", service.id, UserType.ADMIN, {
userType: UserType.ADMIN,
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated service status: ${service.name}`,
});
//
return {
message: CommonMessage.UPDATE_SUCCESS,
isActive: service.isActive,
@@ -278,7 +376,7 @@ export class DanakServicesService {
};
}
/******************************************** */
async createService(createDto: CreateServiceDto, userId: string) {
async createService(createDto: CreateServiceDto, userIpAndHeaders: IUserIpAndHeaders) {
const category = await this.danakServicesCategoryRepository.findOneById(createDto.categoryId);
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
@@ -295,7 +393,7 @@ export class DanakServicesService {
if (createDto.audios) {
audios = createDto.audios.map((audio) => {
return { audioUrl: audio, createBy: { id: userId } as User };
return { audioUrl: audio, createBy: { id: userIpAndHeaders.userId } as User };
});
}
@@ -303,7 +401,7 @@ export class DanakServicesService {
if (createDto.videos) {
videos = createDto.videos.map((video) => {
return { videoUrl: video, createBy: { id: userId } as User };
return { videoUrl: video, createBy: { id: userIpAndHeaders.userId } as User };
});
}
@@ -311,9 +409,12 @@ export class DanakServicesService {
await this.danakServicesRepository.save(service);
// Log the service creation
await this.accessLogService.logCreate("DanakService", service.id, createDto, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logCreate("DanakService", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created service: ${createDto.name}`,
metadata: {
categoryId: createDto.categoryId,
@@ -326,11 +427,11 @@ export class DanakServicesService {
return {
message: CommonMessage.CREATED,
service,
serviceId: service.id,
};
}
/******************************************** */
async updateDanakService(serviceId: string, updateDto: UpdateServiceDto, userId: string) {
async updateDanakService(serviceId: string, updateDto: UpdateServiceDto, userIpAndHeaders: IUserIpAndHeaders) {
const service = await this.findServiceById(serviceId);
let images: DanakServiceImage[] = [];
@@ -370,7 +471,7 @@ export class DanakServicesService {
return this.danakServicesAudioRepository.create({
audioUrl: audio,
danakService: service,
createBy: { id: userId } as User,
createBy: { id: userIpAndHeaders.userId },
});
});
await this.danakServicesAudioRepository.save(newAudios);
@@ -385,7 +486,7 @@ export class DanakServicesService {
return this.danakServicesVideoRepository.create({
videoUrl: video,
danakService: service,
createBy: { id: userId } as User,
createBy: { id: userIpAndHeaders.userId },
});
});
@@ -407,9 +508,29 @@ export class DanakServicesService {
audios: audios.length ? audios : service.audios,
videos: videos.length ? videos : service.videos,
});
await this.accessLogService.logUpdate("DanakService", serviceId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/danak-services",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated service: ${service.name}`,
metadata: {
categoryId: updateDto.categoryId,
slug: updateDto.slug,
hasImages: updateDto.images && updateDto.images.length > 0,
hasAudios: updateDto.audios && updateDto.audios.length > 0,
hasVideos: updateDto.videos && updateDto.videos.length > 0,
},
oldValues: JSON.stringify({ ...service }),
newValues: JSON.stringify({ ...updateDto }),
});
return {
message: CommonMessage.UPDATE_SUCCESS,
service,
serviceId: service.id,
};
}
/******************************************** */
@@ -15,6 +15,6 @@ export class DanakServicesCategoryRepository extends Repository<DanakServiceCate
}
async findOneById(id: string): Promise<DanakServiceCategory | null> {
return this.findOneBy({ id, deletedAt: IsNull() });
return this.findOne({ where: { id, deletedAt: IsNull() }, relations: { danakServices: true } });
}
}
+17 -13
View File
@@ -10,7 +10,7 @@ import { ApiKeyGuards } from "../../common/decorators/api-key-guard.decorator";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { SkipAuth } from "../../common/decorators/skip-auth.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 { VerifyOtpWithUserId } from "../auth/DTO/verify-otp.dto";
import { PermissionEnum } from "../users/enums/permission.enum";
@@ -23,22 +23,22 @@ export class InvoicesController {
@ApiOperation({ summary: "create an invoice ==> admin route" })
@PermissionsDec(PermissionEnum.INVOICES)
@Post()
createInvoice(@Body() createDto: CreateInvoiceDto, @UserDec("id") userId: string) {
return this.invoiceService.createInvoiceAdmin(createDto, userId);
createInvoice(@Body() createDto: CreateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.invoiceService.createInvoiceAdmin(createDto, userIpAndHeaders);
}
@ApiOperation({ summary: "update an invoice ==> admin route" })
@PermissionsDec(PermissionEnum.INVOICES)
@Patch(":id")
updateInvoice(@Param() paramDto: ParamDto, @Body() updateDto: UpdateInvoiceDto, @UserDec("id") userId: string) {
return this.invoiceService.updateInvoiceAdmin(paramDto.id, updateDto, userId);
updateInvoice(@Param() paramDto: ParamDto, @Body() updateDto: UpdateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.invoiceService.updateInvoiceAdmin(paramDto.id, updateDto, userIpAndHeaders);
}
@ApiOperation({ summary: "delete an invoice ==> admin route" })
@PermissionsDec(PermissionEnum.INVOICES)
@Delete(":id")
deleteInvoice(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.invoiceService.deleteInvoiceAdmin(paramDto.id, userId);
deleteInvoice(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.invoiceService.deleteInvoiceAdmin(paramDto.id, userIpAndHeaders);
}
@ApiOperation({ summary: "get all invoices ==> admin route" })
@@ -62,20 +62,24 @@ export class InvoicesController {
@ApiOperation({ summary: "approve request invoice by user" })
@Post(":id/approve/request")
approveRequestInvoiceByUser(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.invoiceService.approveInvoiceRequest(paramDto.id, userId);
approveRequestInvoiceByUser(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.invoiceService.approveInvoiceRequest(paramDto.id, userIpAndHeaders);
}
@ApiOperation({ summary: "approve invoice by user" })
@Patch(":id/approve")
approveInvoiceByUser(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @Body() verifyOtpDto: VerifyOtpWithUserId) {
return this.invoiceService.approveInvoiceByUser(paramDto.id, userId, verifyOtpDto);
approveInvoiceByUser(
@Param() paramDto: ParamDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
@Body() verifyOtpDto: VerifyOtpWithUserId,
) {
return this.invoiceService.approveInvoiceByUser(paramDto.id, userIpAndHeaders, verifyOtpDto);
}
@ApiOperation({ summary: "pay invoice by user" })
@Post(":id/pay")
async payInvoice(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.invoiceService.payInvoice(paramDto.id, userId);
async payInvoice(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.invoiceService.payInvoice(paramDto.id, userIpAndHeaders.userId, userIpAndHeaders);
}
@ApiOperation({ summary: "apply discount on invoice by user" })
@@ -5,6 +5,7 @@ import dayjs from "dayjs";
import Decimal from "decimal.js";
import { Between, DataSource, In, QueryRunner } from "typeorm";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { AuthMessage, InvoiceMessage, WalletMessage } from "../../../common/enums/message.enum";
import { OperationType } from "../../access-logs/enums/operation-type.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
@@ -56,7 +57,7 @@ export class InvoicesService {
///********************************** */
async createInvoiceAdmin(createDto: CreateInvoiceDto, userId: string) {
async createInvoiceAdmin(createDto: CreateInvoiceDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -83,7 +84,7 @@ export class InvoicesService {
const dueDate = dayjs().add(INVOICE.DUEDATE, "day").toDate();
const invoice = queryRunner.manager.create(this.invoiceRepository.target, {
user: { id: userId },
user: { id: createDto.userId },
totalPrice: totalPrice.add(tax).toNumber(),
originalPrice: totalPrice.add(tax).toNumber(),
items: invoiceItems,
@@ -113,9 +114,12 @@ export class InvoicesService {
await this.scheduleInvoiceJobs(invoice, createDto);
await this.accessLogService.logCreate("Invoice", invoice.id, createDto, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logCreate("Invoice", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/invoices",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created invoice: ${invoice.numericId}`,
metadata: {
items: invoiceItems.map((item) => item.name).join(", "),
@@ -141,7 +145,7 @@ export class InvoicesService {
//********************************** */
async updateInvoiceAdmin(invoiceId: string, updateDto: UpdateInvoiceDto, userId: string) {
async updateInvoiceAdmin(invoiceId: string, updateDto: UpdateInvoiceDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -204,10 +208,15 @@ export class InvoicesService {
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate("Invoice", invoice.id, updateDto, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logUpdate("Invoice", invoice.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/invoices",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated invoice: ${invoice.numericId}`,
oldValues: JSON.stringify({ ...invoice }),
newValues: JSON.stringify({ ...updateDto }),
});
return {
@@ -224,7 +233,7 @@ export class InvoicesService {
//********************************** */
async deleteInvoiceAdmin(invoiceId: string, userId: string) {
async deleteInvoiceAdmin(invoiceId: string, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -245,10 +254,16 @@ export class InvoicesService {
await queryRunner.manager.remove(this.invoiceRepository.target, invoice);
await this.accessLogService.logDelete("Invoice", invoice.id, invoice, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logDelete("Invoice", invoice.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/invoices",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted invoice: ${invoice.numericId}`,
metadata: {
items: invoice.items.map((item) => item.name).join(", "),
},
});
await queryRunner.commitTransaction();
@@ -326,16 +341,16 @@ export class InvoicesService {
//********************************** */
async approveInvoiceRequest(invoiceId: string, userId: string) {
async approveInvoiceRequest(invoiceId: string, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
await queryRunner.connect();
await queryRunner.startTransaction();
const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner);
const user = await this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner);
const invoice = await this.validateInvoiceForApproval(invoiceId, userId, queryRunner);
const invoice = await this.validateInvoiceForApproval(invoiceId, userIpAndHeaders.userId, queryRunner);
const existCode = await this.otpService.checkExistOtp(user.phone, "INVOICE_VERIFY");
if (existCode) {
@@ -363,8 +378,11 @@ export class InvoicesService {
invoiceId,
`user approved invoice request: ${invoice.numericId}`,
{
user: { id: userId },
user: { id: userIpAndHeaders.userId },
userType: UserType.USER,
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
},
);
@@ -384,7 +402,7 @@ export class InvoicesService {
//********************************** */
async approveInvoiceByUser(invoiceId: string, userId: string, verifyOtpDto: VerifyOtpWithUserId) {
async approveInvoiceByUser(invoiceId: string, userIpAndHeaders: IUserIpAndHeaders, verifyOtpDto: VerifyOtpWithUserId) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -392,17 +410,17 @@ export class InvoicesService {
await queryRunner.startTransaction();
const { code } = verifyOtpDto;
const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner);
const user = await this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner);
const isValid = await this.otpService.verifyOtp(user.phone, code, "INVOICE_VERIFY");
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
const invoice = await this.validateInvoiceForApproval(invoiceId, userId, queryRunner);
const invoice = await this.validateInvoiceForApproval(invoiceId, userIpAndHeaders.userId, queryRunner);
invoice.status = InvoiceStatus.WAIT_PAYMENT;
await queryRunner.manager.save(Invoice, invoice);
await this.notificationQueue.addApprovedInvoiceNotification(userId, {
await this.notificationQueue.addApprovedInvoiceNotification(userIpAndHeaders.userId, {
invoiceId: invoice.numericId.toString(),
dueDate: invoice.dueDate,
createDate: invoice.createdAt,
@@ -420,7 +438,10 @@ export class InvoicesService {
invoiceId,
`user approved invoice by user: ${invoice.numericId}`,
{
user: { id: userId },
user: { id: userIpAndHeaders.userId },
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
userType: UserType.USER,
},
);
@@ -681,7 +702,12 @@ export class InvoicesService {
//*********************************** */
async payInvoice(invoiceId: string, userId: string, queryRunner = this.dataSource.createQueryRunner()) {
async payInvoice(
invoiceId: string,
userId: string,
userIpAndHeaders?: IUserIpAndHeaders,
queryRunner = this.dataSource.createQueryRunner(),
) {
let transactionStarted = false;
try {
@@ -784,10 +810,15 @@ export class InvoicesService {
paidAt: invoice.paidAt,
});
await this.accessLogService.logUserAction(OperationType.UPDATE, "Invoice", invoiceId, `user paid invoice: ${invoice.numericId}`, {
user: { id: userId },
userType: UserType.USER,
});
if (userIpAndHeaders) {
await this.accessLogService.logUserAction(OperationType.UPDATE, "Invoice", invoiceId, `user paid invoice: ${invoice.numericId}`, {
user: { id: userIpAndHeaders.userId },
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
requestId: userIpAndHeaders.headers["x-request-id"],
userType: UserType.USER,
});
}
if (transactionStarted) await queryRunner.commitTransaction();
@@ -480,6 +480,6 @@ export class InvoiceProcessor extends WorkerProcessor {
userSupportPlan.status = UserSupportPlanStatus.ACTIVE;
await queryRunner.manager.save(UserSupportPlan, userSupportPlan);
await this.invoicesService.payInvoice(newInvoice.id, invoice.user.id, queryRunner);
await this.invoicesService.payInvoice(newInvoice.id, invoice.user.id, undefined, queryRunner);
}
}
@@ -284,7 +284,7 @@ export class SubscriptionsService {
await this.addProvisioningJob(userSubscription, plan, user);
if (isFree) {
await this.invoicesService.payInvoice(invoice.id, user.id, queryRunner);
await this.invoicesService.payInvoice(invoice.id, user.id, undefined, queryRunner);
}
await queryRunner.commitTransaction();
@@ -156,7 +156,7 @@ export class SupportPlansService {
if (supportPlan.isFree) {
userSupportPlan.status = UserSupportPlanStatus.ACTIVE;
await queryRunner.manager.save(this.userSupportPlanRepo.target, userSupportPlan);
await this.invoicesService.payInvoice(invoice.id, user.id, queryRunner);
await this.invoicesService.payInvoice(invoice.id, user.id, undefined, queryRunner);
}
await queryRunner.commitTransaction();
@@ -256,7 +256,7 @@ export class SupportPlansService {
if (newSupportPlan.isFree) {
currentUserSupportPlan.status = UserSupportPlanStatus.ACTIVE;
await queryRunner.manager.save(this.userSupportPlanRepo.target, currentUserSupportPlan);
await this.invoicesService.payInvoice(invoice.id, user.id, queryRunner);
await this.invoicesService.payInvoice(invoice.id, user.id, undefined, queryRunner);
}
await queryRunner.commitTransaction();
+35 -26
View File
@@ -2,6 +2,7 @@ import { BadRequestException, Injectable } from "@nestjs/common";
import slugify from "slugify";
import { DataSource, In, Not, QueryRunner } from "typeorm";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { AdminMessage, AdsMessage, UserMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
import { AccessLogService } from "../../access-logs/providers/access-log.service";
@@ -28,7 +29,7 @@ export class AdminsService {
private readonly accessLogService: AccessLogService,
) {}
async createAdmin(createDto: CreateAdminDto, userId: string) {
async createAdmin(createDto: CreateAdminDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -65,9 +66,12 @@ export class AdminsService {
await queryRunner.commitTransaction();
// Log the admin creation
await this.accessLogService.logCreate("User", adminUser.id, createDto, {
user: { id: userId },
userType: UserType.ADMIN,
await this.accessLogService.logCreate("User", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/admins",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created new admin: ${createDto.email}`,
metadata: {
roleId: createDto.roleId,
@@ -90,7 +94,7 @@ export class AdminsService {
}
/************************************************************ */
async updateAdmin(id: string, updateDto: UpdateAdminDto, userId: string) {
async updateAdmin(id: string, updateDto: UpdateAdminDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -136,18 +140,20 @@ export class AdminsService {
await queryRunner.manager.save(this.userRepository.target, { ...admin, ...updateDto, password: admin.password });
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate(
"User",
admin.id,
{ ...admin },
{ ...admin },
{
user: { id: userId },
userType: UserType.ADMIN,
actionDescription: `Admin updated admin: ${admin.email}`,
await this.accessLogService.logUpdate("User", admin.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/admins",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
userType: UserType.ADMIN,
actionDescription: `Admin updated admin: ${admin.email}`,
metadata: {
roleId: updateDto.roleId,
},
);
oldValues: JSON.stringify({ ...admin }),
newValues: JSON.stringify({ ...updateDto }),
});
return {
message: AdminMessage.ADMIN_UPDATED,
@@ -161,22 +167,25 @@ export class AdminsService {
}
/************************************************************ */
async deleteAdmin(id: string, userId: string) {
async deleteAdmin(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const admin = await this.userRepository.findOne({ where: { id, roles: { isAdmin: true } } });
if (!admin) throw new BadRequestException(AdminMessage.ADMIN_NOT_FOUND);
await this.userRepository.softDelete(id);
await this.accessLogService.logDelete(
"User",
admin.id,
{ ...admin },
{
user: { id: userId },
userType: UserType.ADMIN,
actionDescription: `Admin deleted admin: ${admin.email}`,
await this.accessLogService.logDelete("User", admin.id, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/admins",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted admin: ${admin.email}`,
metadata: {
roleId: admin.roles[0].id,
email: admin.email,
phone: admin.phone,
},
);
});
return {
message: AdminMessage.ADMIN_DELETED,
@@ -1,6 +1,7 @@
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { DataSource, Not } from "typeorm";
import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator";
import { CommonMessage, UserMessage } from "../../../common/enums/message.enum";
import { UserType } from "../../access-logs/enums/user-type.enum";
import { AccessLogService } from "../../access-logs/providers/access-log.service";
@@ -37,7 +38,7 @@ export class CustomersService {
private readonly accessLogService: AccessLogService,
) {}
async createCustomer(createDto: CreateCustomerDto) {
async createCustomer(createDto: CreateCustomerDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
@@ -73,11 +74,14 @@ export class CustomersService {
await queryRunner.commitTransaction();
// Log the customer creation
await this.accessLogService.logCreate("User", user.id, createDto, {
userType: UserType.ADMIN,
await this.accessLogService.logCreate("User", UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/customers",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created customer: ${createDto.email}`,
metadata: {
createdBy: "admin",
email: createDto.email,
phone: createDto.phone,
},
@@ -93,7 +97,7 @@ export class CustomersService {
}
}
//************************************************ */
async updateCustomer(customerId: string, updateDto: UpdateCustomerDto) {
async updateCustomer(customerId: string, updateDto: UpdateCustomerDto, userIpAndHeaders: IUserIpAndHeaders) {
const queryRunner = this.dataSource.createQueryRunner();
try {
await queryRunner.connect();
@@ -241,6 +245,21 @@ export class CustomersService {
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate("User", customerId, UserType.ADMIN, {
user: { id: userIpAndHeaders.userId },
endpoint: "/customers",
requestId: userIpAndHeaders.headers["x-request-id"],
ipAddress: userIpAndHeaders.ip,
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin updated customer: ${customerId}`,
metadata: {
email: email,
phone: phone,
},
oldValues: JSON.stringify({ ...user }),
newValues: JSON.stringify({ ...updateDto }),
});
return {
message: CommonMessage.UPDATE_SUCCESS,
};
+1 -14
View File
@@ -7,8 +7,6 @@ import slugify from "slugify";
import { In, Not, QueryRunner } from "typeorm";
import { AuthMessage, CommonMessage, 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 { AddressService } from "../../address/providers/address.service";
import { RequestOtpDto } from "../../auth/DTO/request-otp.dto";
import { VerifyOtpDto } from "../../auth/DTO/verify-otp.dto";
@@ -49,7 +47,6 @@ export class UsersService {
private readonly smsService: SmsService,
private readonly cacheService: CacheService,
private readonly referralsService: ReferralsService,
private readonly accessLogService: AccessLogService,
) {}
/************************************************************ */
@@ -117,8 +114,6 @@ export class UsersService {
const user = await this.userRepository.findOneBy({ id: userId });
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
const oldUserData = { ...user };
if (updateProfileDto.cityId) {
const { city: existCity } = await this.addressService.getCity(+updateProfileDto.cityId);
if (!existCity) throw new BadRequestException(UserMessage.CITY_NOT_FOUND);
@@ -132,15 +127,7 @@ export class UsersService {
if (existUserName) throw new BadRequestException(UserMessage.USERNAME_EXIST);
}
const updatedUser = await this.userRepository.save({ ...user, ...updateProfileDto, city });
// Log the profile update
await this.accessLogService.logUpdate("User", userId, oldUserData, updatedUser, {
user: { id: userId },
userType: UserType.USER,
actionDescription: `User updated profile: ${user.email}`,
metadata: { updatedFields: Object.keys(updateProfileDto) },
});
await this.userRepository.save({ ...user, ...updateProfileDto, city });
return {
message: CommonMessage.UPDATE_SUCCESS,
+15 -11
View File
@@ -25,7 +25,7 @@ import { AdminRoute } from "../../common/decorators/admin.decorator";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { SkipAuth } from "../../common/decorators/skip-auth.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 { RequestOtpDto } from "../auth/DTO/request-otp.dto";
import { VerifyOtpDto } from "../auth/DTO/verify-otp.dto";
@@ -136,14 +136,18 @@ export class UsersController {
@ApiOperation({ summary: "Create customer" })
@HttpCode(HttpStatus.CREATED)
@Post("customers")
createCustomer(@Body() createDto: CreateCustomerDto) {
return this.customersService.createCustomer(createDto);
createCustomer(@Body() createDto: CreateCustomerDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.customersService.createCustomer(createDto, userIpAndHeaders);
}
@ApiOperation({ summary: "Update customer" })
@Patch("customers/:id")
updateCustomer(@Param() paramDto: ParamDto, @Body() updateDto: UpdateCustomerDto) {
return this.customersService.updateCustomer(paramDto.id, updateDto);
updateCustomer(
@Param() paramDto: ParamDto,
@Body() updateDto: UpdateCustomerDto,
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
) {
return this.customersService.updateCustomer(paramDto.id, updateDto, userIpAndHeaders);
}
@ApiOperation({ summary: "get customers ==> admin route" })
@@ -180,22 +184,22 @@ export class UsersController {
@ApiOperation({ summary: "update admin ==> admin route" })
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
@Patch("admins/:id")
updateAdmin(@Param() paramDto: ParamDto, @Body() updateDto: UpdateAdminDto, @UserDec("id") userId: string) {
return this.adminsService.updateAdmin(paramDto.id, updateDto, userId);
updateAdmin(@Param() paramDto: ParamDto, @Body() updateDto: UpdateAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.adminsService.updateAdmin(paramDto.id, updateDto, userIpAndHeaders);
}
@ApiOperation({ summary: "delete admin ==> admin route" })
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
@Delete("admins/:id")
deleteAdmin(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.adminsService.deleteAdmin(paramDto.id, userId);
deleteAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.adminsService.deleteAdmin(paramDto.id, userIpAndHeaders);
}
@ApiOperation({ summary: "create admin ==> admin route" })
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
@Post("admins")
createAdmin(@Body() createDto: CreateAdminDto, @UserDec("id") userId: string) {
return this.adminsService.createAdmin(createDto, userId);
createAdmin(@Body() createDto: CreateAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
return this.adminsService.createAdmin(createDto, userIpAndHeaders);
}
@ApiOperation({ summary: "get admin permissions" })