From e3c418d92166ce1ec8f2f88de6c4857f077d8934 Mon Sep 17 00:00:00 2001 From: lendry Date: Mon, 29 Jun 2026 17:17:19 +0300 Subject: [PATCH] fix and update --- install.sh | 252 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 235 insertions(+), 17 deletions(-) diff --git a/install.sh b/install.sh index 59b849a..03cbfec 100644 --- a/install.sh +++ b/install.sh @@ -10,7 +10,7 @@ set -euo pipefail -SCRIPT_VERSION="2.2.5" +SCRIPT_VERSION="2.3.0" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$ROOT_DIR" @@ -73,6 +73,7 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл ./install.sh --restart Перезапуск контейнеров ./install.sh --docker-prune Только очистка Docker (без пересборки) ./install.sh --fix-proxy Исправить 502: host Nginx + порты 3000/3002/3003 + ./install.sh --fix-all Комплексное исправление: 502, прокси, порты, Nginx ./install.sh --status Статус сервисов ./install.sh --reset-db Сброс PostgreSQL (новый пароль из .env, данные удалятся) ./install.sh --remove-nginx Удалить конфиги Nginx IdP @@ -146,6 +147,7 @@ parse_args() { --skip-docker-prune) SKIP_DOCKER_PRUNE=1; shift ;; --docker-prune-aggressive) DOCKER_PRUNE_AGGRESSIVE=1; shift ;; --fix-proxy) ACTION="fix-proxy"; shift ;; + --fix-all) ACTION="fix-all"; shift ;; --status) ACTION="status"; shift ;; --reset-db) ACTION="reset-db"; shift ;; --local) INSTALL_LOCAL=1; shift ;; @@ -253,6 +255,45 @@ lendry_host_nginx_configured() { || [[ -L "${NGINX_ENABLED}/${NGINX_PREFIX}-frontend.conf" ]] } +docker_nginx_container_running() { + docker_cmd ps -q -f name=lendry-id-nginx 2>/dev/null | grep -q . +} + +docker_nginx_ports_available() { + local http="${NGINX_HTTP_PORT:-80}" + local https="${NGINX_HTTPS_PORT:-443}" + ! port_in_use "$http" && ! port_in_use "$https" +} + +# Не даём контейнерному nginx занять 80/443, если их уже держит системный nginx +# или явно выбран NGINX_MODE=host (типичная причина «address already in use»). +ensure_docker_nginx_not_conflicting() { + load_env + resolve_nginx_mode_and_ports + + local http="${NGINX_HTTP_PORT:-80}" + local https="${NGINX_HTTPS_PORT:-443}" + local ports_busy=0 + port_in_use "$http" && ports_busy=1 + port_in_use "$https" && ports_busy=1 + + if [[ "$ports_busy" -eq 1 ]] && [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then + if host_nginx_available || system_nginx_is_active || lendry_host_nginx_configured; then + warn "Порты ${http}/${https} заняты системным Nginx — переключаем NGINX_MODE на host" + warn "Контейнер lendry-id-nginx не будет запущен (используйте системный Nginx)" + NGINX_MODE="host" + env_set NGINX_MODE "host" + else + warn "Порты ${http}/${https} заняты другим процессом — Docker Nginx не сможет стартовать" + warn "Освободите порты или укажите другие: --nginx-http-port / --nginx-https-port" + fi + fi + + if [[ "$(env_get NGINX_MODE docker)" == "host" ]] || [[ "$ports_busy" -eq 1 ]]; then + stop_docker_nginx + fi +} + stop_docker_nginx() { local cid cid="$(docker_cmd ps -q -f name=lendry-id-nginx 2>/dev/null || true)" @@ -267,10 +308,7 @@ stop_docker_nginx() { prepare_compose_stack() { resolve_nginx_mode_and_ports write_compose_override - load_env - if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then - stop_docker_nginx - fi + ensure_docker_nginx_not_conflicting } resolve_nginx_mode_and_ports() { @@ -299,7 +337,7 @@ resolve_nginx_mode_and_ports() { if [[ "$NGINX_MODE" == "docker" ]]; then if port_in_use "$NGINX_HTTP_PORT" || port_in_use "$NGINX_HTTPS_PORT"; then - if host_nginx_available && [[ "$NGINX_HTTP_PORT" == "80" ]]; then + if host_nginx_available || system_nginx_is_active || lendry_host_nginx_configured; then warn "Порт ${NGINX_HTTP_PORT}/${NGINX_HTTPS_PORT} занят — переключаемся на системный Nginx (host)" NGINX_MODE="host" env_set NGINX_MODE "host" @@ -350,6 +388,9 @@ compose_uses_proxy_profile() { [[ -n "$(env_get DOMAIN_API "")" ]] || return 1 [[ "$(env_get INSTALL_MODE local)" == "local" ]] && return 1 [[ "$(env_get NGINX_MODE docker)" == "docker" ]] || return 1 + if ! docker_nginx_ports_available; then + return 1 + fi return 0 } @@ -529,9 +570,27 @@ compose_build_and_up() { fi configure_docker_build_env + ensure_docker_nginx_not_conflicting build_compose_stack_cmd compose_build_parallel_flags + if [[ ${#services[@]} -gt 0 ]]; then + local -a filtered=() + local svc + for svc in "${services[@]}"; do + if [[ "$svc" == "nginx" ]] && ! compose_uses_proxy_profile; then + warn "Пропуск сервиса nginx: порты заняты или NGINX_MODE=host — используйте системный Nginx" + continue + fi + filtered+=("$svc") + done + services=("${filtered[@]}") + if [[ ${#services[@]} -eq 0 ]]; then + warn "Нет сервисов для запуска после проверки конфликта Nginx" + return 0 + fi + fi + if [[ "${SKIP_DOCKER_PRUNE:-0}" -ne 1 ]]; then docker_prune_before_rebuild "${DOCKER_PRUNE_AGGRESSIVE:-0}" fi @@ -548,6 +607,8 @@ compose_build_and_up() { docker_cmd "${COMPOSE_STACK_CMD[@]}" up -d --remove-orphans fi + ensure_docker_nginx_not_conflicting + if [[ "${SKIP_DOCKER_PRUNE:-0}" -ne 1 ]]; then docker_prune_after_rebuild fi @@ -2309,9 +2370,7 @@ setup_nginx_proxy() { resolve_nginx_mode_and_ports write_compose_override - if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then - stop_docker_nginx - fi + ensure_docker_nginx_not_conflicting install_nginx_certbot local ssl_type @@ -2494,11 +2553,90 @@ check_local_upstream() { local label="$1" url="$2" if curl -fsS --max-time 3 "$url" >/dev/null 2>&1; then echo -e " ${GREEN}✔${NC} ${label}: ${url}" + return 0 else echo -e " ${RED}✘${NC} ${label}: ${url} — недоступен" + return 1 fi } +# Диагностика типичных проблем: 502 на /idp-api, конфликт nginx, порты upstream. +diagnose_common_issues() { + load_env + local issues=0 + + echo "" + echo -e "${BOLD}Диагностика${NC}" + echo "" + + if docker_nginx_container_running; then + if [[ "$(env_get NGINX_MODE docker)" == "host" ]] \ + || port_in_use "${NGINX_HTTP_PORT:-80}" \ + || port_in_use "${NGINX_HTTPS_PORT:-443}"; then + echo -e " ${RED}✘${NC} Конфликт: контейнер lendry-id-nginx запущен при занятых портах 80/443 или NGINX_MODE=host" + echo " Решение: ./install.sh --fix-all" + issues=$((issues + 1)) + else + echo -e " ${GREEN}✔${NC} Docker Nginx: запущен (режим docker)" + fi + elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] && compose_uses_proxy_profile; then + echo -e " ${YELLOW}!${NC} Docker Nginx не запущен (ожидался в режиме docker)" + issues=$((issues + 1)) + elif [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + if system_nginx_is_active; then + echo -e " ${GREEN}✔${NC} Системный Nginx: активен" + elif host_nginx_available; then + echo -e " ${YELLOW}!${NC} Системный Nginx установлен, но не активен — sudo systemctl start nginx" + issues=$((issues + 1)) + else + echo -e " ${RED}✘${NC} NGINX_MODE=host, но системный Nginx не найден" + issues=$((issues + 1)) + fi + fi + + if [[ "$(env_get NGINX_MODE docker)" == "host" ]] \ + && [[ "$(env_get INSTALL_MODE local)" != "local" ]] \ + && [[ -n "$(env_get DOMAIN_API "")" ]]; then + local port + for port in 3000 3002 3003; do + if ! port_in_use "$port"; then + echo -e " ${RED}✘${NC} Порт 127.0.0.1:${port} не слушается — host Nginx получит 502" + issues=$((issues + 1)) + fi + done + check_local_upstream "API /health" "http://127.0.0.1:3000/health" || issues=$((issues + 1)) + check_local_upstream "Frontend" "http://127.0.0.1:3002" || issues=$((issues + 1)) + check_local_upstream "Docs" "http://127.0.0.1:3003/docs" || issues=$((issues + 1)) + + if has_cmd nginx && [[ -f "${NGINX_ENABLED}/${NGINX_PREFIX}-frontend.conf" ]]; then + if run_root nginx -T 2>/dev/null | grep -q 'proxy_pass http://api-gateway:'; then + echo -e " ${RED}✘${NC} В конфиге host Nginx указан docker-hostname api-gateway — хост его не резолвит" + echo " Решение: ./install.sh --fix-all (перегенерирует proxy_pass на 127.0.0.1)" + issues=$((issues + 1)) + elif run_root nginx -T 2>/dev/null | grep -q 'proxy_pass http://127.0.0.1:3000'; then + echo -e " ${GREEN}✔${NC} /idp-api проксируется на 127.0.0.1:3000" + fi + fi + fi + + if docker_cmd ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -E 'api-gateway.*(Restarting|unhealthy)' >/dev/null 2>&1; then + echo -e " ${RED}✘${NC} api-gateway unhealthy или перезапускается — проверьте: docker compose logs api-gateway" + issues=$((issues + 1)) + elif docker_cmd ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -q 'lendry-id-api-gateway.*Up'; then + echo -e " ${GREEN}✔${NC} api-gateway: контейнер запущен" + fi + + echo "" + if [[ "$issues" -eq 0 ]]; then + ok "Критических проблем не обнаружено" + else + warn "Обнаружено проблем: ${issues}. Запустите: ./install.sh --fix-all" + fi + echo "" + + return "$issues" +} + show_upstream_health() { load_env [[ "$(env_get NGINX_MODE docker)" != "host" ]] && return 0 @@ -2538,14 +2676,14 @@ show_status() { echo "" if docker_cmd ps -q -f name=lendry-id-nginx 2>/dev/null | grep -q .; then if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then - warn "КОНФЛИКТ: контейнер lendry-id-nginx запущен при NGINX_MODE=host — выполните: ./install.sh --fix-proxy" + warn "КОНФЛИКТ: контейнер lendry-id-nginx запущен при NGINX_MODE=host — выполните: ./install.sh --fix-all" else echo " Docker Nginx: запущен (режим docker; curl 127.0.0.1:3002 не нужен)" fi fi if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then docker_cmd port lendry-id-frontend 3000/tcp 2>/dev/null | sed 's/^/ frontend port: /' || \ - warn "frontend не публикует 3000/tcp на хост — выполните: ./install.sh --fix-proxy" + warn "frontend не публикует 3000/tcp на хост — выполните: ./install.sh --fix-all" fi echo "" show_upstream_health @@ -2689,17 +2827,17 @@ action_fix_host_proxy() { log "Исправление прокси (host Nginx + порты 127.0.0.1)..." - log "Шаг 1/4: остановка Docker Nginx..." - stop_docker_nginx + log "Шаг 1/5: остановка Docker Nginx и проверка портов..." + ensure_docker_nginx_not_conflicting - log "Шаг 2/4: compose override (LDAP / Nginx)..." + log "Шаг 2/5: compose override (LDAP / Nginx)..." write_compose_override - log "Шаг 3/4: пересоздание api-gateway, frontend, docs, media-ws..." + log "Шаг 3/5: пересоздание api-gateway, frontend, docs, media-ws..." compose_build_and_up "${HOST_EDGE_SERVICES[@]}" ensure_host_upstreams - log "Шаг 4/4: конфиги системного Nginx..." + log "Шаг 4/5: конфиги системного Nginx..." refresh_host_nginx_configs apply_intranet_selfsigned_runtime @@ -2708,6 +2846,83 @@ action_fix_host_proxy() { compose_build_and_up ldap-auth sso-core ok "Прокси исправлен — проверьте: curl -I http://127.0.0.1:3002" + diagnose_common_issues || true + show_status +} + +# Комплексное исправление: 502 на /idp-api, конфликт Docker/host Nginx, порты upstream, +# пересборка api-gateway после смены IP контейнера, актуализация конфигов Nginx. +action_fix_all_errors() { + [[ -f .env ]] || fail "Файл .env не найден — сначала выполните ./install.sh --install" + load_env + NON_INTERACTIVE=1 + + echo "" + echo -e "${BOLD}Комплексное исправление ошибок IdP${NC}" + echo " (502 Bad Gateway, /idp-api, конфликт Nginx, порты 3000/3002/3003)" + echo "" + + log "Шаг 1/6: диагностика..." + diagnose_common_issues || true + + log "Шаг 2/6: определение режима Nginx и устранение конфликта портов 80/443..." + resolve_nginx_mode_and_ports + ensure_docker_nginx_not_conflicting + write_compose_override + + log "Шаг 3/6: пересборка api-gateway и sso-core..." + compose_build_and_up api-gateway sso-core + wait_for_service "sso-core" 120 + wait_for_service "api-gateway" 90 + + log "Шаг 4/6: пересоздание edge-сервисов (frontend, docs, media-ws)..." + compose_build_and_up "${HOST_EDGE_SERVICES[@]}" + wait_for_service "frontend" 60 + wait_for_service "docs" 60 + + if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + log "Шаг 5/6: host Nginx — порты 127.0.0.1 и конфиги..." + ensure_host_upstreams + refresh_host_nginx_configs + apply_intranet_selfsigned_runtime + compose_build_and_up ldap-auth + else + log "Шаг 5/6: Docker Nginx — перегенерация конфигов и перезапуск..." + local ssl_type + ssl_type="$(env_get SSL_TYPE none)" + case "$ssl_type" in + none) write_nginx_http_only || warn "Не удалось записать HTTP-конфиги Nginx" ;; + selfsigned) + generate_all_self_signed_certs + write_all_nginx_configs "selfsigned" || warn "Не удалось обновить конфиги Nginx" + ;; + custom) + install_custom_certificates + write_all_nginx_configs "custom" || warn "Не удалось обновить конфиги Nginx" + ;; + letsencrypt) + write_all_nginx_configs "letsencrypt" || warn "Не удалось обновить конфиги Nginx" + ;; + esac + if compose_uses_proxy_profile; then + compose_build_and_up nginx + wait_for_service "nginx" 30 + else + warn "Docker Nginx не запущен — порты 80/443 заняты; используется системный Nginx" + env_set NGINX_MODE "host" + NGINX_MODE="host" + refresh_host_nginx_configs + fi + fi + + log "Шаг 6/6: финальная проверка..." + diagnose_common_issues || true + + ok "Исправление завершено" + echo "" + echo " Проверьте в браузере страницу после авторизации — запросы к /idp-api/* должны отвечать 200." + echo " Если ошибки остались: docker compose logs --tail=80 api-gateway" + echo "" show_status } @@ -2739,11 +2954,12 @@ show_menu() { echo " 6) Удалить конфиги Nginx IdP" echo " 7) Сброс PostgreSQL (если ошибка P1000 / пароль БД)" echo " 8) Очистить Docker (образы/кеш, без пересборки)" + echo " 9) Исправить все ошибки (502, /idp-api, Nginx, порты)" echo " 0) Выход" echo "" local choice - read -r -p "Выберите пункт [0-8]: " choice + read -r -p "Выберите пункт [0-9]: " choice case "$choice" in 1) NON_INTERACTIVE=0; action_full_install ;; @@ -2754,6 +2970,7 @@ show_menu() { 6) remove_nginx_configs; ok "Готово" ;; 7) action_reset_db; action_restart ;; 8) action_docker_prune ;; + 9) action_fix_all_errors ;; 0) exit 0 ;; *) warn "Неверный пункт" ;; esac @@ -2775,6 +2992,7 @@ main() { restart) action_restart ;; docker-prune) action_docker_prune ;; fix-proxy) action_fix_host_proxy ;; + fix-all) action_fix_all_errors ;; status) show_status ;; reset-db) action_reset_db ;; remove-nginx) remove_nginx_configs ;;