install.sh: fail-fast валидация прод-значений .env перед подъёмом стека
На свежем клоне install.sh молча принимал непустые dev-плейсхолдеры .env.example (LIVEKIT_PUBLIC_URL=ws://localhost:7880, NGINX_SERVER_NAMES= example.com, LIVEKIT_API_KEY=devkey и т.д.) — ensure_secret/ensure_default генерируют значения только для ПУСТЫХ ключей. Итог: рапорт об успехе, на деле — mixed-content и nginx на несуществующем домене. validate_prod_env проверяет согласованность .env, когда NGINX_SERVER_NAMES указывает на реальный домен (не localhost) — останавливает установку понятной ошибкой, если что-то из прод-обязательных значений осталось плейсхолдером.
This commit is contained in:
106
install.sh
106
install.sh
@@ -125,6 +125,109 @@ ensure_default() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Fail-fast валидация прод-значений .env перед подъёмом стека.
|
||||||
|
#
|
||||||
|
# `ensure_secret`/`ensure_default` пишут значение только если ключ ПУСТ, а
|
||||||
|
# `.env.example` хранит непустые dev-плейсхолдеры (`NGINX_SERVER_NAMES=
|
||||||
|
# example.com`, `LIVEKIT_PUBLIC_URL=ws://localhost:7880`, `LIVEKIT_API_KEY=
|
||||||
|
# devkey` и т.д.) — при `cp .env.example .env` без ручной правки install.sh
|
||||||
|
# доходил до конца и рапортовал об успехе, а на выходе получался прод с
|
||||||
|
# mixed-content (браузер режет `ws://` на HTTPS-странице) и nginx, слушающим
|
||||||
|
# несуществующий домен `example.com` (см. .forcc/deploy/SESSION4-FINDINGS.md,
|
||||||
|
# ГРАБЛИ 1 — реальный инцидент этого проекта).
|
||||||
|
#
|
||||||
|
# Признак «это прод, не dev»: `NGINX_SERVER_NAMES` — реальный домен, не
|
||||||
|
# `localhost`/`127.0.0.1` (дефолт `ensure_default` для локальной разработки).
|
||||||
|
# Плейсхолдер `example.com` из .env.example — ошибка ВСЕГДА (ни для прода,
|
||||||
|
# ни как осознанный dev-дефолт: тот — `localhost`), проверяется отдельно и
|
||||||
|
# раньше остальных.
|
||||||
|
validate_prod_env() {
|
||||||
|
nginx_names="$(get_env_var NGINX_SERVER_NAMES)"
|
||||||
|
|
||||||
|
case "$nginx_names" in
|
||||||
|
*example.com*)
|
||||||
|
echo "[install] ОШИБКА: NGINX_SERVER_NAMES='${nginx_names}' — непочатый плейсхолдер из .env.example." >&2
|
||||||
|
echo "[install] Перед запуском install.sh задайте в .env реальный домен(ы) (боевой сервер)" >&2
|
||||||
|
echo "[install] или 'localhost' (локальная разработка) — см. docs/deploy/DEPLOYMENT.md." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$nginx_names" in
|
||||||
|
localhost|127.0.0.1) return 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Дальше — прод-ветка: NGINX_SERVER_NAMES указывает на реальный домен,
|
||||||
|
# остальные прод-значения обязаны быть с ним согласованы.
|
||||||
|
fail=0
|
||||||
|
fail_check() {
|
||||||
|
echo "[install] ОШИБКА: $1" >&2
|
||||||
|
fail=1
|
||||||
|
}
|
||||||
|
|
||||||
|
livekit_public_url="$(get_env_var LIVEKIT_PUBLIC_URL)"
|
||||||
|
case "$livekit_public_url" in
|
||||||
|
wss://*) ;;
|
||||||
|
*)
|
||||||
|
fail_check "LIVEKIT_PUBLIC_URL='${livekit_public_url}' — должен быть wss://<домен>/livekit/ (иначе mixed-content на HTTPS, конференции не стартуют — см. .forcc/deploy/SESSION2-FINDINGS.md)."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
frontend_url="$(get_env_var FRONTEND_URL)"
|
||||||
|
case "$frontend_url" in
|
||||||
|
https://*) ;;
|
||||||
|
*)
|
||||||
|
fail_check "FRONTEND_URL='${frontend_url}' — должен быть https://<домен> (иначе битые ссылки подтверждения email)."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$(get_env_var AUTH_COOKIE_SECURE)" != "true" ]; then
|
||||||
|
fail_check "AUTH_COOKIE_SECURE должен быть 'true' на HTTPS-проде (сейчас: '$(get_env_var AUTH_COOKIE_SECURE)')."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(get_env_var LIVEKIT_API_KEY)" = "devkey" ]; then
|
||||||
|
fail_check "LIVEKIT_API_KEY='devkey' — публично известный ключ из .env.example, замените на реальный."
|
||||||
|
fi
|
||||||
|
|
||||||
|
node_ip="$(get_env_var LIVEKIT_NODE_IP)"
|
||||||
|
if [ "$node_ip" = "127.0.0.1" ] || [ -z "$node_ip" ]; then
|
||||||
|
fail_check "LIVEKIT_NODE_IP='${node_ip}' — задайте внешний IP/домен сервера (иначе ICE-кандидаты недостижимы извне)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
cert_name="$(get_env_var NGINX_CERT_NAME)"
|
||||||
|
case "$cert_name" in
|
||||||
|
*example.com*)
|
||||||
|
fail_check "NGINX_CERT_NAME='${cert_name}' — непочатый плейсхолдер из .env.example."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
# НЕ хард-фейл: отсутствие реального сертификата — не тихий провал вроде
|
||||||
|
# mixed-content (тот ломает WS в консоли браузера, легко пропустить).
|
||||||
|
# docker-entrypoint-certs.sh сгенерирует самоподписанный и nginx стартует
|
||||||
|
# штатно — HTTPS с явным предупреждением браузера, что сразу заметно. Это
|
||||||
|
# ОСОЗНАННЫЙ путь бутстрапа: сначала поднять стек (self-signed), потом
|
||||||
|
# certbot --webroot (challenge идёт через уже работающий nginx), см.
|
||||||
|
# docs/deploy/DEPLOYMENT.md, раздел «Выпуск SSL».
|
||||||
|
if [ ! -d "/etc/letsencrypt/live/${cert_name}" ]; then
|
||||||
|
echo "[install] ПРЕДУПРЕЖДЕНИЕ: /etc/letsencrypt/live/${cert_name} не найден — nginx поднимется с самоподписанным сертификатом." >&2
|
||||||
|
echo "[install] Это ожидаемо при первом бутстрапе (см. docs/deploy/DEPLOYMENT.md, «Выпуск SSL» — certbot --webroot ПОСЛЕ первого запуска install.sh)." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TURN понижен до опционального (Сессия 5: реальные сети в тестировании
|
||||||
|
# прошли без раздачи TURN клиентам) — только предупреждение, не блокер.
|
||||||
|
turn_ip="$(get_env_var TURN_EXTERNAL_IP)"
|
||||||
|
if [ "$turn_ip" = "127.0.0.1" ]; then
|
||||||
|
echo "[install] ПРЕДУПРЕЖДЕНИЕ: TURN_EXTERNAL_IP=127.0.0.1 — coturn-релей не будет работать для внешних клиентов." >&2
|
||||||
|
echo "[install] Не блокер (TURN опционален, см. docs/deploy/DEPLOYMENT.md, раздел «TURN для экстремального NAT»), но проверьте, что это осознанный выбор." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$fail" = "1" ]; then
|
||||||
|
echo "[install] Остановлено: .env содержит dev-плейсхолдеры на боевом домене (NGINX_SERVER_NAMES='${nginx_names}')." >&2
|
||||||
|
echo "[install] Полный разбор переменных — docs/deploy/DEPLOYMENT.md." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Автодетект железа (ADR-004, таблица требований).
|
# Автодетект железа (ADR-004, таблица требований).
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -365,6 +468,9 @@ esac
|
|||||||
|
|
||||||
echo "[install] .env обновлён (пресет ${PRESET}, COMPOSE_PROFILES=$(get_env_var COMPOSE_PROFILES))"
|
echo "[install] .env обновлён (пресет ${PRESET}, COMPOSE_PROFILES=$(get_env_var COMPOSE_PROFILES))"
|
||||||
|
|
||||||
|
echo "[install] Проверяю .env на dev-плейсхолдеры перед подъёмом стека"
|
||||||
|
validate_prod_env
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Подъём стека и миграции/seed.
|
# Подъём стека и миграции/seed.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user