feat: init Astro blog with wikilinks, KaTeX and local search
- Astro 5 static blog, zero runtime JS - Obsidian wikilink remark plugin (slug matches Astro content collection) - KaTeX math rendering, local search, TOC, archives, tags - 5 posts on math/physics/ML topics - deploy scripts for nginx + certbot + rsync
This commit is contained in:
Executable
+307
@@ -0,0 +1,307 @@
|
||||
#!/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
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
# 备案期间:临时用 http://<服务器IP>:8321 访问博客(不走域名/80/443)
|
||||
#
|
||||
# 用法: ./deploy/ip-preview.sh
|
||||
# 前置: ① 服务器安全组/防火墙放行 TCP 8321
|
||||
# ② 本机配好到服务器的 SSH 免密(REMOTE_HOST 连不通时改成服务器 IP)
|
||||
# 幂等可重跑:每次重新构建同步 + 重写 8321 配置。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ====== 配置区(与 deploy.sh 保持一致,ssh 不通时 REMOTE_HOST 填服务器 IP) ======
|
||||
REMOTE_USER="root" # SSH 用户(服务器是 ubuntu 用户的话改成 ubuntu)
|
||||
REMOTE_HOST="sausagetoast.cloud" # VPS 地址(备案期间建议填服务器 IP)
|
||||
REMOTE_DIR="/var/www/yukun" # 博客站点根目录
|
||||
# ==============================================================================
|
||||
|
||||
SSH_TARGET="${REMOTE_USER}@${REMOTE_HOST}"
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=accept-new"
|
||||
|
||||
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}/"
|
||||
|
||||
echo "==> 生成 8321 访问配置并上传 ..."
|
||||
cat > /tmp/yukun-ip-preview.conf <<'NGINX'
|
||||
# 备案期间:临时用 http://<服务器IP>:8321 访问博客(由 deploy/ip-preview.sh 生成)
|
||||
# 不监听 80/443,与正式站点配置互不干扰;备案完成后删除此文件即可。
|
||||
server {
|
||||
listen 8321;
|
||||
server_name _;
|
||||
|
||||
root /var/www/yukun;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/x-javascript
|
||||
application/json
|
||||
application/xml
|
||||
application/xml+rss
|
||||
image/svg+xml;
|
||||
|
||||
location ~* \.(?:css|js|woff2?|ttf|otf|svg|png|jpg|jpeg|gif|webp|ico|avif)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location = /sitemap-index.xml { add_header Cache-Control "no-cache"; }
|
||||
location ~ ^/sitemap.* { add_header Cache-Control "no-cache"; }
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
access_log /var/log/nginx/yukun.access.log;
|
||||
error_log /var/log/nginx/yukun.error.log;
|
||||
}
|
||||
NGINX
|
||||
scp $SSH_OPTS /tmp/yukun-ip-preview.conf "$SSH_TARGET:/tmp/yukun-ip-preview.conf"
|
||||
rm -f /tmp/yukun-ip-preview.conf
|
||||
|
||||
echo "==> [3/3] 远端启用 8321 配置 ..."
|
||||
ssh $SSH_OPTS "$SSH_TARGET" "bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
SUDO=""
|
||||
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
||||
$SUDO cp /tmp/yukun-ip-preview.conf /etc/nginx/conf.d/ip-preview.conf
|
||||
rm -f /tmp/yukun-ip-preview.conf
|
||||
echo "==> 测试 nginx 配置 ..."
|
||||
$SUDO nginx -t
|
||||
echo "==> reload nginx ..."
|
||||
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
|
||||
REMOTE
|
||||
|
||||
echo ""
|
||||
echo "✓ 完成!访问 http://<服务器IP>:8321"
|
||||
echo " 备案完成后:ssh 到服务器执行 sudo rm /etc/nginx/conf.d/ip-preview.conf && sudo systemctl reload nginx 即可移除"
|
||||
@@ -0,0 +1,47 @@
|
||||
# Yukun's Blog · nginx 共享服务配置片段
|
||||
# 由 deploy/deploy.sh setup-nginx 上传到 /etc/nginx/snippets/yukun.conf
|
||||
# 80 与 443 段都 include 此文件,避免重复
|
||||
|
||||
root /var/www/yukun;
|
||||
index index.html;
|
||||
|
||||
# gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/x-javascript
|
||||
application/json
|
||||
application/xml
|
||||
application/xml+rss
|
||||
image/svg+xml;
|
||||
|
||||
# 静态资源长缓存(Astro 构建产物带 hash,可放心强缓存)
|
||||
location ~* \.(?:css|js|woff2?|ttf|otf|svg|png|jpg|jpeg|gif|webp|ico|avif)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# sitemap 不缓存
|
||||
location = /sitemap-index.xml { add_header Cache-Control "no-cache"; }
|
||||
location ~ ^/sitemap.* { add_header Cache-Control "no-cache"; }
|
||||
|
||||
# Astro 目录式 URL,确保 index.html 能解析
|
||||
location / {
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
}
|
||||
|
||||
# 自定义 404
|
||||
error_page 404 /404.html;
|
||||
|
||||
# 安全头
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
@@ -0,0 +1,68 @@
|
||||
# Yukun's Blog · nginx 站点配置
|
||||
# 由 deploy/deploy.sh setup-nginx 自动上传到 sites-available(Debian) 或 conf.d(CentOS),
|
||||
# 共享服务配置在 /etc/nginx/snippets/yukun.conf(由 nginx-serve.conf 上传)。
|
||||
#
|
||||
# 站点根目录:/var/www/yukun(deploy.sh 会 rsync 到这里)
|
||||
#
|
||||
# 首次部署:只用 80 段(HTTP 直接服务站点),nginx -t 可直接通过。
|
||||
# 申请证书后:./deploy/deploy.sh certbot 会自动 ① 启用 443 段 ② 把 80 段切为跳转。
|
||||
|
||||
# ============================================================
|
||||
# 80 端口:HTTP
|
||||
# · 首次(无证书):include 片段,直接服务站点
|
||||
# · 启用 HTTPS 后:改为 return 301 跳转到 443
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name sausagetoast.cloud www.sausagetoast.cloud;
|
||||
|
||||
# Let's Encrypt 证书验证(certbot webroot 用,无论是否启用 HTTPS 都需保留)
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
# @@HTTPS-REDIRECT-START@@
|
||||
# 启用 HTTPS 后,certbot 会取消下面这行注释,把 80 段变成跳转:
|
||||
# location / { return 301 https://$host$request_uri; }
|
||||
# @@HTTPS-REDIRECT-END@@
|
||||
|
||||
# @@HTTP-SERVE-START@@
|
||||
# 启用 HTTPS 后,certbot 会把这行注释掉(跳转段接管):
|
||||
include /etc/nginx/snippets/yukun.conf;
|
||||
# @@HTTP-SERVE-END@@
|
||||
|
||||
access_log /var/log/nginx/yukun.access.log;
|
||||
error_log /var/log/nginx/yukun.error.log;
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 443 端口:HTTPS
|
||||
# 证书由 deploy/deploy.sh certbot 签发到 /etc/letsencrypt/live/sausagetoast.cloud/
|
||||
# 首次部署此段被注释(无证书时 nginx -t 才能过);certbot 会自动取消注释。
|
||||
# @@HTTPS-START@@
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# listen [::]:443 ssl http2;
|
||||
# server_name sausagetoast.cloud www.sausagetoast.cloud;
|
||||
#
|
||||
# ssl_certificate /etc/letsencrypt/live/sausagetoast.cloud/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/sausagetoast.cloud/privkey.pem;
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_cache shared:SSL:10m;
|
||||
# ssl_session_timeout 1d;
|
||||
#
|
||||
# # www 跳主域名
|
||||
# if ($host = www.sausagetoast.cloud) {
|
||||
# return 301 https://sausagetoast.cloud$request_uri;
|
||||
# }
|
||||
#
|
||||
# include /etc/nginx/snippets/yukun.conf;
|
||||
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
#
|
||||
# access_log /var/log/nginx/yukun.access.log;
|
||||
# error_log /var/log/nginx/yukun.error.log;
|
||||
# }
|
||||
# @@HTTPS-END@@
|
||||
Executable
+273
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env bash
|
||||
# Yukun's Blog · 通用子域名反代 + HTTPS 脚本
|
||||
#
|
||||
# 用法:
|
||||
# ./deploy/proxy.sh <子域名> <端口> 反代子域名 → http://127.0.0.1:<端口>,自动签 HTTPS
|
||||
# ./deploy/proxy.sh remove <子域名> 移除该子域名的反代配置(证书保留)
|
||||
#
|
||||
# 示例:
|
||||
# ./deploy/proxy.sh db 7000 → https://db.sausagetoast.cloud → 127.0.0.1:7000
|
||||
# ./deploy/proxy.sh remove db
|
||||
#
|
||||
# 子域名可传前缀(自动拼主域名)或完整域名(含点,直接使用)。
|
||||
# 前置条件:子域名 DNS 已解析到本机、80 端口可达。幂等可重跑。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ====== 配置区(与 deploy.sh 保持一致) ======
|
||||
REMOTE_USER="root" # SSH 用户
|
||||
REMOTE_HOST="sausagetoast.cloud" # VPS 地址
|
||||
MAIN_DOMAIN="sausagetoast.cloud" # 主域名(拼接子域名用)
|
||||
# ============================================
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=accept-new"
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
# 启用 HTTPS 的 awk 程序(与 deploy.sh 共用同一套标记):
|
||||
# ① 取消 443 段注释 ② 80 段切 301 跳转 ③ 移除 80 段反代块
|
||||
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 { next }
|
||||
{ print }
|
||||
AWKPROG
|
||||
|
||||
usage() {
|
||||
echo "用法: $0 <子域名> <端口> 或 $0 remove <子域名>"
|
||||
echo " 例: $0 db 7000 → https://db.${MAIN_DOMAIN} → 127.0.0.1:7000"
|
||||
echo " $0 remove db"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 生成反代 location 块(80/443 两处共用,缩进 $1)
|
||||
proxy_block() {
|
||||
local indent="$1"
|
||||
echo "${indent}location / {"
|
||||
echo "${indent} proxy_pass http://127.0.0.1:${PORT};"
|
||||
echo "${indent} proxy_http_version 1.1;"
|
||||
echo "${indent} proxy_set_header Host \$host;"
|
||||
echo "${indent} proxy_set_header X-Real-IP \$remote_addr;"
|
||||
echo "${indent} proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;"
|
||||
echo "${indent} proxy_set_header X-Forwarded-Proto \$scheme;"
|
||||
echo "${indent} proxy_set_header Upgrade \$http_upgrade;"
|
||||
echo "${indent} proxy_set_header Connection \"upgrade\";"
|
||||
echo "${indent} proxy_connect_timeout 60s;"
|
||||
echo "${indent} proxy_send_timeout 300s;"
|
||||
echo "${indent} proxy_read_timeout 300s;"
|
||||
echo "${indent} proxy_buffering off;"
|
||||
echo "${indent} client_max_body_size 100m;"
|
||||
echo "${indent}}"
|
||||
}
|
||||
|
||||
# 本地生成站点配置 → /tmp/yukun-proxy-<FQDN>.conf
|
||||
gen_conf() {
|
||||
local f="/tmp/yukun-proxy-${FQDN}.conf"
|
||||
{
|
||||
echo "# ${FQDN} · 反代到本地 ${PORT} 端口服务(由 deploy/proxy.sh 生成)"
|
||||
echo "# 首次部署:80 段直接反代(HTTP 通);证书签发后自动启用 443 + 80 跳转。"
|
||||
echo ""
|
||||
echo "server {"
|
||||
echo " listen 80;"
|
||||
echo " listen [::]:80;"
|
||||
echo " server_name ${FQDN};"
|
||||
echo ""
|
||||
echo " # Let's Encrypt 证书验证(certbot webroot 用,无论是否启用 HTTPS 都需保留)"
|
||||
echo " location /.well-known/acme-challenge/ {"
|
||||
echo " root /var/www/html;"
|
||||
echo " }"
|
||||
echo ""
|
||||
echo " # @@HTTPS-REDIRECT-START@@"
|
||||
echo " # 启用 HTTPS 后取消下面这行注释,把 80 段变成跳转:"
|
||||
echo " # location / { return 301 https://\$host\$request_uri; }"
|
||||
echo " # @@HTTPS-REDIRECT-END@@"
|
||||
echo ""
|
||||
echo " # @@HTTP-SERVE-START@@"
|
||||
echo " # 启用 HTTPS 后,此段会被移除(跳转段接管):"
|
||||
proxy_block " "
|
||||
echo " # @@HTTP-SERVE-END@@"
|
||||
echo "}"
|
||||
echo ""
|
||||
echo "# 证书由 proxy.sh 签发到 /etc/letsencrypt/live/${FQDN}/"
|
||||
echo "# @@HTTPS-START@@"
|
||||
echo "# server {"
|
||||
echo "# listen 443 ssl http2;"
|
||||
echo "# listen [::]:443 ssl http2;"
|
||||
echo "# server_name ${FQDN};"
|
||||
echo "#"
|
||||
echo "# ssl_certificate /etc/letsencrypt/live/${FQDN}/fullchain.pem;"
|
||||
echo "# ssl_certificate_key /etc/letsencrypt/live/${FQDN}/privkey.pem;"
|
||||
echo "# ssl_protocols TLSv1.2 TLSv1.3;"
|
||||
echo "# ssl_ciphers HIGH:!aNULL:!MD5;"
|
||||
echo "# ssl_prefer_server_ciphers on;"
|
||||
echo "# ssl_session_cache shared:SSL:10m;"
|
||||
echo "# ssl_session_timeout 1d;"
|
||||
echo "#"
|
||||
proxy_block "# "
|
||||
echo "# add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;"
|
||||
echo "# }"
|
||||
echo "# @@HTTPS-END@@"
|
||||
} > "$f"
|
||||
echo "$f"
|
||||
}
|
||||
|
||||
# 校验并规范化子域名参数
|
||||
normalize_fqdn() {
|
||||
local raw="$1"
|
||||
[[ -n "$raw" ]] || { echo "✗ 缺少子域名参数"; usage; }
|
||||
[[ "$raw" =~ ^[a-zA-Z0-9.-]+$ ]] && [[ "$raw" != *..* ]] && [[ "$raw" != -* ]] && [[ "$raw" != *- ]] \
|
||||
|| { echo "✗ 非法子域名: $raw"; exit 1; }
|
||||
if [[ "$raw" == *.* ]]; then
|
||||
FQDN="$raw"
|
||||
else
|
||||
FQDN="${raw}.${MAIN_DOMAIN}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 子命令:配置反代 + 签证书 + 启用 HTTPS
|
||||
# ----------------------------------------------------------------
|
||||
cmd_setup() {
|
||||
local SUB="$1" PORT="$2"
|
||||
[[ "$PORT" =~ ^[0-9]+$ ]] && (( PORT >= 1 && PORT <= 65535 )) || { echo "✗ 非法端口: $PORT"; exit 1; }
|
||||
normalize_fqdn "$SUB"
|
||||
|
||||
local CONF_TMP
|
||||
CONF_TMP="$(gen_conf)"
|
||||
echo "==> 上传 ${FQDN} 的 nginx 配置(反代 → 127.0.0.1:${PORT})到 ${REMOTE_USER}@${REMOTE_HOST} ..."
|
||||
scp $SSH_OPTS "$CONF_TMP" "${REMOTE_USER}@${REMOTE_HOST}:/tmp/yukun-proxy.conf"
|
||||
rm -f "$CONF_TMP"
|
||||
|
||||
echo "==> 远端安装配置 + 签证书 + 启用 HTTPS ..."
|
||||
ssh $SSH_OPTS "${REMOTE_USER}@${REMOTE_HOST}" \
|
||||
"SITE='$FQDN' PORT='$PORT' ENABLE_HTTPS_AWK=\"$(printf '%s' "$ENABLE_HTTPS_AWK" | base64)\" bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
SUDO=""
|
||||
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
||||
SITE="$SITE"; PORT="$PORT"
|
||||
ENABLE_HTTPS_AWK_b64="$ENABLE_HTTPS_AWK"; unset ENABLE_HTTPS_AWK
|
||||
|
||||
# 1) 安装站点配置:优先 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-proxy.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-proxy.conf "/etc/nginx/conf.d/$SITE.conf"
|
||||
CONF="/etc/nginx/conf.d/$SITE.conf"
|
||||
echo " 站点: $CONF"
|
||||
fi
|
||||
|
||||
# 1.5) 清理另一位置的重复配置(防止 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
|
||||
|
||||
# 2) 签 HTTPS 证书(已存在则跳过)
|
||||
CERT="/etc/letsencrypt/live/$SITE/fullchain.pem"
|
||||
if $SUDO [ -f "$CERT" ]; then
|
||||
echo "==> 已有证书 $CERT,跳过签发"
|
||||
else
|
||||
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"
|
||||
fi
|
||||
|
||||
# 3) 启用 HTTPS(取消 443 段注释 + 80 段切跳转 + 移除反代块)
|
||||
echo "==> 启用 HTTPS ..."
|
||||
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
|
||||
|
||||
echo "==> 测试 nginx 配置 ..."
|
||||
$SUDO nginx -t
|
||||
echo "==> reload nginx ..."
|
||||
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
|
||||
rm -f /tmp/yukun-proxy.conf
|
||||
echo "✓ 完成!https://$SITE → http://127.0.0.1:$PORT"
|
||||
REMOTE
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 子命令:移除反代配置
|
||||
# ----------------------------------------------------------------
|
||||
cmd_remove() {
|
||||
normalize_fqdn "$1"
|
||||
echo "==> 远端移除 $FQDN 的反代配置 ..."
|
||||
ssh $SSH_OPTS "${REMOTE_USER}@${REMOTE_HOST}" "SITE='$FQDN' bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
SUDO=""
|
||||
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
||||
SITE="$SITE"
|
||||
removed=0
|
||||
# sites-enabled 软链接
|
||||
if $SUDO [ -L "/etc/nginx/sites-enabled/$SITE" ]; then
|
||||
$SUDO rm -f "/etc/nginx/sites-enabled/$SITE"; removed=1
|
||||
fi
|
||||
# sites-available 源文件
|
||||
if $SUDO [ -f "/etc/nginx/sites-available/$SITE" ]; then
|
||||
$SUDO rm -f "/etc/nginx/sites-available/$SITE"; removed=1
|
||||
fi
|
||||
# conf.d 配置
|
||||
if $SUDO [ -f "/etc/nginx/conf.d/$SITE.conf" ]; then
|
||||
$SUDO rm -f "/etc/nginx/conf.d/$SITE.conf"; removed=1
|
||||
fi
|
||||
if [ "$removed" -eq 0 ]; then
|
||||
echo "(未找到 $SITE 的 nginx 配置,可能已移除)"
|
||||
else
|
||||
echo "==> 测试 nginx 配置 ..."
|
||||
$SUDO nginx -t
|
||||
echo "==> reload nginx ..."
|
||||
$SUDO systemctl reload nginx || $SUDO systemctl restart nginx
|
||||
echo "✓ 已移除 $SITE 的反代配置"
|
||||
fi
|
||||
if $SUDO [ -d "/etc/letsencrypt/live/$SITE" ]; then
|
||||
echo " 证书已保留(续期任务仍在):certbot delete --cert-name $SITE 可清理"
|
||||
fi
|
||||
REMOTE
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
case "${1:-}" in
|
||||
remove)
|
||||
[ $# -eq 2 ] || { echo "✗ 用法: $0 remove <子域名>"; exit 1; }
|
||||
cmd_remove "$2" ;;
|
||||
"")
|
||||
usage ;;
|
||||
*)
|
||||
[ $# -eq 2 ] || { echo "✗ 用法: $0 <子域名> <端口>"; exit 1; }
|
||||
cmd_setup "$1" "$2" ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user