Files
HIPCTF2/backend/src/modules/blog/admin-blog.service.ts
T
2026-07-23 10:17:53 +00:00

83 lines
2.7 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { v4 as uuid } from 'uuid';
import { BlogPostEntity } from '../../database/entities/blog-post.entity';
import { ApiError } from '../../common/errors/api-error';
import { AdminBlogPost, CreatePostPayload, UpdatePostPayload } from './dto/blog.dto';
@Injectable()
export class AdminBlogService {
constructor(
@InjectRepository(BlogPostEntity)
private readonly posts: Repository<BlogPostEntity>,
) {}
async listAll(): Promise<AdminBlogPost[]> {
const rows = await this.posts.find({ order: { updatedAt: 'DESC' } });
return rows.map((r) => this.toView(r));
}
async getById(id: string): Promise<AdminBlogPost> {
const row = await this.posts.findOne({ where: { id } });
if (!row) throw ApiError.notFound('Post not found');
return this.toView(row);
}
async create(payload: CreatePostPayload): Promise<AdminBlogPost> {
const status = payload.status ?? 'draft';
const row = this.posts.create({
id: uuid(),
title: payload.title.trim(),
bodyMd: payload.bodyMd ?? '',
status,
publishedAt: status === 'published' ? new Date().toISOString() : null,
});
await this.posts.save(row);
return this.toView(row);
}
async update(id: string, payload: UpdatePostPayload): Promise<AdminBlogPost> {
const row = await this.posts.findOne({ where: { id } });
if (!row) throw ApiError.notFound('Post not found');
if (payload.title !== undefined) row.title = payload.title.trim();
if (payload.bodyMd !== undefined) row.bodyMd = payload.bodyMd;
if (payload.status !== undefined) {
if (payload.status === 'published') {
row.status = 'published';
// Only stamp publishedAt if this post has never been published before;
// preserve the original publishedAt for re-publications.
if (!row.publishedAt) {
row.publishedAt = new Date().toISOString();
}
} else if (payload.status === 'draft') {
row.status = 'draft';
// preserve publishedAt so a later re-publish keeps the original publication timestamp.
}
}
await this.posts.save(row);
return this.toView(row);
}
async remove(id: string): Promise<void> {
const row = await this.posts.findOne({ where: { id } });
if (!row) throw ApiError.notFound('Post not found');
await this.posts.delete({ id });
}
private toView(r: BlogPostEntity): AdminBlogPost {
return {
id: r.id,
title: r.title,
bodyMd: r.bodyMd ?? '',
status: r.status,
createdAt: r.createdAt,
updatedAt: r.updatedAt,
publishedAt: r.publishedAt ?? null,
};
}
}