Add message frontmatter, field. Encrypted the encrypt docs for PoC

This commit is contained in:
Yigit Colakoglu 2025-07-30 20:42:32 +02:00
parent 2533c5b01a
commit ff83a93588
7 changed files with 49 additions and 8 deletions

View File

@ -2,6 +2,9 @@
title: "Encrypt"
tags:
- plugin/transformer
encrypt: true
encrypt_message: '^ Password is "quartz"'
password: "quartz"
---
This plugin enables content encryption for sensitive pages in your Quartz site. It uses AES encryption with password-based access control, allowing you to protect specific pages or entire folders with passwords.
@ -63,6 +66,7 @@ Use frontmatter to encrypt individual pages or override folder passwords:
title: "My Secret Page"
encrypt: true
password: "page-specific-password"
encrypt_message: "Sorry, this one is only for my eyes,"
---
This content will be encrypted and require a password to view.
```
@ -73,6 +77,7 @@ The plugin recognizes these frontmatter fields:
- `encrypt`: Set to `true` to enable encryption for this page
- `password`: The password required to decrypt this page
- `encrypt_message`: Message to be shown on the unlock page.
If a page is in an encrypted folder but has its own `password` field, the page-specific password will be used instead of the folder password.

View File

@ -66,6 +66,11 @@ Quartz supports the following frontmatter:
- `date`
- encrypt
- `encrypt`
- `encrypted`
- encryptMessage
- `encrypt_message`
- `encryptMessage`
- `encrypt-message`
- password
- `password`

View File

@ -32,7 +32,6 @@
}
.encryption-notice p {
margin: 0 0 1.5rem 0;
color: var(--gray);
line-height: 1.4;
font-size: 0.95rem;
@ -151,3 +150,13 @@
font-size: 16px; /* Prevent zoom on iOS */
}
}
.encrypted-message-footnote {
color: var(--gray);
font-size: 0.85rem;
opacity: 0.8;
text-align: center;
width: 100%;
font-style: italic;
margin: 0;
}

View File

@ -59,7 +59,7 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndexMap, limit?:
<title>${escapeHTML(content.title)}</title>
<link>https://${joinSegments(base, encodeURI(slug))}</link>
<guid>https://${joinSegments(base, encodeURI(slug))}</guid>
<description><![CDATA[ ${content.encrypted ? content.description : (content.richContent ?? content.description)} ]]></description>
<description><![CDATA[ ${content.richContent ?? content.description} ]]></description>
<pubDate>${content.date?.toUTCString()}</pubDate>
</item>`
@ -111,7 +111,8 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
links: file.data.links ?? [],
tags: file.data.frontmatter?.tags ?? [],
content: file.data.text ?? "",
richContent: opts?.rssFullHtml
richContent:
!file.data.encrypted && opts?.rssFullHtml
? escapeHTML(toHtml(tree as Root, { allowDangerousHtml: true }))
: undefined,
date: date,

View File

@ -30,9 +30,9 @@ export const Description: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
() => {
return async (tree: HTMLRoot, file) => {
if (file.data?.encrypted) {
file.data.description = i18n(
ctx.cfg.configuration.locale,
).components.encryption.encryptedDescription
file.data.description =
file.data.encryptMessage ||
i18n(ctx.cfg.configuration.locale).components.encryption.encryptedDescription
return
}

View File

@ -146,6 +146,10 @@ export const EncryptPlugin: QuartzTransformerPlugin<Partial<Options>> = (userOpt
}
file.data.encrypted = true
file.data.password = password
if (file.data?.frontmatter?.encryptMessage) {
file.data.encryptMessage = file.data.frontmatter.encryptMessage as string
}
if (file.data?.frontmatter?.title) {
file.data.frontmatter.title = `🔒 ${file.data.frontmatter.title}`
@ -171,6 +175,7 @@ export const EncryptPlugin: QuartzTransformerPlugin<Partial<Options>> = (userOpt
// Encrypt the content
const encryptedContent = encryptContent(htmlContent, password, opts)
console.log(file.data)
// Create a new tree with encrypted content placeholder
const encryptedTree = fromHtml(
@ -182,6 +187,7 @@ export const EncryptPlugin: QuartzTransformerPlugin<Partial<Options>> = (userOpt
<div class="decrypt-form">
<input type="password" class="decrypt-password" placeholder="${t.enterPassword}" />
<button class="decrypt-button">${t.decrypt}</button>
${file.data.encryptMessage ? `<p class="encrypted-message-footnote">${file.data.encryptMessage}</p>` : ""}
</div>
<div class="decrypt-loading">
<div class="loading-spinner"></div>
@ -228,5 +234,7 @@ export const EncryptPlugin: QuartzTransformerPlugin<Partial<Options>> = (userOpt
declare module "vfile" {
interface DataMap {
encrypted: boolean
encryptMessage?: string
password?: string
}
}

View File

@ -118,6 +118,19 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
if (socialImage) data.socialImage = socialImage
const encrypted = coalesceAliases(data, ["encrypted", "encrypt"])
if (encrypted) data.encrypt = true
const password = coalesceAliases(data, ["password"])
if (password) data.password = password
const encryptMessage = coalesceAliases(data, [
"encryptMessage",
"encrypt_message",
"encrypt-message",
])
if (encryptMessage) data.encryptMessage = encryptMessage
// Remove duplicate slugs
const uniqueSlugs = [...new Set(allSlugs)]
allSlugs.splice(0, allSlugs.length, ...uniqueSlugs)