AI Implementation feature(869): Admin Area Blog and Blog Page (#58)

This commit was merged in pull request #58.
This commit is contained in:
2026-07-23 10:17:55 +00:00
parent d468cca766
commit 294873240b
32 changed files with 1798 additions and 117 deletions
+71
View File
@@ -0,0 +1,71 @@
---
type: api
title: Blog Endpoints
description: Public blog listing and administrator-only post management endpoints.
tags: [api, blog, admin, markdown]
timestamp: 2026-07-23T10:12:24Z
---
# Endpoints
| Method | Path | Access | Behavior |
|---|---|---|---|
| `GET` | `/api/v1/blog/posts` | Public | Returns published posts only, newest publication first. |
| `GET` | `/api/v1/admin/blog/posts` | Admin | Returns drafts and published posts, ordered by most recently updated. |
| `GET` | `/api/v1/admin/blog/posts/:id` | Admin | Returns one post or `404 NOT_FOUND`. |
| `POST` | `/api/v1/admin/blog/posts` | Admin + CSRF | Creates a draft or published post; defaults to draft. |
| `PUT` | `/api/v1/admin/blog/posts/:id` | Admin + CSRF | Partially updates title, Markdown body, or status. |
| `DELETE` | `/api/v1/admin/blog/posts/:id` | Admin + CSRF | Deletes a post and returns `204`; missing posts return `404 NOT_FOUND`. |
The public route is marked `@Public()`. Admin routes require a Bearer JWT with the administrator role, and unsafe requests use the standard CSRF cookie/header contract from [REST API Overview](/api/rest-overview.md).
# Schema
## Public list response
```json
{
"posts": [
{
"id": "post-id",
"title": "Event update",
"publishedAt": "2026-07-23T10:00:00.000Z",
"bodyMd": "# Welcome"
}
]
}
```
Drafts and administrative fields such as `status`, `createdAt`, and `updatedAt` are excluded.
## Admin post
| Field | Type | Description |
|---|---|---|
| `id` | string | Server-generated UUID. |
| `title` | string | Trimmed title, required, maximum 200 characters. |
| `bodyMd` | string | Markdown body, maximum 200,000 characters. |
| `status` | `draft` or `published` | Visibility state. |
| `createdAt` | ISO 8601 string | Creation time. |
| `updatedAt` | ISO 8601 string | Last persistence update. |
| `publishedAt` | ISO 8601 string or null | First publication time. |
`POST` accepts `title`, optional `bodyMd`, and optional `status`. `PUT` accepts any subset of those fields. Validation failures use HTTP 400 with code `VALIDATION_FAILED`.
# Publication behavior
* Creating with `status: "published"` sets `publishedAt`; omitted status creates a draft.
* Publishing a never-published draft sets `publishedAt` once.
* Editing a published post does not change `publishedAt`.
* Moving a post back to draft preserves `publishedAt`; republishing retains the original timestamp.
* Only rows currently marked `published` are returned by the public endpoint.
# Wiring
`BlogModule` registers `BlogController`/`BlogService` for the public list and `AdminBlogController`/`AdminBlogService` for management. Both services access `BlogPostEntity` through `TypeOrmModule.forFeature([BlogPostEntity])`. The Angular `BlogApiService` calls these routes for the admin page, authenticated Blog page, and landing-page post list.
# See also
- [Blog Publishing and Reading](/guides/blog.md)
- [Blog Post Table](/database/blog-posts.md)
- [REST API Overview](/api/rest-overview.md)