308 lines
12 KiB
Bash
308 lines
12 KiB
Bash
|
|
#!/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
|