/** * 将JSON格式的HTML结构转换为HTML字符串 * @param {Object} json - JSON格式的HTML结构 * @returns {string} HTML字符串 */ function jsonToHtml(json) { if (!json || typeof json !== 'object') { return ''; } function parseElement(obj) { if (obj === null || obj === undefined) { return ''; } if (typeof obj === 'string') { return obj; } if (Array.isArray(obj)) { return obj.map(item => parseElement(item)).join(''); } // 检查是否为文本节点 (#text) if ('#text' in obj) { let tagName = 'div'; // 默认标签 let attributes = ''; let content = obj['#text'] || ''; // 处理属性和其他设置 for (const key in obj) { if (key === '#text') continue; if (key.startsWith('@')) { // 属性 (@class, @id等) const attrName = key.substring(1); attributes += ` ${attrName}="${obj[key]}"`; } else if (['div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section', 'header', 'footer', 'main', 'article', 'aside'].includes(key)) { // 子元素标签 tagName = key; content = parseElement(obj[key]); } } return `<${tagName}${attributes}>${content}`; } // 普通元素节点 let tagName = 'div'; // 默认标签 let attributes = ''; let content = ''; for (const key in obj) { if (key.startsWith('@')) { // 属性 (@class, @id等) const attrName = key.substring(1); attributes += ` ${attrName}="${obj[key]}"`; } else if (['div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section', 'header', 'footer', 'main', 'article', 'aside', 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'td', 'th'].includes(key)) { // 明确指定的标签类型 tagName = key; content = parseElement(obj[key]); } else if (key === 'style') { // style标签特殊处理 content += ``; } else if (key === 'title') { // title标签特殊处理 content += `${obj[key]}`; } else if (typeof obj[key] === 'object' && obj[key] !== null) { // 其他嵌套对象 content += parseElement(obj[key]); } else if (key !== '#text') { // 其他属性 attributes += ` ${key}="${obj[key]}"`; } } return `<${tagName}${attributes}>${content}`; } // 处理顶层html结构 if (json.html) { const headContent = json.html.head ? parseElement(json.html.head) : ''; const bodyContent = json.html.body ? parseElement(json.html.body) : ''; const headSection = headContent ? `${headContent}` : ''; const bodySection = bodyContent ? `${bodyContent}` : ''; return `${headSection}${bodySection}`; } if (json.head || json.body) { const headContent = json.head ? parseElement(json.head) : ''; const bodyContent = json.body ? parseElement(json.body) : ''; const headSection = headContent ? `${headContent}` : ''; const bodySection = bodyContent ? `${bodyContent}` : ''; return `${headSection}${bodySection}`; } return parseElement(json); } /** * 生成随机字符串 * @param {number} length - 字符串长度 * @returns {string} 随机字符串 */ function randomString(length) { let result = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } /** * Sanitizes HTML content by removing potentially dangerous elements and attributes * @param {string} html - HTML content to sanitize * @returns {string} Sanitized HTML content */ async function sanitizeHtml(html) { if (typeof html !== 'string') { return ''; } // Dynamically import DOMPurify and JSDOM const { JSDOM } = await import('jsdom'); const { default: DOMPurify } = await import('dompurify'); // Create a window object from jsdom for DOMPurify const window = new JSDOM('').window; const purify = DOMPurify(window); // Define allowed tags and attributes for posters const allowedTags = [ 'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section', 'header', 'footer', 'main', 'article', 'aside', 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'strong', 'em', 'b', 'i', 'u', 's', 'small', 'sub', 'sup', 'a', 'img', 'br', 'hr', 'pre', 'code', 'blockquote', 'cite', 'figure', 'figcaption', 'mark', 'time', 'address', 'dl', 'dt', 'dd' ]; const allowedAttributes = [ 'class', 'id', 'style', 'title', 'alt', 'href', 'src', 'width', 'height', 'colspan', 'rowspan', 'align', 'valign', 'scope', 'headers', 'abbr', 'datetime', 'cite', 'rel', 'target', 'name', 'value', 'type' ]; // Sanitize the HTML const sanitized = purify.sanitize(html, { ALLOWED_TAGS: allowedTags, ALLOWED_ATTR: allowedAttributes, FORBID_TAGS: ['script', 'object', 'embed', 'form', 'input', 'button', 'textarea', 'select', 'option', 'iframe', 'frame', 'frameset', 'applet', 'base', 'meta', 'link', 'noscript'], FORBID_ATTR: ['on*', 'src*', 'href*', 'action*', 'data*', 'vbscript*', 'javascript*', 'expression', 'behavior', 'xmlns'] }); return sanitized; } export { jsonToHtml, randomString, sanitizeHtml };