diff --git a/README.md b/README.md index 7b5eb16..e14c5b9 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,6 @@ For instance, the [giscus](https://giscus.app) is an opinionated choice. - [ ] Check article grammar errors by using ChatGPT. Remain **54** posts. - [ ] Add music to the articles. Remain **54** posts. -- [ ] Clean up the legacy links in the weblog comments. - [ ] External the article inner links with different target. - [ ] Slide share components integration. diff --git a/src/components/comment/artalk.ts b/src/components/comment/artalk.ts index 3b15c10..0cf21a2 100644 --- a/src/components/comment/artalk.ts +++ b/src/components/comment/artalk.ts @@ -14,7 +14,7 @@ import { getSecret } from 'astro:env/server'; import _ from 'lodash'; import { marked } from 'marked'; import * as querystring from 'node:querystring'; -import { transform } from 'ultrahtml'; +import { ELEMENT_NODE, transform, walk } from 'ultrahtml'; import sanitize from 'ultrahtml/transformers/sanitize'; // Access the artalk in internal docker host when it was deployed on zeabur. @@ -104,6 +104,18 @@ const parseContent = async (content: string): Promise => { const parsed = await marked.parse(escapedContent); // Avoid the XSS attack. return transform(parsed, [ + async (node) => { + await walk(node, (node) => { + if (node.type === ELEMENT_NODE) { + if (node.name === 'a' && !node.attributes.href?.startsWith('https://yufan.me')) { + node.attributes.target = '_blank'; + node.attributes.rel = 'nofollow'; + } + } + }); + + return node; + }, sanitize({ allowElements: [ 'h1', @@ -135,7 +147,8 @@ const parseContent = async (content: string): Promise => { 'li', ], allowAttributes: { - img: ['src', 'width', 'height', 'rel', 'target'], + img: ['src', 'width', 'height'], + a: ['rel', 'target'], }, allowComments: false, }),