用 marknb 替换 Python markdown,并添加一键安装与升级脚本。

部署时通过 install.sh 自动编译 marknb;日常更新用 upgrade.sh 拉代码并重编译。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cnb
2026-07-05 11:48:18 +08:00
co-authored by Cursor
parent 0333ab1fc1
commit cf8eba082f
8 changed files with 227 additions and 25 deletions
+49 -12
View File
@@ -2,8 +2,8 @@
单文件博客:FastAPI + SQLite + Jinja2Markdown 预渲染为 HTML。
安装依赖:
pip install -r requirements.txt
# 或: pip install fastapi "uvicorn[standard]" jinja2 python-multipart markdown
bash scripts/install.sh
# 或手动: 编译 vendor/marknb/libmarknb.so 后 pip install -r requirements.txt
运行:
python blog.py
@@ -26,18 +26,18 @@
from __future__ import annotations
import asyncio
import ctypes
import hashlib
import os
import re
import secrets
import sqlite3
import sys
from contextlib import contextmanager
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional
from urllib.parse import quote
import markdown
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse
from jinja2 import BaseLoader, Environment, TemplateNotFound
@@ -193,19 +193,56 @@ def bootstrap_secrets() -> str:
# -----------------------------------------------------------------------------
# Markdown → HTML
# Markdown → HTML (marknb: https://cnb.cool/whcself/marknb)
# -----------------------------------------------------------------------------
_MD_EXTENSIONS = [
"markdown.extensions.fenced_code",
"markdown.extensions.tables",
"markdown.extensions.nl2br",
"markdown.extensions.sane_lists",
]
_MARKNB_DEFAULT = 65536
def _load_marknb() -> ctypes.CDLL:
lib_path = os.environ.get("MARKNB_LIB")
if not lib_path:
here = Path(__file__).resolve().parent
for candidate in (
here / "vendor" / "marknb" / "libmarknb.so",
Path("/usr/local/lib/libmarknb.so"),
):
if candidate.is_file():
lib_path = str(candidate)
break
if not lib_path:
print(
"找不到 marknb 动态库。请先运行: bash scripts/install.sh(升级: bash scripts/upgrade.sh",
file=sys.stderr,
)
sys.exit(1)
lib = ctypes.CDLL(lib_path)
lib.marknb_parse.argtypes = [
ctypes.c_char_p,
ctypes.c_size_t,
ctypes.c_char_p,
ctypes.c_size_t,
ctypes.POINTER(ctypes.c_size_t),
]
lib.marknb_parse.restype = ctypes.c_int
return lib
_MARKNB = _load_marknb()
def render_markdown(md_text: str) -> str:
return markdown.markdown(md_text, extensions=_MD_EXTENSIONS)
md = md_text.encode("utf-8")
cap = max(len(md) * 4 + 256, _MARKNB_DEFAULT)
limit = len(md) * 64 + 4096
while cap <= limit:
buf = ctypes.create_string_buffer(cap)
out_len = ctypes.c_size_t(0)
if _MARKNB.marknb_parse(md, len(md), buf, cap, ctypes.byref(out_len)) == 0:
return buf.raw[: out_len.value].decode("utf-8")
cap *= 2
raise RuntimeError("marknb: output buffer exhausted")
# -----------------------------------------------------------------------------