Hi 👋, my name is Ori and I'll help you setup password-protected notes on Quartz!
To do so, you will need to modify a few files.
Firstly, install @expo/spawn-async and Staticrypt with $ npm install @expo/spawn-async staticrypt
After that, paste this code into quartz/password.ts
import spawn from "@expo/spawn-async"
import fs from "fs"
import path from "path"
import { toHtml } from "hast-util-to-html"
import { BuildCtx } from "./util/ctx"
import { Root as HTMLRoot } from "hast"
import { QuartzTransformerPlugin } from "./plugins/types"
import { VFile } from "vfile"
const quartzCache = path.resolve("./quartz/.quartz-cache/password/")
export async function encryptPages() {
const ppath = path.join(quartzCache, "cache.json")
const passwordCache: { password: string; savePath: string }[] = fs.existsSync(ppath)
? JSON.parse(fs.readFileSync(ppath, "utf-8"))
: []
for (const file of passwordCache) {
await spawn("npx", [
"staticrypt",
file.savePath,
"-p",
file.password,
"--short",
"-d",
path.dirname(file.savePath),
])
}
// Little cute delay :3
await setTimeout(() => {}, 100)
}
interface Options {
/*** The description of every password-protected notes */
lockedDescription?: string
}
// Clears the password directory
const passwordCache = path.resolve("./quartz/.quartz-cache/password/cache.json")
fs.rmSync(quartzCache, { recursive: true, force: true })
export const Staticrypt: QuartzTransformerPlugin<Options> = (opts?: Options) => {
return {
name: "Staticrypt",
htmlPlugins(ctx: BuildCtx) {
return [
() => {
return async (tree: HTMLRoot, file: VFile) => {
const savePath = path.join(quartzCache, ctx.argv.output, `${file.data.slug}.html`)
const frontmatter = file.data.frontmatter
file.data.isProtected = false
if (frontmatter && "password" in frontmatter && frontmatter.password) {
if (!fs.existsSync(quartzCache)) {
fs.mkdirSync(quartzCache, { recursive: true })
}
file.data.isProtected = true
// Password cache
let currentCache: { password: string; savePath: string }[] = []
if (!fs.existsSync(passwordCache)) {
fs.writeFileSync(passwordCache, "[]")
} else {
currentCache = JSON.parse(fs.readFileSync(passwordCache, "utf-8"))
}
currentCache.push({
password: frontmatter.password as string,
savePath: path.join(ctx.argv.output, `${file.data.slug}.html`),
})
fs.writeFileSync(passwordCache, JSON.stringify(currentCache))
fs.mkdirSync(path.dirname(savePath), { recursive: true })
fs.writeFileSync(savePath, toHtml(tree))
}
file.data.description = opts?.lockedDescription || "This note is password-protected."
}
},
]
},
}
}
declare module "vfile" {
interface DataMap {
isProtected: boolean
}
}Now, go into your quartz.config.ts file and add the Staticrypt transformer (Staticrypt is the tool used to encrypt static webpages.)
like so:
import { Staticrypt } from "./quartz/password"
// ...
plugins: {
transformers: [
Staticrypt(),
// ... the rest of the fileNow we must add the encrypting function to the build process. To do that, open the quart/build.ts file, and add this import:
import { encryptPages } from "./password"Then, place this line of code
await encryptPages()just before
console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`))You are almost finished! The last step is to add 1 line to the quartz/plugins/emitters/contentIndex.ts file.
You need to paste this:
if (file.data.isProtected) file.data.text = "";just before the line where there is:
const slug = file.data.slug!(tip: press control+f to find the line)
To password lock your note, simply add the password key to the frontmatter. Like so:
---
password: MyCoolPassword1234
---
Hi this is a note!
To unlock your awesome note, just enter your password into the textbox that will appear when you visit the note on your quartz site.
Warning
Due to a little bug, you'll have to refresh the page once you go on an encrypted note for the password-prompting window to appear.
Good writing 🖊️!
This is great, thank you very much! Is it possible to password protect an entire folder?
edit: Doesn't work once deployed, only on localhost.. Not sure what goes.