用 marknb 替换 Python markdown,并添加一键安装与升级脚本。
部署时通过 install.sh 自动编译 marknb;日常更新用 upgrade.sh 拉代码并重编译。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
单文件博客:FastAPI + SQLite + Jinja2,Markdown 预渲染为 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")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user