一个基于 Node.js + SQLite + Redis + Nginx 的个人博客。
项目包含:
- Node.js REST API 后端
- SPA 前端和后台管理页面
- SQLite 数据库存储
- Redis 共享 Session、限流、冷却时间和验证码状态
- Nginx 反向代理
- systemd 常驻运行
- 自动备份、阅读量去重、外部链接检查
- 首页“我的博客 / 每日热点”切换,每天自动刷新热点并缓存到 SQLite
- Ubuntu / Debian Bash 一键部署脚本
推荐在全新的 Ubuntu 22.04 / 24.04 服务器上执行。服务器需要有公网 IP,并将域名 A/AAAA 记录解析到服务器。
sudo mkdir -p /tmp/leaf-blog
sudo chown "$USER":"$USER" /tmp/leaf-blog
git clone https://github.com/FuLiuNiano/LiuNian-Blog.git /tmp/leaf-blog
cd /tmp/leaf-blog如果项目已经上传到服务器,可以直接进入项目目录:
cd /tmp/leaf-blogchmod +x deploy.sh
sudo bash deploy.sh --domain blog.example.com把 blog.example.com 替换成你自己的域名。
首次运行时脚本会询问:
- 后台管理员 QQ 账号
- 后台密码
- QQ 邮箱 SMTP 配置,可留空
- 外部链接黑名单,可留空
脚本会自动生成 SQLite、Redis、systemd 和 Nginx 配置。已有 .env 不会被覆盖,已有数据库和备份也不会删除。
确保域名已经解析成功,并且服务器的 80、443 端口可以从公网访问:
sudo bash deploy.sh \
--domain blog.example.com \
--https \
--https-email admin@example.comHTTPS 证书由 Certbot 申请和续期。没有域名时不要使用 --https。
如果服务器已经由 1Panel 管理网站和 HTTPS,不要让本脚本再配置系统 Nginx。执行:
chmod +x deploy.sh
sudo bash deploy.sh --domain blog.example.com --no-nginx然后在 1Panel 中创建网站并设置反向代理:
- 代理目标:
http://127.0.0.1:8080 - 域名:你的博客域名
- HTTPS:在 1Panel 中申请和配置
这种方式不需要使用 --https,也不会安装或占用系统 Nginx 的 80 端口。Redis 仍由脚本自动安装和启动。
如果不使用默认的 8080 端口,例如改成 9000:
sudo bash deploy.sh --domain blog.example.com --no-nginx --port 9000此时 1Panel 的代理目标也要改为 http://127.0.0.1:9000。部署脚本会把端口写入生产环境配置,并用同一个端口执行健康检查。
deploy.sh 只支持 Ubuntu / Debian,并且需要 root 或 sudo 权限。它会:
- 检查 Node.js 22、npm、SQLite CLI、Redis、Git、编译工具和基础工具;默认还会检查 Nginx。
- 缺少依赖时自动安装,已经安装的依赖跳过。
- 创建低权限运行用户 leaf。
- 将代码同步到 /srv/leaf-blog。
- 将 SQLite 数据库放到 /var/lib/leaf-blog/leaf-blog.db。
- 将备份放到 /var/backups/leaf-blog。
- 启动 Redis,并检查是否返回 PONG。
- 创建并启动 leaf-blog.service。
- 默认配置 Nginx 反向代理,并禁止访问 .env、数据库、日志、备份和依赖目录;使用 --no-nginx 时跳过这一步。
- 检查 /api/health,并检查外部图片、音乐和友情链接。
外部资源检查失败只会给出警告,不会阻止博客启动;部署完成后应替换失效链接。
sudo bash deploy.sh --help常用参数:
# 修改项目正式目录
sudo bash deploy.sh --domain blog.example.com --app-dir /srv/leaf-blog
# 修改运行用户
sudo bash deploy.sh --domain blog.example.com --user leaf
# 修改 Node 本机端口,默认 8080
sudo bash deploy.sh --domain blog.example.com --port 8080
# 自动申请 HTTPS
sudo bash deploy.sh --domain blog.example.com --https --https-email admin@example.com
# 使用 1Panel 反向代理,不安装或配置系统 Nginx
sudo bash deploy.sh --domain blog.example.com --no-nginx
# 使用 1Panel 反向代理并把 Node 端口改为 9000
sudo bash deploy.sh --domain blog.example.com --no-nginx --port 9000Node 服务默认只监听 127.0.0.1:8080,也可以用 --port NUMBER 改成其他本机端口。外部访问统一经过 Nginx 或 1Panel。不要把 Node 端口或 Redis 的 6379 端口开放到公网。
首页左侧的“内容分类”可以切换“我的博客”和“每日热点”。每日热点分为“科技热点”“游戏热点”“GitHub 热点”和“娱乐热点”,服务器按上海时间每天 07:00 自动抓取,并将今天和昨天的标题、来源、时间和原文链接保存到 SQLite;更早的数据会自动清理,外部来源暂时不可用时会继续显示上一次成功的数据。
默认来源如下:科技使用 Ars Technica、TechCrunch、The Verge、MIT Technology Review 和 Wired 的公开 RSS,并按原文发布时间优先;游戏优先使用小黑盒官网公开信息流,RSSHub 的小黑盒路由作为备用,只有这些来源暂时不可用时才用 GameSpot、Steam 补位;GitHub 使用官方公开 API 查找近 3 天新建且已有关注度的项目;娱乐优先使用抖音官网公开热搜接口,同时保留微博热搜和 RSS 备用实例,并会尽量同时保留两个平台的内容。英文标题会自动翻译为中文,原文链接保持不变。RSSHub 文档列出了小黑盒、GitHub Trending、微博热搜和抖音热搜路由,可以在服务器的 .env 中调整刷新时间和来源:
HOT_TOPICS_ENABLED=true
# 每个分类最多 10 条
HOT_TOPICS_LIMIT=10
HOT_TOPICS_REFRESH_HOUR=7
HOT_TOPICS_REFRESH_MINUTE=0
HOT_TOPICS_RETRY_MINUTES=30
# 英文标题自动翻译为中文;翻译服务不可用时保留英文原文
HOT_TOPICS_TRANSLATION_ENABLED=true
# HOT_TOPICS_TRANSLATION_MAX_ITEMS=40
# 可选:GitHub API Token;不填也能运行,但匿名 API 额度较低
# GITHUB_TOKEN=
# 自定义来源格式:分类|URL,多个来源用英文逗号分隔
# HOT_TOPICS_FEEDS=game|https://example.com/game.xml,tech|https://example.com/tech.xml只建议使用允许公开访问的官方 RSS 或授权新闻 API,不要把需要登录的账号信息写进代码或提交到 GitHub。修改 .env 后重启博客:
sudo systemctl restart leaf-blog
curl http://127.0.0.1:8080/api/hot-topics查看服务状态:
sudo systemctl status leaf-blog --no-pager
sudo systemctl status redis-server --no-pager查看实时日志:
sudo journalctl -u leaf-blog -f重启博客:
sudo systemctl restart leaf-blog健康检查:
curl http://127.0.0.1:8080/api/health正常情况下会看到 healthy: true,并且日志中显示共享状态为 Redis。
如果部署时使用了其他端口,例如 9000,健康检查也要使用对应端口:
curl http://127.0.0.1:9000/api/health在服务器上执行:
cd /tmp/leaf-blog
git pull --ff-only
sudo bash deploy.sh --domain blog.example.com更新脚本不会覆盖服务器上的 .env、SQLite 数据库、用户数据和备份。更新后如果需要立即重启:
sudo systemctl restart leaf-blog如果使用 1Panel 反向代理,更新时也要保留 --no-nginx:
sudo bash deploy.sh --domain blog.example.com --no-nginx
sudo systemctl restart leaf-blog生产环境主要使用以下路径:
代码目录:/srv/leaf-blog
SQLite:/var/lib/leaf-blog/leaf-blog.db
备份:/var/backups/leaf-blog
配置:/srv/leaf-blog/.env
日志:journalctl -u leaf-blog
SQLite 不需要单独运行数据库服务。Node.js 的 better-sqlite3 负责读写 SQLite 数据库;脚本安装的 sqlite3 命令行工具主要用于维护和排查。
Redis 用于共享:
- 后台和用户登录 Session
- IP、账号和接口限流
- 验证码状态
- 异常行为冷却时间
如果 Redis 连接失败,程序会退回单机内存状态。正式部署必须确认 .env 中存在:
REDIS_URL=redis://127.0.0.1:6379不要把以下内容上传到公开 GitHub 仓库:
- .env
- SQLite 数据库、WAL 和 SHM 文件
- data/users.json
- data/comments.json
- data/guestbook.json
- data/journals.json
- data/state.json
- data/site.json
- backups/
- logs/
- public/uploads/
项目的 .gitignore 已经默认排除这些文件。不要使用下面的命令强制添加隐私文件:
git add -f .env上传前检查:
git status --short
git ls-files -- .env data/leaf-blog.db data/leaf-blog.db-wal data/leaf-blog.db-shm data/users.json data/comments.json data/guestbook.json data/journals.json data/state.json data/site.json backups logs public/uploads第二条命令没有输出,才表示这些隐私路径没有被 Git 跟踪。
如果密码、SMTP 授权码或其他密钥曾经提交到公开仓库,应立即更换,并清理 Git 历史。.env 文件权限建议保持为仅运行用户可读:
sudo chmod 600 /srv/leaf-blog/.env
sudo chown leaf:leaf /srv/leaf-blog/.env项目提供了 upload-updates.sh,只会上传公开的部署文档和脚本,不会执行 git add .。在项目根目录执行:
chmod +x upload-updates.sh
bash upload-updates.sh脚本会检查暂存文件范围、隐私路径和常见密钥格式。首次使用时,如果 Git 尚未设置提交作者信息,脚本会提示填写;邮箱建议使用 GitHub 的 noreply 地址。
需要 Node.js 22 或更高版本:
npm ci
npm start默认访问地址:
http://127.0.0.1:3000
本地开发可以使用项目根目录的 .env.example 作为配置参考。不要把真实密码、SMTP 授权码或数据库文件复制到 GitHub。
Windows 本地启动脚本也支持设置端口:
.\start-blog.ps1 -Port 9000
.\stop-blog.ps1 -Port 9000MIT