feat: add styling and typography method for macors
This commit is contained in:
@@ -6,8 +6,7 @@
|
|||||||
* @FilePath: /server/apps/server/src/auth/roles.guard.ts
|
* @FilePath: /server/apps/server/src/auth/roles.guard.ts
|
||||||
* Mark: Coding with Love
|
* Mark: Coding with Love
|
||||||
*/
|
*/
|
||||||
import { CanActivate, ExecutionContext } from '@nestjs/common'
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
|
||||||
import { Injectable } from '@nestjs/common'
|
|
||||||
import { AuthGuard } from '@nestjs/passport'
|
import { AuthGuard } from '@nestjs/passport'
|
||||||
|
|
||||||
import { isTest } from '~/global/env.global'
|
import { isTest } from '~/global/env.global'
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export class MarkdownController {
|
|||||||
const { id } = params
|
const { id } = params
|
||||||
const now = performance.now()
|
const now = performance.now()
|
||||||
const [
|
const [
|
||||||
{ html: markdown, document, type },
|
{ html: markdownMacros, document, type },
|
||||||
{
|
{
|
||||||
url: { webUrl },
|
url: { webUrl },
|
||||||
},
|
},
|
||||||
@@ -212,9 +212,7 @@ export class MarkdownController {
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
const url = new URL(relativePath!, webUrl)
|
const url = new URL(relativePath!, webUrl)
|
||||||
const markdownMacros = this.service.renderMarkdownContent(
|
|
||||||
await this.macroService.replaceTextMacro(markdown, document),
|
|
||||||
)
|
|
||||||
const structure = await this.service.getRenderedMarkdownHtmlStructure(
|
const structure = await this.service.getRenderedMarkdownHtmlStructure(
|
||||||
markdownMacros,
|
markdownMacros,
|
||||||
document.title,
|
document.title,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ReturnModelType } from '@typegoose/typegoose'
|
|||||||
|
|
||||||
import { DatabaseService } from '~/processors/database/database.service'
|
import { DatabaseService } from '~/processors/database/database.service'
|
||||||
import { AssetService } from '~/processors/helper/helper.asset.service'
|
import { AssetService } from '~/processors/helper/helper.asset.service'
|
||||||
|
import { TextMacroService } from '~/processors/helper/helper.macro.service'
|
||||||
import { InjectModel } from '~/transformers/model.transformer'
|
import { InjectModel } from '~/transformers/model.transformer'
|
||||||
|
|
||||||
import { CategoryModel } from '../category/category.model'
|
import { CategoryModel } from '../category/category.model'
|
||||||
@@ -39,6 +40,8 @@ export class MarkdownService {
|
|||||||
private readonly pageModel: ReturnModelType<typeof PageModel>,
|
private readonly pageModel: ReturnModelType<typeof PageModel>,
|
||||||
|
|
||||||
private readonly databaseService: DatabaseService,
|
private readonly databaseService: DatabaseService,
|
||||||
|
|
||||||
|
private readonly macroService: TextMacroService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async insertPostsToDb(data: DatatypeDto[]) {
|
async insertPostsToDb(data: DatatypeDto[]) {
|
||||||
@@ -223,7 +226,12 @@ ${text.trim()}
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
html: doc.document.text,
|
html: this.renderMarkdownContent(
|
||||||
|
await this.macroService.replaceTextMacro(
|
||||||
|
doc.document.text,
|
||||||
|
doc.document,
|
||||||
|
),
|
||||||
|
),
|
||||||
...doc,
|
...doc,
|
||||||
document: doc.document,
|
document: doc.document,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { FastifyRequest } from 'fastify'
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
|
|
||||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common'
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
Logger,
|
||||||
|
Scope,
|
||||||
|
} from '@nestjs/common'
|
||||||
|
import { REQUEST } from '@nestjs/core'
|
||||||
|
|
||||||
|
import { UserModel } from '~/modules/user/user.model'
|
||||||
import { deepCloneWithFunction } from '~/utils'
|
import { deepCloneWithFunction } from '~/utils'
|
||||||
import { safeEval } from '~/utils/safe-eval.util'
|
import { safeEval } from '~/utils/safe-eval.util'
|
||||||
|
|
||||||
@Injectable()
|
const logger = new Logger('TextMacroService')
|
||||||
export class TextMacroService {
|
const RegMap = {
|
||||||
private readonly logger: Logger
|
'#': /^#(.*?)$/g,
|
||||||
constructor() {
|
$: /^\$(.*?)$/g,
|
||||||
this.logger = new Logger(TextMacroService.name)
|
'?': /^\?\??(.*?)\??\?$/g,
|
||||||
}
|
} as const
|
||||||
static readonly Reg = {
|
|
||||||
'#': /^#(.*?)$/g,
|
|
||||||
$: /^\$(.*?)$/g,
|
|
||||||
'?': /^\?\??(.*?)\??\?$/g,
|
|
||||||
}
|
|
||||||
|
|
||||||
private ifConditionGrammar<T extends object>(text: string, model: T) {
|
class HelperStatic {
|
||||||
|
public ifConditionGrammar<T extends object>(text: string, model: T) {
|
||||||
const conditionSplitter = text.split('|')
|
const conditionSplitter = text.split('|')
|
||||||
conditionSplitter.forEach((item: string, index: string | number) => {
|
conditionSplitter.forEach((item: string, index: string | number) => {
|
||||||
conditionSplitter[index] = item.replace(/"/g, '')
|
conditionSplitter[index] = item.replace(/"/g, '')
|
||||||
@@ -66,9 +71,42 @@ export class TextMacroService {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private generateFunctionContext = (variables: object) => {
|
||||||
|
return {
|
||||||
|
// time utils
|
||||||
|
dayjs: deepCloneWithFunction(dayjs),
|
||||||
|
fromNow: (time: Date | string) => dayjs(time).fromNow(),
|
||||||
|
|
||||||
|
// typography
|
||||||
|
center: (text: string) => {
|
||||||
|
return `<p align="center">${text}</p>`
|
||||||
|
},
|
||||||
|
right: (text: string) => {
|
||||||
|
return `<p align="right">${text}</p>`
|
||||||
|
},
|
||||||
|
|
||||||
|
// styling
|
||||||
|
opacity: (text: string, opacity = 0.8) => {
|
||||||
|
return `<span style="opacity: ${opacity}">${text}</span>`
|
||||||
|
},
|
||||||
|
blur: (text: string, blur = 1) => {
|
||||||
|
return `<span style="filter: blur(${blur}px)">${text}</span>`
|
||||||
|
},
|
||||||
|
color: (text: string, color = '') => {
|
||||||
|
return `<span style="color: ${color}">${text}</span>`
|
||||||
|
},
|
||||||
|
size: (text: string, size = '1em') => {
|
||||||
|
return `<span style="font-size: ${size}">${text}</span>`
|
||||||
|
},
|
||||||
|
|
||||||
|
...variables,
|
||||||
|
}
|
||||||
|
}
|
||||||
public async replaceTextMacro<T extends object>(
|
public async replaceTextMacro<T extends object>(
|
||||||
text: string,
|
text: string,
|
||||||
model: T,
|
model: T,
|
||||||
|
|
||||||
|
extraContext: Record<string, any> = {},
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const matchedReg = /\[\[\s(.*?)\s\]\]/g
|
const matchedReg = /\[\[\s(.*?)\s\]\]/g
|
||||||
@@ -88,19 +126,19 @@ export class TextMacroService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
condition = condition?.trim()
|
condition = condition?.trim()
|
||||||
if (condition.search(TextMacroService.Reg['?']) != -1) {
|
if (condition.search(RegMap['?']) != -1) {
|
||||||
return this.ifConditionGrammar(condition, model)
|
return helper.ifConditionGrammar(condition, model)
|
||||||
}
|
}
|
||||||
if (condition.search(TextMacroService.Reg['$']) != -1) {
|
if (condition.search(RegMap['$']) != -1) {
|
||||||
const variable = condition
|
const variable = condition
|
||||||
.replace(TextMacroService.Reg['$'], '$1')
|
.replace(RegMap['$'], '$1')
|
||||||
.replace(/\s/g, '')
|
.replace(/\s/g, '')
|
||||||
return model[variable]
|
return model[variable] ?? extraContext[variable]
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
if (condition.search(TextMacroService.Reg['#']) != -1) {
|
if (condition.search(RegMap['#']) != -1) {
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
const functions = condition.replace(TextMacroService.Reg['#'], '$1')
|
const functions = condition.replace(RegMap['#'], '$1')
|
||||||
|
|
||||||
const variables = Object.keys(model).reduce(
|
const variables = Object.keys(model).reduce(
|
||||||
(acc, key) => ({ [`$${key}`]: model[key], ...acc }),
|
(acc, key) => ({ [`$${key}`]: model[key], ...acc }),
|
||||||
@@ -110,12 +148,7 @@ export class TextMacroService {
|
|||||||
try {
|
try {
|
||||||
return safeEval(
|
return safeEval(
|
||||||
`return ${functions}`,
|
`return ${functions}`,
|
||||||
{
|
this.generateFunctionContext({ ...variables, ...extraContext }),
|
||||||
dayjs: deepCloneWithFunction(dayjs),
|
|
||||||
fromNow: (time: Date | string) => dayjs(time).fromNow(),
|
|
||||||
|
|
||||||
...variables,
|
|
||||||
},
|
|
||||||
{ timeout: 1000 },
|
{ timeout: 1000 },
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -126,8 +159,29 @@ export class TextMacroService {
|
|||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.log(err.message)
|
logger.log(err.message)
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const helper = new HelperStatic()
|
||||||
|
@Injectable({ scope: Scope.REQUEST })
|
||||||
|
export class TextMacroService {
|
||||||
|
constructor(
|
||||||
|
@Inject(REQUEST)
|
||||||
|
private readonly request: FastifyRequest & {
|
||||||
|
isMaster: boolean
|
||||||
|
user: UserModel
|
||||||
|
},
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public replaceTextMacro(text: string, model: object) {
|
||||||
|
const isMaster = this.request.isMaster
|
||||||
|
return helper.replaceTextMacro(text, model, {
|
||||||
|
hideForGuest(text: string) {
|
||||||
|
return isMaster ? text : ''
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user