Развёртывание на Debian 12/13: Docker, nginx, работа за обратным прокси

- приложение перенесено в public/, инструкции в docs/
- Dockerfile (nginx без root-прав), docker-compose.yml с ограничениями
- deploy/split-trainer.conf: CSP и заголовки безопасности, no-cache по ETag,
  /healthz, реальный IP из X-Forwarded-For только от частных сетей,
  редиректы без внутреннего хоста
- deploy/install-debian.sh: установка и обновление без Docker
- docs/DEPLOY.md: требования, установка, примеры прокси (nginx, Caddy, Traefik), подпуть

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
darko
2026-10-12 12:35:47 +03:00
co-authored by Claude Sonnet 5.5
parent 42b90a056c
commit e897a6c5eb
14 changed files with 376 additions and 11 deletions
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# Установка и обновление Сплит-тренера на Debian 12/13: nginx + статические файлы.
#
# sudo ./deploy/install-debian.sh # за обратным прокси: слушаем 127.0.0.1:8080
# sudo LISTEN=80 DOMAIN=trainer.example.com ./deploy/install-debian.sh # без прокси, напрямую на порту 80
#
# Повторный запуск обновляет файлы сайта и конфиг. Ничего, кроме этого сайта, скрипт не трогает.
set -euo pipefail
LISTEN="${LISTEN:-127.0.0.1:8080}" # адрес:порт или просто порт
DOMAIN="${DOMAIN:-_}" # server_name; "_" = любое имя
WEBROOT="${WEBROOT:-/var/www/split-trainer}"
NAME="split-trainer"
die() { echo "Ошибка: $*" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "запустите от root: sudo $0"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SRC="$HERE/../public"
CONF_TEMPLATE="$HERE/$NAME.conf"
[ -f "$SRC/index.html" ] || die "не найден $SRC/index.html: запускайте скрипт из клонированного репозитория"
[ -f "$CONF_TEMPLATE" ] || die "не найден $CONF_TEMPLATE"
# Значения подставляются в sed: не пускаем туда ничего, кроме безопасных символов
[[ "$LISTEN" =~ ^[][0-9A-Za-z.:_-]+$ ]] || die "некорректный LISTEN: $LISTEN"
[[ "$DOMAIN" =~ ^[0-9A-Za-z._*-]+( [0-9A-Za-z._*-]+)*$ ]] || die "некорректный DOMAIN: $DOMAIN"
[[ "$WEBROOT" =~ ^/[0-9A-Za-z._/-]+$ ]] || die "некорректный WEBROOT: $WEBROOT"
. /etc/os-release
if [ "${ID:-}" != "debian" ] || { [ "${VERSION_ID:-}" != "12" ] && [ "${VERSION_ID:-}" != "13" ]; }; then
[ "${FORCE:-0}" = "1" ] || die "скрипт рассчитан на Debian 12/13 (у вас: ${PRETTY_NAME:-неизвестно}). Чтобы всё равно продолжить: FORCE=1"
fi
echo "==> Устанавливаю nginx"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq --no-install-recommends nginx >/dev/null
echo "==> Копирую сайт в $WEBROOT"
install -d -m 755 "$WEBROOT"
find "$WEBROOT" -mindepth 1 -maxdepth 1 -type f -delete # убираем только файлы прошлой версии
install -m 644 "$SRC"/* "$WEBROOT"/
echo "==> Настраиваю nginx (listen $LISTEN, server_name $DOMAIN)"
CONF="/etc/nginx/sites-available/$NAME.conf"
sed -e "s|^\(\s*\)listen 8080;.*|\1listen $LISTEN;|" \
-e "s|^\(\s*\)server_name _;.*|\1server_name $DOMAIN;|" \
-e "s|^\(\s*\)root /usr/share/nginx/html;.*|\1root $WEBROOT;|" \
"$CONF_TEMPLATE" > "$CONF"
ln -sf "$CONF" "/etc/nginx/sites-enabled/$NAME.conf"
nginx -t
systemctl enable --now nginx >/dev/null 2>&1
systemctl reload nginx
# Проверяем, что сайт действительно отвечает
HOSTPORT="$LISTEN"; [[ "$HOSTPORT" =~ ^[0-9]+$ ]] && HOSTPORT="127.0.0.1:$HOSTPORT"
[ "${HOSTPORT%%:*}" = "0.0.0.0" ] && HOSTPORT="127.0.0.1:${HOSTPORT##*:}"
if command -v curl >/dev/null 2>&1 && ! curl -fsS -m 5 -o /dev/null "http://$HOSTPORT/healthz" -H "Host: ${DOMAIN%% *}"; then
echo "Предупреждение: проверка http://$HOSTPORT/healthz не прошла. Если порт 80 занят сайтом по умолчанию, см. docs/DEPLOY.md" >&2
fi
echo "==> Готово: http://$HOSTPORT/"