Skip to content

Commit

Permalink
feat: v4.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
surmon-china committed May 19, 2024
1 parent 3e42895 commit 42a6242
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 26 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file.

### 4.8.0 (2024-05-19)

**Feature**

- **[Category]** Add `/all` API
- Upgrade dependencies

### 4.7.0 (2024-05-10)

**Feature**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nodepress",
"version": "4.7.0",
"version": "4.8.0",
"description": "RESTful API service for Surmon.me blog",
"author": "Surmon",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions src/constants/cache.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum CacheKeys {
Option = 'option',
Archive = 'archive',
AllTags = 'all-tags',
AllCategories = 'all-categories',
TodayViewCount = 'today-view-count'
}

Expand Down
2 changes: 1 addition & 1 deletion src/constants/text.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

export const HTTP_ERROR_SUFFIX = ' failed'
export const HTTP_SUCCESS_SUFFIX = ' succeed'
export const HTTP_SUCCESS_SUFFIX = ' succeeded'
export const HTTP_DEFAULT_TEXT = 'request'
export const HTTP_DEFAULT_ERROR_TEXT = HTTP_DEFAULT_TEXT + HTTP_ERROR_SUFFIX
export const HTTP_DEFAULT_SUCCESS_TEXT = HTTP_DEFAULT_TEXT + HTTP_SUCCESS_SUFFIX
Expand Down
7 changes: 7 additions & 0 deletions src/modules/article/article.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SeoService } from '@app/processors/helper/helper.service.seo'
import { CacheService } from '@app/processors/cache/cache.service'
import { increaseTodayViewsCount } from '@app/modules/expansion/expansion.helper'
import { ArchiveService } from '@app/modules/archive/archive.service'
import { CategoryService } from '@app/modules/category/category.service'
import { TagService } from '@app/modules/tag/tag.service'
import { PublishState } from '@app/constants/biz.constant'
import { NULL } from '@app/constants/value.constant'
Expand All @@ -30,6 +31,7 @@ export class ArticleService {
constructor(
private readonly seoService: SeoService,
private readonly tagService: TagService,
private readonly categoryService: CategoryService,
private readonly cacheService: CacheService,
private readonly archiveService: ArchiveService,
@InjectModel(Article) private readonly articleModel: MongooseModel<Article>
Expand Down Expand Up @@ -153,6 +155,7 @@ export class ArticleService {
const article = await this.articleModel.create(newArticle)
this.seoService.push(getArticleUrl(article.id))
this.tagService.updateAllTagsCache()
this.categoryService.updateAllCategoriesCache()
this.archiveService.updateCache()
return article
}
Expand All @@ -175,6 +178,7 @@ export class ArticleService {
}
this.seoService.update(getArticleUrl(article.id))
this.tagService.updateAllTagsCache()
this.categoryService.updateAllCategoriesCache()
this.archiveService.updateCache()
return article
}
Expand All @@ -187,6 +191,7 @@ export class ArticleService {

this.seoService.delete(getArticleUrl(article.id))
this.tagService.updateAllTagsCache()
this.categoryService.updateAllCategoriesCache()
this.archiveService.updateCache()
return article
}
Expand All @@ -196,6 +201,7 @@ export class ArticleService {
.updateMany({ _id: { $in: articleIDs } }, { $set: { state } }, { multi: true })
.exec()
this.tagService.updateAllTagsCache()
this.categoryService.updateAllCategoriesCache()
this.archiveService.updateCache()
return actionResult
}
Expand All @@ -206,6 +212,7 @@ export class ArticleService {

const actionResult = await this.articleModel.deleteMany({ _id: { $in: articleIDs } }).exec()
this.tagService.updateAllTagsCache()
this.categoryService.updateAllCategoriesCache()
this.archiveService.updateCache()
return actionResult
}
Expand Down
9 changes: 9 additions & 0 deletions src/modules/category/category.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ export class CategoryController {
)
}

@Get('all')
@UseGuards(AdminMaybeGuard)
@Responser.handle('Get all categories')
getAllCategories(@QueryParams() { isAuthenticated }: QueryParamsResult): Promise<Array<Category>> {
return isAuthenticated
? this.categoryService.getAllCategories({ aggregatePublicOnly: false })
: this.categoryService.getAllCategoriesCache()
}

@Post()
@UseGuards(AdminOnlyGuard)
@Responser.handle('Create category')
Expand Down
62 changes: 50 additions & 12 deletions src/modules/category/category.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,74 @@
import { Injectable } from '@nestjs/common'
import { InjectModel } from '@app/transformers/model.transformer'
import { getCategoryUrl } from '@app/transformers/urlmap.transformer'
import { MongooseModel, MongooseDoc, MongooseID, MongooseObjectID } from '@app/interfaces/mongoose.interface'
import { MongooseModel, MongooseDoc, MongooseID, MongooseObjectID, WithID } from '@app/interfaces/mongoose.interface'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
import { CacheService, CacheManualResult } from '@app/processors/cache/cache.service'
import { ArchiveService } from '@app/modules/archive/archive.service'
import { SeoService } from '@app/processors/helper/helper.service.seo'
import { Article, ARTICLE_LIST_QUERY_GUEST_FILTER } from '@app/modules/article/article.model'
import { CacheKeys } from '@app/constants/cache.constant'
import { SortType } from '@app/constants/biz.constant'
import { createLogger } from '@app/utils/logger'
import { isDevEnv } from '@app/app.environment'
import { Category } from './category.model'

const logger = createLogger({ scope: 'CategoryService', time: isDevEnv })

@Injectable()
export class CategoryService {
private allCategoriesCache: CacheManualResult<Array<Category>>

constructor(
private readonly seoService: SeoService,
private readonly cacheService: CacheService,
private readonly archiveService: ArchiveService,
@InjectModel(Article) private readonly articleModel: MongooseModel<Article>,
@InjectModel(Category) private readonly categoryModel: MongooseModel<Category>
) {}
) {
this.allCategoriesCache = this.cacheService.manual<Array<Category>>({
key: CacheKeys.AllCategories,
promise: () => this.getAllCategories({ aggregatePublicOnly: true })
})

public async paginator(
query: PaginateQuery<Category>,
options: PaginateOptions,
publicOnly: boolean
): Promise<PaginateResult<Category>> {
const categories = await this.categoryModel.paginate(query, { ...options, lean: true })
this.allCategoriesCache.update().catch((error) => {
logger.warn('init getAllCategories failed!', error)
})
}

private async aggregateArticleCount(publicOnly: boolean, categories: Array<WithID<Category>>) {
const counts = await this.articleModel.aggregate<{ _id: MongooseObjectID; count: number }>([
{ $match: publicOnly ? ARTICLE_LIST_QUERY_GUEST_FILTER : {} },
{ $unwind: '$categories' },
{ $group: { _id: '$categories', count: { $sum: 1 } } }
])

const hydratedDocs = categories.documents.map((category) => {
return categories.map<Category>((category) => {
const found = counts.find((item) => item._id.equals(category._id))
return { ...category, article_count: found ? found.count : 0 } as Category
return { ...category, article_count: found ? found.count : 0 }
})
}

public async getAllCategories(options: { aggregatePublicOnly: boolean }): Promise<Array<Category>> {
const allCategories = await this.categoryModel.find().lean().sort({ _id: SortType.Desc }).exec()
return await this.aggregateArticleCount(options.aggregatePublicOnly, allCategories)
}

public getAllCategoriesCache(): Promise<Array<Category>> {
return this.allCategoriesCache.get()
}

public updateAllCategoriesCache(): Promise<Array<Category>> {
return this.allCategoriesCache.update()
}

return { ...categories, documents: hydratedDocs }
public async paginator(
query: PaginateQuery<Category>,
options: PaginateOptions,
publicOnly: boolean
): Promise<PaginateResult<Category>> {
const categories = await this.categoryModel.paginate(query, { ...options, lean: true })
const documents = await this.aggregateArticleCount(publicOnly, categories.documents)
return { ...categories, documents }
}

// get detail by slug
Expand All @@ -61,6 +95,7 @@ export class CategoryService {
const category = await this.categoryModel.create(newCategory)
this.seoService.push(getCategoryUrl(category.slug))
this.archiveService.updateCache()
this.updateAllCategoriesCache()
return category
}

Expand Down Expand Up @@ -103,6 +138,7 @@ export class CategoryService {
}
this.seoService.push(getCategoryUrl(category.slug))
this.archiveService.updateCache()
this.updateAllCategoriesCache()
return category
}

Expand All @@ -116,6 +152,7 @@ export class CategoryService {
// cache
this.archiveService.updateCache()
this.seoService.delete(getCategoryUrl(category.slug))
this.updateAllCategoriesCache()
// children categories
const categories = await this.categoryModel.find({ pid: categoryID }).exec()
// delete when root category -> { pid: target.id }
Expand All @@ -138,6 +175,7 @@ export class CategoryService {
// DB remove
const actionResult = await this.categoryModel.deleteMany({ _id: { $in: categoryIDs } }).exec()
this.archiveService.updateCache()
this.updateAllCategoriesCache()
return actionResult
}
}
7 changes: 5 additions & 2 deletions src/modules/tag/tag.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ export class TagController {
}

@Get('all')
@UseGuards(AdminMaybeGuard)
@Responser.handle('Get all tags')
getAllTags(): Promise<Array<Tag>> {
return this.tagService.getAllTagsCache()
getAllTags(@QueryParams() { isAuthenticated }: QueryParamsResult): Promise<Array<Tag>> {
return isAuthenticated
? this.tagService.getAllTags({ aggregatePublicOnly: false })
: this.tagService.getAllTagsCache()
}

@Post()
Expand Down
20 changes: 10 additions & 10 deletions src/modules/tag/tag.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import { Injectable } from '@nestjs/common'
import { InjectModel } from '@app/transformers/model.transformer'
import { getTagUrl } from '@app/transformers/urlmap.transformer'
import { MongooseModel, MongooseDoc, MongooseID, MongooseObjectID, WithID } from '@app/interfaces/mongoose.interface'
import { CacheService, CacheManualResult } from '@app/processors/cache/cache.service'
import { SeoService } from '@app/processors/helper/helper.service.seo'
import { MongooseModel, MongooseDoc, MongooseID, MongooseObjectID, WithID } from '@app/interfaces/mongoose.interface'
import { ArchiveService } from '@app/modules/archive/archive.service'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
import { Article, ARTICLE_LIST_QUERY_GUEST_FILTER } from '@app/modules/article/article.model'
import { CacheKeys } from '@app/constants/cache.constant'
import { SortType } from '@app/constants/biz.constant'
import { ArchiveService } from '@app/modules/archive/archive.service'
import { Article, ARTICLE_LIST_QUERY_GUEST_FILTER } from '@app/modules/article/article.model'
import { createLogger } from '@app/utils/logger'
import { isDevEnv } from '@app/app.environment'
import { Tag } from './tag.model'
Expand All @@ -34,15 +34,15 @@ export class TagService {
) {
this.allTagsCache = this.cacheService.manual<Array<Tag>>({
key: CacheKeys.AllTags,
promise: () => this.getAllTags()
promise: () => this.getAllTags({ aggregatePublicOnly: true })
})

this.updateAllTagsCache().catch((error) => {
logger.warn('init tagPaginateCache failed!', error)
this.allTagsCache.update().catch((error) => {
logger.warn('init getAllTags failed!', error)
})
}

private async aggregate(publicOnly: boolean, tags: Array<WithID<Tag>>) {
private async aggregateArticleCount(publicOnly: boolean, tags: Array<WithID<Tag>>) {
const counts = await this.articleModel.aggregate<{ _id: MongooseObjectID; count: number }>([
{ $match: publicOnly ? ARTICLE_LIST_QUERY_GUEST_FILTER : {} },
{ $unwind: '$tags' },
Expand All @@ -54,9 +54,9 @@ export class TagService {
})
}

public async getAllTags(): Promise<Array<Tag>> {
public async getAllTags(options: { aggregatePublicOnly: boolean }): Promise<Array<Tag>> {
const allTags = await this.tagModel.find().lean().sort({ _id: SortType.Desc }).exec()
return await this.aggregate(true, allTags)
return await this.aggregateArticleCount(options.aggregatePublicOnly, allTags)
}

public getAllTagsCache(): Promise<Array<Tag>> {
Expand All @@ -73,7 +73,7 @@ export class TagService {
publicOnly: boolean
): Promise<PaginateResult<Tag>> {
const tags = await this.tagModel.paginate(query, { ...options, lean: true })
const documents = await this.aggregate(publicOnly, tags.documents)
const documents = await this.aggregateArticleCount(publicOnly, tags.documents)
return { ...tags, documents }
}

Expand Down

0 comments on commit 42a6242

Please sign in to comment.