5.4 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | |||||
|---|---|---|---|---|---|---|---|---|---|
| guide | Blog Publishing and Reading | How administrators create, edit, publish, and delete Markdown posts and how users read published posts. |
|
2026-07-23T10:36:45Z |
User views
Published posts are available in two places:
| View | Access | Expected content |
|---|---|---|
/login |
Unauthenticated users | The landing-page blog section lists published posts below the Login button. |
/blog |
Signed-in users | The Blog page loads and lists all published posts. |
Both views use BlogPresenterComponent to show the post title, publication date, and sanitized Markdown body. Draft posts never appear in either public list. The /blog page shows Loading blog posts… while loading, No announcements yet. when no posts are published, and an error message if loading fails.
Admin workflow
- Sign in as an administrator.
- Open Admin, then select Blog, or navigate directly to
/admin/blog. - Confirm the page displays a table with Title, Status, Created, Updated, Published, and Actions columns. If no posts exist, it displays
No posts yet.. - Click + New post.
- Enter a required title and an optional Markdown body. The live preview updates beside the editor.
- Click Save draft to keep the post hidden from users, or Publish to expose it in
/blogand on/login. - Confirm a success banner displays
Draft saved.orPost published., and the refreshed table contains the post with the correct status badge.
Editing and deleting
- Click the pencil action on a row to open the prefilled Edit post modal. It shows created, updated, and publication timestamps where available.
- Saving as draft changes the status to Draft and hides the post from public lists. Republishing preserves the post's original publication timestamp.
- Click the trash action to open Delete post. The confirmation includes the title and warns that deletion cannot be undone.
- Click Delete to remove the post. The table refreshes and displays
Post deleted.. Click Cancel or the modal backdrop to leave the post unchanged.
Closing modals with Escape
Both the New post / Edit post modal and the Delete post confirmation honor the platform-wide Escape-to-close convention:
- Pressing Escape while the modal is open closes it without saving or deleting.
- Escape is suppressed while a save/publish request is in flight (the modal stays open until the current operation completes), so an in-flight create or update cannot be silently cancelled by an accidental keypress.
- Escape, the Cancel / Delete buttons, and clicking the modal backdrop all close the modal via the same
onCancel()→canceloutput flow; no partial post is ever created or modified when Escape is pressed.
Validation and expected errors
| Input or operation | Expected behavior |
|---|---|
| Empty or whitespace-only title | Title is required; save and publish remain unavailable. |
| Title over 200 characters | Title must be at most 200 characters. |
| Markdown body over 200,000 characters | Body must be at most 200000 characters. |
| API list failure | An error banner replaces the table state. |
| Create or update failure | The form stays open and displays the server message. |
| Delete failure | The confirmation stays open and displays the server message. |
Architecture map
| Layer | File | Responsibility |
|---|---|---|
| Routes | frontend/src/app/app.routes.ts |
Registers authenticated /blog and admin-only /admin/blog. |
| Admin page | frontend/src/app/features/admin/blog/blog.component.ts |
Loads all posts and coordinates create, edit, publish, draft, and delete operations. |
| Admin form | frontend/src/app/features/admin/blog/blog-form-modal.component.ts |
Reactive title/body editor with sanitized live Markdown preview. |
| Admin form logic | frontend/src/app/features/admin/blog/blog-form.pure.ts |
Validation, form synchronization, request cleanup, and status labels. |
| Delete confirmation | frontend/src/app/features/admin/blog/blog-delete-modal.component.ts |
Confirms destructive deletion and displays failures. |
| User page | frontend/src/app/features/blog/blog.page.ts |
Loads published posts and selects loading, error, empty, or list state. |
| Shared presenter | frontend/src/app/features/blog/blog-presenter.component.ts |
Renders a published post consistently on /blog and /login. |
| HTTP client | frontend/src/app/core/services/blog.service.ts |
Calls public list and admin CRUD endpoints. |
| Backend wiring | backend/src/modules/blog/blog.module.ts |
Registers public and admin controllers and services with the blog repository. |
| Admin API | backend/src/modules/blog/admin-blog.controller.ts |
Registers guarded CRUD routes under /api/v1/admin/blog/posts. |
| Persistence logic | backend/src/modules/blog/admin-blog.service.ts |
Queries and mutates blog_post rows and controls publication timestamps. |
The Angular client uses HTTP JSON calls. Existing auth and CSRF interceptors attach the administrator JWT and CSRF token to unsafe requests. The backend controller is protected by AdminGuard and @Roles('admin'), then delegates to AdminBlogService, which uses the TypeORM BlogPostEntity repository.