| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- #!/usr/bin/env bash
- set -Eeuo pipefail
- APP_DIR=/opt/xinghen
- PROXY_CONTAINER=meishi_ccdw_life-proxy-1
- NGINX_CONFIG=/opt/meishi_ccdw_life/nginx.conf
- PRIMARY_DOMAIN=xinghen.lessncosmos.com
- FUTURE_DOMAIN=xinghen.work
- ACME_ROOT=/etc/letsencrypt/acme-challenge
- MARKER_BEGIN='# BEGIN XINGHEN MANAGED BLOCK'
- MARKER_END='# END XINGHEN MANAGED BLOCK'
- cd "$APP_DIR"
- if [[ -f .deploy/app-image.tar.gz ]]; then
- gzip -dc .deploy/app-image.tar.gz | docker load
- rm -f .deploy/app-image.tar.gz
- fi
- if [[ ! -f .env ]]; then
- umask 077
- cat >.env <<EOF
- POSTGRES_PASSWORD=$(openssl rand -hex 24)
- ADMIN_PHONE=18888888888
- ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)
- EOF
- fi
- docker compose -p xinghen -f docker-compose.prod.yml up -d --remove-orphans
- install_nginx_block() {
- local block_file=$1
- local staged
- local backup
- staged=$(mktemp)
- backup="${NGINX_CONFIG}.backup-xinghen-$(date +%Y%m%d%H%M%S)"
- cp -a "$NGINX_CONFIG" "$backup"
- awk -v begin="$MARKER_BEGIN" -v end="$MARKER_END" '
- $0 == begin {skip=1; next}
- $0 == end {skip=0; next}
- !skip {lines[++n]=$0}
- END {
- last=n
- while (last > 0 && lines[last] ~ /^[[:space:]]*$/) last--
- if (lines[last] !~ /^}$/) exit 2
- for (i=1; i<last; i++) print lines[i]
- }
- ' "$NGINX_CONFIG" >"$staged"
- printf '\n%s\n' "$MARKER_BEGIN" >>"$staged"
- cat "$block_file" >>"$staged"
- printf '%s\n\n}\n' "$MARKER_END" >>"$staged"
- docker run --rm --network meishi_ccdw_life_default \
- -v "$staged:/etc/nginx/nginx.conf:ro" \
- -v /etc/letsencrypt:/etc/letsencrypt:ro \
- docker.m.daocloud.io/library/nginx:alpine nginx -t
- cp "$staged" "$NGINX_CONFIG"
- docker exec "$PROXY_CONTAINER" nginx -t
- docker exec "$PROXY_CONTAINER" nginx -s reload
- rm -f "$staged"
- }
- http_block=$(mktemp)
- cat >"$http_block" <<EOF
- server {
- listen 80;
- server_name $PRIMARY_DOMAIN;
- location ^~ /.well-known/acme-challenge/ {
- root $ACME_ROOT;
- default_type text/plain;
- try_files \$uri =404;
- }
- location / {
- proxy_pass http://xinghen-app:3001;
- proxy_set_header Host \$host;
- proxy_set_header X-Real-IP \$remote_addr;
- proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto \$scheme;
- client_max_body_size 100m;
- }
- }
- EOF
- install_nginx_block "$http_block"
- rm -f "$http_block"
- certbot certonly --webroot -w "$ACME_ROOT" \
- --cert-name "$PRIMARY_DOMAIN" -d "$PRIMARY_DOMAIN" \
- --non-interactive --agree-tos --register-unsafely-without-email \
- --keep-until-expiring
- server_names="$PRIMARY_DOMAIN"
- cert_name="$PRIMARY_DOMAIN"
- if [[ "${ENABLE_FUTURE_DOMAIN:-0}" == 1 ]]; then
- resolved=$(getent ahostsv4 "$FUTURE_DOMAIN" | awk 'NR==1 {print $1}')
- if [[ "$resolved" != 47.93.193.127 ]]; then
- echo "$FUTURE_DOMAIN 尚未解析到 47.93.193.127,拒绝启用。" >&2
- exit 1
- fi
- certbot certonly --webroot -w "$ACME_ROOT" \
- --cert-name "$PRIMARY_DOMAIN" -d "$PRIMARY_DOMAIN" -d "$FUTURE_DOMAIN" \
- --non-interactive --agree-tos --register-unsafely-without-email --expand
- server_names="$PRIMARY_DOMAIN $FUTURE_DOMAIN"
- fi
- tls_block=$(mktemp)
- cat >"$tls_block" <<EOF
- server {
- listen 80;
- server_name $server_names;
- location ^~ /.well-known/acme-challenge/ {
- root $ACME_ROOT;
- default_type text/plain;
- try_files \$uri =404;
- }
- location / { return 301 https://\$host\$request_uri; }
- }
- server {
- listen 443 ssl;
- server_name $server_names;
- ssl_certificate /etc/letsencrypt/live/$cert_name/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/$cert_name/privkey.pem;
- ssl_protocols TLSv1.2 TLSv1.3;
- location / {
- proxy_pass http://xinghen-app:3001;
- proxy_set_header Host \$host;
- proxy_set_header X-Real-IP \$remote_addr;
- proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto \$scheme;
- proxy_read_timeout 120s;
- client_max_body_size 100m;
- }
- }
- EOF
- install_nginx_block "$tls_block"
- rm -f "$tls_block"
- install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy
- cat >/etc/letsencrypt/renewal-hooks/deploy/reload-xinghen-nginx.sh <<EOF
- #!/usr/bin/env bash
- docker exec $PROXY_CONTAINER nginx -t && docker exec $PROXY_CONTAINER nginx -s reload
- EOF
- chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-xinghen-nginx.sh
- healthy=0
- for _ in {1..15}; do
- if curl --noproxy '*' --fail --silent --max-time 5 \
- --resolve "$PRIMARY_DOMAIN:443:127.0.0.1" "https://$PRIMARY_DOMAIN/" >/dev/null; then
- healthy=1
- break
- fi
- sleep 1
- done
- if [[ "$healthy" != 1 ]]; then
- echo "HTTPS 健康检查失败:$PRIMARY_DOMAIN" >&2
- exit 1
- fi
- docker compose -p xinghen -f docker-compose.prod.yml ps
|