From d5d025e01134d04e20d3120f273a8765d5855e16 Mon Sep 17 00:00:00 2001 From: grassblock Date: Thu, 12 Jun 2025 20:35:32 +0800 Subject: [PATCH 1/2] feat: add ruby helper shortcode --- src/components/shortcodes/Ruby.astro | 6 ++++++ src/content/posts/markdown-example/index.mdx | 15 +++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/components/shortcodes/Ruby.astro diff --git a/src/components/shortcodes/Ruby.astro b/src/components/shortcodes/Ruby.astro new file mode 100644 index 0000000..2b6f572 --- /dev/null +++ b/src/components/shortcodes/Ruby.astro @@ -0,0 +1,6 @@ +--- +const { text, ruby } = Astro.props; +--- + + {text} ({ruby}) + diff --git a/src/content/posts/markdown-example/index.mdx b/src/content/posts/markdown-example/index.mdx index f75364b..99438d2 100644 --- a/src/content/posts/markdown-example/index.mdx +++ b/src/content/posts/markdown-example/index.mdx @@ -7,6 +7,7 @@ tags: ["markdown", "css", "html", "sample"] import Callout from '/src/components/shortcodes/Callout.astro'; import LinkCard from '/src/components/shortcodes/LinkCard.astro'; import Spoiler from '/src/components/shortcodes/Spoiler.astro'; +import Ruby from '/src/components/shortcodes/Ruby.astro'; This article offers a sample of basic and extended Markdown formatting that can be used, also it shows how some basic HTML elements are decorated. ## Markdown in Astro @@ -52,6 +53,17 @@ You can also display a 'tip' for the reader when hovered: ``` +### Ruby +The `Ruby` component allows you to quickly add ruby annotations to your text, which is useful for providing pronunciation or additional information about certain words, especially in East Asian languages. + + + + +```mdx + + +``` + ### Callouts You can use callouts to highlight important information or warnings in your content. Callouts are styled boxes that draw attention to specific parts of the text. @@ -88,6 +100,9 @@ You can use the `LinkCard` component to create cards that link to external resou Or to customize the card further with a title and description: + + + ## Headings The following HTML `

`—`

` elements represent six levels of section headings. `

` is the highest section level while `

` is the lowest. From 93fb60724ca6ea6bc455e0fd49ed016aade4a04c Mon Sep 17 00:00:00 2001 From: grassblock Date: Thu, 12 Jun 2025 22:06:53 +0800 Subject: [PATCH 2/2] feat: add content encryption --- .../shortcodes/ProtectedContent.astro | 131 ++++++++++++++++++ src/config.ts | 4 + src/content/posts/markdown-example/index.mdx | 27 ++++ src/plugins/encrypt.ts | 28 ++++ 4 files changed, 190 insertions(+) create mode 100644 src/components/shortcodes/ProtectedContent.astro create mode 100644 src/plugins/encrypt.ts diff --git a/src/components/shortcodes/ProtectedContent.astro b/src/components/shortcodes/ProtectedContent.astro new file mode 100644 index 0000000..1ab737d --- /dev/null +++ b/src/components/shortcodes/ProtectedContent.astro @@ -0,0 +1,131 @@ +--- +import { encrypt } from '../../plugins/encrypt'; +import {siteConfig} from "../../config"; + +interface Props { + password?: string; + pwEnv?: string; +} + +const { password: propPassword, pwEnv } = Astro.props; +// Get password from props, environment variable, or site config +const password = (pwEnv ? import.meta.env[pwEnv] : propPassword) || siteConfig.contentPassword || import.meta.env.CONTENT_PASSWORD || Math.random().toString(); + +// Get the slot content +const content = await Astro.slots.render('default'); + +// Encrypt content at build time +const { encryptedData, iv } = encrypt(content, password); +--- + +
+
+

This content is protected. Enter the password to view it:

+
+ + +
+
+ +
+ + + + \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index c45b1c5..40171d0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -28,6 +28,10 @@ export const siteConfig = { searchEngine: 'bing', // 'google', 'duckduckgo', 'bing'(broken until M1cr0$0ft get support for it), defaults to 'google' // content displayAvatar: true, // display author avatar in the article list and info line of article page + // encryption + // the global password to encrypt/decrypt the content, if set, all without specifying a password will be encrypted with this password + // you can use a different environment variable to set the password. + contentPassword: import.meta.env.CONTENT_PASSWORD, // footer // yes you can write html safely here customFooter: 'I have no mouth, and I must SCREAM', diff --git a/src/content/posts/markdown-example/index.mdx b/src/content/posts/markdown-example/index.mdx index 99438d2..de1689f 100644 --- a/src/content/posts/markdown-example/index.mdx +++ b/src/content/posts/markdown-example/index.mdx @@ -8,6 +8,7 @@ import Callout from '/src/components/shortcodes/Callout.astro'; import LinkCard from '/src/components/shortcodes/LinkCard.astro'; import Spoiler from '/src/components/shortcodes/Spoiler.astro'; import Ruby from '/src/components/shortcodes/Ruby.astro'; +import ProtectedContent from '/src/components/shortcodes/ProtectedContent.astro'; This article offers a sample of basic and extended Markdown formatting that can be used, also it shows how some basic HTML elements are decorated. ## Markdown in Astro @@ -100,8 +101,34 @@ You can use the `LinkCard` component to create cards that link to external resou Or to customize the card further with a title and description: +### Protected Content +You can use the `ProtectedContent` component to protect certain parts of your content with a password. This is useful for sharing exclusive content or information that should only be accessible to certain users. + +About security: + Although the encrypt process is happened at build and encrypted content is not visible in the rendered HTML. But it's still not 100% secure, as the password can be stored in the source code of a document (which could be public). Use it only for not really sensitive content. + Using a environment variable to store the password is recommended. + + + Yes, what you input is the ultimate answer to *life*, *the universe*, and **everything**. + + +```mdx + + Yes, what you input is the ultimate answer to *life*, *the universe*, and **everything**. + +``` + + + And this will be encrypted with a global password, which is set in the environment variable `CONTENT_PASSWORD` or defined by `config.ts`. (config.ts takes precedence over the environment variable) + + +```mdx + + And this will be encrypted with a global password, which is set in the environment variable `CONTENT_PASSWORD` or defined by `config.ts`. (config.ts takes precedence over the environment variable) + +``` ## Headings diff --git a/src/plugins/encrypt.ts b/src/plugins/encrypt.ts new file mode 100644 index 0000000..505a273 --- /dev/null +++ b/src/plugins/encrypt.ts @@ -0,0 +1,28 @@ +import crypto from 'crypto'; + +export function encrypt(content: string, password: string) { + // Generate a random initialization vector + const iv = crypto.randomBytes(16); + + // Derive key from password using PBKDF2 + const key = crypto.pbkdf2Sync(password, iv, 100000, 32, 'sha256'); + + // Create cipher + const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); + + // Encrypt content + let encryptedData = cipher.update(content, 'utf8', 'base64'); + encryptedData += cipher.final('base64'); + + // Get auth tag and append to encrypted data + const authTag = cipher.getAuthTag(); + const encryptedBuffer = Buffer.concat([ + Buffer.from(encryptedData, 'base64'), + authTag + ]); + + return { + encryptedData: encryptedBuffer.toString('base64'), + iv: iv.toString('base64') + }; +} \ No newline at end of file