diff --git a/docs/README.md b/docs/README.md
index e35e6f51..330bf4de 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -173,6 +173,7 @@ npm run docs:dev
{ github: 'shuoliuchn', name: 'Shuo Liu' },
'Hammuu1112',
'SherkeyXD',
+ { github: 'Kinneyzhang', name: 'Geekinney' },
]"
/>
diff --git a/docs/notes/theme/guide/features/encryption.md b/docs/notes/theme/guide/features/encryption.md
index 00a76b1f..028e4f32 100644
--- a/docs/notes/theme/guide/features/encryption.md
+++ b/docs/notes/theme/guide/features/encryption.md
@@ -89,13 +89,34 @@ export default defineUserConfig({
- 使用 `encrypt.admin` 解锁后,被认为是管理员访问,其它未解锁页面也默认解锁。
:::
+### Frontmatter
+
+在 Markdown 文件的 `Frontmatter` 中,可以使用 `password` 设置文章的密码。
+
+```md
+---
+title: 加密的文章
+password: 123456
+---
+```
+
+还可以添加 `passwordHint` 选项,用于设置密码提示信息。
+
+```md
+---
+title: 加密的文章
+password: 123456
+passwordHint: 密码是 123456
+---
+```
+
## 示例
点击访问 [加密文章,密码:123456](/article/enx7c9s/)
## 相关配置
-以下配置支持在多语言配置中使用。
+以下配置支持在 [多语言配置](../../config/locales.md) 中使用。
### encryptGlobalText
diff --git a/plugins/plugin-md-power/src/node/inline/index.ts b/plugins/plugin-md-power/src/node/inline/index.ts
index 9ede681e..11eeb153 100644
--- a/plugins/plugin-md-power/src/node/inline/index.ts
+++ b/plugins/plugin-md-power/src/node/inline/index.ts
@@ -40,11 +40,8 @@ export function inlineSyntaxPlugin(
md.use(abbrPlugin)
}
- if (
- options.plot === true
- || (isPlainObject(options.plot) && options.plot.tag !== false)
- ) {
- // !!plot!!
+ // !!plot!!
+ if (options.plot === true || isPlainObject(options.plot)) {
md.use(plotPlugin)
}
}
diff --git a/theme/src/client/components/VPEncryptPage.vue b/theme/src/client/components/VPEncryptPage.vue
index 7b616afb..d8be5c5a 100644
--- a/theme/src/client/components/VPEncryptPage.vue
+++ b/theme/src/client/components/VPEncryptPage.vue
@@ -2,7 +2,7 @@
import VPEncryptForm from '@theme/VPEncryptForm.vue'
import { useData } from '../composables/index.js'
-const { theme } = useData()
+const { theme, frontmatter } = useData<'post'>()
@@ -11,7 +11,7 @@ const { theme } = useData()
-
+
diff --git a/theme/src/client/composables/encrypt.ts b/theme/src/client/composables/encrypt.ts
index cd8cdf41..35c8afde 100644
--- a/theme/src/client/composables/encrypt.ts
+++ b/theme/src/client/composables/encrypt.ts
@@ -89,6 +89,9 @@ export function setupEncrypt(): void {
const hasPageEncrypt = computed(() => {
const pagePath = route.path
const filePathRelative = page.value.filePathRelative
+ if (page.value._e)
+ return true
+
return encrypt.value.ruleList.length
? encrypt.value.matches.some(match => toMatch(match, pagePath, filePathRelative))
: false
@@ -106,10 +109,16 @@ export function setupEncrypt(): void {
const hashList = computed(() => {
const pagePath = route.path
const filePathRelative = page.value.filePathRelative
- return encrypt.value.ruleList.length
+ const passwords = typeof page.value._e === 'string' ? page.value._e.split(':') : []
+ const pageRule: EncryptDataRule | undefined = passwords.length
+ ? { key: pagePath.replace(/\//g, '').replace(/\.html$/, ''), match: pagePath, rules: passwords }
+ : undefined
+ const rules = encrypt.value.ruleList.length
? encrypt.value.ruleList
.filter(item => toMatch(item.match, pagePath, filePathRelative))
: []
+
+ return [pageRule, ...rules].filter(Boolean) as EncryptDataRule[]
})
const isPageDecrypted = computed(() => {
diff --git a/theme/src/node/pages/encryptPage.ts b/theme/src/node/pages/encryptPage.ts
new file mode 100644
index 00000000..ffdb8b1c
--- /dev/null
+++ b/theme/src/node/pages/encryptPage.ts
@@ -0,0 +1,14 @@
+import type { Page } from 'vuepress/core'
+import type { ThemePageData } from '../../shared/index.js'
+import { toArray } from '@pengzhanbo/utils'
+import { genEncrypt } from '../utils/index.js'
+
+export function encryptPage(
+ page: Page,
+): void {
+ const password = toArray(page.frontmatter.password)
+ if (password.length) {
+ page.data._e = password.map(pwd => genEncrypt(pwd as string)).join(':')
+ }
+ delete page.frontmatter.password
+}
diff --git a/theme/src/node/pages/extendsPage.ts b/theme/src/node/pages/extendsPage.ts
index f34b7339..fc617f3e 100644
--- a/theme/src/node/pages/extendsPage.ts
+++ b/theme/src/node/pages/extendsPage.ts
@@ -2,6 +2,7 @@ import type { Page } from 'vuepress/core'
import type { ThemePageData } from '../../shared/index.js'
import { getThemeConfig } from '../loadConfig/index.js'
import { autoCategory } from './autoCategory.js'
+import { encryptPage } from './encryptPage.js'
import { enableBulletin } from './pageBulletin.js'
export function extendsPageData(
@@ -9,6 +10,7 @@ export function extendsPageData(
): void {
const options = getThemeConfig()
cleanPageData(page)
+ encryptPage(page)
autoCategory(page, options)
enableBulletin(page, options)
}
@@ -30,11 +32,6 @@ function cleanPageData(page: Page) {
delete page.frontmatter.home
}
- // if (page.frontmatter.article === false) {
- // page.frontmatter.draft = true
- // }
- // delete page.frontmatter.article
-
if (page.headers) {
page.data.headers = []
}
diff --git a/theme/src/node/prepare/prepareEncrypt.ts b/theme/src/node/prepare/prepareEncrypt.ts
index e098e731..3cba5539 100644
--- a/theme/src/node/prepare/prepareEncrypt.ts
+++ b/theme/src/node/prepare/prepareEncrypt.ts
@@ -2,10 +2,9 @@ import type { App } from 'vuepress'
import type { Page } from 'vuepress/core'
import type { EncryptOptions, ThemePageData } from '../../shared/index.js'
import type { FsCache } from '../utils/index.js'
-import { isNumber, isString, random, toArray } from '@pengzhanbo/utils'
-import { genSaltSync, hashSync } from 'bcrypt-ts'
+import { isNumber, isString, toArray } from '@pengzhanbo/utils'
import { getThemeConfig } from '../loadConfig/index.js'
-import { createFsCache, hash, perf, resolveContent, writeTemp } from '../utils/index.js'
+import { createFsCache, genEncrypt, hash, perf, resolveContent, writeTemp } from '../utils/index.js'
export type EncryptConfig = readonly [
boolean, // global
@@ -45,13 +44,11 @@ export async function prepareEncrypt(app: App): Promise {
perf.log('prepare:encrypt')
}
-const salt = () => genSaltSync(random(8, 16))
-
function resolveEncrypt(encrypt?: EncryptOptions): EncryptConfig {
const admin = encrypt?.admin
? toArray(encrypt.admin)
.filter(isStringLike)
- .map(item => hashSync(String(item), salt()))
+ .map(item => genEncrypt(item))
.join(separator)
: ''
@@ -64,7 +61,7 @@ function resolveEncrypt(encrypt?: EncryptOptions): EncryptConfig {
rules[String(index)] = toArray(encrypt.rules![key])
.filter(isStringLike)
- .map(item => hashSync(String(item), salt()))
+ .map(item => genEncrypt(item))
.join(separator)
})
}
@@ -76,6 +73,9 @@ export function isEncryptPage(page: Page, encrypt?: EncryptOption
if (!encrypt)
return false
+ if (page.data._e)
+ return true
+
const rules = encrypt.rules ?? {}
return Object.keys(rules).some((match) => {
diff --git a/theme/src/node/utils/encrypt.ts b/theme/src/node/utils/encrypt.ts
new file mode 100644
index 00000000..1313efcc
--- /dev/null
+++ b/theme/src/node/utils/encrypt.ts
@@ -0,0 +1,6 @@
+import { random } from '@pengzhanbo/utils'
+import { genSaltSync, hashSync } from 'bcrypt-ts'
+
+export function genEncrypt(pwd: string): string {
+ return hashSync(String(pwd), genSaltSync(random(8, 16)))
+}
diff --git a/theme/src/node/utils/index.ts b/theme/src/node/utils/index.ts
index fea79631..f043c77c 100644
--- a/theme/src/node/utils/index.ts
+++ b/theme/src/node/utils/index.ts
@@ -1,5 +1,6 @@
export * from './constants.js'
export * from './createFsCache.js'
+export * from './encrypt.js'
export * from './hash.js'
export * from './interopDefault.js'
export * from './logger.js'
diff --git a/theme/src/shared/frontmatter/post.ts b/theme/src/shared/frontmatter/post.ts
index c3a176af..b25a709f 100644
--- a/theme/src/shared/frontmatter/post.ts
+++ b/theme/src/shared/frontmatter/post.ts
@@ -52,6 +52,16 @@ export interface ThemePostFrontmatter extends ThemePageFrontmatter {
* 版权信息
*/
copyright?: boolean | CopyrightLicense | CopyrightFrontmatter
+
+ /**
+ * 文章加密密码
+ */
+ password?: string | string[]
+
+ /**
+ * 文章加密密码提示文本
+ */
+ passwordHint?: string
}
export interface CopyrightFrontmatter extends CopyrightOptions {