利用 Webhook 实现博客自动部署到服务器并通过飞书通知
由于近期网站速度不稳定,只好再把博客解析改到服务器上。博客源代码托管在 GitHub 上,每次更新的流程如下:
- 提交源码到私有仓库
- 私有仓库执行 Action
2.1. 执行 hexo g 生成静态文件
2.2. 部署到 xaoxuu.github.io 公开仓库
2.3. 触发服务器同步拉取 xaoxuu.github.io 更新 本文内容 - Vercel/Netlify/Cloudflare等平台同步部署(基于 xaoxuu.github.io 静态内容)
2.3 曾经是 rsync 同步到服务器、也曾是同步到 oss,但前者配置复杂,这次我已经忘了怎么操作了;后者用了几天感觉同步速度极慢。
rsync 方案: 服务器问题记录
整理思路
全自动化部署,当然要避免手动 pull,所以我希望实现:
- 每次推送后,GitHub Action 自动触发 Webhook
- Webhook 服务运行在服务器上,收到请求后拉取仓库最新代码
- 同时,通过飞书发送部署成功、失败的通知
技术栈选型
模块 | 工具 |
---|
Web 服务 | Node.js + Express |
后台守护 | pm2 |
通知方式 | 飞书流程自动化 Webhook(支持快捷指令) |
飞书自动化这块我很熟悉,而且体验极佳,YYDS~ 
网站根目录下关联 GitHub 仓库
以下是我的配置,可以根据实际情况修改为你自己的:
- 我的网站目录是:
/opt/1panel/www/sites/xaoxuu.com/index
- 要同步的 GitHub 静态文件仓库是:
https://github.com/xaoxuu/xaoxuu.github.io
执行前确保没有重要数据
cd /opt/1panel/www/sites/xaoxuu.com rm -rf index git clone https://github.com/xaoxuu/xaoxuu.github.io.git index
|
忽略 .git
我用的 1panel,在网站的【配置文件】中的 server
块中增加以下设置:
location ~ /\.git { deny all; }
|
效果类似于:
server { listen 80 ; listen 443 ssl ; ... root /www/sites/xaoxuu.com/index; location ~ /\.git { deny all; } ... }
|
创建两个 js 文件
我放在了在网站项目目录下,放在别处也可以,但注意不要放到网站根目录下,也就是 git clone 的地方,否则会被覆盖掉。
/opt/1panel/www/sites/xaoxuu.comindex/ <- 网站根目录,也就是 git 仓库 log/ proxy/ ssl/ webhook.js <- 创建的 ecosystem.config.js <- 创建的
|
ecosystem.config.js 内容
module.exports = { apps: [ { name: "webhook", script: "./webhook.js", env: { PORT: 2333, SYNC_TOKEN: "xxx" } } ] };
|
SYNC_TOKEN 就是随便创建一串字符串,粘贴到 GitHub Secrets 中,简单防刷保护,不需要的话,下文的 SYNC_TOKEN
部分删掉即可。
webhook.js 内容
监听本地 2333
端口的 Webhook 服务如下:
const express = require('express'); const { exec } = require('child_process'); const https = require('https'); const app = express();
const PORT = process.env.PORT || 2333; const TOKEN = process.env.SYNC_TOKEN || 'your_secret_token'; const SITE_DIR = '/opt/1panel/www/sites/xaoxuu.com/index'; const FEISHU_WEBHOOK = 'https://www.feishu.cn/flow/api/trigger-webhook/xxxxxx';
app.use(express.json()); app.use(express.urlencoded({ extended: true }));
function notifyFeishu(payload) { const data = JSON.stringify(payload); const url = new URL(FEISHU_WEBHOOK);
const options = { hostname: url.hostname, path: url.pathname + url.search, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data), }, };
const req = https.request(options, (res) => { let body = ''; res.on('data', (chunk) => body += chunk.toString()); res.on('end', () => { console.log('✅ 飞书返回:', body); }); });
req.on('error', (error) => { console.error('❌ 飞书发送失败:', error); });
req.write(data); req.end(); }
app.post('/sync', (req, res) => { const token = req.query.token || req.headers['x-sync-token']; if (token !== TOKEN) return res.status(403).send('Forbidden');
exec(`cd ${SITE_DIR} && git fetch origin gh-pages && git reset --hard origin/gh-pages`, (err, stdout, stderr) => { if (err) { console.error('❌ 拉取失败:', stderr); notifyFeishu({ text: `❌ 部署失败:${stderr}` }); return res.status(500).send('Sync failed'); }
console.log('✅ 同步成功:', stdout); notifyFeishu({ text: `✅ 部署成功:${new Date().toLocaleString()}` }); res.send('Sync success'); }); });
app.listen(PORT, () => { console.log(`Webhook 启动成功:http://localhost:${PORT}/sync`); });
|
反代到监听服务
- 增加一个反代配置,前端请求路径随便设置,例如:
/your-sync-path
- 后端代理地址填写:(http)
127.0.0.1:2333/sync
创建并执行 pm2 任务
如果没有安装依赖,需要先安装一下:
安装 Node.js
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - apt install -y nodejs
|
安装 pm2(全局)
启动 webhook 服务
假设你的文件跟我前面的命名一样叫 webhook.js,就这么启动:
pm2 start /opt/1panel/www/sites/xaoxuu.com/webhook.js --name webhook
|
这样就可以后台常驻了。你可以通过这些命令管理它:
pm2 list pm2 logs webhook pm2 restart webhook pm2 stop webhook
|
设置开机自启(重要)
pm2 startup sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u root --hp /root pm2 save
|
检查一下:
配置飞书通知
- 进入飞书 → 应用 → 流程自动化
- 新建一个流程,触发器选择「Webhook」
- 把 webhook 地址填入代码中
- 添加“发送消息到群聊”动作,并映射
text
字段为内容
另一种方式是在群里创建一个机器人,但我更推荐自动化,后续也可以修改规则,功能也更强大。
GitHub Action 调用 Webhook
name: auto-deploy
on: push: branches: - main
jobs: notify: runs-on: ubuntu-latest steps: - name: Call Webhook run: | curl -X POST "https://yourdomain.com/your-sync-path?token=${{ secrets.SYNC_TOKEN }}"
|
调试与验证
修改了 webhook.js 之后重启一下:
在服务器上测试效果:
在本地电脑上测试效果,这个能通就代表全流程通了: