UNPKG

@rytass/cms-base-nestjs-module

Version:

Rytass Content Management System NestJS Base Module

1,240 lines (1,216 loc) 90.5 kB
'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