Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

188 lines
6.8 KiB
Bash
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.

#!/bin/bash
# ════════════════════════════════════════════════════════════════
# 光湖 · 开机自愈脚本 (Boot Heal)
# CVM 开机后自动拉起所有服务 + 检测 IP 变化
# ════════════════════════════════════════════════════════════════
#
# 部署方式: systemctl enable guanghulab-boot-heal
# 触发时机: 开机自动运行 (multi-user.target)
#
# 功能:
# 1. 检测当前公网 IP
# 2. 如果 IP 变了写入警告日志EIP 绑定后不应变)
# 3. 拉起 Forgejo、Nginx、Runner
# 4. 拉起 PM2 进程 (Wake Gate, Git Sync)
# 5. 检查 SSL 证书是否仍然有效
# 6. 输出健康报告
# ════════════════════════════════════════════════════════════════
set -e
LOG_FILE="/data/guanghulab/.runtime/boot-heal.log"
IP_RECORD="/data/guanghulab/.runtime/last-known-ip"
mkdir -p /data/guanghulab/.runtime
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "═══════════════════════════════════════"
log "光湖 · 开机自愈启动"
log "═══════════════════════════════════════"
# ─── 1. 检测公网 IP ─────────────────────────────────────────
PUBLIC_IP=""
for api in "https://ifconfig.me" "https://api.ipify.org" "https://ipinfo.io/ip"; do
PUBLIC_IP=$(curl -sf --connect-timeout 5 "$api" 2>/dev/null | tr -d '[:space:]')
if [ -n "$PUBLIC_IP" ] && echo "$PUBLIC_IP" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
break
fi
PUBLIC_IP=""
done
if [ -n "$PUBLIC_IP" ]; then
log "当前公网 IP: $PUBLIC_IP"
# 与上次记录的 IP 对比
if [ -f "$IP_RECORD" ]; then
LAST_IP=$(cat "$IP_RECORD" | tr -d '[:space:]')
if [ "$PUBLIC_IP" != "$LAST_IP" ]; then
log "⚠️ IP 变化! 上次: $LAST_IP → 当前: $PUBLIC_IP"
log "⚠️ 请检查域名解析是否需要更新 (guanghulab.com 应指向 $PUBLIC_IP)"
else
log "✅ IP 未变化,域名无需更新"
fi
fi
# 记录当前 IP
echo "$PUBLIC_IP" > "$IP_RECORD"
else
log "⚠️ 无法检测公网 IP (网络可能未就绪)"
fi
# ─── 2. 等待网络就绪 ─────────────────────────────────────────
log "等待网络就绪..."
for i in $(seq 1 30); do
if curl -sf --connect-timeout 2 http://metadata.tencentyun.com > /dev/null 2>&1; then
log "✅ 网络就绪"
break
fi
sleep 2
done
# ─── 3. 拉起系统服务 ─────────────────────────────────────────
log ""
log "▸ 拉起系统服务..."
# Forgejo
if systemctl is-active --quiet forgejo 2>/dev/null; then
log "✅ Forgejo: 已运行"
else
systemctl start forgejo 2>/dev/null && log "✅ Forgejo: 已启动" || log "❌ Forgejo: 启动失败"
fi
# Nginx
if systemctl is-active --quiet nginx 2>/dev/null; then
log "✅ Nginx: 已运行"
else
systemctl start nginx 2>/dev/null && log "✅ Nginx: 已启动" || log "❌ Nginx: 启动失败"
fi
# Gitea Runner
if systemctl is-active --quiet gitea-runner 2>/dev/null; then
log "✅ Gitea Runner: 已运行"
else
systemctl start gitea-runner 2>/dev/null && log "✅ Gitea Runner: 已启动" || log "❌ Gitea Runner: 启动失败"
fi
# ─── 4. 拉起 PM2 进程 ────────────────────────────────────────
log ""
log "▸ 拉起 PM2 进程..."
if command -v pm2 &>/dev/null; then
pm2 resurrect 2>/dev/null && log "✅ PM2: 进程已恢复" || log "⚠️ PM2: 无保存的进程"
# 等 3 秒让进程启动
sleep 3
# 逐个检查
for proc in wake-gate guanghulab-git-sync guanghulab-mcp-server; do
if pm2 describe "$proc" > /dev/null 2>&1; then
STATUS=$(pm2 describe "$proc" 2>/dev/null | grep "status" | head -1 | awk '{print $4}')
if [ "$STATUS" = "online" ]; then
log "$proc: 运行中"
else
pm2 restart "$proc" 2>/dev/null && log "$proc: 已重启" || log "$proc: 重启失败"
fi
else
log "⚠️ $proc: PM2 中不存在,需要手动部署"
fi
done
else
log "❌ PM2 未安装"
fi
# ─── 5. 检查 SSL 证书 ────────────────────────────────────────
log ""
log "▸ 检查 SSL 证书..."
if [ -f /etc/letsencrypt/live/guanghulab.com/fullchain.pem ]; then
EXPIRY=$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/guanghulab.com/fullchain.pem 2>/dev/null | cut -d= -f2)
if [ -n "$EXPIRY" ]; then
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || echo 0)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -gt 7 ]; then
log "✅ SSL 证书有效,剩余 ${DAYS_LEFT}"
else
log "⚠️ SSL 证书即将过期! 剩余 ${DAYS_LEFT} 天,请运行 certbot renew"
fi
fi
else
log "⚠️ SSL 证书文件不存在"
fi
# ─── 6. 健康报告 ─────────────────────────────────────────────
log ""
log "═══════════════════════════════════════"
log "开机自愈完成"
# 快速健康检查
ISSUES=0
# Forgejo
if ! curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then
log "❌ Forgejo 不可达"; ISSUES=$((ISSUES+1))
fi
# Nginx
if ! systemctl is-active --quiet nginx 2>/dev/null; then
log "❌ Nginx 不可达"; ISSUES=$((ISSUES+1))
fi
# Wake Gate
if ! curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then
log "⚠️ Wake Gate 不可达"; ISSUES=$((ISSUES+1))
fi
# Git Sync
if ! curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then
log "⚠️ Git Sync 不可达"; ISSUES=$((ISSUES+1))
fi
# MCP Server
if ! curl -sf http://127.0.0.1:8083/health > /dev/null 2>&1; then
log "⚠️ MCP Server 不可达"; ISSUES=$((ISSUES+1))
fi
if [ "$ISSUES" -eq 0 ]; then
log "✅ 所有服务正常"
else
log "⚠️ ${ISSUES} 个服务异常,需要人工介入"
fi
log "公网 IP: ${PUBLIC_IP:-未知}"
log "ICE-GL-ZY001 · TCS-0002∞"
log "═══════════════════════════════════════"