由于近期网站速度不稳定,只好再把博客解析改到服务器上。博客源代码托管在 GitHub 上,每次更新的流程如下:

  1. 提交源码到私有仓库
  2. 私有仓库执行 Action
    2.1. 执行 hexo g 生成静态文件
    2.2. 部署到 xaoxuu.github.io 公开仓库
    2.3. 触发服务器同步拉取 xaoxuu.github.io 更新 本文内容
  3. Vercel/Netlify/Cloudflare等平台同步部署(基于 xaoxuu.github.io 静态内容)

2.3 曾经是 rsync 同步到服务器、也曾是同步到 oss,但前者配置复杂,这次我已经忘了怎么操作了;后者用了几天感觉同步速度极慢。

整理思路

全自动化部署,当然要避免手动 pull,所以我希望实现:

  1. 每次推送后,GitHub Action 自动触发 Webhook
  2. Webhook 服务运行在服务器上,收到请求后拉取仓库最新代码
  3. 同时,通过飞书发送部署成功、失败的通知

技术栈选型

模块工具
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.com
index/ <- 网站根目录,也就是 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`);
});

反代到监听服务

  1. 增加一个反代配置,前端请求路径随便设置,例如:/your-sync-path
  2. 后端代理地址填写:(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(全局)

npm install -g pm2

启动 webhook 服务

假设你的文件跟我前面的命名一样叫 webhook.js,就这么启动:

pm2 start /opt/1panel/www/sites/xaoxuu.com/webhook.js --name webhook

这样就可以后台常驻了。你可以通过这些命令管理它:

pm2 list            # 查看所有进程状态
pm2 logs webhook # 查看 webhook 的实时日志
pm2 restart webhook # 重启服务
pm2 stop webhook # 停止服务

设置开机自启(重要)

pm2 startup
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u root --hp /root
pm2 save

检查一下:

pm2 ls

配置飞书通知

  1. 进入飞书 → 应用 → 流程自动化
  2. 新建一个流程,触发器选择「Webhook」
  3. 把 webhook 地址填入代码中
  4. 添加“发送消息到群聊”动作,并映射 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 之后重启一下:

bash

在服务器上测试效果:

bash