diff --git a/src/modules/content/repositories/post.repository.ts b/src/modules/content/repositories/post.repository.ts new file mode 100644 index 0000000..80835dc --- /dev/null +++ b/src/modules/content/repositories/post.repository.ts @@ -0,0 +1,11 @@ +import { Repository } from 'typeorm'; + +import { PostEntity } from '@/modules/content/entities/post.entity'; +import { CustomRepository } from '@/modules/database/decorators/repository.decorator'; + +@CustomRepository(PostEntity) +export class PostRepository extends Repository { + buildBaseQB() { + return this.createQueryBuilder('post'); + } +} diff --git a/src/modules/content/services/SanitizeService.ts b/src/modules/content/services/SanitizeService.ts new file mode 100644 index 0000000..0c794e2 --- /dev/null +++ b/src/modules/content/services/SanitizeService.ts @@ -0,0 +1,23 @@ +import sanitizeHtml from 'sanitize-html'; + +import { deepMerge } from '@/modules/core/helpers'; + +export class SanitizeService { + protected config: sanitizeHtml.IOptions = {}; + constructor() { + this.config = { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'code']), + allowedAttributes: { + ...sanitizeHtml.defaults.allowedAttributes, + '*': ['class', 'style', 'height', 'width'], + }, + parser: { + lowerCaseTags: true, + }, + }; + } + + sanitize(body: string, options: sanitizeHtml.IOptions = {}) { + return sanitizeHtml(body, deepMerge(this.config, options ?? {}, 'replace')); + } +} diff --git a/src/modules/content/subscribers/post.subscriber.ts b/src/modules/content/subscribers/post.subscriber.ts new file mode 100644 index 0000000..de67c65 --- /dev/null +++ b/src/modules/content/subscribers/post.subscriber.ts @@ -0,0 +1,26 @@ +import { DataSource, EventSubscriber } from 'typeorm'; + +import { PostBodyType } from '@/modules/content/constants'; +import { PostEntity } from '@/modules/content/entities/post.entity'; +import { PostRepository } from '@/modules/content/repositories/post.repository'; +import { SanitizeService } from '@/modules/content/services/SanitizeService'; + +@EventSubscriber() +export class PostSubscriber { + constructor( + protected dataSource: DataSource, + protected sanitizeService: SanitizeService, + protected postRepository: PostRepository, + ) { + dataSource.subscribers.push(this); + } + listenTo() { + return PostEntity; + } + + async afterLoad(entity: PostEntity) { + if (entity.type === PostBodyType.HTML) { + entity.body = this.sanitizeService.sanitize(entity.body); + } + } +}