114 lines
4.6 KiB
Markdown
114 lines
4.6 KiB
Markdown
---
|
|
title: BlogPost
|
|
tags: [data-model, mongoose]
|
|
aliases: [Blog Post, Article, IBlogPost]
|
|
---
|
|
|
|
# BlogPost
|
|
|
|
Editorial content for the marketplace's blog. Each post has a title, an auto-generated slug, rich `content`, optional cover image, gallery, and embedded videos (YouTube / Vimeo / Aparat / other). Carries publication workflow (`draft` / `published` / `archived`), denormalised author info, SEO metadata, and counters for views, likes, and comments. Two pre-save hooks handle slug generation and `publishedAt` stamping.
|
|
|
|
> [!note] Source
|
|
> `backend/src/models/BlogPost.ts:39` — schema definition
|
|
> `backend/src/models/BlogPost.ts:182` — model export
|
|
|
|
## Schema
|
|
|
|
| Field | Type | Required | Default | Validation | Index | Description |
|
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
| `title` | String | yes | — | trim, maxlength 200 | — | Post title. |
|
|
| `slug` | String | no | (auto-generated) | lowercase, trim | unique, sparse | URL slug. |
|
|
| `description` | String | yes | — | maxlength 500 | — | Short summary. |
|
|
| `content` | String | yes | — | — | — | Full body (markdown / HTML). |
|
|
| `coverImage` | String | no | — | — | — | Hero image URL. |
|
|
| `images[]` | String[] | no | — | — | — | Gallery URLs. |
|
|
| `videos[].url` | String | yes | — | — | — | Video URL. |
|
|
| `videos[].title` | String | no | — | — | — | Video title. |
|
|
| `videos[].platform` | String | no | `youtube` | enum: `youtube` / `vimeo` / `aparat` / `other` | — | Platform. |
|
|
| `videos[].embedId` | String | no | — | — | — | Embed id (if applicable). |
|
|
| `author.id` | ObjectId → [[User]] | yes | — | — | — | Author user. |
|
|
| `author.name` | String | yes | — | — | — | Denormalised author name. |
|
|
| `author.avatar` | String | no | — | — | — | Avatar URL. |
|
|
| `category` | String | yes | `tutorial` | enum: `tutorial` / `news` / `guide` / `tips` / `announcement` / `other` | yes (compound) | Editorial category. |
|
|
| `tags[]` | String[] | no | — | trim | yes | Free-form tags. |
|
|
| `status` | String | no | `draft` | enum: `draft` / `published` / `archived` | yes (compound) | Workflow state. |
|
|
| `publishedAt` | Date | no | — | — | yes (compound) | Auto-set when status → `published`. |
|
|
| `views` | Number | no | `0` | — | — | View counter. |
|
|
| `likes` | Number | no | `0` | — | — | Like counter. |
|
|
| `comments` | Number | no | `0` | — | — | Comment counter. |
|
|
| `readTime` | Number | no | `5` | — | — | Estimated read time (minutes). |
|
|
| `featured` | Boolean | no | `false` | — | yes (compound) | Front-page promotion. |
|
|
| `seo.metaTitle` | String | no | — | — | — | SEO title. |
|
|
| `seo.metaDescription` | String | no | — | — | — | SEO description. |
|
|
| `seo.metaKeywords[]` | String[] | no | — | — | — | SEO keywords. |
|
|
| `createdAt` | Date | auto | — | — | — | Mongoose timestamp. |
|
|
| `updatedAt` | Date | auto | — | — | — | Mongoose timestamp. |
|
|
|
|
Virtuals are enabled in `toJSON` and `toObject` even though none are declared on the schema.
|
|
|
|
## Virtuals
|
|
|
|
None defined (but enabled in serialisation).
|
|
|
|
## Indexes
|
|
|
|
Defined at `backend/src/models/BlogPost.ts:148-151`. Plus the implicit unique sparse index on `slug`:
|
|
|
|
- `{ status: 1, publishedAt: -1 }` — published feed.
|
|
- `{ category: 1, status: 1 }` — category page.
|
|
- `{ tags: 1 }` — tag lookup.
|
|
- `{ featured: 1, status: 1 }` — featured posts.
|
|
|
|
## Pre/Post Hooks
|
|
|
|
| Hook | Behaviour |
|
|
| --- | --- |
|
|
| `pre('save')` (`backend/src/models/BlogPost.ts:154`) | Auto-generates `slug` from the title (English letters only) plus a timestamp suffix; falls back to `post-<timestamp>` for non-Latin titles. |
|
|
| `pre('save')` (`backend/src/models/BlogPost.ts:175`) | When `status` is modified to `published` and `publishedAt` is empty, sets `publishedAt = new Date()`. |
|
|
|
|
## Instance Methods
|
|
|
|
None defined.
|
|
|
|
## Static Methods
|
|
|
|
None defined.
|
|
|
|
## Relationships
|
|
|
|
- **References**: [[User]] (`author.id`).
|
|
- **Referenced by**: none.
|
|
|
|
## State Transitions
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> draft
|
|
draft --> published : author publishes
|
|
published --> archived : admin archives
|
|
published --> draft : unpublish
|
|
archived --> published : restore
|
|
archived --> [*]
|
|
```
|
|
|
|
## Common Queries
|
|
|
|
```ts
|
|
// Public feed
|
|
BlogPost.find({ status: 'published' }).sort({ publishedAt: -1 }).limit(20);
|
|
|
|
// By slug (detail page)
|
|
BlogPost.findOne({ slug, status: 'published' });
|
|
|
|
// Featured carousel
|
|
BlogPost.find({ featured: true, status: 'published' }).sort({ publishedAt: -1 });
|
|
|
|
// Tag search
|
|
BlogPost.find({ tags: tag, status: 'published' });
|
|
|
|
// Increment views atomically
|
|
BlogPost.updateOne({ _id }, { $inc: { views: 1 } });
|
|
```
|
|
|
|
Related: [[User]].
|