从零搭建 HexoAdmin:手机发帖 + 微博式时间线,让静态博客动起来
背景
Hexo 是一个优秀的静态博客框架,但它有一个天然短板:发文必须用电脑。写文章、生成、部署,整个流程离不开终端和编辑器。
对于我来说,很多时候灵感是碎片化的——路上看到一件事、吃饭时想到一个点子,想随手记下来,但掏手机打开 Hexo 后台根本不现实。
于是就有了这个项目:HexoAdmin。
核心目标很简单:
在阿里云服务器上跑一个轻量 API 服务,绑定独立域名,通过手机浏览器就能发帖、上传图片,博客前端以微博式时间线展示。
功能概览
后台管理(手机可用)
- 密码登录,Token 认证,30 天有效期
- 富文本输入,支持换行、空格、Emoji 表情
- 拍照/选图上传,支持 JPG、PNG、GIF、WebP,单张最大 20MB
- 图片九宫格预览,可删除
- 历史记录管理,支持编辑和删除
- 公开/私密切换:默认公开,点击锁图标切换为私密,私密文章前台不可见
前台展示(微博式时间线)
- 卡片式布局,头像 + 时间 + 内容 + 图片
- 图片自适应九宫格(1张单图、2-3张横排、4张2x2、5-9张3x3)
- 单张图片限制最大 200px,避免过大
- 超过 300 字自动折叠,点击展开全文
- 图片点击放大查看(Lightbox)
- 无限滚动加载,每页 10 条
- 仅展示公开文章,私密文章对访客不可见
技术架构
1 | 手机浏览器 ──HTTPS──→ api.weixing.me(阿里云 ECS + 宝塔面板) |
技术选型理由
| 组件 | 选择 | 理由 |
|---|---|---|
| 后端框架 | Flask | 轻量、简单,API 就几个接口 |
| WSGI 服务器 | Gunicorn | 生产级,多进程,宝塔 Python 项目管理器原生支持 |
| 数据库 | SQLite | 零配置,单文件,博客数据量小完全够用 |
| 反向代理 | Nginx | 宝塔自带,SSL + 反代一站式搞定 |
| 前端 | 原生 JS + Pug | 不引入框架,Hexo 主题直接集成 |
核心代码
1. 后端 API(app.py)
核心接口:
1 | # 健康检查 |
数据库 posts 表包含 is_public 字段(1=公开,0=私密),公开接口 GET /posts 只查询 is_public = 1 的记录:
1 | rows = db.execute( |
数据库初始化用了 before_request 钩子,确保 Gunicorn 多进程下也能正确建表:
1 |
|
三张表:posts(文章)、tokens(认证令牌)、images(图片关联)。
2. 前台时间线(timeline.pug)
Hexo 的 Butterfly 主题使用 Pug 模板引擎。在 themes/butterfly/layout/ 下新建 timeline.pug,继承 layout.pug:
1 | extends layout |
关键设计:
- 滚动加载:监听
window.scroll,距底部 800px 时触发下一页 - 图片九宫格:根据图片数量动态计算 grid 列数
- 文字折叠:超过 300 字截断,显示”展开全文”按钮
- Lightbox:点击图片弹出全屏遮罩查看大图
3. 后台管理页(admin.html)
纯 HTML + CSS + JS 单文件,不依赖任何框架:
- 密码输入框 + 登录按钮
- 文本域(支持 Emoji 表情面板)
- 文件选择器(
accept="image/*",支持多选) - 图片预览 + 删除
- 历史记录列表,每条记录支持三个操作:
- 🔒 锁按钮:点击切换公开/私密状态,私密文章前台不可见
- 编辑按钮:将内容回填到输入框,修改后保存
- 删除按钮:确认后删除文章及关联图片
- 编辑模式下顶部显示黄色提示条,发布按钮变为”保存”
- 私密文章显示”私密”标签 + 锁图标
服务器配置
宝塔面板部署
- 安装软件:Nginx + Python 项目管理器 + Python 环境管理器
- 安装 Python 3.11:Python 项目管理器 → 版本管理
- 创建网站:
api.weixing.me,纯静态,申请 Let’s Encrypt SSL,强制 HTTPS - 添加 Python 项目:
- 入口文件:
app:app - 启动方式:Gunicorn
- 端口:5000
- 环境变量:
TIMELINE_ADMIN_PASSWORD、TIMELINE_SECRET_KEY
- 入口文件:
- Nginx 反向代理:
1 | client_max_body_size 60m; |
关键配置项
| 配置 | 值 | 说明 |
|---|---|---|
MAX_IMAGE_SIZE |
20MB | 单张图片大小限制 |
ALLOWED_EXTENSIONS |
jpg/jpeg/png/gif/webp | 允许的图片格式 |
POSTS_PER_PAGE |
10 | 每页文章数 |
client_max_body_size |
60m | Nginx 上传限制 |
| CORS origins | www.weixing.me + weixing.me |
跨域允许域名 |
数据备份
宝塔计划任务,每天凌晨 3 点自动备份 SQLite 数据库:
1 | mkdir -p /www/backup/timeline |
踩过的坑
1. CORS 反引号问题
在 shell 里用 sed 修改 Python 代码时,反引号被当作命令替换符号,导致 CORS origin 变成了 `https://www.weixing.me`(带反引号),浏览器不认。最终用 cat > /tmp/fix.py << 'EOF' 的 heredoc 方式解决。
2. Gunicorn 多进程下数据库表不存在
init_db() 放在模块顶层执行时,Gunicorn 多进程启动可能导致并发写入 SQLite,表创建不完整。改用 @app.before_request 钩子,在首次请求时确保表已创建。
3. Nginx 全局上传限制
站点配置里设了 client_max_body_size 60m,但 Nginx 主配置文件 nginx.conf 里还有全局的 50m 限制,大文件上传仍然被拦截。需要同时修改两处。
4. CORS 域名匹配
博客同时有 www.weixing.me 和 weixing.me 两个域名,CORS 必须同时允许两者,否则其中一个会被拦截。
5. IntersectionObserver 不触发
用 visibility: hidden 隐藏加载指示器后,IntersectionObserver 不再触发滚动加载。改用 window.scroll 事件监听,距底部 800px 时触发下一页,更可靠。
总结
整个项目从零到上线,核心代码不到 500 行(后端 ~200 行 + 前台 ~150 行 + 后台 ~150 行),但实现了一个完整的内容管理系统:
- 手机发帖:随时随地记录灵感
- 微博式展示:轻量、直观、适合碎片化内容
- 零维护成本:SQLite 单文件数据库 + 宝塔自动管理进程
- 静态博客动态化:Hexo 负责主站内容,API 负责动态时间线,各司其职
对于有类似需求的朋友,这套方案可以直接复用。代码已开源在 HexoAdmin 目录下,欢迎参考。

