diff --git a/.gitignore b/.gitignore index 016b59e..bc7691a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ pnpm-debug.log* # jetbrains setting folder .idea/ + +src/content/blog/* +!src/content/blog/*.md +!src/content/blog/*.mdx diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 710465a..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "src/content/blog"] - path = src/content/blog - url = git@github.com:rayhanadev/blog-content-pool.git diff --git a/.husky/post-checkout b/.husky/post-checkout new file mode 100644 index 0000000..bd299b0 --- /dev/null +++ b/.husky/post-checkout @@ -0,0 +1,11 @@ +ENCRYPTED_POSTS=$(find src/content/blog_encrypted/ -type f -name "*.gpg") + +if [ -z "$ENCRYPTED_POSTS" ]; then + echo "No encrypted posts found. Skipping decryption." + exit 0 +fi + +echo "Decrypting blog posts..." +bun contents:decrypt + +echo "Decryption complete. Decrypted posts available locally." diff --git a/.lintstagedrc.json b/.lintstagedrc.json index 832d466..fde43f5 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -1,4 +1,9 @@ { + "src/content/blog/*.{md,mdx}": [ + "bun run contents:encrypt", + "git add src/content/blog_encrypted/", + "bash -c 'git ls-files --cached \"src/content/blog/*.{md,mdx}\" | xargs -r git rm --cached'" + ], "*/**/*.{js,jsx,ts,tsx,astro}": ["astro check"], - "*/**/*.{js,jsx,ts,tsx,json,css,md}": ["prettier --write"] + "*/**/*.{js,jsx,ts,tsx,json,css,md,mdx}": ["prettier --write"] } diff --git a/bun.lockb b/bun.lockb index 71dd184..fe6bd54 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index c9b0d34..88a2a78 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "lint:fix": "astro check --fix", "format": "prettier --no-error-on-unmatched-pattern --check */**/*.{js,json,ts,css,md,mdx,astro} --cache --cache-strategy metadata", "format:fix": "prettier --write */**/*.{js,json,ts,css,md,mdx,astro} --cache --cache-strategy metadata", + "contents:encrypt": "bun run scripts/encrypt-content.ts", + "contents:decrypt": "bun run scripts/decrypt-content.ts", "prepare": "husky" }, "dependencies": { @@ -30,6 +32,8 @@ "astro-seo": "^0.8.4", "canvaskit-wasm": "^0.39.1", "dayjs": "^1.11.13", + "fdir": "^6.4.2", + "picomatch": "^4.0.2", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwindcss": "^3.4.16", diff --git a/public/images/2024-12-18-burst/burst-design-exploration.mp4 b/public/images/2024-12-18-burst/burst-design-exploration.mp4 new file mode 100644 index 0000000..fa33067 Binary files /dev/null and b/public/images/2024-12-18-burst/burst-design-exploration.mp4 differ diff --git a/scripts/decrypt-content.ts b/scripts/decrypt-content.ts new file mode 100644 index 0000000..7b38eb1 --- /dev/null +++ b/scripts/decrypt-content.ts @@ -0,0 +1,48 @@ +import path from "node:path"; +import fs from "node:fs/promises"; +import { $ } from "bun"; +import { fdir } from "fdir"; + +import { EMAIL_ADDRESS } from "lib/consts"; + +const OUTPUT_DIR = path.resolve(process.cwd(), "./src/content/blog_encrypted"); +const CONTENT_DIR = path.resolve(process.cwd(), "./src/content/blog"); + +try { + const result = await $`gpg --list-keys ${EMAIL_ADDRESS}`.quiet(); + if (result.stdout.includes(EMAIL_ADDRESS)) { + console.log(`Found GPG key for ${EMAIL_ADDRESS}`); + } else { + const GPG_PRIVATE_KEY = Bun.env.GPG_PRIVATE_KEY; + + if (!GPG_PRIVATE_KEY) { + console.error("GPG_PRIVATE_KEY is not set."); + process.exit(1); + } + + await fs.writeFile("private-key.asc", GPG_PRIVATE_KEY); + await $`gpg --import private-key.asc`; + await $`rm -f private-key.asc`.quiet(); + } +} catch (error: any) { + console.error( + `Could not find GPG key for ${EMAIL_ADDRESS}`, + error.stderr || error.message, + ); + process.exit(1); +} + +await $`rm -rf ${CONTENT_DIR}`.quiet(); +await $`mkdir -p ${CONTENT_DIR}`.quiet(); + +const api = new fdir().withFullPaths().glob("**/*.gpg"); +const files = await api.crawl(OUTPUT_DIR).withPromise(); + +console.log(`Found ${files.length} files to decrypt.`); + +for (const file of files) { + const output = file.replace(OUTPUT_DIR, CONTENT_DIR).replace(/\.gpg$/, ""); // Remove .gpg extension + + console.log(`Decrypting ${file} to ${output}`); + await $`gpg --decrypt --output ${output} ${file}`.quiet(); +} diff --git a/scripts/encrypt-content.ts b/scripts/encrypt-content.ts new file mode 100644 index 0000000..9787248 --- /dev/null +++ b/scripts/encrypt-content.ts @@ -0,0 +1,40 @@ +import path from "node:path"; +import { $ } from "bun"; + +import { EMAIL_ADDRESS } from "lib/consts"; + +try { + const result = await $`gpg --list-keys ${EMAIL_ADDRESS}`.quiet(); + if (result.stdout.includes(EMAIL_ADDRESS)) { + console.log(`Found GPG key for ${EMAIL_ADDRESS}`); + } else { + console.error(`Could not find GPG key for ${EMAIL_ADDRESS}`); + process.exit(1); + } +} catch (error: any) { + console.error( + `Could not find GPG key for ${EMAIL_ADDRESS}`, + error.stderr || error.message, + ); + process.exit(1); +} + +const CONTENT_DIR = path.resolve(process.cwd(), "./src/content/blog"); +const OUTPUT_DIR = path.resolve(process.cwd(), "./src/content/blog_encrypted"); + +await $`rm -rf ${OUTPUT_DIR}`.quiet(); +await $`mkdir -p ${OUTPUT_DIR}`.quiet(); + +const GPG_ENCRYPTION_SUBKEY = + "01B8 8479 18BA 65A0 D4C6 18CB 0479 18E3 6D57 A322"; + +const files = process.argv.slice(2); + +console.log(`Found ${files.length} files to encrypt.`); + +for (const file of files) { + const output = `${file.replace(CONTENT_DIR, OUTPUT_DIR)}.gpg`; + + console.log(`Encrypting ${file} to ${output}`); + await $`gpg --encrypt --armor --recipient ${GPG_ENCRYPTION_SUBKEY} --output ${output} ${file}`.quiet(); +} diff --git a/src/content.config.ts b/src/content.config.ts index c33fc41..c429b82 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -4,7 +4,7 @@ import { glob } from "astro/loaders"; const blog = defineCollection({ loader: glob({ pattern: import.meta.env.DEV ? "**/*.{md,mdx}" : "**/[^_]*.{md,mdx}", - base: "./src/content/blog/posts", + base: "./src/content/blog", }), schema: z.object({ title: z.string(), diff --git a/src/content/blog b/src/content/blog deleted file mode 160000 index c16d4bb..0000000 --- a/src/content/blog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c16d4bb3f8faf903c52f7d1880fb831a88a39f55 diff --git a/src/content/blog_encrypted/2024-12-17-hello-world.mdx.gpg b/src/content/blog_encrypted/2024-12-17-hello-world.mdx.gpg new file mode 100644 index 0000000..6b527eb --- /dev/null +++ b/src/content/blog_encrypted/2024-12-17-hello-world.mdx.gpg @@ -0,0 +1,36 @@ +-----BEGIN PGP MESSAGE----- + +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 +=K1lE +-----END PGP MESSAGE----- diff --git a/src/content/blog_encrypted/_2024-12-18-burst.mdx.gpg b/src/content/blog_encrypted/_2024-12-18-burst.mdx.gpg new file mode 100644 index 0000000..b1754d8 --- /dev/null +++ b/src/content/blog_encrypted/_2024-12-18-burst.mdx.gpg @@ -0,0 +1,59 @@ +-----BEGIN PGP MESSAGE----- + +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 ++gA= +=nXP1 +-----END PGP MESSAGE----- diff --git a/src/lib/blog/utils.ts b/src/lib/blog/utils.ts index b8cd661..b31eade 100644 --- a/src/lib/blog/utils.ts +++ b/src/lib/blog/utils.ts @@ -1,11 +1,11 @@ export function isDraft(slug: string) { - const file = slug.split("/")[4]!; + const file = slug.split("/")[3]!; return file.startsWith("_"); } export function parseDateFromFilePath(slug: string) { - const [y, m, d] = slug.split("/")[4]!.split("-") as [ + const [y, m, d] = slug.split("/")[3]!.split("-") as [ string, string, string,