File

src/modules/comment/comment.model.ts

Index

Properties

Properties

Optional agent
Type : string | null
Decorators :
@IsString()
@prop({type: String, default: undefined})
author
Type : Author
Decorators :
@Type(undefined)
@ValidateNested()
@IsObject()
@IsNotEmpty()
@IsDefined({message: 'comment author?'})
@prop({required: true, _id: false})
content
Type : string
Decorators :
@MinLength(3)
@MaxLength(3000)
@IsString()
@IsNotEmpty({message: 'comment content?'})
@prop({required: true, validate: /\S+/})
pid
Type : number
Decorators :
@IsInt()
@prop({default: ROOT_COMMENT_PID, index: true})
post_id
Type : number
Decorators :
@IsInt()
@IsNotEmpty({message: 'post ID?'})
@prop({required: true, index: true})
import { AutoIncrementID } from '@typegoose/auto-increment'
import { prop, plugin, modelOptions, Severity } from '@typegoose/typegoose'
import { Type } from 'class-transformer'
import {
  IsString,
  MinLength,
  MaxLength,
  IsDefined,
  IsIn,
  IsIP,
  IsUrl,
  IsEmail,
  IsInt,
  IsOptional,
  IsNotEmpty,
  IsArray,
  IsObject,
  ValidateNested,
  ArrayUnique
} from 'class-validator'
import { GENERAL_AUTO_INCREMENT_ID_CONFIG } 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 { ROOT_COMMENT_PID, CommentState } from '@app/constants/biz.constant'
import { IPLocation } from '@app/processors/helper/helper.service.ip'
import { KeyValueModel } from '@app/models/key-value.model'

export const COMMENT_STATES = [
  CommentState.Auditing,
  CommentState.Published,
  CommentState.Deleted,
  CommentState.Spam
] as const

export const COMMENT_GUEST_QUERY_FILTER = Object.freeze({
  state: CommentState.Published
})

@modelOptions({
  schemaOptions: {
    versionKey: false,
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
})
export class Author {
  @MaxLength(20)
  @IsString()
  @IsNotEmpty()
  @prop({ required: true, validate: /\S+/ })
  name: string

  // MARK: can't get Disqus user's email
  @IsEmail()
  @IsString()
  @IsOptional()
  @prop({ type: String, default: null })
  email?: string | null

  @IsUrl({ require_protocol: true })
  @IsString()
  @IsOptional()
  @prop({ type: String, default: null })
  site?: string | null

  public get email_hash() {
    const email = this.email?.trim().toLowerCase()
    return email ? decodeMD5(email) : null
  }
}

export class CommentBase {
  @IsInt()
  @IsNotEmpty({ message: 'post ID?' })
  @prop({ required: true, index: true })
  post_id: number

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

  @MinLength(3) // sync with Disqus
  @MaxLength(3000)
  @IsString()
  @IsNotEmpty({ message: 'comment content?' })
  @prop({ required: true, validate: /\S+/ })
  content: string

  // user agent
  @IsString()
  @prop({ type: String, default: null })
  agent?: string | null

  @Type(() => Author)
  @ValidateNested()
  @IsObject()
  @IsNotEmpty()
  @IsDefined({ message: 'comment author?' })
  @prop({ required: true, _id: false })
  author: Author
}

@plugin(mongoosePaginate)
@plugin(AutoIncrementID, GENERAL_AUTO_INCREMENT_ID_CONFIG)
@modelOptions({
  // https://typegoose.github.io/typegoose/docs/api/decorators/model-options/#allowmixed
  options: { allowMixed: Severity.ALLOW },
  schemaOptions: {
    versionKey: false,
    timestamps: {
      createdAt: 'created_at',
      updatedAt: 'updated_at'
    }
  }
})
export class Comment extends CommentBase {
  @prop({ unique: true })
  id?: number

  // state
  @IsIn(COMMENT_STATES)
  @IsInt()
  @prop({ enum: CommentState, default: CommentState.Published, index: true })
  state: CommentState

  // likes
  @IsInt()
  @prop({ default: 0, index: true })
  likes: number

  @IsInt()
  @prop({ default: 0, index: true })
  dislikes: number

  // IP address
  @IsIP()
  @IsOptional()
  @prop({ type: String, default: null })
  ip: string | null

  // IP location
  @prop({ type: Object, default: null })
  ip_location: Partial<IPLocation> | null

  @prop({ default: Date.now, immutable: true })
  created_at?: Date

  @prop({ default: Date.now })
  updated_at?: Date

  @ArrayUnique()
  @IsArray()
  @prop({ _id: false, default: [], type: () => [KeyValueModel] })
  extends: KeyValueModel[]
}

export const CommentProvider = getProviderByTypegooseClass(Comment)

results matching ""

    No results matching ""