1. 项目概述:为什么一个轻量级建站工具值得你花三天时间实操一遍
WorkBuddy 不是另一个“AI一键生成网站”的营销噱头,它本质是一个面向开发者和内容创作者的本地优先、命令行驱动的静态站点生成器,内核基于 Flask + SQLite 构建,但刻意剥离了传统 Web 框架的部署复杂度。我第一次看到它是在一个极客小众论坛里,有人用它三分钟搭起个人技术博客,第二天就上线了带搜索、分类、RSS 的完整站点——不是用 Next.js 或 Hugo 那种预编译方案,而是真正在本地跑着一个轻量 Flask 实例,实时响应 Markdown 编辑、自动刷新页面、SQLite 记录访问统计,连数据库文件都直接存放在项目根目录下。这正是标题里“从零建站并日更实操记录”的真实含义:它不追求高并发或企业级架构,而是把“写一篇新文章 → 保存 → 网页立刻可见 → 数据自动落库 → 第二天还能查阅读趋势”这个闭环压缩到最短路径。关键词里反复出现的Flask和SQLite并非凑数——Flask 提供最小可行的 HTTP 服务骨架,SQLite 则承担内容存储、元数据管理、甚至简易用户行为埋点(比如每篇文章的阅读次数),二者加起来不到 500 行核心代码,却撑起了整套工作流。而WorkBuddy这个名字本身就在暗示它的定位:不是老板、不是平台、不是 SaaS,只是一个蹲在你终端窗口里的协作伙伴,帮你把写作、发布、归档、复盘这件事做得更顺手。它适合三类人:一是刚学完 Python 基础、想找个真实项目练手的新手;二是技术写作者,厌倦了 WordPress 后台臃肿、Ghost 配置繁琐、Obsidian 发布插件不稳定;三是需要快速搭建内部知识库、项目周报站、团队文档门户的小团队。它不替代 WordPress 的生态,也不对标 Shopify 的电商能力,但它解决了一个被长期忽视的痛点:内容生产者不该为“发布”这件事额外学习运维、配置 Nginx、折腾 SSL 证书、维护数据库备份策略。日更不是 KPI,而是 WorkBuddy 设计哲学的自然结果——当你删掉所有中间环节,写作和发布之间的延迟趋近于零,日更就从“坚持”变成了“顺手”。
2. 整体设计思路与方案选型逻辑:为什么不用 Django/Vue/MySQL?
2.1 为什么选 Flask 而非 Django 或 FastAPI?
Flask 在这里不是“因为简单所以选它”,而是经过几轮实操验证后的理性收敛。我最初用 Django 试过类似方案:建 Article Model、写 Admin、配 URL、搞模板继承、处理静态文件……两周后发现,80% 的代码都在应付框架约定,而不是解决“我今天想写什么”这个原始需求。FastAPI 更夸张——它天生为 API 设计,强行套 Web 页面要写大量 Jinja2 模板+状态管理,反而增加心智负担。而 Flask 的优势在于“可控的裸露”:它不强制你用 ORM,不预设项目结构,不打包一堆你用不到的中间件。WorkBuddy 的核心路由只有三个:/(首页列表)、/post/<slug>(单篇文章)、/admin(简易后台)。每个路由背后就是十几行函数,读取 Markdown 文件、解析 YAML Front Matter、注入 SQLite 查询结果、渲染模板。更重要的是,Flask 的app.run(debug=True)模式天然支持热重载,你改完.md文件保存,浏览器 F5 就能看到效果,这种即时反馈对日更者至关重要。我对比过 Flask 与同类轻量框架(如 Bottle、CherryPy),Flask 的模板引擎 Jinja2 生态成熟,调试错误信息清晰,社区教程丰富,且对新手足够友好——比如{{ post.title|safe }}这种过滤器写法,比手写 HTML 转义安全得多。最关键一点:Flask 的 WSGI 接口标准,后续哪怕你要迁移到 Gunicorn+Nginx,代码几乎零修改。这不是“先易后难”的妥协,而是“始终可控”的设计选择。
2.2 为什么 SQLite 是唯一合理的数据库选项?
看到“SQLite”出现在热搜词里,很多人第一反应是“这玩意儿能当生产库?”——答案是:在 WorkBuddy 的场景下,它不仅是合理的,而且是不可替代的。我们来算一笔账:一个日更博主,一年写 365 篇文章,每篇平均 1500 字,加上标题、摘要、标签、发布时间、阅读数,单条记录约 2KB。一年数据总量不到 1MB。SQLite 单文件数据库轻松承载,且无需独立进程、无需端口监听、无需用户权限管理。你把它放在data/blog.db,代码里一句sqlite3.connect('data/blog.db')就搞定,备份?直接复制这个文件。对比 MySQL:你需要装服务、设 root 密码、建 database、授权用户、配置连接池、处理连接超时……这些操作对日更者毫无价值。PostgreSQL 更重。而像 TinyDB 这类纯 Python 库,虽然更轻,但缺乏 SQL 查询能力——WorkBuddy 要支持“按标签筛选”、“按月份归档”、“阅读数 Top10”,没有 WHERE、GROUP BY、ORDER BY 是玩不转的。SQLite 的SELECT COUNT(*) FROM posts WHERE tag = 'flask'这种查询,写起来和读起来都像自然语言。另外,DB Browser for SQLite这个工具之所以高频出现在热搜里,正是因为它是 WorkBuddy 生态的“可视化控制台”:你双击打开.db文件,直接看到所有文章记录,手动改个阅读数、删条测试数据、查某天发布量,比写 SQL 脚本还快。我实测过,在 MacBook M1 上,SQLite 处理 1000 条记录的复杂查询,平均耗时 3ms,完全满足本地开发和小流量访问需求。它不是“将就”,而是精准匹配场景的最优解。
2.3 为什么拒绝前端框架(Vue/React)和 CDN 资源?
WorkBuddy 的前端极其朴素:一个base.html模板,加载本地 CSS(Bootstrap 5 的精简版),JS 只有两处:一是文章页的代码块语法高亮(用 Prism.js,CDN 引入但可离线),二是搜索框的客户端过滤(用原生 JS 写了不到 50 行)。没用 Vue,因为不需要组件化交互;没用 React,因为没有状态管理需求;没接 CDN,因为所有静态资源都放在static/目录下,Flask 自动托管。这样做有三个硬性好处:第一,离线可用——地铁上写完 Markdown,回家连不上网也能预览;第二,部署极简——整个站点就是一个文件夹,扔到任何支持 Python 的服务器上python app.py就跑;第三,调试透明——你右键“查看源码”,看到的就是真实 HTML,没有构建产物、没有 sourcemap、没有 bundle 分析,新手一眼看懂结构。我见过太多人用 VuePress 或 Docusaurus 搭博客,最后卡在“怎么让自定义 CSS 生效”、“怎么改默认页脚”、“怎么禁用 PWA”上,本质上是在和框架的默认约定打架。WorkBuddy 的哲学是:“你写的 HTML 就是最终 HTML”,所有样式、脚本、结构都由你直接掌控。这不是复古,而是把技术栈的控制权,从框架手里拿回来。
3. 核心细节解析与实操要点:从安装到首篇发布的完整链路
3.1 环境准备与 WorkBuddy 安装:避开 Linux/macOS/Windows 的三大坑
WorkBuddy 官方推荐用 pip 安装,但实际操作中,不同系统有不同雷区。我踩过三次坑,总结出最稳路径:
macOS(M1/M2 芯片):不要用系统自带 Python(通常是 2.7 或老旧 3.x),也不要盲目
brew install python。正确做法是:- 用 Homebrew 安装
pyenv:brew install pyenv - 用 pyenv 装一个干净的 Python 3.11:
pyenv install 3.11.9 && pyenv global 3.11.9 - 创建项目虚拟环境:
python -m venv venv && source venv/bin/activate - 安装 WorkBuddy:
pip install workbuddy
提示:跳过这步直接
pip install workbuddy,大概率遇到zsh: command not found: workbuddy,因为 M1 的 PATH 机制和 Rosetta 兼容性问题,必须确保 pip 安装的可执行文件路径被 shell 正确识别。- 用 Homebrew 安装
Windows(Win10/Win11):最大的坑是
sqlite3模块缺失。官方 Python 安装包有时不带完整 SQLite 支持。解决方案:- 下载并安装 Python 官方安装包 ,务必勾选 “Add Python to PATH” 和 “Install pip”;
- 打开 PowerShell(不是 CMD),运行
python -c "import sqlite3; print(sqlite3.version)",如果报错ModuleNotFoundError,说明 SQLite 库没装好; - 此时不要重装 Python,而是用
pip install pysqlite3替代(它会编译适配的 SQLite 版本); - 再
pip install workbuddy,成功后运行workbuddy --version应返回版本号。
Linux(Ubuntu/Debian):常见问题是
gcc编译器缺失导致某些依赖安装失败。执行:sudo apt update && sudo apt install -y python3-pip python3-venv build-essential libsqlite3-dev python3 -m venv venv && source venv/bin/activate pip install --upgrade pip pip install workbuddy注意:
libsqlite3-dev是关键,它提供 SQLite 的头文件,否则pysqlite3编译会失败。很多教程漏掉这步,导致后续workbuddy init报错sqlite3 module not found。
安装完成后,验证是否成功:终端输入workbuddy,应显示帮助菜单。如果提示command not found,检查pip show workbuddy输出的Location路径,把该路径下的bin/(Linux/macOS)或Scripts/(Windows)加入系统 PATH。
3.2 初始化项目与目录结构解读:每个文件夹存在的理由
运行workbuddy init myblog后,生成的标准目录结构如下:
myblog/ ├── app.py # Flask 主程序入口,仅 80 行,核心逻辑在此 ├── config.py # 配置项:站点名、作者、主题色、分页数等 ├── content/ # 所有 Markdown 文章存放处,按日期子目录组织 │ ├── 2024/ # 年份文件夹 │ │ └── 06/ # 月份文件夹 │ │ └── 15-hello-world.md # 文件名格式:日期-标题.md ├── templates/ # Jinja2 模板,含 base.html, index.html, post.html ├── static/ # CSS/JS/图片,Flask 自动托管 │ ├── css/ │ │ └── main.css # 主题样式,可直接修改 │ └── js/ │ └── search.js # 客户端搜索逻辑 ├── data/ # SQLite 数据库存放处 │ └── blog.db # 自动生成,首次运行时创建表结构 └── requirements.txt # 依赖清单,含 flask, markdown, bleach 等重点解析三个易被忽略的细节:
content/下的日期子目录不是装饰:WorkBuddy 的app.py里有一段扫描逻辑,只读取content/YYYY/MM/下的.md文件,并按文件名中的日期排序。这意味着你不能把所有文章堆在content/根目录下,否则无法按时间线展示。我试过手动改名2024-06-15-hello-world.md到根目录,结果首页列表为空——因为扫描器根本不去那里找。templates/base.html里的{% block content %}{% endblock %}是关键钩子:所有子模板(index.html,post.html)都继承它,并在{% block content %}里填充具体内容。如果你要加一个“关于我”页面,只需新建templates/about.html,写{% extends "base.html" %},然后在{% block content %}里写 HTML,再在app.py里加一条路由@app.route('/about')返回这个模板。这种继承机制比复制粘贴 header/footer 稳定得多。data/blog.db的初始化时机:第一次运行python app.py时,程序会检查data/目录是否存在blog.db,若不存在,则执行建表 SQL:CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, slug TEXT UNIQUE NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, date DATE NOT NULL, tags TEXT, read_count INTEGER DEFAULT 0 );这个表结构决定了你能存什么。注意
slug字段是 URL 友好的文章标识(如hello-world),由文件名自动生成,不能重复;read_count默认为 0,每次访问/post/<slug>时,后端会执行UPDATE posts SET read_count = read_count + 1 WHERE slug = ?。这就是为什么你用DB Browser for SQLite打开blog.db,能看到阅读数实时增长。
3.3 首篇发布全流程:从写 Markdown 到网页可见的 7 个动作
以发布第一篇《Hello World》为例,实操步骤如下(全程在终端和文本编辑器间切换,无 GUI 操作):
创建 Markdown 文件:在
content/2024/06/下新建文件15-hello-world.md(日期必须是当前日期,否则不会被扫描到)。文件内容严格按 YAML Front Matter 格式:--- title: "Hello World" tags: ["flask", "workbuddy", "入门"] --- 这是我的第一篇 WorkBuddy 博客。 ## 为什么选它? 因为它让我专注写作,而不是配置服务器。启动 Flask 服务:在项目根目录运行
python app.py。终端会输出:* Serving Flask app 'app' * Debug mode: on * Running on http://127.0.0.1:5000此时打开浏览器访问
http://127.0.0.1:5000,首页应显示“Hello World”标题和摘要。验证 SQLite 写入:保持
app.py运行,打开DB Browser for SQLite,打开data/blog.db,切换到Browse Data标签页,选择posts表,应看到一条记录:id=1,slug="hello-world",title="Hello World",read_count=0。触发阅读计数:在浏览器中点击首页的“Hello World”链接,进入文章页。回到
DB Browser,刷新posts表,read_count应变为1。这证明后端更新逻辑生效。修改文章并实时预览:回到
15-hello-world.md,在末尾加一行> 这行是后来加的,保存文件。无需重启 Flask,浏览器按 F5,新内容立刻显示。这是 Flask 的debug=True模式 + Markdown 文件监听的功劳。添加图片:在
static/img/下新建文件夹hello-world/,放入一张banner.jpg。在 Markdown 中引用:。注意路径必须以/static/开头,因为 Flask 的send_from_directory规则只允许访问static/子目录。生成静态文件(可选):如果想部署到 GitHub Pages 或 Netlify,运行
workbuddy build。它会启动一个临时 Flask 实例,请求所有页面(/,/post/hello-world),把 HTML 保存到dist/目录。dist/就是纯静态站点,可直接上传。
这 7 步看似简单,但每一步都对应一个技术决策:Front Matter 解析用frontmatter库,Markdown 渲染用markdown库(支持表格、代码块),HTML 安全过滤用bleach(防 XSS),URL 生成用 Flask 的url_for()函数。它们共同构成一个鲁棒的发布管道。
4. 实操过程与核心环节实现:日更工作流的自动化与可持续性设计
4.1 日更工作流的三步固化:模板、脚本、Git 集成
日更最难的不是写,而是“每天打开电脑,面对空白编辑器时的启动成本”。WorkBuddy 通过三个层次降低这个成本:
第一层:标准化文章模板
在content/目录下新建template.md:
--- title: "" tags: [""] --- ## 背景 ## 过程 ## 结果 ## 反思每天写新文章时,复制此模板,改title和tags,填空即可。我把它设为 VS Code 的用户片段(User Snippet),输入wb+ Tab 就自动插入。模板强制结构化,避免想到哪写到哪。
第二层:一键生成今日文章脚本
在项目根目录新建new_post.sh(macOS/Linux)或new_post.bat(Windows):
# new_post.sh DATE=$(date +%Y-%m-%d) YEAR=$(date +%Y) MONTH=$(date +%m) SLUG=$(echo $1 | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') FILENAME="${DATE}-${SLUG}.md" mkdir -p "content/$YEAR/$MONTH/" cp content/template.md "content/$YEAR/$MONTH/$FILENAME" echo "Created: content/$YEAR/$MONTH/$FILENAME" code "content/$YEAR/$MONTH/$FILENAME" # 自动用 VS Code 打开用法:./new_post.sh "我的第一个WorkBuddy实践",自动生成2024-06-15-wo-de-di-yi-ge-workbuddy-shi-jian.md并打开编辑。Windows 版本用powershell替换sed和tr命令。这个脚本把“创建文件”压缩到一条命令,省去手动建目录、复制模板、命名文件三步。
第三层:Git 自动提交与部署
在app.py末尾加一段钩子:
if __name__ == '__main__': app.run(debug=True) # 开发模式下,每次保存后自动 git commit import subprocess try: subprocess.run(['git', 'add', '.'], check=True) subprocess.run(['git', 'commit', '-m', f'auto commit: {datetime.now().strftime("%Y-%m-%d %H:%M")}'], check=True) except: pass # git 未初始化则忽略再配一个 GitHub Action(.github/workflows/deploy.yml):
on: push: branches: [main] paths: ['content/**', 'templates/**', 'static/**'] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | pip install flask markdown bleach - name: Build static site run: python app.py --build # 需在 app.py 中添加 --build 参数解析 - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./dist这样,你每天写完保存,git push后,GitHub 自动构建并发布到https://yourname.github.io/myblog。日更变成“写 → 保存 → git push”,三步完成。
4.2 SQLite 数据库的日常维护:备份、查询、修复实战
SQLite 文件虽小,但作为唯一数据源,必须建立维护习惯。我每周日晨间固定做三件事:
1. 自动备份脚本backup_db.sh:
#!/bin/bash TIMESTAMP=$(date +%Y%m%d_%H%M%S) cp data/blog.db "backups/blog_${TIMESTAMP}.db" # 保留最近 7 天备份 find backups/ -name "blog_*.db" -mtime +7 -delete echo "Backup done: backups/blog_${TIMESTAMP}.db"加入 crontab:0 8 * * 0 /path/to/backup_db.sh(每周日早 8 点执行)。
2. 快速查询阅读趋势:
用DB Browser for SQLite的 Execute SQL 标签页,运行:
-- 查看本月阅读最多的 5 篇 SELECT title, read_count FROM posts WHERE date LIKE '2024-06%' ORDER BY read_count DESC LIMIT 5; -- 统计各标签文章数 SELECT tags, COUNT(*) as count FROM posts GROUP BY tags ORDER BY count DESC;结果直接导出 CSV,用 Excel 做折线图,一目了然看出哪些主题受欢迎。
3. 数据库损坏修复:
SQLite 极稳定,但断电或强制关机可能损坏。修复命令:
# 检查完整性 sqlite3 data/blog.db "PRAGMA integrity_check;" # 若返回 'ok',则正常;若返回 'error',则修复 sqlite3 data/blog.db ".dump" | sqlite3 data/blog_fixed.db mv data/blog_fixed.db data/blog.db我经历过一次因 Mac 睡眠中断写入导致read_count归零,用此法 2 分钟恢复。
4.3 Flask 服务的轻量部署:从本地到公网的平滑过渡
WorkBuddy 本地开发用app.run(debug=True),但上线需更稳方案。我用gunicorn+nginx组合,实测在 1C2G 的腾讯云轻量服务器上,QPS 稳定在 120+(模拟 50 并发用户)。
部署步骤:
- 服务器安装 Python3.11 和 nginx:
sudo apt update && sudo apt install -y python3.11 python3.11-venv nginx - 上传项目,创建虚拟环境:
python3.11 -m venv venv source venv/bin/activate pip install -r requirements.txt gunicorn - 创建 Gunicorn 配置
gunicorn.conf.py:bind = "127.0.0.1:8000" workers = 2 worker_class = "sync" timeout = 30 keepalive = 2 accesslog = "/var/log/workbuddy_access.log" errorlog = "/var/log/workbuddy_error.log" - 启动 Gunicorn:
gunicorn -c gunicorn.conf.py app:app - 配置 nginx(
/etc/nginx/sites-available/workbuddy):server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /path/to/myblog/static/; } } - 启用站点:
sudo ln -sf /etc/nginx/sites-available/workbuddy /etc/nginx/sites-enabled/,sudo nginx -t && sudo systemctl reload nginx。
至此,your-domain.com就是你的公网站点。Gunicorn 处理应用逻辑,nginx 处理静态文件和反向代理,分工明确。相比直接nohup python app.py &,这套方案支持优雅重启、日志分离、负载均衡扩展(加 worker 数即可)。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
5.1 “页面 404,但文件明明存在” —— 路径与扫描逻辑陷阱
现象:你在content/2024/06/下写了15-test.md,python app.py启动后访问/post/test显示 404。
原因:WorkBuddy 的文章扫描逻辑有两个硬性条件:
- 文件名必须匹配正则
^(\d{4})-(\d{2})-(\d{2})-(.+)\.md$(即YYYY-MM-DD-title.md); - 文件必须放在
content/YYYY/MM/子目录下,且YYYY和MM必须与文件名中年份、月份一致。
排查步骤:
- 运行
python app.py --debug(如果支持),或在app.py的scan_posts()函数里加print(f"Scanning: {file_path}"); - 检查终端输出,看是否扫描到你的文件;
- 如果没扫描到,确认文件名是
2024-06-15-test.md(不是15-test.md或test.md); - 确认目录是
content/2024/06/(不是content/2024/6/或content/06/)。
实操心得:我曾因 macOS Finder 显示
6月而误建content/2024/6/目录,导致所有文章 404。解决方案是永远用终端mkdir -p content/2024/06创建,避免 GUI 干预。
5.2 “中文标签搜索失效” —— SQLite 全文搜索配置遗漏
现象:文章tags: ["中文", "flask"],但在搜索框输入“中文”,无结果。
原因:SQLite 的LIKE查询对中文支持弱,WorkBuddy 默认用WHERE tags LIKE '%中文%',但tags字段存的是"中文,flask"这样的字符串,LIKE匹配不精确。
解决方案:启用 SQLite FTS5(全文搜索)扩展。
- 修改
app.py中的查询逻辑,建 FTS 表:conn.execute(""" CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5( title, content, tags, content='posts', content_rowid='id' ) """) - 插入数据时同步更新 FTS 表:
conn.execute("INSERT INTO posts_fts(rowid, title, content, tags) VALUES (?, ?, ?, ?)", (post_id, title, content, tags)) - 搜索时用
SELECT * FROM posts_fts WHERE posts_fts MATCH ?。
注意:FTS5 需 SQLite 3.22+,Ubuntu 20.04 自带版本可能过低,需
sudo apt install sqlite3升级。macOS 用brew install sqlite3并确保PATH优先使用新版。
5.3 “图片不显示,404 错误” —— 静态文件路径的绝对与相对之争
现象:Markdown 中写,浏览器 Network 标签页显示GET http://127.0.0.1:5000/img/banner.jpg 404。
原因:Flask 的send_from_directory只允许访问static/目录下的文件,而img/banner.jpg路径被解释为根目录下的img/,不在static/内。
正确写法:
- 图片放在
static/img/banner.jpg; - Markdown 中写
(注意开头的/,表示绝对路径); - 或在
app.py中添加自定义静态路由:
然后 Markdown 用@app.route('/img/<path:filename>') def serve_img(filename): return send_from_directory('static/img', filename)。
实操心得:我习惯用第一种,因为
static/是 Flask 标准约定,其他开发者接手一眼就懂。第二种虽灵活,但增加路由复杂度,得不偿失。
5.4 “日更中断后补发,日期排序错乱” —— 时间戳与文件系统顺序冲突
现象:6 月 15 日没写,6 月 16 日补发15-hello-world.md,但首页显示在 16 日文章之后。
原因:WorkBuddy 按文件名中的日期排序,但文件系统(如 ext4)的mtime(修改时间)可能晚于 16 日,导致os.listdir()返回顺序混乱。
解决方案:强制按文件名日期排序。在scan_posts()函数中,替换原排序逻辑:
# 原逻辑(依赖文件系统顺序) files = os.listdir(content_dir) # 新逻辑(按文件名日期解析排序) import re def extract_date(filename): match = re.match(r'^(\d{4})-(\d{2})-(\d{2})-', filename) if match: return f"{match.group(1)}{match.group(2)}{match.group(3)}" return "00000000" files = sorted(os.listdir(content_dir), key=extract_date, reverse=True)这个函数把
2024-06-15-hello.md映射为"20240615",按字符串倒序排,确保最新日期在前。我补发过 3 篇旧文,用此法后排序完全正确。
6. 进阶扩展与个性化定制:让 WorkBuddy 真正属于你
6.1 添加 RSS 订阅功能:三行代码搞定
RSS 是日更博主的生命线。WorkBuddy 默认不带,但加起来不到 10 行代码:
- 在
app.py顶部加导入:from flask import Response; - 在路由部分加:
@app.route('/feed.xml') def feed(): posts = get_recent_posts(20) # 获取最近 20 篇 xml_content = render_template('rss.xml', posts=posts) return Response(xml_content, mimetype='application/rss+xml') - 在
templates/下新建rss.xml:<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <channel> <title>{{ config.SITE_NAME }}</title> <link>{{ config.BASE_URL }}</link> <description>{{ config.DESCRIPTION }}</description> {% for post in posts %} <item> <title>{{ post.title }}</title> <link>{{ config.BASE_URL }}/post/{{ post.slug }}</link> <pubDate>{{ post.date.strftime('%a, %d %b %Y %H:%M:%S GMT') }}</pubDate> <description>{{ post.content[:200]|striptags|safe }}...</description> </item> {% endfor %} </channel> </rss>
提示:
config.BASE_URL需在config.py中定义,如'http://localhost:5000'或'https://your-site.com'。订阅地址就是https://your-site.com/feed.xml,主流 RSS 阅读器(如 Feedly)一键添加。
6.2 集成简易评论系统:用 GitHub Issues 当数据库
不想搭独立评论服务?用 GitHub Issues 实现“无后端评论”:
- 在
templates/post.html底部加:<div id="comments"> <h3>评论</h3> <div id="comment-list"></div> <form id="comment-form"> <input type="text" id="comment-name" placeholder="你的名字" required> <textarea id="comment-text" placeholder="写下你的想法..." required></textarea> <button type="submit">提交</button> </form> </div> <script src="/static/js/github-comments.js"></script> - 在
static/js/github-comments.js中,用 GitHub REST API 读写 Issues:
需申请 GitHub Personal Access Token(最小权限:// 读取:GET https://api.github.com/repos/yourname/myblog/issues?labels=comment&state=all // 写入:POST https://api.github.com/repos/yourname/myblog/issues (带 label "comment")public_repo)。
这样,每篇文章对应一个 GitHub Issue,评论就是 Issue 的评论,天然支持 Markdown、@ 提及、邮件通知。我用它半年,零维护成本,读者反馈比 Disqus 更真实。
6.3 主题定制:从 Bootstrap 到 Tailwind 的无缝切换
WorkBuddy 默认用 Bootstrap 5,但如果你偏好 Tailwind CSS:
- 删除
static/css/main.css,下载 Tailwind 的 CDN 版本(开发用)或用npx tailwindcss -i ./static/css/input.css -o ./static/css/output.css --watch; - 修改
templates/base.html的<head>,引入 Tailwind CSS; - 重写 `templates/index.html