comment tauri docker compose
This commit is contained in:
188
install.sh
188
install.sh
@@ -10,7 +10,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="2.3.9"
|
||||
SCRIPT_VERSION="2.4.0"
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
@@ -71,6 +71,7 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл
|
||||
|
||||
./install.sh Интерактивное меню
|
||||
./install.sh --install Полная установка
|
||||
./install.sh --fresh-install Полная очистка Docker + установка с нуля
|
||||
./install.sh --configure-domains Изменить домены / Nginx / SSL
|
||||
./install.sh --renew-ssl Обновить сертификаты (Let's Encrypt / custom / selfsigned)
|
||||
./install.sh --restart Перезапуск контейнеров
|
||||
@@ -108,6 +109,7 @@ ${BOLD}Прочие опции:${NC}
|
||||
${BOLD}Переменные окружения:${NC}
|
||||
FORCE_ENV=1 Новые JWT-секреты (пароли БД/MinIO/RabbitMQ сохраняются если volume уже есть)
|
||||
OFFLINE=1 То же, что --offline
|
||||
FORCE_ENV=1 При --fresh-install: удалить .env и секреты, создать заново
|
||||
SKIP_DOCKER_PRUNE=1 Не очищать образы/кеш перед пересборкой
|
||||
DOCKER_PRUNE_AGGRESSIVE=1 Глубже чистить неиспользуемые образы проекта
|
||||
COMPOSE_PARALLEL_LIMIT=4 Параллельная сборка сервисов (по умолчанию 4)
|
||||
@@ -135,6 +137,14 @@ ${BOLD}Примеры:${NC}
|
||||
--docs-domain docs.example.ru \\
|
||||
--ssl letsencrypt --email admin@example.ru
|
||||
./install.sh --configure-domains
|
||||
|
||||
${BOLD}Полная переустановка (очистка Docker + volumes + установка):${NC}
|
||||
./install.sh --fresh-install --yes \\
|
||||
--intranet \\
|
||||
--api-domain api.idpmvk.lpr \\
|
||||
--frontend-domain sso.idpmvk.lpr \\
|
||||
--docs-domain docs.idpmvk.lpr \\
|
||||
--ssl selfsigned
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -143,6 +153,7 @@ parse_args() {
|
||||
case "$1" in
|
||||
-y | --yes) NON_INTERACTIVE=1; shift ;;
|
||||
--install) ACTION="install"; shift ;;
|
||||
--fresh-install) ACTION="fresh-install"; shift ;;
|
||||
--configure-domains) ACTION="configure-domains"; shift ;;
|
||||
--renew-ssl) ACTION="renew-ssl"; shift ;;
|
||||
--restart) ACTION="restart"; shift ;;
|
||||
@@ -403,10 +414,44 @@ recreate_docker_nginx() {
|
||||
compose_uses_proxy_profile || return 0
|
||||
build_compose_stack_cmd
|
||||
log "Пересоздание контейнера lendry-id-nginx (подхват конфигов из ./nginx/conf.d)..."
|
||||
docker_cmd "${COMPOSE_STACK_CMD[@]}" up -d --force-recreate nginx
|
||||
docker_cmd "${COMPOSE_STACK_CMD[@]}" up -d --force-recreate nginx nginx-gateway-sync
|
||||
wait_for_service "nginx" 60
|
||||
}
|
||||
|
||||
ensure_nginx_gateway_sync() {
|
||||
compose_uses_proxy_profile || return 0
|
||||
build_compose_stack_cmd
|
||||
log "Запуск nginx-gateway-sync (авто-reload Nginx после пересборки api-gateway)..."
|
||||
docker_cmd "${COMPOSE_STACK_CMD[@]}" up -d nginx-gateway-sync
|
||||
}
|
||||
|
||||
verify_api_gateway_static_ip() {
|
||||
nginx_api_uses_static_ip || return 0
|
||||
load_env
|
||||
local expected="${IDP_API_GATEWAY_IP:-172.28.50.10}"
|
||||
local net_name actual
|
||||
net_name="$(docker_network_name idp_static)"
|
||||
actual="$(docker_cmd inspect lendry-id-api-gateway --format "{{index .NetworkSettings.Networks \"${net_name}\" \"IPAddress\"}}" 2>/dev/null || true)"
|
||||
if [[ "$actual" == "$expected" ]]; then
|
||||
ok "api-gateway: статический IP ${expected} (сеть ${net_name})"
|
||||
return 0
|
||||
fi
|
||||
warn "api-gateway IP=${actual:-?}, ожидался ${expected} на ${net_name}"
|
||||
return 1
|
||||
}
|
||||
|
||||
post_deploy_proxy_verify() {
|
||||
compose_uses_proxy_profile || return 0
|
||||
wait_for_service "nginx" 30
|
||||
ensure_nginx_gateway_sync
|
||||
verify_api_gateway_static_ip || true
|
||||
sync_nginx_after_api_gateway_restart
|
||||
verify_nginx_reaches_media_ws || true
|
||||
if [[ "$(env_get USE_NGINX_SSL false)" == "true" ]]; then
|
||||
check_sso_idp_api_proxy || warn "SSO /idp-api/health не прошёл — выполните ./install.sh --fix-all"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_compose_stack() {
|
||||
resolve_nginx_mode_and_ports
|
||||
write_compose_override
|
||||
@@ -884,6 +929,7 @@ load_env() {
|
||||
# shellcheck disable=SC1091
|
||||
set -a && source .env && set +a
|
||||
fi
|
||||
IDP_API_GATEWAY_IP="${IDP_API_GATEWAY_IP:-172.28.50.10}"
|
||||
}
|
||||
|
||||
env_get() {
|
||||
@@ -1342,6 +1388,7 @@ persist_domain_env() {
|
||||
env_set SSL_CERT_KEY "${SSL_CERT_KEY:-$(env_get SSL_CERT_KEY "")}"
|
||||
env_set MINIO_PUBLIC_ENDPOINT "${MINIO_PUBLIC_ENDPOINT:-localhost:9000}"
|
||||
env_set MINIO_USE_SSL "${MINIO_USE_SSL:-false}"
|
||||
env_set IDP_API_GATEWAY_IP "${IDP_API_GATEWAY_IP:-172.28.50.10}"
|
||||
|
||||
if [[ "$(env_get USE_NGINX_SSL false)" == "true" ]]; then
|
||||
env_set FEDCM_COOKIE_SECURE "true"
|
||||
@@ -2547,8 +2594,17 @@ validate_docker_api_nginx_config() {
|
||||
[[ -f "$conf" ]] || fail "Не найден ${conf} — конфиг API-домена не записан"
|
||||
|
||||
if grep -q "upstream ${NGINX_PREFIX}_api" "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} содержит static upstream — обновите install.sh и выполните ./install.sh --fix-all"
|
||||
fail "Конфиг ${conf} содержит legacy upstream{} — выполните ./install.sh --fix-all"
|
||||
fi
|
||||
|
||||
if nginx_api_uses_static_ip; then
|
||||
if ! grep -q "${IDP_API_GATEWAY_IP}:3000" "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} без proxy_pass на ${IDP_API_GATEWAY_IP}:3000 — выполните ./install.sh --fix-all"
|
||||
fi
|
||||
ok "Конфиг API-домена: статический upstream ${IDP_API_GATEWAY_IP}:3000"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q 'resolver 127.0.0.11' "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} без resolver 127.0.0.11 — API-домен не переживёт пересборку api-gateway"
|
||||
fi
|
||||
@@ -2566,6 +2622,18 @@ validate_docker_frontend_nginx_config() {
|
||||
if ! grep -q 'location \^~ /idp-api/' "$conf" 2>/dev/null && ! grep -q 'location.*idp-api' "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} без location /idp-api — браузер SPA получит 502. Выполните ./install.sh --fix-all"
|
||||
fi
|
||||
|
||||
if nginx_api_uses_static_ip; then
|
||||
if ! grep -q "${IDP_API_GATEWAY_IP}:3000" "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} без proxy_pass на ${IDP_API_GATEWAY_IP}:3000 — выполните ./install.sh --fix-all"
|
||||
fi
|
||||
if ! grep -q 'location \^~ /idp-api/ws' "$conf" 2>/dev/null; then
|
||||
warn "Конфиг ${conf}: location ^~ /idp-api/ws отсутствует — WebSocket может идти на api-gateway вместо media-ws"
|
||||
fi
|
||||
ok "Конфиг SSO-домена: /idp-api → ${IDP_API_GATEWAY_IP}:3000 (статический IP)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! grep -q 'resolver 127.0.0.11' "$conf" 2>/dev/null; then
|
||||
fail "Конфиг ${conf} без resolver 127.0.0.11 — /idp-api на SSO не переживёт пересборку api-gateway"
|
||||
fi
|
||||
@@ -2584,14 +2652,25 @@ verify_nginx_reaches_api_gateway() {
|
||||
local max_attempts="${1:-1}"
|
||||
local delay_sec="${2:-0}"
|
||||
local attempt=0
|
||||
local body gw_ip
|
||||
local body gw_ip static_ip="${IDP_API_GATEWAY_IP:-172.28.50.10}"
|
||||
|
||||
while [[ $attempt -lt $max_attempts ]]; do
|
||||
gw_ip="$(docker_cmd inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lendry-id-api-gateway 2>/dev/null || true)"
|
||||
|
||||
if nginx_api_uses_static_ip; then
|
||||
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
|
||||
wget -qO- --timeout=5 "http://${static_ip}:3000/health" 2>/dev/null || true)"
|
||||
else
|
||||
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
|
||||
wget -qO- --timeout=5 http://lendry-id-api-gateway:3000/health 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [[ "$body" == *'"status":"ok"'* ]] || [[ "$body" == *'"status": "ok"'* ]]; then
|
||||
if nginx_api_uses_static_ip; then
|
||||
ok "Docker Nginx → ${static_ip}:3000/health OK (статический IP api-gateway)"
|
||||
else
|
||||
ok "Docker Nginx → lendry-id-api-gateway:3000/health OK (IP api-gateway: ${gw_ip:-?})"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
@@ -2600,8 +2679,8 @@ verify_nginx_reaches_api_gateway() {
|
||||
fi
|
||||
done
|
||||
|
||||
warn "Docker Nginx не достучался до lendry-id-api-gateway:3000 (IP: ${gw_ip:-?}, попыток: ${max_attempts})"
|
||||
warn "Проверьте: docker compose ps api-gateway && docker compose logs --tail=40 api-gateway"
|
||||
warn "Docker Nginx не достучался до api-gateway:3000/health (IP: ${gw_ip:-?}, static: ${static_ip}, попыток: ${max_attempts})"
|
||||
warn "Проверьте: docker compose ps api-gateway nginx && docker compose logs --tail=40 api-gateway nginx"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -2622,6 +2701,7 @@ verify_nginx_reaches_media_ws() {
|
||||
|
||||
# Проверка того же пути, что использует браузер после логина: https://sso.../idp-api/*
|
||||
check_sso_idp_api_proxy() {
|
||||
load_env
|
||||
local frontend_domain code body
|
||||
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||
[[ -n "$frontend_domain" ]] || return 0
|
||||
@@ -2649,8 +2729,12 @@ check_sso_idp_api_proxy() {
|
||||
return 0
|
||||
elif [[ "$code" == "502" ]]; then
|
||||
echo -e " ${RED}✘${NC} SSO /idp-api/health → 502 — Nginx не проксирует /idp-api на api-gateway"
|
||||
echo " Проверьте: grep -E 'idp-api|idp_up_api|resolver' nginx/conf.d/${NGINX_PREFIX}-frontend.conf"
|
||||
echo " Проверьте: grep -E 'idp-api|172.28.50.10|idp_up_api' nginx/conf.d/${NGINX_PREFIX}-frontend.conf"
|
||||
if nginx_api_uses_static_ip; then
|
||||
echo " docker compose --profile proxy exec nginx wget -qO- http://${IDP_API_GATEWAY_IP}:3000/health"
|
||||
else
|
||||
echo " docker compose --profile proxy exec nginx wget -qO- http://lendry-id-api-gateway:3000/health"
|
||||
fi
|
||||
return 1
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} SSO /idp-api/health → HTTP ${code} (ожидалось 200)"
|
||||
@@ -2862,12 +2946,10 @@ deploy_stack() {
|
||||
|
||||
wait_for_service "sso-core" 120
|
||||
wait_for_service "api-gateway" 90
|
||||
sync_nginx_after_api_gateway_restart
|
||||
wait_for_api_grpc_ready 60 || warn "gRPC api-gateway → sso-core не готов — API может отдавать 502"
|
||||
wait_for_service "frontend" 60
|
||||
wait_for_service "docs" 60
|
||||
if compose_uses_proxy_profile; then
|
||||
wait_for_service "nginx" 30
|
||||
fi
|
||||
post_deploy_proxy_verify
|
||||
}
|
||||
|
||||
save_install_state() {
|
||||
@@ -2944,6 +3026,7 @@ print_summary() {
|
||||
echo -e " ${BOLD}Смена доменов:${NC} ./install.sh --configure-domains"
|
||||
echo -e " ${BOLD}Логи:${NC} docker compose logs -f api-gateway nginx"
|
||||
echo -e " ${BOLD}Очистка Docker:${NC} ./install.sh --docker-prune"
|
||||
echo -e " ${BOLD}С нуля:${NC} ./install.sh --fresh-install --yes"
|
||||
echo ""
|
||||
echo " Первый зарегистрированный пользователь станет супер-администратором."
|
||||
echo " Секреты в .env — не коммитьте в git."
|
||||
@@ -3100,11 +3183,21 @@ diagnose_common_issues() {
|
||||
echo -e " ${YELLOW}!${NC} Docker Nginx не запущен (ожидался в режиме docker)"
|
||||
issues=$((issues + 1))
|
||||
elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] \
|
||||
&& [[ -f "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-api.conf" ]] \
|
||||
&& grep -q "upstream ${NGINX_PREFIX}_api" "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-api.conf" 2>/dev/null; then
|
||||
echo -e " ${RED}✘${NC} Конфиг API-домена использует статический upstream — после пересборки api-gateway будет 502"
|
||||
echo " Решение: ./install.sh --fix-all (перегенерирует конфиг с динамическим DNS)"
|
||||
&& [[ -f "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" ]]; then
|
||||
load_env
|
||||
if grep -q "upstream ${NGINX_PREFIX}_api" "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-api.conf" 2>/dev/null; then
|
||||
echo -e " ${RED}✘${NC} Конфиг API-домена: legacy upstream{} — после пересборки api-gateway будет 502"
|
||||
echo " Решение: ./install.sh --fix-all"
|
||||
issues=$((issues + 1))
|
||||
elif nginx_api_uses_static_ip \
|
||||
&& grep -q "${IDP_API_GATEWAY_IP}:3000" "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" 2>/dev/null; then
|
||||
echo -e " ${GREEN}✔${NC} Nginx /idp-api → ${IDP_API_GATEWAY_IP}:3000 (статический IP, без stale DNS)"
|
||||
elif grep -q 'idp_up_api' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" 2>/dev/null; then
|
||||
echo -e " ${GREEN}✔${NC} Nginx /idp-api: динамический DNS (resolver + idp_up_api)"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} Конфиг SSO без /idp-api proxy — выполните ./install.sh --fix-all"
|
||||
issues=$((issues + 1))
|
||||
fi
|
||||
elif [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then
|
||||
if system_nginx_is_active; then
|
||||
echo -e " ${GREEN}✔${NC} Системный Nginx: активен"
|
||||
@@ -3267,12 +3360,71 @@ show_status() {
|
||||
action_full_install() {
|
||||
configure_domains_interactive
|
||||
persist_domain_env
|
||||
ensure_split_domain_public_urls
|
||||
setup_nginx_proxy
|
||||
deploy_stack
|
||||
save_install_state
|
||||
diagnose_common_issues || true
|
||||
print_summary
|
||||
}
|
||||
|
||||
docker_fresh_purge() {
|
||||
prepare_compose_stack
|
||||
build_compose_stack_cmd
|
||||
|
||||
log "Остановка стека и удаление volumes (PostgreSQL, Redis, MinIO, RabbitMQ)..."
|
||||
docker_cmd "${COMPOSE_STACK_CMD[@]}" down -v --remove-orphans 2>/dev/null || true
|
||||
docker_cmd compose --env-file .env down -v --remove-orphans 2>/dev/null || true
|
||||
docker_cmd compose --env-file .env --profile proxy down -v --remove-orphans 2>/dev/null || true
|
||||
|
||||
log "Удаление образов, кеша сборки и неиспользуемых volumes..."
|
||||
docker_cmd container prune -f 2>/dev/null || true
|
||||
docker_cmd image prune -af 2>/dev/null || true
|
||||
docker_cmd volume prune -f 2>/dev/null || true
|
||||
docker_cmd builder prune -af 2>/dev/null || true
|
||||
docker_prune_stale_project_images "$(compose_project_name)" 1
|
||||
|
||||
log "Очистка устаревших конфигов Nginx..."
|
||||
rm -f "${LOCAL_CONF_DIR}/${NGINX_PREFIX}"*.conf 2>/dev/null || true
|
||||
|
||||
ok "Docker полностью очищен"
|
||||
docker_disk_usage_brief
|
||||
}
|
||||
|
||||
action_fresh_install() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Полная переустановка Lendry ID${NC}"
|
||||
echo " Будут удалены: все контейнеры проекта, volumes (БД, Redis, MinIO), образы и build cache."
|
||||
echo " Файл .env сохранится (домены и секреты). Для нового .env: FORCE_ENV=1 ./install.sh --fresh-install"
|
||||
echo ""
|
||||
|
||||
if [[ "$NON_INTERACTIVE" -ne 1 ]]; then
|
||||
local confirm
|
||||
read -r -p "Продолжить полную очистку и установку? [y/N]: " confirm
|
||||
case "${confirm,,}" in
|
||||
y | yes | д | да) ;;
|
||||
*) fail "Отменено" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
load_env
|
||||
docker_fresh_purge
|
||||
else
|
||||
warn ".env не найден — выполняется только очистка Docker..."
|
||||
docker_fresh_purge 2>/dev/null || docker_cmd system prune -af --volumes 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ "${FORCE_ENV:-0}" -eq 1 ]]; then
|
||||
warn "FORCE_ENV=1: удаление .env и файлов состояния..."
|
||||
rm -f .env "$STATE_FILE" "$INFRA_SECRETS_FILE"
|
||||
fi
|
||||
|
||||
SKIP_DOCKER_PRUNE=1
|
||||
DOCKER_PRUNE_AGGRESSIVE=0
|
||||
action_full_install
|
||||
}
|
||||
|
||||
action_configure_domains() {
|
||||
if [[ ! -f .env ]]; then
|
||||
warn ".env не найден — выполняем полную установку"
|
||||
@@ -3486,6 +3638,7 @@ action_fix_all_errors() {
|
||||
esac
|
||||
apply_intranet_selfsigned_runtime
|
||||
recreate_docker_nginx
|
||||
ensure_nginx_gateway_sync
|
||||
sync_nginx_after_api_gateway_restart
|
||||
verify_nginx_reaches_api_gateway 24 5 || compose_build_and_up api-gateway
|
||||
sync_nginx_after_api_gateway_restart
|
||||
@@ -3540,6 +3693,7 @@ show_menu() {
|
||||
[[ -f .env ]] && load_env && echo -e " Режим: ${INSTALL_MODE:-local} | API: ${DOMAIN_API:-localhost:3000}"
|
||||
echo ""
|
||||
echo " 1) Полная установка / переустановка стека"
|
||||
echo " 10) Полная очистка Docker + установка с нуля (--fresh-install)"
|
||||
echo " 2) Настроить / изменить домены и Nginx + SSL"
|
||||
echo " 3) SSL-сертификаты (обновить / включить custom / Let's Encrypt)"
|
||||
echo " 4) Перезапуск / пересборка контейнеров (+ очистка Docker)"
|
||||
@@ -3552,10 +3706,11 @@ show_menu() {
|
||||
echo ""
|
||||
|
||||
local choice
|
||||
read -r -p "Выберите пункт [0-9]: " choice
|
||||
read -r -p "Выберите пункт [0-10]: " choice
|
||||
|
||||
case "$choice" in
|
||||
1) NON_INTERACTIVE=0; action_full_install ;;
|
||||
10) NON_INTERACTIVE=0; action_fresh_install ;;
|
||||
2) NON_INTERACTIVE=0; action_configure_domains ;;
|
||||
3) action_renew_ssl ;;
|
||||
4) action_restart ;;
|
||||
@@ -3580,6 +3735,7 @@ main() {
|
||||
|
||||
case "${ACTION:-}" in
|
||||
install) action_full_install ;;
|
||||
fresh-install) action_fresh_install ;;
|
||||
configure-domains) action_configure_domains ;;
|
||||
renew-ssl) action_renew_ssl ;;
|
||||
restart) action_restart ;;
|
||||
|
||||
Reference in New Issue
Block a user