#!/usr/bin/env bash # Генерирует и включает конфиг nginx для сайта расписания. # # SSL обеспечивает сертификат родительского домена (обычно wildcard *.example.ru): # отдельный certbot для поддомена не нужен — достаточно имени сертификата. # Список имён: sudo ls /etc/letsencrypt/live/ # # Имя сертификата берётся из --cert, иначе — родительский домен от --domain # (sched.example.ru -> example.ru). Порт фронта берётся из --port, иначе из # FRONTEND_PORT файла .env в корне проекта, иначе 8080. # # Примеры: # deploy/nginx-site.sh --print --domain sched.example.ru # показать конфиг # sudo deploy/nginx-site.sh --domain sched.example.ru # поставить и перечитать nginx # sudo deploy/nginx-site.sh --domain sched.example.ru --cert example.ru set -euo pipefail usage() { grep '^#' "$0" | sed 's/^# \{0,1\}//' | tail -n +2 } die() { echo "Ошибка: $*" >&2; exit 2; } DOMAIN="" CERT_NAME="" FRONTEND_PORT="" PRINT=0 while [[ $# -gt 0 ]]; do case "$1" in --domain) DOMAIN="$2"; shift 2 ;; --cert) CERT_NAME="$2"; shift 2 ;; --port) FRONTEND_PORT="$2"; shift 2 ;; --print) PRINT=1; shift ;; -h|--help) usage; exit 0 ;; *) echo "Неизвестный аргумент: $1 (см. --help)" >&2; exit 2 ;; esac done [[ -n $DOMAIN ]] || die "Укажите домен: --domain sched.example.ru (см. --help)" [[ $DOMAIN =~ ^[a-zA-Z0-9.-]+$ ]] || die "Кривое имя домена: $DOMAIN" if [[ -z $CERT_NAME ]]; then CERT_NAME="${DOMAIN#*.}" # sched.example.ru -> example.ru (wildcard родителя) fi [[ $CERT_NAME =~ ^[a-zA-Z0-9.-]+$ ]] || { echo "Кривое имя сертификата: $CERT_NAME" >&2; exit 2; } if [[ -z $FRONTEND_PORT ]]; then # FRONTEND_PORT из .env; значение вида «127.0.0.1:8090» трактуем как «порт после двоеточия» FRONTEND_PORT=$( grep -E '^FRONTEND_PORT=' .env 2>/dev/null | tail -1 | cut -d= -f2- \ | tr -d ' "' | awk -F: '{print $NF}' ) || FRONTEND_PORT="" fi FRONTEND_PORT=${FRONTEND_PORT:-8080} [[ $FRONTEND_PORT =~ ^[0-9]+$ ]] || { echo "Не разобрал FRONTEND_PORT из .env, задайте --port" >&2; exit 2; } read -r -d '' CONFIG <<'TEMPLATE' || true # Сгенерировано deploy/nginx-site.sh — правки вносить в скрипт, не сюда. server { listen 80; listen [::]:80; server_name __DOMAIN__; location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name __DOMAIN__; # сертификат родительского домена (обычно wildcard), обновляет certbot родителя ssl_certificate /etc/letsencrypt/live/__CERT__/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/__CERT__/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; client_max_body_size 8m; location / { # порт FRONTEND_PORT из .env стенда proxy_pass http://127.0.0.1:__PORT__; 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 https; # холодный кэш: бэкенд качает таблицу, до ~2 минут proxy_read_timeout 300s; proxy_send_timeout 300s; } } TEMPLATE CONFIG=${CONFIG//__DOMAIN__/$DOMAIN} CONFIG=${CONFIG//__CERT__/$CERT_NAME} CONFIG=${CONFIG//__PORT__/$FRONTEND_PORT} if [[ $PRINT == 1 ]]; then printf '%s\n' "$CONFIG" exit 0 fi if [[ $EUID -ne 0 ]]; then echo "Установка требует root: sudo deploy/nginx-site.sh (посмотреть конфиг — deploy/nginx-site.sh --print)" >&2 exit 1 fi DEST="/etc/nginx/sites-available/${DOMAIN}" printf '%s\n' "$CONFIG" > "$DEST" ln -sf "$DEST" "/etc/nginx/sites-enabled/${DOMAIN}" if nginx -t 2>&1; then systemctl reload nginx echo "Готово: https://${DOMAIN} -> 127.0.0.1:${FRONTEND_PORT} (сертификат ${CERT_NAME})" else echo "nginx -t не прошёл: конфиг записан в ${DEST} и включён, но nginx НЕ перечитан. Разберитесь и выполните: sudo nginx -t && sudo systemctl reload nginx" >&2 exit 1 fi