83 lines
2.7 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
}
|