使用 Flask 和 Bootstrap 构建的轻量博客,基于 Git Repo 存储博客内容。
项目缘起于在前司工作期间,需要搭建一个团队技术博客,需要支持团队多人发布文章,并放在公网访问。彼时团队代码托管在 Coding,刚好也不想增加新的一套账号体系,因此想到了 Coding repo 作为文章来源,使用 Coding API 来获取内容并展示的方案。不巧的是,在职期间一直没时间实现,离职后终于动手实现了基本功能。
- 使用 Git Repo 管理 markdown 文章,支持扩展 markdown 语法;
- 通过 webhook 自动更新文章内容(目前支持 Coding 和 GitHub);
- 支持文章拥有以下元数据:分类和标签,另外通过跟踪 commit 记录获取文章创建时间和更新时间;
- 支持按分类和标签查看文章;
- 支持全文检索文章标题和内容;
- 支持自动生成文章目录,并显示当前阅读位置;
- Docker 快速部署;
- 通过钉钉接收 HTTP 500 告警;
- Bootstrap
- jQuery
- Moment.js
- Font Awesome
- Flask
- RQ
- Gunicorn
- Flask-SQLAlchemy
- Flask-WhooshAlchemyPlus
- Flask-RQ2
- Whoosh
- Jieba
- python-markdown
- Pygments
- requests
- PyMySQL
- GitPython
安装 redis 和 mysql,并创建 mysql 数据库(建议使用 utf8mb4 编码以兼容 emoji 表情等字符)。
拉取代码:
git clone https://github.com/chiqj/MrChiBlog.git
安装依赖:
cd MrChiBlog
pipenv install --deploy
在项目根目录创建 .env
环境变量文件,示例如下:
# 生产环境 mysql 数据库
PROD_DATABASE_URI=mysql+pymysql://test:test123@127.0.0.1:3306/mrchiblog?charset=utf8mb4
# 指定 Flask app
FLASK_APP=manage:app
# 指定 Flask 在 production 模式
FLASK_ENV=production
# Git 仓库配置,依次为 Repo 目录,Repo 部署公钥(用于拉代码)对应的私钥
REPO_DIR=$HOME/notes/
REPO_SSHKEY=$HOME/.ssh/id_rsa
# Coding 上该 repo 的 webhook token,用于接收 push 代码消息,触发更新
WEBHOOK_TOKEN=1234567890
# 生成文章固定链接的密钥
HMAC_KEY=foobar
# 钉钉 robot token,用于接收 HTTP 500 消息
PROD_DINGTALK_TOKEN=123456789000000
# 生产环境 redis 数据库
PROD_REDIS_URI=redis://127.0.0.1:6379/0
# 生产环境 whoosh 索引目录配置
PROD_WHOOSH_BASE=./prod_whoosh_idx
# 生产环境 rq 配置
PROD_RQ_REDIS_URI=redis://127.0.0.1:6379/1
初始化文章 Repo:
cd $REPO_DIR
git init
git remote add origin <git ssh url>
初始化数据库:
pipenv run flask deploy
运行:
pipenv run gunicorn -c gunicorn.py -b 0.0.0.0:5000 manage:app
启动 rq worker:
pipenv run flask rq worker update
pipenv run flask rq worker notify
访问 5000
端口即可。
拉取代码:
git clone https://github.com/chiqj/MrChiBlog.git
在项目根目录创建 .env
环境变量文件。与普通部署时略有不同:
- 需要分别指定 mysql 数据库用户名、密码、数据库名;
- redis 和 rq redis url 中的主机部分都使用容器名。
示例如下:
# 生产环境 mysql 数据库
MYSQL_USER=test
MYSQL_PASSWORD=test123
MYSQL_DATABASE=mrchiblog
# 指定 Flask app
FLASK_APP=manage:app
# 指定 Flask 在 production 模式
FLASK_ENV=production
# Coding 上该 repo 的 webhook token,用于接收 push 代码消息,触发更新
WEBHOOK_TOKEN=1234567890
# 生成文章固定链接的密钥
HMAC_KEY=foobar
# 钉钉 robot token,用于接收 HTTP 500 消息
PROD_DINGTALK_TOKEN=123456789000000
# 生产环境 redis 数据库
PROD_REDIS_URI=redis://redis:6379/0
# 生产环境 whoosh 索引目录配置
PROD_WHOOSH_BASE=./prod_whoosh_idx
# 生产环境 rq 配置
PROD_RQ_REDIS_URI=redis://127.0.0.1:6379/1
构建镜像
docker-compose build
初始化文章 Repo:
cd $REPO_DIR
git init
git remote add origin <git ssh url>
复制 ssh_key(私钥):
cp $HOME/.ssh/id_rsa /mrchiblog/sshkey/id_rsa
chmod 600 /mrchiblog/sshkey/id_rsa
运行
docker-compose up -d
初始化数据库:
docker-compose exec web flask deploy
在宿主机上访问 127.0.0.1:5000
即可。
目前我是将一些元数据直接放到了 markdown 文档中, python-markdown 库在转换为 html 时,通过 Meta-data 扩展读取这些元数据并存入数据库,而转换得到的 html 中不会包含这些元数据。这种方式,无疑给写文档的人增加了一定工作量,但好处是,所有信息都在 Coding repo 中,任何时候都可以删库重建而不会造成数据丢失。
在 markdown 中包含的元数据包括:分类(category)和标签(labels)。示例如下:
---
category: Docker
labels: Docker
Dockerfile
---
这是 markdown 正文第一句话。Good luck, have fun.
元数据写在 markdown 文档的开头,以 ---
行开头(L1)和结尾(L6),并与 markdown 正文之间有一个空行(上面的 L7)。元数据采用 yaml 语法,分类和创建时间只能有一个,标签可以有多个。多个标签分别写在不同行,并保证从第二个标签开始,缩进大于 2 个空格。
上面的示例中,该文章分类为 “Docker”,标签为 “Docker” 和 “Dockerfile”。
开发时,Flask 应被设置为 development 模式。相比普通部署的 .env
文件,部分环境变量需要调整,包括:
# flask 应用工作在 development 模式,开启 debug 和 auto_reload
FLASK_ENV = "development"
# 开发环境 mysql 数据库
DEV_DATABASE_URI
# 开发环境 redis 数据库
DEV_REDIS_URI
# 开发环境 whoosh 索引目录
DEV_WHOOSH_BASE
# 开发环境 dingtalk robot token
DEV_DINGTALK_TOKEN
初始化:
flask deploy
运行:
flask run
访问 http://127.0.0.1:5000
即可。
- 感谢“学长”在 Docker 方面的技术指导,帮助了我很多。
- 感谢不愿透露姓名的“菜鸡”同学帮我解决前端 bug。
- 由于我前端水平不够高,博客页面样式和配色大量借鉴了 SegmentFault,在此对 SegmentFault 表示感谢。
- 增加测试用例并实现 TDD;
- [DONE]使用“操作 Git 仓库”方式替代“使用 Coding API”方式获得内容和数据(可兼容任意 Git 托管平台);
- 文章支持多作者,并增加按作者展示文章内容;
- 增加后台管理;
- 增加评论;
- 前后端分离 + Restful 接口;