Adding a Microblog to VitePress

Adding a Microblog to VitePress

A microblog lets you share short thoughts whenever they occur, without the overhead of publishing a full static-blog post. This implementation uses Cloudflare Workers for the backend and KV for storage and management. A Vue component embedded in VitePress displays the updates, adding a lively, interactive element to the blog.

Introduction

Many dynamic blogs have a microblog feature: essentially a special kind of post that takes advantage of dynamic publishing to let you write and publish immediately.

Static blogs first compile HTML locally or on a server and then deploy it, so they are less immediate. Writing a long post at a computer and deploying with Git is no great hassle. Having to get on a computer just to post a short thought is a bigger mental burden. Using Git on a phone is awkward too—not exactly elegant. I often end up deciding not to post at all.

So I built the frontend and backend for a microblog system, visible on this blog’s Memos page. The backend runs on Cloudflare Workers, with storage conveniently next door in our generous benefactor’s KV, and a simple admin page. Since the blog uses VitePress, the frontend is a Vue component embedded in a dedicated page.

You can see the frontend for yourself. Here is the backend management page: Memo management page

Backend: Cloudflare Workers + KV

Overview

The backend provides:

  • Create, edit, and delete posts—the basics.
  • Authentication on the admin page and all write endpoints, for adequate security.
  • Live Markdown preview, powered by marked.

KV stores an index key whose value is an array of UIDs indexing all posts. Each post is stored separately under its uid, with a value formatted like this:

js
{
    "uid":"唯一 id",
    "createTime":"发布时间",
    "content":"说说内容",
}

Implementation

First, create a Cloudflare KV namespace dedicated to the microblog. Go to Account Home → Storage & Databases → KV and click Create. The name does not matter much as long as you remember it; I simply used memos.

Next, create a Cloudflare Worker for the application logic. Under Account Home → Compute (Workers) → Workers & Pages, click Create. Again, the name is unimportant; mine is memos-api. Open the Worker’s details, then add a binding under Settings → Bindings. Choose KV namespace, set the variable name to KV, and select the namespace you just created, memos in my case. The code can now access that namespace directly through env.KV. Finally, click Edit code in the upper-right toolbar.

Now, code time!

First, create index.html for the admin page’s HTML, CSS, and JavaScript.

The JavaScript shows the backend endpoints below:

  • POST /api/auth: authenticate the page.
  • GET /api/memos: retrieve posts, with pagination.
  • POST /api/memos: publish a new post.
  • PUT /api/memos/{uid}: update a post.
  • DELETE /api/memos/{uid}: delete a post.

Then implement these endpoints in worker.js.

Configure the three constants at the top:

  • CORRECT_PASSWORD: the admin page password.
  • CALLBACK_URL: the callback URL triggered after publishing, editing, or deleting a post.
  • ALLOWED_ORIGINS: the allowed origins for CORS. Include at least your blog domain and admin page domain.

Once configured, click Deploy.

Because of the Great Firewall, the default workers.dev domain is difficult to access from China. Give the Worker a custom domain instead. Under memos details → Settings → Domains & Routes, add a custom domain hosted on Cloudflare. Remember to add it to ALLOWED_ORIGINS in worker.js too.

The admin page is now ready at https://{your-domain}/manage. Enter the password, then enjoy!

Frontend

Thanks to VitePress, writing the microblog frontend as a Vue component and embedding it in the blog is straightforward.

First, install the markedjs dependency. With pnpm:

shell
pnpm add marked

Next to your blog’s theme configuration file, usually docs/.vitepress/theme/index.ts (the path and extension may differ), create a components directory if one does not already exist, and add memos.vue inside it.

Replace {你的域名} in the code with your Cloudflare Worker’s domain.

You may have noticed that the component’s initial content does not come from a Worker API request. It comes from a JSON file: import memosRaw from '../../../../memos.json'. The Worker is queried only when you click Load More. Why?

  • For the user experience: fetching initial data from the API leaves the page blank for a while on arrival, which feels unpleasant.
  • To save money: the free Cloudflare Workers plan limits request counts. Loading the initial data statically cuts requests dramatically.

memos.json contains the first ten posts, fetched from the API at build time. That is why the Worker has a CALLBACK_URL: it triggers a rebuild when you publish a new post or edit or delete one of the first ten. Look up the appropriate URL for your deployment platform. If you fetch everything dynamically, you do not need this callback.

The following code generates memos.json at build time. Next to the theme configuration file, usually docs/.vitepress/theme/index.ts (path and extension may vary), create a utils directory if needed, then add memos.js inside it.

Edit package.json in the blog root and prepend node docs/.vitepress/theme/utils/memos.js to both the dev and build commands. The exact place may vary; here is mine:

json
{
  ...
  "scripts": {
    "dev": "node docs/.vitepress/theme/utils/memos.js && vitepress dev docs",
    "build": "node docs/.vitepress/theme/utils/memos.js && vitepress build docs",
    "serve": "vitepress serve docs"
  },
  ...
}

Both dev and build now run memos.js first, generating memos.json in the blog root. Adjust the import path in memos.vue to match your directory layout.

The component and data are ready. Next, register the component globally.

Import and register it in the theme configuration file, usually docs/.vitepress/theme/index.ts, though the path and extension may differ.

js
...
import Memos from './components/memos.vue'
...
export default {
    ...
    enhanceApp({ app }) {
        ...
        app.component('Memos', Memos);// [!code highlight]
    }
} satisfies Theme

You can now insert the component anywhere in the blog with <Memos />.

Finally, create a standalone page just for this component.

What? You have never used a standalone page in VitePress?

Create a pages directory at the root. Then, in VitePress’s main configuration—not the theme configuration; usually docs/.vitepress/config.ts, though your path and extension may differ—add the rewrite rule 'pages/:file.md': ':file.md'. Files under pages will then be accessible directly at /filename. See the official documentation for rewrites.

Create balabala.md under pages with the following contents:

markdown
---
title: 碎碎念
hidden: true
comment: false
sidebar: false
aside: false
readingTime: false
showMeta: false
---

<Memos />

All done.

Routing Selected VPS Traffic Through WARP over IPv6
Transparent Proxying and Traffic Routing with OPNsense