350 lines
12 KiB
TypeScript
Executable File
350 lines
12 KiB
TypeScript
Executable File
import { BadRequestException, Injectable } from "@nestjs/common";
|
|
import { Not } from "typeorm";
|
|
|
|
import { AdminMessage, BlogMessage, CommonMessage } from "../../../common/enums/message.enum";
|
|
import { CategoryListSearchQueryDto } from "../../danak-services/DTO/category-search-query.dto";
|
|
import { UsersService } from "../../users/providers/users.service";
|
|
import { BlogCommentQueryDto } from "../DTO/blog-comment-query.dto";
|
|
import { CreateBlogDto } from "../DTO/create-blog.dto";
|
|
import { CreateBlogCategoryDto } from "../DTO/create-category.dto";
|
|
import { CreateCommentDto } from "../DTO/create-comment.dto";
|
|
import { BlogListSearchQueryDto, BlogSearchQueryDto } from "../DTO/search-blog-query.dto";
|
|
import { UpdateBlogDto } from "../DTO/update-blog.dto";
|
|
import { UpdateBlogCategoryDto } from "../DTO/update-category.dto";
|
|
import { UpdateCommentStatusDto } from "../DTO/update-comment-status.dto";
|
|
import { CommentStatus } from "../enums/comment-status.enum";
|
|
import { BlogCategoriesRepository } from "../repositories/blog-categories.repository";
|
|
import { BlogCommentsRepository } from "../repositories/blog-comments.repository";
|
|
import { BlogsRepository } from "../repositories/blogs.repository";
|
|
@Injectable()
|
|
export class BlogsService {
|
|
constructor(
|
|
private readonly blogsRepository: BlogsRepository,
|
|
private readonly blogCommentsRepository: BlogCommentsRepository,
|
|
private readonly blogCategoriesRepository: BlogCategoriesRepository,
|
|
private readonly usersService: UsersService,
|
|
) {}
|
|
|
|
//*********************************** */
|
|
async createCategory(createCategoryDto: CreateBlogCategoryDto) {
|
|
const existingCategory = await this.blogCategoriesRepository.findOneBy({ title: createCategoryDto.title });
|
|
if (existingCategory) throw new BadRequestException(BlogMessage.CATEGORY_ALREADY_EXISTS);
|
|
|
|
const category = await this.blogCategoriesRepository.createCategory(createCategoryDto);
|
|
|
|
return {
|
|
message: CommonMessage.CREATED,
|
|
category,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async updateCategory(id: string, updateCategoryDto: UpdateBlogCategoryDto) {
|
|
const category = await this.findCategoryById(id);
|
|
|
|
if (updateCategoryDto.title) {
|
|
await this.checkCategoryExists(updateCategoryDto.title, id);
|
|
}
|
|
|
|
await this.blogCategoriesRepository.save({ ...category, ...updateCategoryDto });
|
|
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
category,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async deleteCategory(id: string, userId: string) {
|
|
const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
|
|
if (!isSuperAdmin) throw new BadRequestException(AdminMessage.NOT_ALLOWED);
|
|
|
|
const category = await this.blogCategoriesRepository.findOneBy({ id });
|
|
if (!category) throw new BadRequestException(BlogMessage.CATEGORY_NOT_FOUND);
|
|
|
|
const blogs = await this.blogsRepository.find({ where: { category: { id } }, take: 2 });
|
|
if (blogs.length > 0) throw new BadRequestException(BlogMessage.CATEGORY_HAS_BLOGS);
|
|
|
|
await this.blogCategoriesRepository.remove(category);
|
|
return {
|
|
message: CommonMessage.DELETED,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async toggleCategoryStatus(id: string) {
|
|
const category = await this.findCategoryById(id);
|
|
|
|
//
|
|
category.isActive = !category.isActive;
|
|
await this.blogCategoriesRepository.save(category);
|
|
|
|
//
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
category,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async getCategory(id: string) {
|
|
const category = await this.findCategoryById(id);
|
|
return { category };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getCategoriesUserSide() {
|
|
const categories = await this.blogCategoriesRepository.getCategoriesUserSide();
|
|
return { categories };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getCategories(queryDto: CategoryListSearchQueryDto) {
|
|
const [categories, count] = await this.blogCategoriesRepository.getCategories(queryDto);
|
|
return { categories, count, paginate: true };
|
|
}
|
|
//*********************************** */
|
|
|
|
async createBlog(createBlogDto: CreateBlogDto, authorId: string) {
|
|
const existingBlog = await this.blogsRepository.findOne({ where: { title: createBlogDto.title } });
|
|
if (existingBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS);
|
|
|
|
const category = await this.findCategoryById(createBlogDto.categoryId);
|
|
|
|
const slug = this.generateSlug(createBlogDto.slug);
|
|
|
|
const blog = this.blogsRepository.create({
|
|
...createBlogDto,
|
|
author: { id: authorId },
|
|
category,
|
|
slug,
|
|
});
|
|
|
|
await this.blogsRepository.save(blog);
|
|
|
|
return {
|
|
message: CommonMessage.CREATED,
|
|
blog,
|
|
};
|
|
}
|
|
|
|
//*********************************** */
|
|
|
|
async updateBlog(id: string, updateBlogDto: UpdateBlogDto) {
|
|
const blog = await this.findBlogById(id);
|
|
|
|
if (updateBlogDto.title) {
|
|
const existBlog = await this.blogsRepository.findOne({ where: { title: updateBlogDto.title, id: Not(id) } });
|
|
if (existBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS);
|
|
}
|
|
|
|
if (updateBlogDto.categoryId) {
|
|
const category = await this.findCategoryById(updateBlogDto.categoryId);
|
|
blog.category = category;
|
|
}
|
|
|
|
if (updateBlogDto.slug) {
|
|
const existBlog = await this.blogsRepository.findOne({ where: { slug: updateBlogDto.slug, id: Not(id) } });
|
|
if (existBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS);
|
|
}
|
|
|
|
await this.blogsRepository.save({
|
|
...blog,
|
|
...updateBlogDto,
|
|
slug: this.generateSlug(updateBlogDto.slug ?? blog.slug),
|
|
});
|
|
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
blog,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async deleteBlog(id: string, userId: string) {
|
|
const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
|
|
if (!isSuperAdmin) throw new BadRequestException(AdminMessage.NOT_ALLOWED);
|
|
|
|
const blog = await this.blogsRepository.findOneBy({ id });
|
|
if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND);
|
|
|
|
await this.blogsRepository.softDelete({ id });
|
|
return {
|
|
message: CommonMessage.DELETED,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async getBlogById(id: string, isAdmin: boolean = false) {
|
|
const blog = await this.findBlogById(id);
|
|
|
|
if (isAdmin) return { blog };
|
|
|
|
blog.viewCount += 1;
|
|
await this.blogsRepository.save(blog);
|
|
const comments = await this.blogCommentsRepository.findBlogCommentsByBlogId(id);
|
|
|
|
return { blog, comments };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getBlogBySlug(slug: string, isAdmin: boolean = false) {
|
|
const blog = await this.blogsRepository.findOneBySlug(slug);
|
|
if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND);
|
|
|
|
if (isAdmin) return { blog };
|
|
|
|
blog.viewCount += 1;
|
|
await this.blogsRepository.save(blog);
|
|
const comments = await this.blogCommentsRepository.findBlogCommentsByBlogId(blog.id);
|
|
|
|
return { blog, comments };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getBlogsPublic(queryDto: BlogSearchQueryDto) {
|
|
const [blogs, count] = await this.blogsRepository.getBlogsForUserSide(queryDto);
|
|
|
|
//;
|
|
return {
|
|
blogs,
|
|
count,
|
|
paginate: true,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async getCombinedBlogs() {
|
|
const [pinnedBlogs, mostVisitedBlogs] = await Promise.all([
|
|
this.blogsRepository.getPinnedBlogs(),
|
|
this.blogsRepository.getMostVisitedBlogs(),
|
|
]);
|
|
return { pinnedBlogs, mostVisitedBlogs };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getBlogList(queryDto: BlogListSearchQueryDto) {
|
|
const [blogs, count] = await this.blogsRepository.getBlogListForAdmin(queryDto);
|
|
return { blogs, count, paginate: true };
|
|
}
|
|
//*********************************** */
|
|
|
|
async createComment(blogId: string, createCommentDto: CreateCommentDto, userId: string) {
|
|
const blog = await this.findBlogById(blogId);
|
|
|
|
const { user } = await this.usersService.findOneById(userId);
|
|
|
|
const comment = this.blogCommentsRepository.create({
|
|
...createCommentDto,
|
|
blog,
|
|
user,
|
|
status: CommentStatus.PENDING,
|
|
});
|
|
|
|
await this.blogCommentsRepository.save(comment);
|
|
|
|
return {
|
|
message: CommonMessage.CREATED,
|
|
comment,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async updateCommentStatus(commentId: string, updateCommentStatusDto: UpdateCommentStatusDto) {
|
|
const comment = await this.blogCommentsRepository.findOneById(commentId);
|
|
if (!comment) throw new BadRequestException(BlogMessage.COMMENT_NOT_FOUND);
|
|
|
|
comment.status = updateCommentStatusDto.status;
|
|
await this.blogCommentsRepository.save(comment);
|
|
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
comment,
|
|
};
|
|
}
|
|
//*********************************** */
|
|
|
|
async getBlogCommentsForAdmin(blogId: string) {
|
|
const comments = await this.blogCommentsRepository.findBlogCommentsForAdmin(blogId);
|
|
return { comments };
|
|
}
|
|
|
|
//*********************************** */
|
|
|
|
async getAllCommentForAdmin(queryDto: BlogCommentQueryDto) {
|
|
const [comments, count] = await this.blogCommentsRepository.getAllCommentsForAdmin(queryDto);
|
|
return { comments, count, paginate: true };
|
|
}
|
|
|
|
//*********************************** */
|
|
private generateSlug(slug: string): string {
|
|
if (!slug) return "";
|
|
|
|
// Replace spaces and special characters with hyphens, preserve Farsi characters
|
|
return slug
|
|
.trim()
|
|
.replace(/[&\\#,+()$~%.'":*?<>{}]/g, "") // Remove special characters
|
|
.replace(/\s+/g, "-") // Replace spaces with hyphens
|
|
.replace(/-+/g, "-") // Replace multiple hyphens with single hyphen
|
|
.trim();
|
|
}
|
|
|
|
//*********************************** */
|
|
private async checkCategoryExists(title: string, id?: string) {
|
|
const existingCategory = await this.blogCategoriesRepository.findOneBy({ title, ...(id && { id: Not(id) }) });
|
|
if (existingCategory) throw new BadRequestException(BlogMessage.CATEGORY_ALREADY_EXISTS);
|
|
}
|
|
|
|
//*********************************** */
|
|
private async findCategoryById(id: string) {
|
|
const category = await this.blogCategoriesRepository.findOneById(id);
|
|
if (!category) throw new BadRequestException(BlogMessage.CATEGORY_NOT_FOUND);
|
|
return category;
|
|
}
|
|
|
|
//*********************************** */
|
|
async incrementViewCount(id: string) {
|
|
const blog = await this.findBlogById(id);
|
|
blog.viewCount += 1;
|
|
await this.blogsRepository.save(blog);
|
|
return { message: "View count incremented successfully" };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getMostVisitedBlogs() {
|
|
const blogs = await this.blogsRepository.getMostVisitedBlogs();
|
|
return { blogs };
|
|
}
|
|
//*********************************** */
|
|
|
|
async getPinnedBlogs() {
|
|
const blogs = await this.blogsRepository.getPinnedBlogs();
|
|
return { blogs };
|
|
}
|
|
//*********************************** */
|
|
|
|
async togglePinnedStatus(id: string) {
|
|
const blog = await this.findBlogById(id);
|
|
blog.isPinned = !blog.isPinned;
|
|
await this.blogsRepository.save(blog);
|
|
return { message: CommonMessage.UPDATE_SUCCESS, blog };
|
|
}
|
|
//*********************************** */
|
|
|
|
async toggleBlogStatus(id: string) {
|
|
const blog = await this.findBlogById(id);
|
|
blog.isActive = !blog.isActive;
|
|
|
|
//
|
|
await this.blogsRepository.save(blog);
|
|
return { message: CommonMessage.UPDATE_SUCCESS, blog };
|
|
}
|
|
|
|
//*********************************** */
|
|
private async findBlogById(id: string) {
|
|
const blog = await this.blogsRepository.findOneById(id);
|
|
if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND);
|
|
return blog;
|
|
}
|
|
}
|