astro-yukunhh-blog/deploy/deploy.sh

308 lines
12 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Yukun's Blog · 部署脚本
#
# 用法:
# ./deploy/deploy.sh 博客:构建 → rsync → reload nginx
# ./deploy/deploy.sh setup-nginx 博客上传并启用站点配置HTTP 直通)
# ./deploy/deploy.sh certbot 博客:签 HTTPS 证书并自动启用 443 + 80 跳转
# ./deploy/deploy.sh doctor 诊断并清理残留的 baidu 反代配置
#
# 子域名反代请用同目录的 proxy.sh如 ./deploy/proxy.sh db 7000
# 首次使用前改下面「配置区」。建议以 root 运行;非 root 需配好免密 sudo。
set -euo pipefail
# ====== 配置区(按你的 VPS 修改) ======
REMOTE_USER="root" # SSH 用户
REMOTE_HOST="sausagetoast.cloud" # VPS 地址 / 主域名
REMOTE_DIR="/var/www/yukun" # 博客站点根目录
NGINX_RELOAD=1 # 1=博客发布后 reload nginx
# ========================================
SSH_TARGET="${REMOTE_USER}@${REMOTE_HOST}"
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SSH_OPTS="-o StrictHostKeyChecking=accept-new"
# 通用远端执行(支持 &&root 免 sudo非 root 自动 sudo
remote_run() {
local cmd="$1"
ssh $SSH_OPTS "$SSH_TARGET" "bash -s" <<REMOTE
set -euo pipefail
SUDO=""
[ "\$(id -u)" -ne 0 ] && SUDO="sudo"
if [ -n "\$SUDO" ]; then exec \$SUDO bash -c '$cmd'; else $cmd; fi
REMOTE
}
# 启用 HTTPS 的 awk 程序(取消 443 段注释 + 80 段切跳转 + 注释 include
# 同时适用于博客与 db共用 @@HTTPS@@ / @@HTTPS-REDIRECT@@ / @@HTTP-SERVE@@ 标记)
read -r -d '' ENABLE_HTTPS_AWK <<'AWKPROG' || true
BEGIN { in443=0; inredir=0; inserv=0 }
/@@HTTPS-START@@/ { in443=1; next }
/@@HTTPS-END@@/ { in443=0; next }
/@@HTTPS-REDIRECT-START@@/ { inredir=1; next }
/@@HTTPS-REDIRECT-END@@/ { inredir=0; next }
/@@HTTP-SERVE-START@@/ { inserv=1; next }
/@@HTTP-SERVE-END@@/ { inserv=0; next }
in443 && /^# / { sub(/^# ?/, ""); print; next }
in443 && /^#/ { sub(/^#/, ""); print; next }
inredir && /^[[:space:]]*#.*return 301 https:/ { sub(/^[[:space:]]*#[[:space:]]*/, ""); print; next }
inserv && /^[[:space:]]*include / && !/^[[:space:]]*#/ { $0 = "#" $0; print; next }
{ print }
AWKPROG
export ENABLE_HTTPS_AWK
# ----------------------------------------------------------------
# 通用:上传并启用一个站点(主配置 + 共享片段)
# $1 site 域名,如 sausagetoast.cloud / blog.sausagetoast.cloud
# $2 main_conf deploy/ 下的主配置文件名
# $3 serve_conf deploy/ 下的片段文件名
# $4 snippet 片段在远端 /etc/nginx/snippets/ 下的名字
# ----------------------------------------------------------------
do_setup_site() {
local site="$1" main_conf="$2" serve_conf="$3" snippet="$4"
echo "==> 上传 $main_conf + $serve_conf${SSH_TARGET} ..."
scp $SSH_OPTS "$ROOT_DIR/deploy/$main_conf" "$SSH_TARGET:/tmp/yukun-main.conf"
scp $SSH_OPTS "$ROOT_DIR/deploy/$serve_conf" "$SSH_TARGET:/tmp/yukun-serve.conf"
echo "==> 远端启用站点 $site ..."
ssh $SSH_OPTS "$SSH_TARGET" "SITE='$site' SNIPPET='$snippet' ENABLE_HTTPS_AWK=\"$(printf '%s' "$ENABLE_HTTPS_AWK" | base64)\" bash -s" <<'REMOTE'
set -euo pipefail
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
SITE="$SITE"; SNIPPET="$SNIPPET"
ENABLE_HTTPS_AWK_b64="$ENABLE_HTTPS_AWK"; unset ENABLE_HTTPS_AWK
_enable_https() {
local conf="$1"
printf '%s' "$ENABLE_HTTPS_AWK_b64" | base64 -d > /tmp/enable-https.awk
$SUDO bash -c 'f="$1"; awk -f /tmp/enable-https.awk "$f" > "$f.tmp" && mv "$f.tmp" "$f"' _ "$conf"
rm -f /tmp/enable-https.awk
}
# 1) 共享片段 → /etc/nginx/snippets/<snippet>
$SUDO mkdir -p /etc/nginx/snippets
$SUDO cp /tmp/yukun-serve.conf "/etc/nginx/snippets/$SNIPPET"
echo " 片段: /etc/nginx/snippets/$SNIPPET"
# 2) 站点配置:优先 sites-available(Debian),其次 conf.d(CentOS)
CONF=""
if [ -d /etc/nginx/sites-enabled ] || $SUDO [ -d /etc/nginx/sites-enabled ]; then
AVAIL=/etc/nginx/sites-available; EN=/etc/nginx/sites-enabled
$SUDO mkdir -p "$AVAIL" "$EN"
$SUDO cp /tmp/yukun-main.conf "$AVAIL/$SITE"
$SUDO ln -sfn "$AVAIL/$SITE" "$EN/$SITE" # 幂等
if ! $SUDO grep -q "sites-enabled" /etc/nginx/nginx.conf 2>/dev/null; then
echo " nginx.conf 缺 include sites-enabled/*,自动追加"
$SUDO sed -i '/http {/a\ include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf
fi
CONF="$AVAIL/$SITE"
echo " 站点: $CONF (+软链接 $EN/$SITE)"
else
$SUDO mkdir -p /etc/nginx/conf.d
$SUDO cp /tmp/yukun-main.conf "/etc/nginx/conf.d/$SITE.conf"
CONF="/etc/nginx/conf.d/$SITE.conf"
echo " 站点: $CONF"
fi
# 3) 清理另一位置的重复配置(防止 conflicting server name 警告)
if [ "$CONF" = "/etc/nginx/sites-available/$SITE" ]; then
if $SUDO [ -f "/etc/nginx/conf.d/$SITE.conf" ]; then
$SUDO rm -f "/etc/nginx/conf.d/$SITE.conf"
echo " 已移除重复配置 /etc/nginx/conf.d/$SITE.conf"
fi
else
if $SUDO [ -f "/etc/nginx/sites-available/$SITE" ]; then
$SUDO rm -f "/etc/nginx/sites-available/$SITE" "/etc/nginx/sites-enabled/$SITE"
echo " 已移除重复配置 sites-available/$SITE"
fi
fi
# 4) 若证书已存在(重跑场景),自动启用 HTTPS
if $SUDO [ -f "/etc/letsencrypt/live/$SITE/fullchain.pem" ]; then
echo "==> 检测到已有证书,自动启用 HTTPS ..."
_enable_https "$CONF"
fi
echo "==> 测试 nginx 配置 ..."
$SUDO nginx -t
echo "==> reload nginx ..."
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
rm -f /tmp/yukun-main.conf /tmp/yukun-serve.conf
echo "✓ 站点 $SITE 已启用HTTP 直通)"
echo " 下一步(可选):申请 HTTPS 证书"
REMOTE
}
# ----------------------------------------------------------------
# 通用:为一个站点签 HTTPS 证书并自动启用 443 + 80 跳转
# $1 site 域名
# ----------------------------------------------------------------
do_certbot_site() {
local site="$1"
echo "==> 远端为 $site 申请证书webroot 方式)..."
echo " 前置:站点已 setup$site 已解析到本机、80 端口可达"
ssh $SSH_OPTS "$SSH_TARGET" "SITE='$site' ENABLE_HTTPS_AWK=\"$(printf '%s' "$ENABLE_HTTPS_AWK" | base64)\" bash -s" <<'REMOTE'
set -euo pipefail
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
SITE="$SITE"
ENABLE_HTTPS_AWK_b64="$ENABLE_HTTPS_AWK"; unset ENABLE_HTTPS_AWK
_enable_https() {
local conf="$1"
printf '%s' "$ENABLE_HTTPS_AWK_b64" | base64 -d > /tmp/enable-https.awk
$SUDO bash -c 'f="$1"; awk -f /tmp/enable-https.awk "$f" > "$f.tmp" && mv "$f.tmp" "$f"' _ "$conf"
rm -f /tmp/enable-https.awk
}
# 没装 certbot 就装
if ! command -v certbot >/dev/null 2>&1; then
echo "==> 安装 certbot ..."
if command -v apt >/dev/null 2>&1; then $SUDO apt update && $SUDO apt install -y certbot python3-certbot-nginx
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y certbot python3-certbot-nginx
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y certbot python3-certbot-nginx
else echo "✗ 未识别的包管理器,请手动装 certbot"; exit 1; fi
fi
$SUDO mkdir -p /var/www/html
echo "==> 签发证书 ..."
$SUDO certbot certonly --webroot -w /var/www/html \
-d "$SITE" \
--non-interactive --agree-tos --register-unsafe --email "root@$SITE"
# 找到站点配置文件
CONF=""
for p in "/etc/nginx/sites-available/$SITE" "/etc/nginx/conf.d/$SITE.conf"; do
if $SUDO [ -f "$p" ]; then CONF="$p"; break; fi
done
if [ -z "$CONF" ]; then echo "✗ 找不到 $SITE 的配置,请先 setup"; exit 1; fi
echo "==> 启用 HTTPS取消 443 段注释 + 80 段切跳转)..."
_enable_https "$CONF"
echo "==> 测试并重载 nginx ..."
$SUDO nginx -t && $SUDO systemctl reload nginx
echo "$SITE 的 HTTPS 已启用,访问 https://$SITE"
echo " 证书: /etc/letsencrypt/live/$SITE/ (certbot 已自动配置续期)"
REMOTE
}
# ----------------------------------------------------------------
# 博客专用命令
# ----------------------------------------------------------------
cmd_setup_nginx() { do_setup_site "sausagetoast.cloud" "nginx.conf" "nginx-serve.conf" "yukun.conf"; }
cmd_certbot() { do_certbot_site "sausagetoast.cloud"; }
# ----------------------------------------------------------------
# 诊断 + 清理:找出并禁用残留的 baidu 反代配置
# ----------------------------------------------------------------
cmd_doctor() {
echo "==> 远端诊断:搜索 baidu 反代残留 ..."
ssh $SSH_OPTS "$SSH_TARGET" "bash -s" <<'REMOTE'
set +e
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
echo "########## 1. 含 baidu 的配置/脚本nginx/宝塔/caddy/apache/定时任务)##########"
hits=$($SUDO grep -rln "baidu" /etc/nginx/ /www/server/ /etc/caddy/ /etc/apache2/ /etc/httpd/ 2>/dev/null)
if [ -n "$hits" ]; then
for f in $hits; do
echo ">> $f"
$SUDO grep -nE "baidu|server_name|proxy_pass|return 30|listen " "$f" 2>/dev/null
echo ""
done
else
echo "(配置文件里没有 baidu)"
fi
echo "-- crontab / cron.d 里的 baidu --"
($SUDO crontab -l 2>/dev/null | grep -n "baidu"; $SUDO grep -rn "baidu" /etc/cron.d/ /var/spool/cron/ 2>/dev/null) || echo "(定时任务里没有 baidu)"
echo "########## 2. 生效配置里 sausagetoast.cloud 的去向 ##########"
$SUDO nginx -T 2>/dev/null | grep -B2 -A10 "sausagetoast" || echo "(nginx -T 无相关配置)"
echo "########## 3. 80/443 监听者 + 其他 web 服务 ##########"
ss -tlnp 2>/dev/null | grep -E ':80|:443' || netstat -tlnp 2>/dev/null | grep -E ':80|:443'
systemctl list-units --type=service --state=running 2>/dev/null | grep -iE 'caddy|apache|httpd|traefik|nginx|bt|baota' || true
echo ""
echo "########## 4. 清理:禁用已启用且含 baidu 的配置 ##########"
TS=$(date +%Y%m%d-%H%M%S)
BK="/tmp/yukun-doctor-backup-$TS"
mkdir -p "$BK"
disabled=0
# 检查 sites-enabled可能是软链接和 conf.d
for d in /etc/nginx/sites-enabled /etc/nginx/conf.d; do
[ -d "$d" ] || continue
for f in "$d"/*; do
[ -e "$f" ] || continue
if $SUDO grep -ql "baidu" "$f" 2>/dev/null; then
echo ">> 发现 baidu 配置: $f"
$SUDO cp -a "$f" "$BK/$(basename "$f").bak"
if [ -L "$f" ]; then
echo " 软链接,删除链接(保留源文件)"
$SUDO rm -f "$f"
else
echo " 普通文件,改名为 .disabled"
$SUDO mv "$f" "$f.disabled"
fi
disabled=$((disabled+1))
fi
done
done
echo "备份目录: $BK"
if [ "$disabled" -gt 0 ]; then
echo "==> 测试 nginx ..."
if $SUDO nginx -t 2>/dev/null; then
$SUDO systemctl reload nginx
echo "✓ 已禁用 $disabled 个 baidu 配置并 reload nginx"
else
echo "✗ nginx -t 失败,恢复备份 ..."
# 恢复(简化:提示手动从 $BK 恢复)
echo " 请从 $BK 手动恢复,或检查 nginx -t 报错"
fi
else
echo "(已启用的配置里没有 baidu若仍跳转 baidu看上面第 2/3 项排查其他服务或 DNS)"
fi
REMOTE
}
# ----------------------------------------------------------------
# 博客发布:构建 + rsync + reload
# ----------------------------------------------------------------
cmd_deploy() {
echo "==> 工作目录: $ROOT_DIR"
echo "==> [1/3] 构建站点 (astro build)..."
cd "$ROOT_DIR"
npm run build
[ -d "$ROOT_DIR/dist" ] || { echo "✗ 构建失败dist 不存在"; exit 1; }
echo "==> [2/3] 同步到 ${SSH_TARGET}:${REMOTE_DIR} ..."
rsync -avz --delete --human-readable \
"$ROOT_DIR/dist/" "${SSH_TARGET}:${REMOTE_DIR}/"
if [ "$NGINX_RELOAD" = "1" ]; then
echo "==> [3/3] reload nginx..."
remote_run "nginx -t && systemctl reload nginx"
fi
echo ""
echo "✓ 部署完成!访问 https://${REMOTE_HOST}"
}
# ----------------------------------------------------------------
case "${1:-deploy}" in
setup-nginx) cmd_setup_nginx ;;
certbot) cmd_certbot ;;
doctor) cmd_doctor ;;
deploy|"") cmd_deploy ;;
*)
echo "用法: $0 [setup-nginx|certbot|doctor|deploy]"
echo " setup-nginx 博客:上传并启用站点配置"
echo " certbot 博客:签 HTTPS 证书并启用 443"
echo " doctor 诊断并清理残留的 baidu 反代配置"
echo " deploy 博客:构建并同步到 VPS默认"
echo " 子域名反代请用: $0 所在目录的 proxy.sh"
exit 1 ;;
esac