Skip to content

Commit

Permalink
Showing 21 changed files with 81 additions and 54 deletions.
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": "3.8.3",
"version": "3.8.4",
"description": "RESTful API service for Surmon.me blog",
"author": {
"name": "Surmon",
30 changes: 15 additions & 15 deletions src/interfaces/biz.interface.ts → src/constants/biz.constant.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
/**
* @file Business constants & interfaces
* @module interface/biz
* @module constants/biz
* @author Surmon <https://github.com/surmon-china>
*/

// 排序
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
// language
export enum Language {
English = 'en', // English
Chinese = 'zh', // 简体中文
}

// sort
export enum SortType {
Asc = 1, // 升序
Desc = -1, // 降序
Hottest = 2, // 热序
}

// 发布状态
// publish state
export enum PublishState {
Draft = 0, // 草稿
Published = 1, // 已发布
Recycle = -1, // 回收站
}

// 公开状态
// public state
export enum PublicState {
Public = 1, // 公开
Secret = -1, // 私密
Reserve = 0, // 保留(限制)
}

// 转载状态
// origin state
export enum OriginState {
Original = 0, // 原创
Reprint = 1, // 转载
Hybrid = 2, // 混合
}

// 评论状态
// comment state
export enum CommentState {
Auditing = 0, // 待审核
Published = 1, // 通过正常
Deleted = -1, // 已删除
Spam = -2, // 垃圾评论
}

// 评论宿主页面的 POST_ID 类型
export enum CommentPostID {
Guestbook = 0, // 留言板
}

// 评论本身的类型
export enum CommentParentID {
Self = 0, // 自身一级评论
}
export const GUESTBOOK_POST_ID = 0
export const ROOT_COMMENT_PID = 0
2 changes: 1 addition & 1 deletion src/models/paginate.model.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import { IsIn, IsInt, IsOptional, IsNotEmpty, Min, Max } from 'class-validator'
import { Transform } from 'class-transformer'
import { SortType } from '@app/interfaces/biz.interface'
import { SortType } from '@app/constants/biz.constant'
import { unknowToNumber } from '@app/transformers/value.transformer'

export class PaginateBaseOptionDTO {
2 changes: 1 addition & 1 deletion src/modules/announcement/announcement.dto.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import { Transform } from 'class-transformer'
import { IsInt, IsIn, IsNotEmpty, IsOptional, IsArray, ArrayNotEmpty, ArrayUnique } from 'class-validator'
import { unknowToNumber } from '@app/transformers/value.transformer'
import { WhenGuest } from '@app/decorators/guest.decorator'
import { PublishState } from '@app/interfaces/biz.interface'
import { PublishState } from '@app/constants/biz.constant'
import { PaginateOptionDTO } from '@app/models/paginate.model'
import { KeywordQueryDTO } from '@app/models/query.model'
import { ANNOUNCEMENT_STATES } from './announcement.model'
2 changes: 1 addition & 1 deletion src/modules/announcement/announcement.model.ts
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import { IsString, IsInt, IsIn, IsDefined, IsNotEmpty } from 'class-validator'
import { generalAutoIncrementIDConfig } from '@app/constants/increment.constant'
import { getProviderByTypegooseClass } from '@app/transformers/model.transformer'
import { mongoosePaginate } from '@app/utils/paginate'
import { PublishState } from '@app/interfaces/biz.interface'
import { PublishState } from '@app/constants/biz.constant'

export const ANNOUNCEMENT_STATES = [PublishState.Draft, PublishState.Published] as const

2 changes: 1 addition & 1 deletion src/modules/archive/archive.service.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { Injectable } from '@nestjs/common'
import { InjectModel } from '@app/transformers/model.transformer'
import { CacheService, CacheIOResult } from '@app/processors/cache/cache.service'
import { MongooseModel } from '@app/interfaces/mongoose.interface'
import { SortType } from '@app/interfaces/biz.interface'
import { SortType } from '@app/constants/biz.constant'
import { Category } from '@app/modules/category/category.model'
import {
Article,
7 changes: 6 additions & 1 deletion src/modules/article/article.controller.ts
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import { AdminOnlyGuard } from '@app/guards/admin-only.guard'
import { AdminMaybeGuard } from '@app/guards/admin-maybe.guard'
import { PermissionPipe } from '@app/pipes/permission.pipe'
import { ExposePipe } from '@app/pipes/expose.pipe'
import { SortType } from '@app/interfaces/biz.interface'
import { SortType } from '@app/constants/biz.constant'
import { TagService } from '@app/modules/tag/tag.service'
import { CategoryService } from '@app/modules/category/category.service'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
@@ -56,6 +56,11 @@ export class ArticleController {
}
}

// language
if (!lodash.isUndefined(filters.lang)) {
paginateQuery.lang = filters.lang
}

// states
if (!lodash.isUndefined(filters.state)) {
paginateQuery.state = filters.state
15 changes: 13 additions & 2 deletions src/modules/article/article.dto.ts
Original file line number Diff line number Diff line change
@@ -19,12 +19,17 @@ import {
ArrayNotEmpty,
ArrayUnique,
} from 'class-validator'
import { PublishState, PublicState, OriginState } from '@app/interfaces/biz.interface'
import { PublishState, PublicState, OriginState } from '@app/constants/biz.constant'
import { WhenGuest } from '@app/decorators/guest.decorator'
import { unknowToNumber } from '@app/transformers/value.transformer'
import { DateQueryDTO, KeywordQueryDTO } from '@app/models/query.model'
import { PaginateOptionWithHotSortDTO } from '@app/models/paginate.model'
import { ARTICLE_PUBLISH_STATES, ARTICLE_PUBLIC_STATES, ARTICLE_ORIGIN_STATES } from './article.model'
import {
ARTICLE_PUBLISH_STATES,
ARTICLE_PUBLIC_STATES,
ARTICLE_ORIGIN_STATES,
ARTICLE_LANGUAGES,
} from './article.model'

export class ArticlePaginateQueryDTO extends IntersectionType(
PaginateOptionWithHotSortDTO,
@@ -63,6 +68,12 @@ export class ArticlePaginateQueryDTO extends IntersectionType(
@IsNotEmpty()
@IsOptional()
category_slug?: string

@IsIn(ARTICLE_LANGUAGES)
@IsString()
@IsNotEmpty()
@IsOptional()
lang: string
}

export class ArticleListQueryDTO {
23 changes: 17 additions & 6 deletions src/modules/article/article.model.ts
Original file line number Diff line number Diff line change
@@ -20,14 +20,15 @@ import {
ArrayNotEmpty,
ArrayUnique,
} from 'class-validator'
import { Language, SortType, PublishState, PublicState, OriginState } from '@app/constants/biz.constant'
import { generalAutoIncrementIDConfig } from '@app/constants/increment.constant'
import { getProviderByTypegooseClass } from '@app/transformers/model.transformer'
import { mongoosePaginate } from '@app/utils/paginate'
import { SortType, PublishState, PublicState, OriginState } from '@app/interfaces/biz.interface'
import { Category } from '@app/modules/category/category.model'
import { ExtendModel } from '@app/models/extend.model'
import { Tag } from '@app/modules/tag/tag.model'

export const ARTICLE_LANGUAGES = [Language.English, Language.Chinese] as const
export const ARTICLE_PUBLISH_STATES = [PublishState.Draft, PublishState.Published, PublishState.Recycle] as const
export const ARTICLE_PUBLIC_STATES = [PublicState.Public, PublicState.Secret, PublicState.Reserve] as const
export const ARTICLE_ORIGIN_STATES = [OriginState.Original, OriginState.Reprint, OriginState.Hybrid] as const
@@ -126,11 +127,6 @@ export class Article {
@prop()
thumb: string

// disabled comment
@IsBoolean()
@prop({ default: false })
disabled_comment: boolean

// publish state
@IsIn(ARTICLE_PUBLISH_STATES)
@IsInt()
@@ -164,6 +160,21 @@ export class Article {
@prop({ ref: () => Tag, index: true })
tag: Ref<Tag>[]

// language
// MARK: can't use 'language' field
// https://docs.mongodb.com/manual/tutorial/specify-language-for-text-index/
// https://docs.mongodb.com/manual/reference/text-search-languages/#std-label-text-search-languages
@IsIn(ARTICLE_LANGUAGES)
@IsString()
@IsDefined()
@prop({ default: Language.Chinese, index: true })
lang: Language

// disabled comment
@IsBoolean()
@prop({ default: false })
disabled_comment: boolean

@prop({ _id: false, default: { ...ARTICLE_DEFAULT_META } })
meta: ArticleMeta

2 changes: 1 addition & 1 deletion src/modules/article/article.service.ts
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import { SeoService } from '@app/processors/helper/helper.service.seo'
import { CacheService, CacheIntervalResult } from '@app/processors/cache/cache.service'
import { ArchiveService } from '@app/modules/archive/archive.service'
import { TagService } from '@app/modules/tag/tag.service'
import { PublishState } from '@app/interfaces/biz.interface'
import { PublishState } from '@app/constants/biz.constant'
import { MongooseModel, MongooseDoc, MongooseID } from '@app/interfaces/mongoose.interface'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
import {
2 changes: 1 addition & 1 deletion src/modules/comment/comment.controller.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import { AdminOnlyGuard } from '@app/guards/admin-only.guard'
import { AdminMaybeGuard } from '@app/guards/admin-maybe.guard'
import { PermissionPipe } from '@app/pipes/permission.pipe'
import { ExposePipe } from '@app/pipes/expose.pipe'
import { SortType } from '@app/interfaces/biz.interface'
import { SortType } from '@app/constants/biz.constant'
import { Responsor } from '@app/decorators/responsor.decorator'
import { QueryParams, QueryParamsResult } from '@app/decorators/queryparams.decorator'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
2 changes: 1 addition & 1 deletion src/modules/comment/comment.dto.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { IntersectionType } from '@nestjs/mapped-types'
import { IsNotEmpty, IsArray, IsIn, IsInt, IsOptional, Min, ArrayNotEmpty, ArrayUnique } from 'class-validator'
import { Transform } from 'class-transformer'
import { WhenGuest } from '@app/decorators/guest.decorator'
import { CommentState } from '@app/interfaces/biz.interface'
import { CommentState } from '@app/constants/biz.constant'
import { COMMENT_STATES } from './comment.model'
import { KeywordQueryDTO } from '@app/models/query.model'
import { PaginateOptionWithHotSortDTO } from '@app/models/paginate.model'
4 changes: 2 additions & 2 deletions src/modules/comment/comment.model.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ import { generalAutoIncrementIDConfig } from '@app/constants/increment.constant'
import { mongoosePaginate } from '@app/utils/paginate'
import { getProviderByTypegooseClass } from '@app/transformers/model.transformer'
import { decodeMD5 } from '@app/transformers/codec.transformer'
import { CommentParentID, CommentState } from '@app/interfaces/biz.interface'
import { ROOT_COMMENT_PID, CommentState } from '@app/constants/biz.constant'
import { IPLocation } from '@app/processors/helper/helper.service.ip'
import { ExtendModel } from '@app/models/extend.model'

@@ -84,7 +84,7 @@ export class CommentBase {

// parent comment ID
@IsInt()
@prop({ default: CommentParentID.Self, index: true })
@prop({ default: ROOT_COMMENT_PID, index: true })
pid: number

@MinLength(3) // sync with Disqus
6 changes: 3 additions & 3 deletions src/modules/comment/comment.service.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { Injectable } from '@nestjs/common'
import { InjectModel } from '@app/transformers/model.transformer'
import { MongooseModel, MongooseDoc, MongooseID } from '@app/interfaces/mongoose.interface'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
import { CommentPostID, CommentState } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID, CommentState } from '@app/constants/biz.constant'
import { ArticleService } from '@app/modules/article/article.service'
import { IPService } from '@app/processors/helper/helper.service.ip'
import { EmailService } from '@app/processors/helper/helper.service.email'
@@ -35,7 +35,7 @@ export class CommentService {

private async emailToAdminAndTargetAuthor(comment: Comment) {
let onWhere = ''
if (comment.post_id === CommentPostID.Guestbook) {
if (comment.post_id === GUESTBOOK_POST_ID) {
onWhere = 'guestbook'
} else {
const article = await this.articleService.getDetailByNumberIDOrSlug({ idOrSlug: comment.post_id })
@@ -150,7 +150,7 @@ export class CommentService {

// validate comment target commentable
public async isCommentableTarget(targetPostID: number): Promise<void> {
if (targetPostID !== CommentPostID.Guestbook) {
if (targetPostID !== GUESTBOOK_POST_ID) {
const isCommentable = await this.articleService.isCommentableArticle(targetPostID)
if (!isCommentable) {
return Promise.reject(`Comment target ${targetPostID} was disabled comment`)
6 changes: 3 additions & 3 deletions src/modules/disqus/disqus.constant.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import { APP } from '@app/app.config'
import { isProdEnv } from '@app/app.environment'
import { CommentPostID } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID } from '@app/constants/biz.constant'

export const DISQUS_OAUTH_CALLBACK_URL = isProdEnv
? `${APP.URL}/disqus/oauth-callback`
@@ -24,8 +24,8 @@ export const ARTICLE_THREAD_ID_EXTEND_KEY = 'disqus-thread-id'
const GUESTBOOK_IDENTIFIER = 'guestbook'
const ARTICLE_IDENTIFIER_PREFIX = 'article-'
export const getThreadIdentifierByID = (postID: number) => {
return postID === CommentPostID.Guestbook ? GUESTBOOK_IDENTIFIER : `${ARTICLE_IDENTIFIER_PREFIX}${postID}`
return postID === GUESTBOOK_POST_ID ? GUESTBOOK_IDENTIFIER : `${ARTICLE_IDENTIFIER_PREFIX}${postID}`
}
export const getIDByThreadIdentifier = (id: string) => {
return id === GUESTBOOK_IDENTIFIER ? CommentPostID.Guestbook : id.replace(ARTICLE_IDENTIFIER_PREFIX, '')
return id === GUESTBOOK_IDENTIFIER ? GUESTBOOK_POST_ID : id.replace(ARTICLE_IDENTIFIER_PREFIX, '')
}
4 changes: 2 additions & 2 deletions src/modules/disqus/disqus.service.private.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import { ArticleService } from '@app/modules/article/article.service'
import { CommentService } from '@app/modules/comment/comment.service'
import { Comment } from '@app/modules/comment/comment.model'
import { Article } from '@app/modules/article/article.model'
import { CommentPostID, CommentState } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID, CommentState } from '@app/constants/biz.constant'
import { getExtendObject } from '@app/transformers/extend.transformer'
import { getPermalinkByID } from '@app/transformers/urlmap.transformer'
import { DISQUS } from '@app/app.config'
@@ -136,7 +136,7 @@ export class DisqusPrivateService {
if (comment.pid && !todoCommentIDs.includes(comment.pid)) {
comment.pid = 0
}
if (comment.post_id === CommentPostID.Guestbook) {
if (comment.post_id === GUESTBOOK_POST_ID) {
guestbook.push(comment)
} else if (treeMap.has(comment.post_id)) {
treeMap.get(comment.post_id)!.comments.push(comment)
2 changes: 1 addition & 1 deletion src/modules/disqus/disqus.service.public.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { Injectable } from '@nestjs/common'
import { CommentService } from '@app/modules/comment/comment.service'
import { Comment, CommentBase } from '@app/modules/comment/comment.model'
import { QueryVisitor } from '@app/decorators/queryparams.decorator'
import { CommentState } from '@app/interfaces/biz.interface'
import { CommentState } from '@app/constants/biz.constant'
import { getDisqusCacheKey } from '@app/constants/cache.constant'
import { CacheService } from '@app/processors/cache/cache.service'
import { DISQUS } from '@app/app.config'
6 changes: 3 additions & 3 deletions src/modules/disqus/disqus.xml.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import moment from 'moment'
import { Comment } from '@app/modules/comment/comment.model'
import { Article } from '@app/modules/article/article.model'
import { CommentPostID, CommentState } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID, CommentState } from '@app/constants/biz.constant'
import { getPermalinkByID } from '@app/transformers/urlmap.transformer'
import { getThreadIdentifierByID } from './disqus.constant'
import { ThreadState } from './disqus.dto'
@@ -46,9 +46,9 @@ export const getDisqusXML = (data: XMLItemData[], guestbook: Array<Comment>) =>
<channel>
<item>
<title>Guestbook</title>
<link>${getPermalinkByID(CommentPostID.Guestbook)}</link>
<link>${getPermalinkByID(GUESTBOOK_POST_ID)}</link>
<content:encoded><![CDATA[${APP.FE_NAME}]]></content:encoded>
<dsq:thread_identifier>${getThreadIdentifierByID(CommentPostID.Guestbook)}</dsq:thread_identifier>
<dsq:thread_identifier>${getThreadIdentifierByID(GUESTBOOK_POST_ID)}</dsq:thread_identifier>
<wp:post_date_gmt>2017-01-01 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
${guestbook.map(getCommentItemXML).join('\n')}
2 changes: 1 addition & 1 deletion src/modules/tag/tag.service.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ import { CacheService, CacheIOResult } from '@app/processors/cache/cache.service
import { SeoService } from '@app/processors/helper/helper.service.seo'
import { MongooseModel, MongooseDoc, MongooseID } from '@app/interfaces/mongoose.interface'
import { PaginateResult, PaginateQuery, PaginateOptions } from '@app/utils/paginate'
import { SortType } from '@app/interfaces/biz.interface'
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 { Tag } from './tag.model'
10 changes: 5 additions & 5 deletions src/modules/vote/vote.controller.ts
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ import { Author } from '@app/modules/comment/comment.model'
import { DisqusPublicService } from '@app/modules/disqus/disqus.service.public'
import { DisqusToken } from '@app/modules/disqus/disqus.token'
import { AccessToken } from '@app/utils/disqus'
import { CommentPostID } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID } from '@app/constants/biz.constant'
import { getPermalinkByID } from '@app/transformers/urlmap.transformer'
import { VoteAuthorDTO, CommentVoteDTO, PageVoteDTO } from './vote.dto'
import * as APP_CONFIG from '@app/app.config'
@@ -54,7 +54,7 @@ export class VoteController {
}

private async getTargetTitle(post_id: number) {
if (post_id === CommentPostID.Guestbook) {
if (post_id === GUESTBOOK_POST_ID) {
return 'guestbook'
} else {
const article = await this.articleService.getDetailByNumberIDOrSlug({ idOrSlug: post_id })
@@ -120,18 +120,18 @@ export class VoteController {
// NodePress
const likes = await this.optionService.incrementLikes()
// Disqus
this.voteDisqusThread(CommentPostID.Guestbook, 1, token?.access_token).catch(() => {})
this.voteDisqusThread(GUESTBOOK_POST_ID, 1, token?.access_token).catch(() => {})
// email to admin
this.getAuthor(voteBody.author, token?.access_token).then(async (author) => {
if (author) {
this.emailToTargetVoteMessage({
to: APP_CONFIG.APP.ADMIN_EMAIL,
subject: `You have a new site vote`,
on: await this.getTargetTitle(CommentPostID.Guestbook),
on: await this.getTargetTitle(GUESTBOOK_POST_ID),
vote: '+1',
author: author || 'Anonymous user',
location: await this.ipService.queryLocation(visitor.ip),
link: getPermalinkByID(CommentPostID.Guestbook),
link: getPermalinkByID(GUESTBOOK_POST_ID),
})
}
})
4 changes: 2 additions & 2 deletions src/transformers/urlmap.transformer.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
* @author Surmon <https://github.com/surmon-china>
*/

import { CommentPostID } from '@app/interfaces/biz.interface'
import { GUESTBOOK_POST_ID } from '@app/constants/biz.constant'
import * as APP_CONFIG from '@app/app.config'

export function getTagUrl(tagSlug: string): string {
@@ -25,5 +25,5 @@ export function getGuestbookPageUrl(): string {
}

export function getPermalinkByID(id: number): string {
return id === CommentPostID.Guestbook ? getGuestbookPageUrl() : getArticleUrl(id)
return id === GUESTBOOK_POST_ID ? getGuestbookPageUrl() : getArticleUrl(id)
}

0 comments on commit b97b388

Please sign in to comment.