guanghulab/.github/workflows/deploy-brain-proxy.yml
2026-05-10 13:12:44 +08:00

402 lines
16 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ═══════════════════════════════════════════════
# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
# 📜 Copyright: 国作登字-2026-A-00037559
# ═══════════════════════════════════════════════
# 🌐 铸渊专线V2 · 大脑服务器部署 (多用户独立专线)
#
# 部署在大脑服务器 ZY-SVR-005 (43.156.237.110)
# 独立于面孔服务器的V1 VPN两套系统并行运行
#
# 功能: 每个邮箱绑定独立专线,线路互不干扰
# ═══════════════════════════════════════════════
name: '🌐 铸渊专线V2 · 大脑服务器'
on:
workflow_dispatch:
inputs:
action:
description: '操作类型'
required: true
type: choice
options:
- install
- update
- status
- restart
- add-user
- remove-user
- list-users
- send-subscription
- send-subscription-v3
- deploy-v3
- switch-v3
- deploy-infinity
default: 'status'
email:
description: '用户邮箱 (add-user/remove-user/send-subscription/send-subscription-v3时需要)'
required: false
type: string
quota_gb:
description: '月配额GB (仅add-user时可选默认500)'
required: false
type: string
default: '500'
permissions:
contents: read
concurrency:
group: brain-proxy-deploy
cancel-in-progress: false
jobs:
# ═══ §1 代理服务部署/管理 ═══
deploy-proxy-v2:
name: '🌐 V2 · ${{ github.event.inputs.action }}'
runs-on: ubuntu-latest
if: >-
github.event.inputs.action == 'install' ||
github.event.inputs.action == 'update' ||
github.event.inputs.action == 'status' ||
github.event.inputs.action == 'restart' ||
github.event.inputs.action == 'deploy-v3' ||
github.event.inputs.action == 'switch-v3' ||
github.event.inputs.action == 'deploy-infinity'
steps:
- name: '📥 检出代码'
uses: actions/checkout@v4
- name: '🔑 配置SSH密钥'
env:
SSH_KEY: ${{ secrets.ZY_BRAIN_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_brain
chmod 600 ~/.ssh/id_brain
if head -1 ~/.ssh/id_brain | grep -q "BEGIN" && tail -1 ~/.ssh/id_brain | grep -q "END"; then
echo "✅ SSH私钥格式正确"
else
echo "❌ SSH私钥格式异常 — 检查 ZY_BRAIN_KEY"
exit 1
fi
ssh-keyscan -H ${{ secrets.ZY_BRAIN_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: '🔍 SSH连接测试'
run: |
ssh -i ~/.ssh/id_brain -o BatchMode=yes -o ConnectTimeout=10 \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
'echo "✅ 大脑服务器连接成功 · $(hostname) · $(date)"'
- name: '📦 上传V2代码'
run: |
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"mkdir -p /opt/zhuyuan-brain/proxy-deploy"
rsync -avz --delete \
-e "ssh -i ~/.ssh/id_brain" \
server/proxy/ \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }}:/opt/zhuyuan-brain/proxy-deploy/
- name: '🚀 执行V2部署'
env:
ZY_BRAIN_HOST: ${{ secrets.ZY_BRAIN_HOST }}
ZY_SVR_SV_HOST: ${{ secrets.ZY_SVR_SV_HOST }}
ZY_SVR_SV_REALITY_PUBLIC_KEY: ${{ secrets.ZY_SVR_SV_REALITY_PUBLIC_KEY }}
ZY_SVR_SV_REALITY_SHORT_ID: ${{ secrets.ZY_SVR_SV_REALITY_SHORT_ID }}
ZY_SVR_SV_PORT: ${{ secrets.ZY_SVR_SV_PORT }}
ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }}
ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }}
run: |
ACTION="${{ github.event.inputs.action }}"
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"export ZY_BRAIN_HOST=${ZY_BRAIN_HOST} && \
export ZY_SVR_SV_HOST=${ZY_SVR_SV_HOST:-} && \
export ZY_SVR_SV_REALITY_PUBLIC_KEY=${ZY_SVR_SV_REALITY_PUBLIC_KEY:-} && \
export ZY_SVR_SV_REALITY_SHORT_ID=${ZY_SVR_SV_REALITY_SHORT_ID:-} && \
export ZY_SVR_SV_PORT=${ZY_SVR_SV_PORT:-443} && \
export ZY_SMTP_USER='${ZY_SMTP_USER:-}' && \
export ZY_SMTP_PASS='${ZY_SMTP_PASS:-}' && \
cd /opt/zhuyuan-brain/proxy-deploy && \
bash deploy-brain-proxy.sh ${ACTION}"
# ═══ §2 用户管理 ═══
manage-user:
name: '👤 用户管理 · ${{ github.event.inputs.action }}'
runs-on: ubuntu-latest
if: >-
github.event.inputs.action == 'add-user' ||
github.event.inputs.action == 'remove-user' ||
github.event.inputs.action == 'list-users'
steps:
- name: '📥 检出代码'
uses: actions/checkout@v4
- name: '🔑 配置SSH密钥'
env:
SSH_KEY: ${{ secrets.ZY_BRAIN_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_brain
chmod 600 ~/.ssh/id_brain
ssh-keyscan -H ${{ secrets.ZY_BRAIN_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: '👤 执行用户操作'
run: |
ACTION="${{ github.event.inputs.action }}"
EMAIL="${{ github.event.inputs.email }}"
QUOTA="${{ github.event.inputs.quota_gb }}"
case "$ACTION" in
add-user)
if [ -z "$EMAIL" ]; then
echo "❌ 添加用户需要提供邮箱"
exit 1
fi
echo " 添加用户: $EMAIL (配额: ${QUOTA}GB/月)"
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node service/user-manager.js add '$EMAIL' '${QUOTA}'"
echo ""
echo "════════════════════════════════════════"
echo "✅ 用户已添加!"
echo ""
echo "下一步: 运行 'send-subscription' 将订阅链接发送到 $EMAIL"
echo "════════════════════════════════════════"
;;
remove-user)
if [ -z "$EMAIL" ]; then
echo "❌ 移除用户需要提供邮箱"
exit 1
fi
echo " 移除用户: $EMAIL"
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node service/user-manager.js remove '$EMAIL'"
;;
list-users)
echo "📋 用户列表:"
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node service/user-manager.js list"
;;
esac
# 同步UUID到面孔服务器 (add/remove后自动同步)
- name: '🔄 同步UUID到面孔服务器'
if: >-
(github.event.inputs.action == 'add-user' ||
github.event.inputs.action == 'remove-user') &&
success()
env:
ZY_FACE_KEY: ${{ secrets.ZY_SERVER_KEY }}
ZY_FACE_HOST: ${{ secrets.ZY_SERVER_HOST }}
ZY_FACE_USER: ${{ secrets.ZY_SERVER_USER }}
run: |
# 只在面孔服务器密钥可用时同步
if [ -z "$ZY_FACE_KEY" ] || [ -z "$ZY_FACE_HOST" ]; then
echo "⚠️ 面孔服务器密钥未配置跳过UUID同步"
echo " 用户仍可使用大脑服务器节点"
exit 0
fi
echo "$ZY_FACE_KEY" > ~/.ssh/id_face
chmod 600 ~/.ssh/id_face
ssh-keyscan -H $ZY_FACE_HOST >> ~/.ssh/known_hosts 2>/dev/null
echo "🔄 从大脑服务器获取V2用户列表..."
V2_USERS=$(ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node -e \"
const um = require('./service/user-manager');
const users = um.getEnabledUsers();
const clients = users.map(u => ({id: u.uuid, flow: 'xtls-rprx-vision', email: u.email}));
console.log(JSON.stringify(clients));
\"")
echo " V2用户数: $(echo "$V2_USERS" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.length)")"
echo "🔄 同步到面孔服务器Xray配置..."
ssh -i ~/.ssh/id_face \
${ZY_FACE_USER}@${ZY_FACE_HOST} \
"XRAY_CONF='/usr/local/etc/xray/config.json'
if [ ! -f \"\$XRAY_CONF\" ]; then
echo '⚠️ 面孔服务器Xray配置不存在跳过'
exit 0
fi
# 保存V2用户列表
echo '${V2_USERS}' > /opt/zhuyuan/proxy/data/v2-clients.json 2>/dev/null || true
# 将V2用户追加到Xray配置的clients数组
# 先移除旧的V2用户(email包含@)再添加新的保留V1原始用户
if command -v jq &>/dev/null; then
V2_CLIENTS=\$(cat /opt/zhuyuan/proxy/data/v2-clients.json)
jq --argjson v2 \"\$V2_CLIENTS\" '
.inbounds[0].settings.clients = (
[.inbounds[0].settings.clients[] | select(.email == \"bingshuo@zy-proxy\")] + \$v2
)
' \"\$XRAY_CONF\" > /tmp/xray-config-merged.json
if xray run -test -c /tmp/xray-config-merged.json 2>/dev/null; then
mv /tmp/xray-config-merged.json \"\$XRAY_CONF\"
systemctl restart xray
echo '✅ 面孔服务器Xray已同步V2用户并重启'
else
echo '❌ 合并后配置验证失败,保持原配置'
rm -f /tmp/xray-config-merged.json
fi
else
echo '⚠️ jq未安装请在面孔服务器安装: apt install jq'
fi"
# ═══ §3 发送订阅 ═══
send-subscription:
name: '📧 发送V2订阅'
runs-on: ubuntu-latest
if: github.event.inputs.action == 'send-subscription'
steps:
- name: '📥 检出代码'
uses: actions/checkout@v4
- name: '🔑 配置SSH密钥'
env:
SSH_KEY: ${{ secrets.ZY_BRAIN_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_brain
chmod 600 ~/.ssh/id_brain
ssh-keyscan -H ${{ secrets.ZY_BRAIN_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: '📧 获取用户信息并发送'
env:
ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }}
ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }}
ZY_BRAIN_HOST: ${{ secrets.ZY_BRAIN_HOST }}
run: |
EMAIL="${{ github.event.inputs.email }}"
if [ -z "$EMAIL" ]; then
echo "❌ 发送订阅需要提供邮箱"
exit 1
fi
echo "📧 获取 $EMAIL 的订阅信息..."
# 获取用户token
USER_INFO=$(ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node -e \"
const um = require('./service/user-manager');
const user = um.loadUsers().users.find(u => u.email === '$EMAIL');
if (user) console.log(JSON.stringify({token: user.token, label: user.label}));
else { console.error('用户不存在'); process.exit(1); }
\"")
if [ -z "$USER_INFO" ]; then
echo "❌ 用户 $EMAIL 不存在,请先运行 add-user"
exit 1
fi
TOKEN=$(echo "$USER_INFO" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.token)")
LABEL=$(echo "$USER_INFO" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.label)")
SUB_URL="http://${ZY_BRAIN_HOST}/api/proxy-v2/sub/${TOKEN}"
echo "📎 订阅链接: ${SUB_URL}"
# 通过大脑服务器发送邮件
ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"export ZY_SMTP_USER='${ZY_SMTP_USER}' ZY_SMTP_PASS='${ZY_SMTP_PASS}' && \
cd /opt/zhuyuan-brain/proxy && \
node service/send-subscription.js send '$EMAIL' '$SUB_URL'" || {
echo ""
echo "⚠️ 邮件发送失败,请手动将以下订阅链接发送给用户:"
echo " $SUB_URL"
}
echo ""
echo "✅ 完成!${LABEL} (${EMAIL}) 的独立专线已就绪"
# ═══ §4 发送V3订阅 ═══
send-subscription-v3:
name: '📧 发送V3订阅'
runs-on: ubuntu-latest
if: github.event.inputs.action == 'send-subscription-v3'
steps:
- name: '📥 检出代码'
uses: actions/checkout@v4
- name: '🔑 配置SSH密钥'
env:
SSH_KEY: ${{ secrets.ZY_BRAIN_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_brain
chmod 600 ~/.ssh/id_brain
ssh-keyscan -H ${{ secrets.ZY_BRAIN_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: '📧 获取用户信息并发送V3链接'
env:
ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }}
ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }}
ZY_BRAIN_HOST: ${{ secrets.ZY_BRAIN_HOST }}
run: |
EMAIL="${{ github.event.inputs.email }}"
if [ -z "$EMAIL" ]; then
echo "❌ 发送V3订阅需要提供邮箱"
exit 1
fi
echo "📧 获取 $EMAIL 的V3订阅信息..."
# 获取用户token
USER_INFO=$(ssh -i ~/.ssh/id_brain \
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }} \
"cd /opt/zhuyuan-brain/proxy && \
node -e \"
const um = require('./service/user-manager');
const user = um.loadUsers().users.find(u => u.email === '$EMAIL');
if (user) console.log(JSON.stringify({token: user.token, label: user.label}));
else { console.error('用户不存在'); process.exit(1); }
\"")
if [ -z "$USER_INFO" ]; then
echo "❌ 用户 $EMAIL 不存在,请先运行 add-user"
exit 1
fi
TOKEN=$(echo "$USER_INFO" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.token)")
LABEL=$(echo "$USER_INFO" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.label)")
SUB_URL="http://${ZY_BRAIN_HOST}/api/proxy-v3/sub/${TOKEN}"
DASHBOARD_URL="http://${ZY_BRAIN_HOST}/api/proxy-v3/dashboard/${TOKEN}"
echo "📎 V3订阅链接: ${SUB_URL}"
echo "📊 V3仪表盘: ${DASHBOARD_URL}"
# 本地发送V3邮件使用仓库最新代码避免服务器脚本版本滞后
cd ${{ github.workspace }}/server/proxy
node service/send-subscription.js send-v3 "$EMAIL" "$SUB_URL" "$DASHBOARD_URL" || {
echo ""
echo "⚠️ 邮件发送失败,请手动访问以下链接:"
echo " 订阅: $SUB_URL"
echo " 仪表盘: $DASHBOARD_URL"
}
echo ""
echo "✅ 完成!${LABEL} (${EMAIL}) 的V3光湖语言世界订阅已发送"