placebo.mk/backend/src/modules/articles.service.ts
2026-02-03 21:18:25 +01:00

143 lines
3.7 KiB
TypeScript

import { Injectable, NotFoundException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Article, ArticleStatus } from './entities';
import {
CreateArticleDto,
UpdateArticleDto,
FindArticlesDto,
} from './articles.dto';
@Injectable()
export class ArticlesService {
private readonly logger = new Logger(ArticlesService.name);
constructor(
@InjectRepository(Article)
private readonly articleRepository: Repository<Article>,
) {}
async create(dto: CreateArticleDto): Promise<Article> {
const article = this.articleRepository.create({
...dto,
status: dto.status || ArticleStatus.DRAFT,
});
return await this.articleRepository.save(article);
}
async findAll(
dto: FindArticlesDto,
): Promise<{ data: Article[]; total: number }> {
const {
category,
author,
tag,
status = ArticleStatus.PUBLISHED,
search,
page = 1,
limit = 10,
} = dto;
const queryBuilder = this.articleRepository
.createQueryBuilder('article')
.leftJoinAndSelect('article.author', 'author')
.leftJoinAndSelect('article.category', 'category')
.where('article.status = :status', { status });
if (category) {
queryBuilder.andWhere('category.slug = :category', { category });
}
if (author) {
queryBuilder.andWhere('author.slug = :author', { author });
}
if (tag) {
queryBuilder.andWhere(':tag = ANY(article.tags)', { tag });
}
if (search) {
queryBuilder.andWhere(
'(article.title ILIKE :search OR article.content ILIKE :search)',
{ search: `%${search}%` },
);
}
const [data, total] = await queryBuilder
.orderBy('article.createdAt', 'DESC')
.skip((page - 1) * limit)
.take(limit)
.getManyAndCount();
return { data, total };
}
async findOne(id: string): Promise<Article> {
const article = await this.articleRepository.findOne({
where: { id },
relations: ['author', 'category'],
});
if (!article) {
throw new NotFoundException(`Article with ID ${id} not found`);
}
return article;
}
async findBySlug(slug: string): Promise<Article> {
const article = await this.articleRepository.findOne({
where: { slug },
relations: ['author', 'category'],
});
if (!article) {
throw new NotFoundException(`Article with slug ${slug} not found`);
}
return article;
}
async update(id: string, dto: UpdateArticleDto): Promise<Article> {
const article = await this.findOne(id);
Object.assign(article, dto);
return await this.articleRepository.save(article);
}
async remove(id: string): Promise<void> {
const article = await this.findOne(id);
await this.articleRepository.remove(article);
}
async syncFromStrapi(
strapiId: string,
data: Partial<CreateArticleDto>,
): Promise<Article> {
let article = await this.articleRepository.findOne({ where: { strapiId } });
if (!article) {
article = this.articleRepository.create({
strapiId,
...data,
status: data.status || ArticleStatus.DRAFT,
});
} else {
Object.assign(article, data);
}
return await this.articleRepository.save(article);
}
async removeByStrapiId(strapiId: string): Promise<void> {
const article = await this.articleRepository.findOne({ where: { strapiId } });
if (!article) {
this.logger.warn(`Article with strapiId ${strapiId} not found`);
return;
}
await this.articleRepository.remove(article);
this.logger.log(`Successfully deleted article with strapiId: ${strapiId}`);
}
}