@rytass/cms-base-nestjs-module
Version:
Rytass Content Management System NestJS Base Module
1,240 lines (1,216 loc) • 90.5 kB
JavaScript
'use strict';
var common = require('@nestjs/common');
var typeorm$1 = require('@nestjs/typeorm');
var typeorm = require('typeorm');
var core = require('@quadrats/core');
var DataLoader = require('dataloader');
var lruCache = require('lru-cache');
const DEFAULT_LANGUAGE = 'DEFAULT';
const FULL_TEXT_SEARCH_TOKEN_VERSION = '0.0.1';
const EMPTY_QUADRATS_ELEMENTS = [
{
type: core.PARAGRAPH_TYPE,
children: [
{
text: ''
}
]
}
];
function _ts_decorate$f(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseArticleVersionContentRepo = Symbol('BaseArticleVersionContentRepo');
class BaseArticleVersionContentEntity {
articleId;
version;
language;
title;
description;
content;
searchTokens;
searchTokenVersion;
articleVersion;
}
_ts_decorate$f([
typeorm.PrimaryColumn('uuid')
], BaseArticleVersionContentEntity.prototype, "articleId", void 0);
_ts_decorate$f([
typeorm.PrimaryColumn('int', {
default: 0
})
], BaseArticleVersionContentEntity.prototype, "version", void 0);
_ts_decorate$f([
typeorm.PrimaryColumn('varchar', {
default: DEFAULT_LANGUAGE
})
], BaseArticleVersionContentEntity.prototype, "language", void 0);
_ts_decorate$f([
typeorm.Column('varchar'),
typeorm.Index()
], BaseArticleVersionContentEntity.prototype, "title", void 0);
_ts_decorate$f([
typeorm.Column('varchar', {
nullable: true,
comment: 'Use for SEO'
}),
typeorm.Index()
], BaseArticleVersionContentEntity.prototype, "description", void 0);
_ts_decorate$f([
typeorm.Column('json', {
default: EMPTY_QUADRATS_ELEMENTS
})
], BaseArticleVersionContentEntity.prototype, "content", void 0);
_ts_decorate$f([
typeorm.Column('tsvector', {
nullable: true,
select: false
})
], BaseArticleVersionContentEntity.prototype, "searchTokens", void 0);
_ts_decorate$f([
typeorm.Column('varchar', {
default: FULL_TEXT_SEARCH_TOKEN_VERSION,
select: false
})
], BaseArticleVersionContentEntity.prototype, "searchTokenVersion", void 0);
_ts_decorate$f([
typeorm.ManyToOne(()=>BaseArticleVersionEntity, (articleVersion)=>articleVersion.multiLanguageContents, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
orphanedRowAction: 'delete'
}),
typeorm.JoinColumn([
{
name: 'articleId',
referencedColumnName: 'articleId'
},
{
name: 'version',
referencedColumnName: 'version'
}
])
], BaseArticleVersionContentEntity.prototype, "articleVersion", void 0);
BaseArticleVersionContentEntity = _ts_decorate$f([
typeorm.Entity('article_version_contents'),
typeorm.Index([
'articleId',
'version'
]),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseArticleVersionContentEntity);
var ArticleSignatureResult = /*#__PURE__*/ function(ArticleSignatureResult) {
ArticleSignatureResult["APPROVED"] = "APPROVED";
ArticleSignatureResult["REJECTED"] = "REJECTED";
return ArticleSignatureResult;
}({});
function _ts_decorate$e(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseSignatureLevelRepo = Symbol('BaseSignatureLevelRepo');
class BaseSignatureLevelEntity {
id;
name;
sequence;
required;
createdAt;
deletedAt;
signatures;
}
_ts_decorate$e([
typeorm.PrimaryGeneratedColumn('uuid')
], BaseSignatureLevelEntity.prototype, "id", void 0);
_ts_decorate$e([
typeorm.Column('varchar')
], BaseSignatureLevelEntity.prototype, "name", void 0);
_ts_decorate$e([
typeorm.Column('int', {
default: 0
}),
typeorm.Index({
unique: true,
where: '"deletedAt" IS NULL'
})
], BaseSignatureLevelEntity.prototype, "sequence", void 0);
_ts_decorate$e([
typeorm.Column('boolean', {
default: true
})
], BaseSignatureLevelEntity.prototype, "required", void 0);
_ts_decorate$e([
typeorm.CreateDateColumn('timestamptz')
], BaseSignatureLevelEntity.prototype, "createdAt", void 0);
_ts_decorate$e([
typeorm.DeleteDateColumn('timestamptz')
], BaseSignatureLevelEntity.prototype, "deletedAt", void 0);
_ts_decorate$e([
typeorm.OneToMany(()=>ArticleSignatureEntity, (signature)=>signature.signatureLevel)
], BaseSignatureLevelEntity.prototype, "signatures", void 0);
BaseSignatureLevelEntity = _ts_decorate$e([
typeorm.Entity('signature_levels'),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseSignatureLevelEntity);
function _ts_decorate$d(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const ArticleSignatureRepo = Symbol('ArticleSignatureRepo');
class ArticleSignatureEntity {
id;
articleId;
version;
signatureLevelId;
result;
rejectReason;
signerId;
signedAt;
deletedAt;
articleVersion;
signatureLevel;
}
_ts_decorate$d([
typeorm.PrimaryGeneratedColumn('uuid')
], ArticleSignatureEntity.prototype, "id", void 0);
_ts_decorate$d([
typeorm.Column('uuid')
], ArticleSignatureEntity.prototype, "articleId", void 0);
_ts_decorate$d([
typeorm.Column('int')
], ArticleSignatureEntity.prototype, "version", void 0);
_ts_decorate$d([
typeorm.Column('uuid', {
nullable: true
}),
typeorm.Index()
], ArticleSignatureEntity.prototype, "signatureLevelId", void 0);
_ts_decorate$d([
typeorm.Column('enum', {
enum: ArticleSignatureResult,
default: ArticleSignatureResult.APPROVED
})
], ArticleSignatureEntity.prototype, "result", void 0);
_ts_decorate$d([
typeorm.Column('varchar', {
nullable: true
})
], ArticleSignatureEntity.prototype, "rejectReason", void 0);
_ts_decorate$d([
typeorm.Column('uuid', {
nullable: true
})
], ArticleSignatureEntity.prototype, "signerId", void 0);
_ts_decorate$d([
typeorm.CreateDateColumn('timestamptz')
], ArticleSignatureEntity.prototype, "signedAt", void 0);
_ts_decorate$d([
typeorm.DeleteDateColumn('timestamptz')
], ArticleSignatureEntity.prototype, "deletedAt", void 0);
_ts_decorate$d([
typeorm.ManyToOne(()=>BaseArticleVersionEntity, (version)=>version.signatures),
typeorm.JoinColumn([
{
name: 'articleId',
referencedColumnName: 'articleId'
},
{
name: 'version',
referencedColumnName: 'version'
}
])
], ArticleSignatureEntity.prototype, "articleVersion", void 0);
_ts_decorate$d([
typeorm.ManyToOne(()=>BaseSignatureLevelEntity, (level)=>level.signatures, {
nullable: true
}),
typeorm.JoinColumn({
name: 'signatureLevelId',
referencedColumnName: 'id'
})
], ArticleSignatureEntity.prototype, "signatureLevel", void 0);
ArticleSignatureEntity = _ts_decorate$d([
typeorm.Entity('article_signatures'),
typeorm.Index('article_signature_article_id_version', [
'articleId',
'version',
'signatureLevelId'
], {
unique: true,
where: '"deletedAt" IS NULL'
}),
typeorm.Index([
'articleId',
'version'
])
], ArticleSignatureEntity);
function _ts_decorate$c(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseArticleVersionRepo = Symbol('BaseArticleVersionRepo');
class BaseArticleVersionEntity {
articleId;
version;
tags;
releasedAt;
createdAt;
deletedAt;
article;
multiLanguageContents;
signatures;
}
_ts_decorate$c([
typeorm.PrimaryColumn('uuid'),
typeorm.Index()
], BaseArticleVersionEntity.prototype, "articleId", void 0);
_ts_decorate$c([
typeorm.PrimaryColumn('int', {
default: 0
})
], BaseArticleVersionEntity.prototype, "version", void 0);
_ts_decorate$c([
typeorm.Column('jsonb')
], BaseArticleVersionEntity.prototype, "tags", void 0);
_ts_decorate$c([
typeorm.Column('timestamptz', {
nullable: true
}),
typeorm.Index()
], BaseArticleVersionEntity.prototype, "releasedAt", void 0);
_ts_decorate$c([
typeorm.CreateDateColumn('timestamptz')
], BaseArticleVersionEntity.prototype, "createdAt", void 0);
_ts_decorate$c([
typeorm.DeleteDateColumn('timestamptz')
], BaseArticleVersionEntity.prototype, "deletedAt", void 0);
_ts_decorate$c([
typeorm.ManyToOne(()=>BaseArticleEntity, (article)=>article.versions, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
orphanedRowAction: 'delete'
}),
typeorm.JoinColumn({
name: 'articleId',
referencedColumnName: 'id'
})
], BaseArticleVersionEntity.prototype, "article", void 0);
_ts_decorate$c([
typeorm.OneToMany(()=>BaseArticleVersionContentEntity, (content)=>content.articleVersion)
], BaseArticleVersionEntity.prototype, "multiLanguageContents", void 0);
_ts_decorate$c([
typeorm.OneToMany(()=>ArticleSignatureEntity, (signature)=>signature.articleVersion)
], BaseArticleVersionEntity.prototype, "signatures", void 0);
BaseArticleVersionEntity = _ts_decorate$c([
typeorm.Entity('article_versions'),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseArticleVersionEntity);
function _ts_decorate$b(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseCategoryMultiLanguageNameRepo = Symbol('BaseCategoryMultiLanguageNameRepo');
class BaseCategoryMultiLanguageNameEntity {
categoryId;
language;
name;
createdAt;
updatedAt;
category;
}
_ts_decorate$b([
typeorm.PrimaryColumn('uuid'),
typeorm.Index()
], BaseCategoryMultiLanguageNameEntity.prototype, "categoryId", void 0);
_ts_decorate$b([
typeorm.PrimaryColumn('varchar', {
default: DEFAULT_LANGUAGE
})
], BaseCategoryMultiLanguageNameEntity.prototype, "language", void 0);
_ts_decorate$b([
typeorm.Column('varchar')
], BaseCategoryMultiLanguageNameEntity.prototype, "name", void 0);
_ts_decorate$b([
typeorm.CreateDateColumn('timestamptz')
], BaseCategoryMultiLanguageNameEntity.prototype, "createdAt", void 0);
_ts_decorate$b([
typeorm.UpdateDateColumn('timestamptz')
], BaseCategoryMultiLanguageNameEntity.prototype, "updatedAt", void 0);
_ts_decorate$b([
typeorm.ManyToOne(()=>BaseCategoryEntity, (category)=>category.multiLanguageNames, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
orphanedRowAction: 'delete'
}),
typeorm.JoinColumn({
name: 'categoryId',
referencedColumnName: 'id'
})
], BaseCategoryMultiLanguageNameEntity.prototype, "category", void 0);
BaseCategoryMultiLanguageNameEntity = _ts_decorate$b([
typeorm.Entity('category_multi_language_names'),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseCategoryMultiLanguageNameEntity);
function _ts_decorate$a(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseCategoryRepo = Symbol('BaseCategoryRepo');
class BaseCategoryEntity {
id;
bindable;
createdAt;
updatedAt;
deletedAt;
parents;
children;
multiLanguageNames;
articles;
}
_ts_decorate$a([
typeorm.PrimaryGeneratedColumn('uuid')
], BaseCategoryEntity.prototype, "id", void 0);
_ts_decorate$a([
typeorm.Column('boolean', {
default: false,
comment: 'is article bindable'
})
], BaseCategoryEntity.prototype, "bindable", void 0);
_ts_decorate$a([
typeorm.CreateDateColumn('timestamptz')
], BaseCategoryEntity.prototype, "createdAt", void 0);
_ts_decorate$a([
typeorm.UpdateDateColumn('timestamptz')
], BaseCategoryEntity.prototype, "updatedAt", void 0);
_ts_decorate$a([
typeorm.DeleteDateColumn('timestamptz')
], BaseCategoryEntity.prototype, "deletedAt", void 0);
_ts_decorate$a([
typeorm.ManyToMany(()=>BaseCategoryEntity, (category)=>category.children),
typeorm.JoinTable({
name: 'category_relations',
joinColumn: {
name: 'childId',
referencedColumnName: 'id'
},
inverseJoinColumn: {
name: 'parentId',
referencedColumnName: 'id'
}
})
], BaseCategoryEntity.prototype, "parents", void 0);
_ts_decorate$a([
typeorm.ManyToMany(()=>BaseCategoryEntity, (category)=>category.parents, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
orphanedRowAction: 'delete'
})
], BaseCategoryEntity.prototype, "children", void 0);
_ts_decorate$a([
typeorm.OneToMany(()=>BaseCategoryMultiLanguageNameEntity, (multiLanguageName)=>multiLanguageName.category)
], BaseCategoryEntity.prototype, "multiLanguageNames", void 0);
_ts_decorate$a([
typeorm.ManyToMany(()=>BaseArticleEntity, (article)=>article.categories, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
orphanedRowAction: 'delete'
})
], BaseCategoryEntity.prototype, "articles", void 0);
BaseCategoryEntity = _ts_decorate$a([
typeorm.Entity('categories'),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseCategoryEntity);
function _ts_decorate$9(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const BaseArticleRepo = Symbol('BaseArticleRepo');
class BaseArticleEntity {
id;
createdAt;
deletedAt;
versions;
categories;
}
_ts_decorate$9([
typeorm.PrimaryGeneratedColumn('uuid')
], BaseArticleEntity.prototype, "id", void 0);
_ts_decorate$9([
typeorm.CreateDateColumn('timestamptz')
], BaseArticleEntity.prototype, "createdAt", void 0);
_ts_decorate$9([
typeorm.DeleteDateColumn('timestamptz')
], BaseArticleEntity.prototype, "deletedAt", void 0);
_ts_decorate$9([
typeorm.OneToMany(()=>BaseArticleVersionEntity, (articleVersion)=>articleVersion.article)
], BaseArticleEntity.prototype, "versions", void 0);
_ts_decorate$9([
typeorm.ManyToMany(()=>BaseCategoryEntity, (category)=>category.articles),
typeorm.JoinTable({
name: 'article_categories',
joinColumn: {
name: 'articleId',
referencedColumnName: 'id'
},
inverseJoinColumn: {
name: 'categoryId',
referencedColumnName: 'id'
}
})
], BaseArticleEntity.prototype, "categories", void 0);
BaseArticleEntity = _ts_decorate$9([
typeorm.Entity('articles'),
typeorm.TableInheritance({
column: {
type: 'varchar',
name: 'entityName'
}
})
], BaseArticleEntity);
function _ts_decorate$8(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const CategoryRelationRepo = Symbol('CategoryRelationRepo');
class CategoryRelationEntity {
parentId;
childId;
}
_ts_decorate$8([
typeorm.PrimaryColumn('uuid')
], CategoryRelationEntity.prototype, "parentId", void 0);
_ts_decorate$8([
typeorm.PrimaryColumn('uuid')
], CategoryRelationEntity.prototype, "childId", void 0);
CategoryRelationEntity = _ts_decorate$8([
typeorm.Entity('category_relations')
], CategoryRelationEntity);
function _ts_decorate$7(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const ArticleCategoryRepo = Symbol('ArticleCategoryRepo');
class ArticleCategoryEntity {
articleId;
categoryId;
}
_ts_decorate$7([
typeorm.PrimaryColumn('uuid')
], ArticleCategoryEntity.prototype, "articleId", void 0);
_ts_decorate$7([
typeorm.PrimaryColumn('uuid')
], ArticleCategoryEntity.prototype, "categoryId", void 0);
ArticleCategoryEntity = _ts_decorate$7([
typeorm.Entity('article_categories')
], ArticleCategoryEntity);
function _ts_decorate$6(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
const models = [
[
BaseArticleRepo,
BaseArticleEntity
],
[
BaseArticleVersionRepo,
BaseArticleVersionEntity
],
[
BaseArticleVersionContentRepo,
BaseArticleVersionContentEntity
],
[
BaseCategoryRepo,
BaseCategoryEntity
],
[
BaseCategoryMultiLanguageNameRepo,
BaseCategoryMultiLanguageNameEntity
],
[
BaseSignatureLevelRepo,
BaseSignatureLevelEntity
],
[
CategoryRelationRepo,
CategoryRelationEntity
],
[
ArticleCategoryRepo,
ArticleCategoryEntity
],
[
ArticleSignatureRepo,
ArticleSignatureEntity
]
];
class CMSBaseModelsModule {
}
CMSBaseModelsModule = _ts_decorate$6([
common.Module({
imports: [
typeorm$1.TypeOrmModule.forFeature(models.map((model)=>model[1]))
],
providers: models.map(([symbol, entity])=>({
provide: symbol,
useFactory: (dataSource)=>dataSource.getRepository(entity),
inject: [
typeorm.DataSource
]
})),
exports: models.map((model)=>model[0])
})
], CMSBaseModelsModule);
const CMS_BASE_MODULE_OPTIONS = Symbol('CMS_BASE_MODULE_OPTIONS');
const MULTIPLE_LANGUAGE_MODE = Symbol('MULTIPLE_LANGUAGE_MODE');
const MULTIPLE_CATEGORY_PARENT_MODE = Symbol('MULTIPLE_CATEGORY_PARENT_MODE');
const CIRCULAR_CATEGORY_MODE = Symbol('CIRCULAR_CATEGORY_MODE');
const FULL_TEXT_SEARCH_MODE = Symbol('FULL_TEXT_SEARCH_MODE');
const ENABLE_SIGNATURE_MODE = Symbol('ENABLE_SIGNATURE_MODE');
const SIGNATURE_LEVELS = Symbol('SIGNATURE_LEVELS');
const DRAFT_MODE = Symbol('DRAFT_MODE');
const AUTO_RELEASE_AFTER_APPROVED = Symbol('AUTO_RELEASE_AFTER_APPROVED');
// Options Entity Providers
const PROVIDE_ARTICLE_ENTITY = Symbol('PROVIDE_ARTICLE_ENTITY');
const PROVIDE_ARTICLE_VERSION_ENTITY = Symbol('PROVIDE_ARTICLE_VERSION_ENTITY');
const PROVIDE_ARTICLE_VERSION_CONTENT_ENTITY = Symbol('PROVIDE_ARTICLE_VERSION_CONTENT_ENTITY');
const PROVIDE_CATEGORY_ENTITY = Symbol('PROVIDE_CATEGORY_ENTITY');
const PROVIDE_CATEGORY_MULTI_LANGUAGE_NAME_ENTITY = Symbol('PROVIDE_CATEGORY_MULTI_LANGUAGE_NAME_ENTITY');
const PROVIDE_SIGNATURE_LEVEL_ENTITY = Symbol('PROVIDE_SIGNATURE_LEVEL_ENTITY');
// Resolved Entity Repository Providers
const RESOLVED_ARTICLE_REPO = Symbol('RESOLVED_ARTICLE_REPO');
const RESOLVED_ARTICLE_VERSION_REPO = Symbol('RESOLVED_ARTICLE_VERSION_REPO');
const RESOLVED_ARTICLE_VERSION_CONTENT_REPO = Symbol('RESOLVED_ARTICLE_VERSION_CONTENT_REPO');
const RESOLVED_CATEGORY_REPO = Symbol('RESOLVED_CATEGORY_REPO');
const RESOLVED_CATEGORY_MULTI_LANGUAGE_NAME_REPO = Symbol('RESOLVED_CATEGORY_MULTI_LANGUAGE_NAME_REPO');
const RESOLVED_SIGNATURE_LEVEL_REPO = Symbol('RESOLVED_SIGNATURE_LEVEL_REPO');
// Internal Use Injection Token
const CATEGORY_DATA_LOADER = Symbol('CATEGORY_DATA_LOADER');
const ARTICLE_SIGNATURE_SERVICE = Symbol('ARTICLE_SIGNATURE_SERVICE');
var ArticleSorter = /*#__PURE__*/ function(ArticleSorter) {
ArticleSorter["CREATED_AT_DESC"] = "CREATED_AT_DESC";
ArticleSorter["CREATED_AT_ASC"] = "CREATED_AT_ASC";
return ArticleSorter;
}({});
class MultipleLanguageModeIsDisabledError extends common.BadRequestException {
constructor(){
super('Multiple language mode is disabled');
}
code = 100;
}
class ArticleNotFoundError extends common.BadRequestException {
constructor(){
super('Article not found');
}
code = 200;
}
class ArticleVersionNotFoundError extends common.BadRequestException {
constructor(){
super('Article version not found');
}
code = 201;
}
class CategoryNotFoundError extends common.BadRequestException {
constructor(){
super('Category not found');
}
code = 300;
}
class CircularCategoryNotAllowedError extends common.BadRequestException {
constructor(){
super('Circular category is not allowed');
}
code = 301;
}
class MultipleParentCategoryNotAllowedError extends common.BadRequestException {
constructor(){
super('Multiple parent categories not allowed, please enable on module forRoot options');
}
code = 302;
}
class ParentCategoryNotFoundError extends common.BadRequestException {
constructor(){
super('Parent category not found');
}
code = 303;
}
var ArticleSearchMode = /*#__PURE__*/ function(ArticleSearchMode) {
ArticleSearchMode["FULL_TEXT"] = "full_text";
ArticleSearchMode["TITLE"] = "title";
ArticleSearchMode["TITLE_AND_TAG"] = "title-and-tag";
return ArticleSearchMode;
}({});
const ArticleNotIncludeFields = [
'versions',
'categories'
];
const ArticleVersionNotIncludeFields = [
...ArticleNotIncludeFields,
'articleId',
'createdAt',
'deletedAt',
'article',
'multiLanguageContents'
];
const ArticleVersionContentNotIncludeFields = [
...ArticleVersionNotIncludeFields,
'version',
'language',
'articleVersion'
];
var ArticleFindVersionType = /*#__PURE__*/ function(ArticleFindVersionType) {
ArticleFindVersionType["LATEST"] = "LATEST";
ArticleFindVersionType["RELEASED"] = "RELEASED";
return ArticleFindVersionType;
}({});
/* eslint-disable quotes */ function _ts_decorate$5(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function _ts_param$4(paramIndex, decorator) {
return function(target, key) {
decorator(target, key, paramIndex);
};
}
class ArticleBaseService {
baseArticleRepo;
baseArticleVersionRepo;
baseArticleVersionContentRepo;
baseCategoryRepo;
multipleLanguageMode;
fullTextSearchMode;
signatureMode;
articleSignatureService;
draftMode;
dataSource;
constructor(baseArticleRepo, baseArticleVersionRepo, baseArticleVersionContentRepo, baseCategoryRepo, multipleLanguageMode, fullTextSearchMode, signatureMode, articleSignatureService, draftMode, dataSource){
this.baseArticleRepo = baseArticleRepo;
this.baseArticleVersionRepo = baseArticleVersionRepo;
this.baseArticleVersionContentRepo = baseArticleVersionContentRepo;
this.baseCategoryRepo = baseCategoryRepo;
this.multipleLanguageMode = multipleLanguageMode;
this.fullTextSearchMode = fullTextSearchMode;
this.signatureMode = signatureMode;
this.articleSignatureService = articleSignatureService;
this.draftMode = draftMode;
this.dataSource = dataSource;
this.logger = new common.Logger(ArticleBaseService.name);
}
logger;
getDefaultQueryBuilder(alias = 'articles', options) {
const qb = this.baseArticleRepo.createQueryBuilder(alias);
qb.leftJoinAndSelect(`${alias}.categories`, 'categories');
qb.innerJoinAndSelect(`${alias}.versions`, 'versions');
qb.innerJoinAndSelect('versions.multiLanguageContents', 'multiLanguageContents');
qb.innerJoin((subQb)=>{
subQb.from(BaseArticleVersionEntity, 'versions');
subQb.select('versions.articleId', 'articleId');
subQb.addSelect('MAX(versions.version)', 'version');
subQb.groupBy('versions.articleId');
if (this.draftMode && options?.versionType === ArticleFindVersionType.RELEASED) {
subQb.andWhere('versions.releasedAt IS NOT NULL');
}
if (this.signatureMode && options?.onlyApproved && options?.signatureLevel) {
this.logger.debug(`When signature level provided with onlyApproved, only signature level will be used.`);
}
if (this.signatureMode && options?.signatureLevel) {
subQb.innerJoin('versions.signatures', 'signatures');
subQb.andWhere('signatures.result = :result', {
result: ArticleSignatureResult.APPROVED
});
subQb.andWhere('signatures.signatureLevelId = :signatureLevelId', {
signatureLevelId: options.signatureLevel
});
} else if (this.signatureMode && options?.onlyApproved) {
subQb.innerJoin('versions.signatures', 'signatures');
subQb.andWhere('signatures.result = :result', {
result: ArticleSignatureResult.APPROVED
});
const latestId = this.articleSignatureService.finalSignatureLevel?.id;
if (latestId) {
subQb.andWhere('signatures.signatureLevelId = :signatureLevelId', {
signatureLevelId: latestId
});
} else {
subQb.andWhere('signatures.signatureLevelId IS NULL');
}
}
return subQb;
}, 'target', 'target.version = versions.version AND target."articleId" = versions."articleId"');
return qb;
}
async bindSearchTokens(articleContent, tags, runner) {
const { cut } = await import('@node-rs/jieba');
const tokens = cut(articleContent.content.filter((content)=>content.type === 'p').map((content)=>content.children.filter((child)=>child.text).map((child)=>child.text).join('\n')).join('\n'));
await (runner ?? this.dataSource).query(`UPDATE "${this.baseArticleVersionContentRepo.metadata.tableName}" SET
"searchTokenVersion" = '${FULL_TEXT_SEARCH_TOKEN_VERSION}',
"searchTokens" = setweight(to_tsvector('simple', $1), 'A') || setweight(to_tsvector('simple', $2), 'B') || setweight(to_tsvector('simple', $3), 'C') || setweight(to_tsvector('simple', $4), 'D')
WHERE "articleId" = $5 AND "version" = $6 AND "language" = $7`, [
articleContent.title,
tags?.join(' ') ?? '',
articleContent.description ?? '',
tokens.join(' ') ?? '',
articleContent.articleId,
articleContent.version,
articleContent.language
]);
}
async onApplicationBootstrap() {
// Auto indexing if full text search mode enabled
if (this.fullTextSearchMode) {
const qb = this.getDefaultQueryBuilder('articles');
qb.orWhere('multiLanguageContents.searchTokens IS NULL');
qb.orWhere('multiLanguageContents.searchTokenVersion != :searchTokenVersion', {
searchTokenVersion: FULL_TEXT_SEARCH_TOKEN_VERSION
});
const articles = await qb.getMany();
if (articles.length) {
this.logger.log('Start indexing articles...');
await articles.map((article)=>()=>this.bindSearchTokens(article.versions[0].multiLanguageContents[0], article.versions[0].tags)).reduce((prev, next)=>prev.then(next), Promise.resolve());
this.logger.log('Indexing articles done.');
}
await this.dataSource.query(`CREATE INDEX IF NOT EXISTS "article_version_contents_search_tokens_idx" ON "${this.baseArticleVersionContentRepo.metadata.tableName}" USING GIN ("searchTokens")`);
}
}
async findById(id, options) {
if (options?.language && !this.multipleLanguageMode) {
throw new MultipleLanguageModeIsDisabledError();
}
const qb = this.getDefaultQueryBuilder('articles', {
versionType: (this.draftMode ? options?.versionType : ArticleFindVersionType.RELEASED) ?? ArticleFindVersionType.RELEASED,
onlyApproved: options?.onlyApproved
});
qb.andWhere('articles.id = :id', {
id
});
if (options?.language) {
qb.andWhere('multiLanguageContents.language = :language', {
language: options.language
});
}
const article = await qb.getOne();
if (!article) {
throw new ArticleNotFoundError();
}
if (options?.language || !this.multipleLanguageMode) {
const defaultContent = article.versions[0].multiLanguageContents.find((content)=>content.language === (options?.language || DEFAULT_LANGUAGE));
return {
...article,
versions: undefined,
...defaultContent,
id: article.id,
version: article.versions[0].version,
tags: article.versions[0].tags
};
}
return {
...article,
versions: undefined,
id: article.id,
tags: article.versions[0].tags,
version: article.versions[0].version,
multiLanguageContents: article.versions[0].multiLanguageContents
};
}
async getFindAllQueryBuilder(options) {
const qb = this.getDefaultQueryBuilder('articles', {
versionType: (this.draftMode ? options?.versionType : ArticleFindVersionType.RELEASED) ?? ArticleFindVersionType.RELEASED,
onlyApproved: options?.onlyApproved,
signatureLevel: options?.signatureLevel
});
if (options?.ids?.length) {
qb.andWhere('articles.id IN (:...ids)', {
ids: options.ids
});
}
if (options?.language) {
qb.andWhere('multiLanguageContents.language = :language', {
language: options.language
});
}
if (options?.requiredCategoryIds?.length) {
const relationMetadata = this.baseArticleRepo.metadata.manyToManyRelations.find((relation)=>`${this.baseArticleRepo.metadata.schema}.${relation.propertyPath}` === this.baseCategoryRepo.metadata.tablePath)?.junctionEntityMetadata;
options?.requiredCategoryIds?.forEach((categoryId, index)=>{
const relationQb = this.dataSource.createQueryBuilder();
relationQb.from(`${this.baseArticleRepo.metadata.schema}.${relationMetadata?.tableName}`, `requiredCategoryRelations${index}`);
relationQb.andWhere(`"requiredCategoryRelations${index}"."categoryId" = :requiredCategoryId${index}`, {
[`requiredCategoryId${index}`]: categoryId
});
relationQb.andWhere(`"requiredCategoryRelations${index}"."articleId" = articles.id`);
qb.andWhereExists(relationQb);
});
}
if (options?.categoryIds?.length) {
const relationMetadata = this.baseArticleRepo.metadata.manyToManyRelations.find((relation)=>`${this.baseArticleRepo.metadata.schema}.${relation.propertyPath}` === this.baseCategoryRepo.metadata.tablePath)?.junctionEntityMetadata;
const relationQb = this.dataSource.createQueryBuilder();
relationQb.from(`${this.baseArticleRepo.metadata.schema}.${relationMetadata?.tableName}`, `categoryRelations`);
relationQb.andWhere('"categoryRelations"."categoryId" IN (:...categoryIds)', {
categoryIds: options.categoryIds
});
relationQb.andWhere(`"categoryRelations"."articleId" = articles.id`);
qb.andWhereExists(relationQb);
}
if (options?.searchTerm) {
switch(options?.searchMode){
case ArticleSearchMode.FULL_TEXT:
{
if (!this.fullTextSearchMode) throw new Error('Full text search is disabled.');
const { cut } = await import('@node-rs/jieba');
const tokens = cut(options.searchTerm.trim());
const searchQb = this.dataSource.createQueryBuilder();
searchQb.from(`${this.baseArticleVersionContentRepo.metadata.schema}.${this.baseArticleVersionContentRepo.metadata.tableName}`, 'contents');
searchQb.andWhere("contents.searchTokens @@ to_tsquery('simple', :searchTerm)", {
searchTerm: tokens.join('|')
});
searchQb.andWhere('contents."entityName" = :entityName', {
entityName: this.baseArticleVersionContentRepo.metadata.targetName
});
searchQb.andWhere('contents."articleId" = "versions"."articleId"');
searchQb.andWhere('contents."version" = "versions"."version"');
qb.andWhereExists(searchQb);
break;
}
case ArticleSearchMode.TITLE_AND_TAG:
case ArticleSearchMode.TITLE:
default:
{
const searchQb = this.dataSource.createQueryBuilder();
searchQb.from(`${this.baseArticleVersionContentRepo.metadata.schema}.${this.baseArticleVersionContentRepo.metadata.tableName}`, 'contents');
if (options?.searchMode === ArticleSearchMode.TITLE_AND_TAG) {
searchQb.orWhere(':tagSearchTerm = ANY (SELECT LOWER(value) FROM jsonb_array_elements_text(versions.tags))', {
tagSearchTerm: `${options.searchTerm?.toLocaleLowerCase()}`
});
}
searchQb.orWhere('contents.title ILIKE :searchTerm', {
searchTerm: `%${options.searchTerm}%`
});
searchQb.orWhere('contents.description ILIKE :searchTerm', {
searchTerm: `%${options.searchTerm}%`
});
searchQb.andWhere('contents."entityName" = :entityName', {
entityName: this.baseArticleVersionContentRepo.metadata.targetName
});
searchQb.andWhere('contents."articleId" = "versions"."articleId"');
searchQb.andWhere('contents."version" = "versions"."version"');
qb.andWhereExists(searchQb);
break;
}
}
}
switch(options?.sorter){
case ArticleSorter.CREATED_AT_ASC:
qb.addOrderBy('articles.createdAt', 'ASC');
break;
case ArticleSorter.CREATED_AT_DESC:
default:
qb.addOrderBy('articles.createdAt', 'DESC');
break;
}
return qb;
}
async findCollection(options) {
if (options?.language && !this.multipleLanguageMode) {
throw new MultipleLanguageModeIsDisabledError();
}
const qb = await this.getFindAllQueryBuilder(options);
qb.skip(options?.offset ?? 0);
qb.take(Math.min(options?.limit ?? 20, 100));
const [articles, total] = await qb.getManyAndCount();
if (options?.language || !this.multipleLanguageMode) {
return {
articles: articles.map((article)=>{
const defaultContent = article.versions[0].multiLanguageContents.find((content)=>content.language === (options?.language || DEFAULT_LANGUAGE));
return {
...article,
versions: undefined,
...defaultContent,
id: article.id,
tags: article.versions[0].tags
};
}),
total,
offset: options?.offset ?? 0,
limit: Math.min(options?.limit ?? 20, 100)
};
}
return {
articles: articles.map((article)=>({
...article,
versions: undefined,
version: article.versions[0].version,
tags: article.versions[0].tags,
multiLanguageContents: article.versions[0].multiLanguageContents
})),
total,
offset: options?.offset ?? 0,
limit: Math.min(options?.limit ?? 20, 100)
};
}
async findAll(options) {
if (options?.language && !this.multipleLanguageMode) {
throw new MultipleLanguageModeIsDisabledError();
}
const qb = await this.getFindAllQueryBuilder(options);
qb.skip(options?.offset ?? 0);
qb.take(Math.min(options?.limit ?? 20, 100));
const articles = await qb.getMany();
if (options?.language || !this.multipleLanguageMode) {
return articles.map((article)=>{
const defaultContent = article.versions[0].multiLanguageContents.find((content)=>content.language === (options?.language || DEFAULT_LANGUAGE));
return {
...article,
versions: undefined,
...defaultContent,
id: article.id,
tags: article.versions[0].tags
};
});
}
return articles.map((article)=>({
...article,
versions: undefined,
version: article.versions[0].version,
tags: article.versions[0].tags,
multiLanguageContents: article.versions[0].multiLanguageContents
}));
}
async archive(id) {
const article = await this.baseArticleRepo.findOne({
where: {
id
}
});
if (!article) {
throw new ArticleNotFoundError();
}
await this.baseArticleRepo.softDelete(id);
}
async release(id, releasedAt) {
if (!this.draftMode) {
throw new Error('Draft mode is disabled.');
}
const article = await this.findById(id, {
versionType: ArticleFindVersionType.LATEST
});
if (article.releasedAt) {
this.logger.debug(`Article ${id} is already released [${article.version}] at ${article.releasedAt}.`);
return article;
}
this.logger.debug(`Release article ${id} [${article.version}]`);
const willReleasedAt = releasedAt ?? new Date();
await this.baseArticleVersionRepo.update({
articleId: id,
version: article.version
}, {
releasedAt: willReleasedAt
});
article.releasedAt = willReleasedAt;
return article;
}
async addVersion(id, options) {
const targetCategories = options?.categoryIds?.length ? await this.baseCategoryRepo.find({
where: {
id: typeorm.In(options.categoryIds),
bindable: true
}
}) : [];
if (targetCategories.length !== (options?.categoryIds?.length ?? 0)) {
throw new CategoryNotFoundError();
}
const article = await this.baseArticleRepo.findOne({
where: {
id
},
relations: [
'categories'
]
});
if (!article) {
throw new ArticleNotFoundError();
}
if (article.categories.length && !options.categoryIds) {
this.logger.warn(`Article ${id} has categories, but no categoryIds provided when add version. The article categories will no change after version added.`);
}
const latestQb = this.baseArticleVersionRepo.createQueryBuilder('articleVersions');
latestQb.andWhere('articleVersions.articleId = :id', {
id
});
latestQb.addOrderBy('articleVersions.version', 'DESC');
const latestVersion = await latestQb.getOne();
if (!latestVersion) {
throw new ArticleVersionNotFoundError();
}
const runner = this.dataSource.createQueryRunner();
await runner.connect();
await runner.startTransaction();
try {
await runner.manager.save(this.baseArticleRepo.create({
...article,
...Object.entries(options).filter(([key])=>!~ArticleNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
...options.categoryIds ? {
categories: targetCategories
} : {}
}));
const version = this.baseArticleVersionRepo.create({
...Object.entries(options).filter(([key])=>!~ArticleVersionNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
articleId: article.id,
version: latestVersion.version + 1,
tags: options.tags ?? [],
releasedAt: this.draftMode ? options.releasedAt : new Date()
});
await runner.manager.save(version);
if ('multiLanguageContents' in options) {
if (!this.multipleLanguageMode) throw new MultipleLanguageModeIsDisabledError();
const savedContents = await runner.manager.save(Object.entries(options.multiLanguageContents).map(([language, content])=>this.baseArticleVersionContentRepo.create({
...content,
articleId: article.id,
version: latestVersion.version + 1,
language
})));
if (this.fullTextSearchMode) {
await savedContents.map((articleContent)=>()=>this.bindSearchTokens(articleContent, options.tags ?? [], runner)).reduce((prev, next)=>prev.then(next), Promise.resolve());
}
} else {
const savedContent = await runner.manager.save(this.baseArticleVersionContentRepo.create({
...Object.entries(options).filter(([key])=>!~ArticleVersionContentNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
articleId: article.id,
version: latestVersion.version + 1,
language: DEFAULT_LANGUAGE
}));
if (this.fullTextSearchMode) {
await this.bindSearchTokens(savedContent, options.tags ?? [], runner);
}
}
await runner.commitTransaction();
return article;
} catch (ex) {
await runner.rollbackTransaction();
throw new common.BadRequestException(ex);
} finally{
await runner.release();
}
}
async create(options) {
const targetCategories = options?.categoryIds?.length ? await this.baseCategoryRepo.find({
where: {
id: typeorm.In(options.categoryIds),
bindable: true
}
}) : [];
if (targetCategories.length !== (options?.categoryIds?.length ?? 0)) {
throw new CategoryNotFoundError();
}
const article = this.baseArticleRepo.create({
...Object.entries(options).filter(([key])=>!~ArticleNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
categories: targetCategories
});
const runner = this.dataSource.createQueryRunner();
await runner.connect();
await runner.startTransaction();
try {
await runner.manager.save(article);
const version = this.baseArticleVersionRepo.create({
...Object.entries(options).filter(([key])=>!~ArticleVersionNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
articleId: article.id,
tags: options.tags ?? [],
releasedAt: this.draftMode ? options.releasedAt : new Date()
});
await runner.manager.save(version);
if ('multiLanguageContents' in options) {
if (!this.multipleLanguageMode) throw new MultipleLanguageModeIsDisabledError();
const savedContents = await runner.manager.save(Object.entries(options.multiLanguageContents).map(([language, content])=>this.baseArticleVersionContentRepo.create({
...content,
articleId: article.id,
version: version.version,
language
})));
if (this.fullTextSearchMode) {
await savedContents.map((articleContent)=>()=>this.bindSearchTokens(articleContent, options.tags ?? [], runner)).reduce((prev, next)=>prev.then(next), Promise.resolve());
}
} else {
const savedContent = await runner.manager.save(this.baseArticleVersionContentRepo.create({
...Object.entries(options).filter(([key])=>!~ArticleVersionContentNotIncludeFields.indexOf(key)).reduce((vars, [key, value])=>({
...vars,
[key]: value
}), {}),
articleId: article.id,
version: version.version,
language: DEFAULT_LANGUAGE
}));
if (this.fullTextSearchMode) {
await this.bindSearchTokens(savedContent, options.tags ?? [], runner);
}
}
await runner.commitTransaction();
return article;
} catch (ex) {
await runner.rollbackTransaction();
throw new common.BadRequestException(ex);
} finally{
await runner.release();
}
}
}
ArticleBase