AndrewBlog 开发者文档
项目文档

AndrewBlog 开发者文档

Flask 3.1 · SQLite · machinaix.com

基于 Flask 构建的个人博客系统,支持 Markdown 写作、封面图片上传、标签分类、评论系统和管理员后台。本文档涵盖从本地开发到生产部署、日常运维与搜索引擎收录的完整流程。

框架 Flask 3.1数据库 SQLite许可证 MIT线上站点 machinaix.com

技术栈

层次技术
Web 框架Flask 3.1
数据库 ORMFlask-SQLAlchemy 3.1 + SQLite
数据库迁移Flask-Migrate 4.0(Alembic)
用户认证Flask-Login 0.6
表单与 CSRFFlask-WTF 1.2
Markdown 渲染Python-Markdown 3.7
Markdown 编辑器EasyMDE(CDN)
前端 UIPico.css v2(CDN)
密码安全Werkzeug generate_password_hash
频率限制Flask-Limiter 3.11

项目结构

AndrewBlog/
├── app/
│   ├── __init__.py          # 应用工厂 create_app()
│   ├── config.py            # 开发/生产配置
│   ├── extensions.py        # db / login_manager / migrate / csrf 实例
│   ├── models.py            # User / Post / Comment 数据模型
│   ├── forms.py             # WTForms 表单定义
│   ├── utils.py             # 文件上传 / 删除工具函数
│   └── routes/
│       ├── auth.py          # 注册 / 登录 / 忘记密码 / 退出
│       ├── blog.py          # 前台博客(首页 / 文章详情 / 评论)
│       └── admin.py         # 后台管理(文章列表 / 新建 / 编辑 / 删除)
│
├── app/templates/
│   ├── base.html            # 公共布局(导航 / Flash 消息)
│   ├── auth/
│   │   ├── login.html
│   │   ├── register.html
│   │   └── forgot_password.html
│   ├── blog/
│   │   ├── index.html       # 文章列表(分页)
│   │   └── post_detail.html # 文章详情 + 评论
│   └── admin/
│       ├── posts.html       # 文章管理列表
│       └── editor.html      # 文章编辑器(EasyMDE)
│
├── scripts/
│   ├── init_db.py           # 首次部署初始化(建表 + 管理员账号)
│   └── db_manage.py         # 数据库管理工具(查看 / 删文章 / 删用户)
├── migrations/              # Alembic 数据库迁移文件
├── instance/                # SQLite 数据库(不提交到 Git)
├── uploads/                 # 用户上传的封面图片(不提交到 Git)
├── .env                     # 环境变量(不提交到 Git)
├── .env.example             # 环境变量示例
├── requirements.txt
└── run.py                   # 入口文件

路由总览

前台(博客)

方法URL功能
GET/文章列表首页(分页,每页 10 篇)
GET/post/<slug>文章详情页(含评论,管理员可预览草稿)
POST/post/<slug>/comment提交评论(需登录)
GET/uploads/<filename>访问上传的封面图片

认证

方法URL功能
GET/POST/auth/register注册新账号
GET/POST/auth/login登录(支持用户名或邮箱)
GET/POST/auth/forgot-password忘记密码(用户名 + 邮箱验证后重置)
GET/auth/logout退出登录

后台管理(需管理员权限)

方法URL功能
GET/admin/重定向到文章列表
GET/admin/posts文章管理列表
GET/POST/admin/post/new新建文章
GET/POST/admin/post/<id>/edit编辑文章
POST/admin/post/<id>/delete删除文章

数据模型

User

字段类型说明
idInteger主键
usernameString(64)唯一,用户名
emailString(120)唯一,邮箱
password_hashString(256)哈希后的密码
is_adminBoolean是否管理员
created_atDateTime注册时间

Post

字段类型说明
idInteger主键
titleString(200)文章标题
slugString(200)URL 标识,唯一
summaryString(500)摘要(可选)
bodyText正文(Markdown 原文)
tagsString(200)标签(逗号分隔)
cover_imageString(200)封面图片文件名
publishedBoolean是否发布
created_at / updated_atDateTime时间戳

Comment

字段类型说明
idInteger主键
bodyText评论内容
post_idInteger外键 → Post
author_idInteger外键 → User
created_atDateTime评论时间

本地运行

1. 克隆仓库

git clone https://github.com/ANDREW-SVIP/AndrewBlog.git
cd AndrewBlog

2. 创建虚拟环境并安装依赖

#如果没有权限 先解锁脚本执行权限(PowerShell 默认限制直接运行激活脚本)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process

#接着执行
python -m venv venv

# Windows
venv\Scripts\activate
# 初始化数据库
python scripts/init_db.py

# macOS / Linux
source venv/bin/activate

pip install -r requirements.txt

3. 配置环境变量

cp .env.example .env

编辑 .env,至少修改 SECRET_KEY

FLASK_APP=run.py
FLASK_ENV=development
SECRET_KEY=your-long-random-secret-key-here
DATABASE_URL=sqlite:///blog.db
UPLOAD_FOLDER=uploads
MAX_CONTENT_LENGTH=5242880

4. 初始化数据库

flask db upgrade

5. 创建管理员账号

python - <<'EOF'
from app import create_app
from app.extensions import db
from app.models import User

app = create_app()
with app.app_context():
    u = User(username='admin', email='admin@example.com', is_admin=True)
    u.set_password('your-password')
    db.session.add(u)
    db.session.commit()
    print('管理员账号创建成功')
EOF

6. 启动开发服务器

flask run

或者直接运行:

python run.py

访问 http://127.0.0.1:5000

主要功能

  • Markdown 编辑器:集成 EasyMDE,支持实时预览、分屏模式、自动保存(localStorage)
  • 封面图片:上传后即时预览,支持替换和删除,限制 5MB / jpg png gif webp
  • 草稿系统:文章可保存为草稿,管理员登录后可预览草稿,普通访客只能看已发布文章
  • 标签:逗号分隔,在文章列表和详情页展示
  • 评论:登录用户可评论,按时间升序排列
  • 忘记密码:通过用户名 + 注册邮箱双重验证后直接设置新密码,无需邮件
  • CSRF 保护:所有 POST 表单均受 Flask-WTF CSRF 保护

生产部署建议

  1. FLASK_ENV 改为 production
  2. 使用强随机 SECRET_KEY(至少 32 位随机字符串)
  3. 将 SQLite 替换为 PostgreSQL 或 MySQL(修改 DATABASE_URL
  4. 使用 Gunicorn + Nginx 作为 WSGI 服务器
  5. uploads/ 目录托管到对象存储(如 OSS / S3)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app('production')"

生产服务器信息

项目内容
服务器博客服务器 IP(SSH 端口 博客 SSH 端口
项目目录/www/wwwroot/andrewblog
进程管理systemd(服务名 andrewblog
WSGI 服务器Gunicorn,监听 127.0.0.1:8000
Web 服务器Nginx(宝塔面板管理)
数据库SQLite,路径 instance/blog.db
日志/var/log/andrewblog/

修改代码后的发布流程

本地开发完成后

# 1. 提交代码
git add .
git commit -m "描述本次修改内容"
git push

服务器拉取更新

# SSH 登录服务器
ssh -p <博客 SSH 端口> root@<博客服务器 IP>

# 进入项目目录
cd /www/wwwroot/andrewblog

# 拉取最新代码
git pull

# 如果新增了依赖包,重新安装
venv/bin/pip install -r requirements.txt

# 只有修改了 models.py(新增/删除字段或表)时才需要执行,日常发布不需要
# FLASK_ENV=production venv/bin/flask db upgrade

# 重启服务(优雅重启,不中断用户请求)
systemctl reload andrewblog

验证部署成功

# 查看服务状态
systemctl status andrewblog

# 查看最新日志
tail -f /var/log/andrewblog/error.log

常用运维命令

# 完全重启服务
systemctl restart andrewblog

# 停止服务
systemctl stop andrewblog

# 备份数据库
cp /www/wwwroot/andrewblog/instance/blog.db ~/backup/blog_$(date +%Y%m%d).db

# 查看实时访问日志
tail -f /var/log/andrewblog/access.log

如果改动了静态文件(CSS/JS)

Nginx 缓存了静态文件,改动后需要让浏览器重新加载:

# 方法一:在文件名后加版本号(推荐)
# 例如 style.css?v=2

# 方法二:重载 Nginx
nginx -s reload

访问日志解读(爬虫 & 收录验证)

日志路径:/var/log/andrewblog/access.log

查看最近 20 条日志

tail -20 /var/log/andrewblog/access.log

实时监控(持续滚动)

tail -f /var/log/andrewblog/access.log

日志格式说明

每一行格式如下:

IP地址 - - [时间 +0800] "请求方法 路径 协议" 状态码 响应字节数 "来源页" "User-Agent"

示例:

66.249.68.32 - - [22/May/2026:14:21:21 +0800] "GET /post/markdown-cheatsheet HTTP/1.1" 200 5091 "-" "Mozilla/5.0 (compatible; Googlebot/2.1)"

如何识别 Google 爬虫

特征: IP 段 66.249.x.x,User-Agent 含 Googlebot

grep -i "googlebot" /var/log/andrewblog/access.log | tail -20

看到以下内容说明 Google 正在抓取:

66.249.68.32 - "GET /post/文章slug" 200 ... "Googlebot"
66.249.68.33 - "GET /sitemap.xml"  200 ... "Googlebot"

收录验证: 在 Google 搜索 site:machinaix.com,有结果说明已收录。

如何识别百度爬虫

特征: User-Agent 含 Baiduspider

grep -i "baiduspider" /var/log/andrewblog/access.log | tail -20

看到以下内容说明百度正在抓取:

x.x.x.x - "GET /post/文章slug" 200 ... "Baiduspider"
x.x.x.x - "GET /robots.txt"   200 ... "Baiduspider"

收录验证: 在百度搜索 site:machinaix.com,有结果说明已收录。

如何确认百度主动推送成功

发布文章后,查看日志里有没有 POST /admin/post/newPOST /admin/post/ID/edit 的 302 响应:

grep "POST /admin" /var/log/andrewblog/access.log | tail -10

看到 302 状态码说明文章发布成功,百度推送函数已自动执行。

去百度搜索资源平台「普通收录 → 数据反馈」可查看历史推送数量(数据有延迟,次日更新)。

常见爬虫 IP / UA 速查

爬虫IP 特征User-Agent 关键词
Google66.249.x.xGooglebot
百度180.76.x.x / 220.181.x.xBaiduspider
UptimeRobot216.144.x.x / 69.162.x.xUptimeRobot
Bing157.55.x.xbingbot

数据库管理工具(db_manage.py)

所有操作在服务器上执行:

查看数据库状态(只读,随时可用)

venv/bin/python scripts/db_manage.py status

显示所有用户、文章、评论数量,不修改任何数据。

删除指定文章(按 slug)

# 预览(不会真正删除)
venv/bin/python scripts/db_manage.py delete-posts --slugs "slug1,slug2"

# 确认无误后加 --execute 执行
venv/bin/python scripts/db_manage.py delete-posts --slugs "slug1,slug2" --execute

删除指定用户(按 ID)

# 预览
venv/bin/python scripts/db_manage.py delete-users --ids 2,6

# 执行(管理员账号自动跳过,不会误删)
venv/bin/python scripts/db_manage.py delete-users --ids 2,6 --execute
💡

slug 是文章 URL 中的标识符,在 status 输出里每篇文章都能看到。

垃圾注册账号处理

如何发现

定期运行 db_manage.py status 查看用户列表,重点关注:

  • 用户名含俄语、阿拉伯语、随机数字
  • 用户名含 http://https:// 链接
  • 邮箱域名可疑

如何清理

venv/bin/python scripts/db_manage.py status            # 确认可疑用户 ID
venv/bin/python scripts/db_manage.py delete-users --ids 可疑ID          # 预览
venv/bin/python scripts/db_manage.py delete-users --ids 可疑ID --execute # 执行

已有防护措施

防护说明
频率限制同一 IP 每小时最多注册 5 次,登录每分钟最多 20 次
蜜罐字段注册页隐藏字段,机器人自动填写后静默拦截
用户名格式只允许中文、英文、数字、下划线、连字符

UptimeRobot 监控

UptimeRobot 监控博客可用性,宕机时自动推送告警。

查看监控状态

  1. 访问 uptimerobot.com 并登录
  2. 首页查看所有监控项:绿色 = 正常,红色 = 宕机

添加新监控

  1. 点击「+ Add New Monitor」
  2. Monitor Type 选「HTTP(s)」
  3. URL 填 https://machinaix.com
  4. Monitoring Interval 选「5 minutes」

宕机后处理流程

ssh -p <博客 SSH 端口> root@<博客服务器 IP>
cd /www/wwwroot/andrewblog
systemctl status andrewblog          # 查看状态
journalctl -u andrewblog -n 100 --no-pager  # 查看错误日志
systemctl restart andrewblog         # 重启服务

Google 收录(Search Console)

查看收录情况

  1. 访问 search.google.com/search-console
  2. 选择 machinaix.com 资源 → 左侧「覆盖率」查看已收录页面

提交 Sitemap(只需做一次)

  1. Search Console → 左侧「站点地图」
  2. 输入 sitemap.xml 点击提交

新文章发布后请求收录

  1. Search Console 顶部搜索框输入文章完整 URL
  2. 点击「请求编入索引」,1-3 天内生效

验证文章是否被收录

在 Google 搜索:

site:machinaix.com/post/文章slug

有结果 = 已收录,无结果 = 尚未收录(等待或手动请求)。

License

MIT