songquanpeng/blog

个人博客系统

Go

72

505 commits

updated Sep 13, 2026

See the code

README

个人博客系统

使用 Gin + GORM + React + Bulma 的单人博客系统。公开页面保留经典 Bulma 主题并由 Go 服务端渲染,管理后台使用独立的 React 设计系统;旧版 Sequelize SQLite 数据可直接挂载使用。

特性

  • 公开站点保留兼容旧内容的 Bulma 主题,管理后台使用更适合写作与内容管理的独立界面。
  • 公开站点可在经典 Bulma 与 Studio 宋韵主题之间即时切换;Studio 使用黑灰与暖纸白的散文式视觉,以及更适合中文长文的宋体排版。
  • 服务端输出完整语义化 HTML,包含 canonical、Open Graph、JSON-LD、Atom、sitemap 和 robots。
  • 显式沿用旧表名及字段名:Pages、Users、Options、Files。旧用户数据只用于显示历史作者,不再参与认证。
  • 后台仅支持一个 GitHub OAuth 管理员,推荐使用不可变的 GitHub User ID 建立白名单。
  • 内置 blog-cli:通过本站 device flow 授权,覆盖页面与微博客 CRUD、正文统一检索、页面发布状态、站点标题与侧边栏、全部设置和文件管理;token 默认有效一年并可随时撤销。
  • 内置微博客子功能:公开短内容位于独立路径(默认 /microblog),支持 Markdown、公开/私密状态、分页和完整后台 CRUD;启停、访问路径、标题与简介均可即时调整,停用不会删除数据。
  • 内置隐私友好的文章统计:后台可查看 PV、UV、每日趋势、文章排行、Referrer、搜索引擎与可见关键词、User-Agent;不保存原始 IP。
  • Markdown 经过安全渲染;站点所有者发布的 HTML 会被完整保留,其中独立 HTML 页面运行在无同源权限的沙箱中。应用还包含 CSP、安全 Cookie、OAuth state + PKCE、同源检查、请求体限制和安全文件上传。
  • 历史 Raw 工具页在无 same-origin 权限的 CSP sandbox 中运行,保留脚本交互但不能读取主站 Cookie、后台 API 响应或父页面 DOM。
  • 保留 PORT、SQLITE_PATH、UPLOAD_PATH、3000 端口、/app/data 卷、旧 URL 与主要 /api 路径。
  • 支持 Docker、Makefile、npm start,以及旧的 pm2 start app.js 启动入口。

GitHub OAuth 配置

在 GitHub 的 Developer settings → OAuth Apps 中创建 OAuth App,回调地址填写:

https://你的域名/auth/github/callback

至少设置以下环境变量:

GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_ALLOWED_USER_ID=123456
GITHUB_CALLBACK_URL=https://你的域名/auth/github/callback
PUBLIC_URL=https://你的域名
SESSION_SECRET=至少32字节的随机字符串

GITHUB_ALLOWED_USER_ID 比用户名可靠,因为用户名可以修改。若暂时无法取得 ID,也可使用 GITHUB_ALLOWED_LOGIN。

blog-cli

管理员登录后台后打开“CLI”页面,复制页面中根据 PUBLIC_URL 生成的安装命令。blog-cli 是位于本仓库 cli/ 的独立 Go 程序;安装器自动选择 Linux/macOS 的 amd64/arm64 二进制并校验 SHA-256,默认安装到 ~/.local/bin/blog-cli,运行时不依赖 Python 或 Go。

blog-cli auth login
blog-cli search "关键词" --json
blog-cli page search "正文关键词" --status published --json
blog-cli page list --json
blog-cli page create --title "标题" --link slug --content-file post.md --status published
blog-cli page hide slug
blog-cli microblog create "一条短内容" --status public
blog-cli microblog list --search "关键词" --status public --json
blog-cli microblog update 42 --content-file note.md
blog-cli microblog private 42
blog-cli microblog delete 42 --yes
blog-cli site title "新的博客标题"
blog-cli site sidebar set sidebar.json
blog-cli file upload cover.webp --description "文章封面"

blog-cli search 默认同时检索文章标题、链接、摘要、标签、完整正文以及微博客正文;结果包含正文、阅读量、赞踩数、发布时间和更新时间等元数据。可用 --scope page|microblog 缩小范围,或使用 --type、--page-status、--micro-status、--limit、--offset 继续筛选。page list --search 保留紧凑列表输出,page search 则返回正文和完整元数据。

blog-cli help --json 会输出完整的机器可读命令目录、参数约定、页面类型和状态值。微博客正文可以直接作为参数传入,也可用 --content-file FILE(- 表示 stdin)。CLI 在 stdout 不是终端时自动输出稳定 JSON envelope,错误使用非零退出码,并在结果中给出下一步命令。删除与关机等破坏性操作必须显式传入 --yes。

登录采用由本站实现的 device flow:CLI 只显示一次性授权码,管理员在后台核对客户端名称并批准后才签发 token。数据库仅保存 device code 和 token 的 SHA-256 摘要。可在后台 CLI 页面查看和撤销所有有效凭据,也可执行 blog-cli auth logout 撤销当前凭据。

微博客

登录后台后打开“微博客”,即可发布、编辑或删除短内容,并为每条内容选择公开或私密状态。公开页面默认位于 /microblog;同一设置页可直接停用整个公开功能,或把它改为 notes、notes/daily 等未被系统占用的独立路径。启用时该入口会自动加入主导航和 sitemap。

微博数据保存在独立的 MicroPosts 表中,不与文章 Pages 表混用。旧版 songquanpeng/microblog 数据不会在应用启动时自动迁移;升级后可使用一次性工具先预演、再明确写入:

python3 bin/import_microblog.py /path/to/microblog.db /path/to/blog/data.db
python3 bin/import_microblog.py /path/to/microblog.db /path/to/blog/data.db --apply

导入器不会删除目标数据;相同记录会跳过,ID 冲突但内容不同的记录会以新 ID 追加。执行前仍应备份两个数据库。

前台主题

登录后台后打开“站点设置 → 前台主题”,可在 Bulma 经典 与 Studio 宋韵 之间切换。保存后新请求立即使用所选主题,不需要重启服务;读者自己的日间/夜间偏好在两套主题中都会保留。

Studio 宋韵采用暖纸白、炭黑与中性灰,不使用彩色强调;首页以文章目录式分隔取代卡片堆叠,导航与中文正文优先使用系统自带的 Songti SC、STSong 或 SimSun,元信息使用系统无衬线字体,代码仍使用等宽字体。

本地部署

需要 Go 1.25+、Node.js 22+、npm 和 C 编译工具链(GORM SQLite 驱动需要 CGO)。

make install
make test
make build

export GITHUB_CLIENT_ID=...
export GITHUB_CLIENT_SECRET=...
export GITHUB_ALLOWED_USER_ID=...
export GITHUB_CALLBACK_URL=http://localhost:3000/auth/github/callback
export PUBLIC_URL=http://localhost:3000
export SESSION_SECRET="$(openssl rand -hex 32)"
./bin/blog

开发时可分别运行:

make dev
make dev-admin

旧源码部署入口仍可使用:

npm start
# 或先 make build,然后:
pm2 start ./app.js --name blog

Docker 部署

旧版数据卷和端口参数保持不变,只需增加 OAuth 环境变量:

docker build -t blog:local .
docker run --restart=always -d \
  -p 3000:3000 \
  -v /home/ubuntu/data/blog:/app/data \
  -e TZ=Asia/Shanghai \
  -e PUBLIC_URL=https://你的域名 \
  -e GITHUB_CLIENT_ID=... \
  -e GITHUB_CLIENT_SECRET=... \
  -e GITHUB_ALLOWED_USER_ID=... \
  -e GITHUB_CALLBACK_URL=https://你的域名/auth/github/callback \
  -e SESSION_SECRET=... \
  blog:local

容器启动时会修正旧数据卷权限,服务进程随后以非 root 用户运行。

历史数据升级

升级前请备份 data/data.db 和 data/upload。应用对已有 Sequelize 表不执行 GORM AutoMigrate,避免 SQLite 重建旧表;只会补建缺失表(包括 MicroPosts、PageViews 和 BrowserSessions)并补充缺失的默认设置。未选择主题的旧站点继续使用 bulma;后台切换后会保存所选主题。明细统计从升级后开始积累,旧 Pages.view 累计值会继续保留。

旧 Users 表不会删除,以免破坏文章作者外键和历史展示,但所有密码、角色及 access token 均不再用于认证。自动化管理统一使用由本站 device flow 签发且可撤销的 CLI token。

站点采用单一所有者写入模型,因此默认完整保留受信任的历史自定义 HTML;独立 HTML 页面仍在无法访问后台登录态的沙箱中运行。如果部署环境允许不受信任的用户写入,可设置 BLOG_ALLOW_UNSAFE_HTML=false 开启净化。

合并旧域名时应先在数据库和上传目录副本上检查同路径异内容冲突,保持 /page/:link、/upload/:file 等原路径不变。验收通过后再把旧域名的每一个请求按原 path 和 query 做永久重定向到新域名,同时更新 PUBLIC_URL、canonical、sitemap 和搜索引擎站点设置;不要把所有旧链接统一跳到首页。

常用环境变量

变量默认值用途
PORT3000HTTP 端口
SQLITE_PATH./data/data.dbSQLite 文件
UPLOAD_PATH./data/upload上传目录
PUBLIC_URL自动推断canonical、sitemap、feed 基础 URL
TRUSTED_PROXIES空逗号分隔的可信反向代理 CIDR/IP
BLOG_ANALYTICS_TIMEZONEAsia/Shanghai统计数据按日归属的 IANA 时区
MAX_UPLOAD_MB20单文件上限
SESSION_TTL_HOURS8760管理会话时长;会话持久化在 SQLite 中,部署重启不会清空
CLI_TOKEN_TTL_HOURS8760CLI token 有效期,默认 365 天
CLI_DEVICE_CODE_TTL_MINUTES10device flow 授权码有效期
GITHUB_PROXY_MODEdirectGitHub 服务端请求的默认出口:direct 或 proxy;后台设置可覆盖
GITHUB_PROXY_URL空GitHub 专用代理地址,例如 socks5://proxy.internal:1080
GITHUB_PROXY_FORCE_MODE空紧急恢复用,强制 direct 或 proxy 并暂时禁止后台覆盖
BLOG_ENABLE_SHUTDOWNfalse是否恢复旧远程关机接口
BLOG_ALLOW_UNSAFE_HTMLtrue是否完整保留站点所有者发布的 HTML;多人写入时建议设为 false

反向代理可继续使用仓库中的 blog.conf,生产环境应启用 HTTPS。

GitHub OAuth 的换 token 与用户身份请求可以单独使用 HTTP(S) 或 SOCKS5 代理,站点设置页可即时切换并测试连接。代理应由部署者放在可信私有网络内,地址不能包含用户名或密码。首次恢复登录可设置 GITHUB_PROXY_MODE=proxy 和 GITHUB_PROXY_URL;如后台配置导致无法登录,可用 GITHUB_PROXY_FORCE_MODE 临时强制恢复。

测试

make test   # 前端单元测试与生产构建 + Go race tests
make check  # 额外运行 go vet、govulncheck 与 npm audit

测试包含旧 Sequelize schema 读取、受信任 HTML 与沙箱隔离、固定链接校验、SEO 元数据和 GitHub 不可变 ID 白名单。

License

MIT

blog
bootstrap4
bulma-css
hexo-theme-next
v2ex
w3-css

songquanpeng/blog

个人博客系统

Go

72

505 commits

updated Sep 13, 2026

See the code

README

个人博客系统

使用 Gin + GORM + React + Bulma 的单人博客系统。公开页面保留经典 Bulma 主题并由 Go 服务端渲染,管理后台使用独立的 React 设计系统;旧版 Sequelize SQLite 数据可直接挂载使用。

特性

  • 公开站点保留兼容旧内容的 Bulma 主题,管理后台使用更适合写作与内容管理的独立界面。
  • 公开站点可在经典 Bulma 与 Studio 宋韵主题之间即时切换;Studio 使用黑灰与暖纸白的散文式视觉,以及更适合中文长文的宋体排版。
  • 服务端输出完整语义化 HTML,包含 canonical、Open Graph、JSON-LD、Atom、sitemap 和 robots。
  • 显式沿用旧表名及字段名:Pages、Users、Options、Files。旧用户数据只用于显示历史作者,不再参与认证。
  • 后台仅支持一个 GitHub OAuth 管理员,推荐使用不可变的 GitHub User ID 建立白名单。
  • 内置 blog-cli:通过本站 device flow 授权,覆盖页面与微博客 CRUD、正文统一检索、页面发布状态、站点标题与侧边栏、全部设置和文件管理;token 默认有效一年并可随时撤销。
  • 内置微博客子功能:公开短内容位于独立路径(默认 /microblog),支持 Markdown、公开/私密状态、分页和完整后台 CRUD;启停、访问路径、标题与简介均可即时调整,停用不会删除数据。
  • 内置隐私友好的文章统计:后台可查看 PV、UV、每日趋势、文章排行、Referrer、搜索引擎与可见关键词、User-Agent;不保存原始 IP。
  • Markdown 经过安全渲染;站点所有者发布的 HTML 会被完整保留,其中独立 HTML 页面运行在无同源权限的沙箱中。应用还包含 CSP、安全 Cookie、OAuth state + PKCE、同源检查、请求体限制和安全文件上传。
  • 历史 Raw 工具页在无 same-origin 权限的 CSP sandbox 中运行,保留脚本交互但不能读取主站 Cookie、后台 API 响应或父页面 DOM。
  • 保留 PORT、SQLITE_PATH、UPLOAD_PATH、3000 端口、/app/data 卷、旧 URL 与主要 /api 路径。
  • 支持 Docker、Makefile、npm start,以及旧的 pm2 start app.js 启动入口。

GitHub OAuth 配置

在 GitHub 的 Developer settings → OAuth Apps 中创建 OAuth App,回调地址填写:

https://你的域名/auth/github/callback

至少设置以下环境变量:

GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_ALLOWED_USER_ID=123456
GITHUB_CALLBACK_URL=https://你的域名/auth/github/callback
PUBLIC_URL=https://你的域名
SESSION_SECRET=至少32字节的随机字符串

GITHUB_ALLOWED_USER_ID 比用户名可靠,因为用户名可以修改。若暂时无法取得 ID,也可使用 GITHUB_ALLOWED_LOGIN。

blog-cli

管理员登录后台后打开“CLI”页面,复制页面中根据 PUBLIC_URL 生成的安装命令。blog-cli 是位于本仓库 cli/ 的独立 Go 程序;安装器自动选择 Linux/macOS 的 amd64/arm64 二进制并校验 SHA-256,默认安装到 ~/.local/bin/blog-cli,运行时不依赖 Python 或 Go。

blog-cli auth login
blog-cli search "关键词" --json
blog-cli page search "正文关键词" --status published --json
blog-cli page list --json
blog-cli page create --title "标题" --link slug --content-file post.md --status published
blog-cli page hide slug
blog-cli microblog create "一条短内容" --status public
blog-cli microblog list --search "关键词" --status public --json
blog-cli microblog update 42 --content-file note.md
blog-cli microblog private 42
blog-cli microblog delete 42 --yes
blog-cli site title "新的博客标题"
blog-cli site sidebar set sidebar.json
blog-cli file upload cover.webp --description "文章封面"

blog-cli search 默认同时检索文章标题、链接、摘要、标签、完整正文以及微博客正文;结果包含正文、阅读量、赞踩数、发布时间和更新时间等元数据。可用 --scope page|microblog 缩小范围,或使用 --type、--page-status、--micro-status、--limit、--offset 继续筛选。page list --search 保留紧凑列表输出,page search 则返回正文和完整元数据。

blog-cli help --json 会输出完整的机器可读命令目录、参数约定、页面类型和状态值。微博客正文可以直接作为参数传入,也可用 --content-file FILE(- 表示 stdin)。CLI 在 stdout 不是终端时自动输出稳定 JSON envelope,错误使用非零退出码,并在结果中给出下一步命令。删除与关机等破坏性操作必须显式传入 --yes。

登录采用由本站实现的 device flow:CLI 只显示一次性授权码,管理员在后台核对客户端名称并批准后才签发 token。数据库仅保存 device code 和 token 的 SHA-256 摘要。可在后台 CLI 页面查看和撤销所有有效凭据,也可执行 blog-cli auth logout 撤销当前凭据。

微博客

登录后台后打开“微博客”,即可发布、编辑或删除短内容,并为每条内容选择公开或私密状态。公开页面默认位于 /microblog;同一设置页可直接停用整个公开功能,或把它改为 notes、notes/daily 等未被系统占用的独立路径。启用时该入口会自动加入主导航和 sitemap。

微博数据保存在独立的 MicroPosts 表中,不与文章 Pages 表混用。旧版 songquanpeng/microblog 数据不会在应用启动时自动迁移;升级后可使用一次性工具先预演、再明确写入:

python3 bin/import_microblog.py /path/to/microblog.db /path/to/blog/data.db
python3 bin/import_microblog.py /path/to/microblog.db /path/to/blog/data.db --apply

导入器不会删除目标数据;相同记录会跳过,ID 冲突但内容不同的记录会以新 ID 追加。执行前仍应备份两个数据库。

前台主题

登录后台后打开“站点设置 → 前台主题”,可在 Bulma 经典 与 Studio 宋韵 之间切换。保存后新请求立即使用所选主题,不需要重启服务;读者自己的日间/夜间偏好在两套主题中都会保留。

Studio 宋韵采用暖纸白、炭黑与中性灰,不使用彩色强调;首页以文章目录式分隔取代卡片堆叠,导航与中文正文优先使用系统自带的 Songti SC、STSong 或 SimSun,元信息使用系统无衬线字体,代码仍使用等宽字体。

本地部署

需要 Go 1.25+、Node.js 22+、npm 和 C 编译工具链(GORM SQLite 驱动需要 CGO)。

make install
make test
make build

export GITHUB_CLIENT_ID=...
export GITHUB_CLIENT_SECRET=...
export GITHUB_ALLOWED_USER_ID=...
export GITHUB_CALLBACK_URL=http://localhost:3000/auth/github/callback
export PUBLIC_URL=http://localhost:3000
export SESSION_SECRET="$(openssl rand -hex 32)"
./bin/blog

开发时可分别运行:

make dev
make dev-admin

旧源码部署入口仍可使用:

npm start
# 或先 make build,然后:
pm2 start ./app.js --name blog

Docker 部署

旧版数据卷和端口参数保持不变,只需增加 OAuth 环境变量:

docker build -t blog:local .
docker run --restart=always -d \
  -p 3000:3000 \
  -v /home/ubuntu/data/blog:/app/data \
  -e TZ=Asia/Shanghai \
  -e PUBLIC_URL=https://你的域名 \
  -e GITHUB_CLIENT_ID=... \
  -e GITHUB_CLIENT_SECRET=... \
  -e GITHUB_ALLOWED_USER_ID=... \
  -e GITHUB_CALLBACK_URL=https://你的域名/auth/github/callback \
  -e SESSION_SECRET=... \
  blog:local

容器启动时会修正旧数据卷权限,服务进程随后以非 root 用户运行。

历史数据升级

升级前请备份 data/data.db 和 data/upload。应用对已有 Sequelize 表不执行 GORM AutoMigrate,避免 SQLite 重建旧表;只会补建缺失表(包括 MicroPosts、PageViews 和 BrowserSessions)并补充缺失的默认设置。未选择主题的旧站点继续使用 bulma;后台切换后会保存所选主题。明细统计从升级后开始积累,旧 Pages.view 累计值会继续保留。

旧 Users 表不会删除,以免破坏文章作者外键和历史展示,但所有密码、角色及 access token 均不再用于认证。自动化管理统一使用由本站 device flow 签发且可撤销的 CLI token。

站点采用单一所有者写入模型,因此默认完整保留受信任的历史自定义 HTML;独立 HTML 页面仍在无法访问后台登录态的沙箱中运行。如果部署环境允许不受信任的用户写入,可设置 BLOG_ALLOW_UNSAFE_HTML=false 开启净化。

合并旧域名时应先在数据库和上传目录副本上检查同路径异内容冲突,保持 /page/:link、/upload/:file 等原路径不变。验收通过后再把旧域名的每一个请求按原 path 和 query 做永久重定向到新域名,同时更新 PUBLIC_URL、canonical、sitemap 和搜索引擎站点设置;不要把所有旧链接统一跳到首页。

常用环境变量

变量默认值用途
PORT3000HTTP 端口
SQLITE_PATH./data/data.dbSQLite 文件
UPLOAD_PATH./data/upload上传目录
PUBLIC_URL自动推断canonical、sitemap、feed 基础 URL
TRUSTED_PROXIES空逗号分隔的可信反向代理 CIDR/IP
BLOG_ANALYTICS_TIMEZONEAsia/Shanghai统计数据按日归属的 IANA 时区
MAX_UPLOAD_MB20单文件上限
SESSION_TTL_HOURS8760管理会话时长;会话持久化在 SQLite 中,部署重启不会清空
CLI_TOKEN_TTL_HOURS8760CLI token 有效期,默认 365 天
CLI_DEVICE_CODE_TTL_MINUTES10device flow 授权码有效期
GITHUB_PROXY_MODEdirectGitHub 服务端请求的默认出口:direct 或 proxy;后台设置可覆盖
GITHUB_PROXY_URL空GitHub 专用代理地址,例如 socks5://proxy.internal:1080
GITHUB_PROXY_FORCE_MODE空紧急恢复用,强制 direct 或 proxy 并暂时禁止后台覆盖
BLOG_ENABLE_SHUTDOWNfalse是否恢复旧远程关机接口
BLOG_ALLOW_UNSAFE_HTMLtrue是否完整保留站点所有者发布的 HTML;多人写入时建议设为 false

反向代理可继续使用仓库中的 blog.conf,生产环境应启用 HTTPS。

GitHub OAuth 的换 token 与用户身份请求可以单独使用 HTTP(S) 或 SOCKS5 代理,站点设置页可即时切换并测试连接。代理应由部署者放在可信私有网络内,地址不能包含用户名或密码。首次恢复登录可设置 GITHUB_PROXY_MODE=proxy 和 GITHUB_PROXY_URL;如后台配置导致无法登录,可用 GITHUB_PROXY_FORCE_MODE 临时强制恢复。

测试

make test   # 前端单元测试与生产构建 + Go race tests
make check  # 额外运行 go vet、govulncheck 与 npm audit

测试包含旧 Sequelize schema 读取、受信任 HTML 与沙箱隔离、固定链接校验、SEO 元数据和 GitHub 不可变 ID 白名单。

License

MIT

blog
bootstrap4
bulma-css
hexo-theme-next
v2ex
w3-css

Project health

Latest release

v0.15.0 · Sep 13, 2026

Releases this year

25