diff --git a/.gitignore b/.gitignore index 0cdc8bf..951b926 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist !.env.example docker-compose.override.yml .idp-install.json +.idp-infra.secrets nginx/certs/* !nginx/certs/.gitkeep nginx/conf.d/*.conf diff --git a/apps/frontend/app/auth/login/page.tsx b/apps/frontend/app/auth/login/page.tsx index a28396a..cc77a6a 100644 --- a/apps/frontend/app/auth/login/page.tsx +++ b/apps/frontend/app/auth/login/page.tsx @@ -358,7 +358,6 @@ export default function LoginPage() { ) : null} -

Узнать больше

); diff --git a/apps/frontend/app/auth/register/page.tsx b/apps/frontend/app/auth/register/page.tsx index 845b221..86aede6 100644 --- a/apps/frontend/app/auth/register/page.tsx +++ b/apps/frontend/app/auth/register/page.tsx @@ -69,10 +69,6 @@ export default function RegisterPage() { {isSubmitting ? 'Отправляем...' : 'Получить код'} -
- -

Первый зарегистрированный пользователь автоматически станет суперадминистратором.

-
diff --git a/apps/frontend/components/id/sidebar.tsx b/apps/frontend/components/id/sidebar.tsx index 5569887..6e2048a 100644 --- a/apps/frontend/components/id/sidebar.tsx +++ b/apps/frontend/components/id/sidebar.tsx @@ -48,9 +48,7 @@ export function Sidebar({ active }: { active: string }) { Выйти -

Русский

-

Справка

-

© 2026 Lendry

+

© 2026

); diff --git a/install.sh b/install.sh index 6fb4f94..53ed836 100644 --- a/install.sh +++ b/install.sh @@ -22,6 +22,7 @@ LOCAL_CONF_DIR="${ROOT_DIR}/nginx/conf.d" LOCAL_CERTS_DIR="${ROOT_DIR}/nginx/certs" COMPOSE_OVERRIDE="${ROOT_DIR}/docker-compose.override.yml" STATE_FILE="${ROOT_DIR}/.idp-install.json" +INFRA_SECRETS_FILE="${ROOT_DIR}/.idp-infra.secrets" NON_INTERACTIVE=0 FORCE_ENV="${FORCE_ENV:-0}" @@ -62,6 +63,7 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл ./install.sh --renew-ssl Обновить сертификаты Let's Encrypt ./install.sh --restart Перезапуск контейнеров ./install.sh --status Статус сервисов + ./install.sh --reset-db Сброс PostgreSQL (новый пароль из .env, данные удалятся) ./install.sh --remove-nginx Удалить конфиги Nginx IdP ${BOLD}Режим локальной сети (без интернета / AD-домены .local .lpr):${NC} @@ -85,7 +87,7 @@ ${BOLD}Прочие опции:${NC} -h, --help Справка ${BOLD}Переменные окружения:${NC} - FORCE_ENV=1 Пересоздать .env (секреты будут новые) + FORCE_ENV=1 Новые JWT-секреты (пароли БД/MinIO/RabbitMQ сохраняются если volume уже есть) OFFLINE=1 То же, что --offline ${BOLD}Примеры:${NC} @@ -117,7 +119,7 @@ parse_args() { --renew-ssl) ACTION="renew-ssl"; shift ;; --restart) ACTION="restart"; shift ;; --status) ACTION="status"; shift ;; - --remove-nginx) ACTION="remove-nginx"; shift ;; + --reset-db) ACTION="reset-db"; shift ;; --local) INSTALL_LOCAL=1; shift ;; --intranet) INSTALL_INTRANET=1; shift ;; --offline) OFFLINE=1; shift ;; @@ -637,17 +639,139 @@ write_env_secrets_if_missing() { cp .env.example .env 2>/dev/null || touch .env fi - if [[ "$FORCE_ENV" == "1" ]] || [[ "$(env_get POSTGRES_PASSWORD change-me-postgres)" == "change-me-postgres" ]]; then + load_infra_secrets_into_env + + if docker_volume_exists postgres_data; then + if [[ -n "$(env_get POSTGRES_PASSWORD "")" ]]; then + warn "Volume PostgreSQL уже существует — POSTGRES_PASSWORD в .env не меняем" + else + fail "Volume PostgreSQL существует, но POSTGRES_PASSWORD не найден. Восстановите .env или выполните: ./install.sh --reset-db" + fi + pg_pass="$(env_get POSTGRES_PASSWORD "$pg_pass")" + fi + + if docker_volume_exists rabbitmq_data; then + warn "Volume RabbitMQ уже существует — RABBITMQ_DEFAULT_PASS не меняем" + rmq_pass="$(env_get RABBITMQ_DEFAULT_PASS "$rmq_pass")" + fi + + if docker_volume_exists minio_data; then + warn "Volume MinIO уже существует — MINIO_ROOT_PASSWORD не меняем" + minio_pass="$(env_get MINIO_ROOT_PASSWORD "$minio_pass")" + fi + + local need_secrets=0 + if [[ "$FORCE_ENV" == "1" ]]; then + need_secrets=1 + elif [[ "$(env_get POSTGRES_PASSWORD change-me-postgres)" == "change-me-postgres" ]]; then + need_secrets=1 + elif [[ "$(env_get JWT_ACCESS_SECRET change-me-access-secret)" == "change-me-access-secret" ]]; then + need_secrets=1 + fi + + if [[ "$need_secrets" -eq 1 ]]; then env_set POSTGRES_PASSWORD "$pg_pass" env_set RABBITMQ_DEFAULT_PASS "$rmq_pass" env_set MINIO_ROOT_PASSWORD "$minio_pass" env_set JWT_ACCESS_SECRET "$jwt_access" env_set JWT_REFRESH_SECRET "$jwt_refresh" env_set DATA_ENCRYPTION_KEY "$data_key" - ok "Секреты сгенерированы" + save_infra_secrets + ok "Секреты сгенерированы / синхронизированы" fi } +compose_project_name() { + load_env + echo "${COMPOSE_PROJECT_NAME:-lendry-id}" +} + +docker_volume_name() { + echo "$(compose_project_name)_$1" +} + +docker_volume_exists() { + docker volume inspect "$(docker_volume_name "$1")" >/dev/null 2>&1 +} + +save_infra_secrets() { + load_env + cat >"$INFRA_SECRETS_FILE" </dev/null || true +} + +load_infra_secrets_into_env() { + [[ -f "$INFRA_SECRETS_FILE" ]] || return 0 + # shellcheck disable=SC1091 + set -a && source "$INFRA_SECRETS_FILE" && set +a + [[ -n "${POSTGRES_PASSWORD:-}" ]] && env_set POSTGRES_PASSWORD "$POSTGRES_PASSWORD" + [[ -n "${RABBITMQ_DEFAULT_PASS:-}" ]] && env_set RABBITMQ_DEFAULT_PASS "$RABBITMQ_DEFAULT_PASS" + [[ -n "${MINIO_ROOT_PASSWORD:-}" ]] && env_set MINIO_ROOT_PASSWORD "$MINIO_ROOT_PASSWORD" +} + +verify_postgres_auth() { + load_env + load_infra_secrets_into_env + [[ -f .env ]] || return 0 + docker_cmd compose ps postgres 2>/dev/null | grep -q postgres || return 0 + + local user pass db + user="$(env_get POSTGRES_USER lendry)" + pass="$(env_get POSTGRES_PASSWORD "")" + db="$(env_get POSTGRES_DB lendry_id)" + [[ -n "$pass" ]] || return 0 + + if docker_cmd compose exec -T -e PGPASSWORD="$pass" postgres \ + psql -U "$user" -d "$db" -c 'SELECT 1' >/dev/null 2>&1; then + ok "PostgreSQL: пароль из .env подходит" + save_infra_secrets + return 0 + fi + + echo "" + fail "PostgreSQL: пароль в .env не совпадает с данными в volume (ошибка P1000). + + Это происходит после FORCE_ENV=1 или смены POSTGRES_PASSWORD при существующей БД. + + Решения: + 1) Сбросить БД (удалит всех пользователей и данные): + ./install.sh --reset-db --yes && ./install.sh --restart + + 2) Вернуть старый POSTGRES_PASSWORD в .env (если сохранили) + + 3) Если есть файл ${INFRA_SECRETS_FILE} — скопируйте пароль оттуда в .env" +} + +action_reset_db() { + load_env + local vol + vol="$(docker_volume_name postgres_data)" + + if [[ "$NON_INTERACTIVE" -ne 1 ]]; then + echo "" + warn "Будет удалён volume ${vol} — все пользователи, сессии и настройки в PostgreSQL пропадут." + read -r -p "Продолжить? [y/N]: " confirm + [[ "$confirm" =~ ^[Yy]$ ]] || fail "Отменено" + fi + + log "Остановка сервисов..." + docker_cmd compose --env-file .env --profile proxy down 2>/dev/null || docker_cmd compose --env-file .env down 2>/dev/null || true + + log "Удаление ${vol}..." + if docker volume inspect "$vol" >/dev/null 2>&1; then + docker volume rm "$vol" || fail "Не удалось удалить ${vol}" + else + warn "Volume ${vol} не найден — возможно, уже удалён" + fi + + ok "PostgreSQL volume удалён. PostgreSQL создастся заново с паролем из .env." + save_infra_secrets +} + persist_domain_env() { write_env_secrets_if_missing @@ -1319,6 +1443,7 @@ deploy_stack() { write_compose_override load_env + verify_postgres_auth local compose_cmd=(compose --env-file .env) if [[ -n "$(env_get DOMAIN_API "")" && "$(env_get INSTALL_MODE local)" != "local" ]]; then @@ -1495,11 +1620,12 @@ show_menu() { echo " 4) Перезапуск / пересборка контейнеров" echo " 5) Статус сервисов" echo " 6) Удалить конфиги Nginx IdP" + echo " 7) Сброс PostgreSQL (если ошибка P1000 / пароль БД)" echo " 0) Выход" echo "" local choice - read -r -p "Выберите пункт [0-6]: " choice + read -r -p "Выберите пункт [0-7]: " choice case "$choice" in 1) NON_INTERACTIVE=0; action_full_install ;; @@ -1508,6 +1634,7 @@ show_menu() { 4) action_restart ;; 5) show_status ;; 6) remove_nginx_configs; ok "Готово" ;; + 7) action_reset_db; action_restart ;; 0) exit 0 ;; *) warn "Неверный пункт" ;; esac @@ -1528,6 +1655,7 @@ main() { renew-ssl) action_renew_ssl ;; restart) action_restart ;; status) show_status ;; + reset-db) action_reset_db ;; remove-nginx) remove_nginx_configs ;; "") if [[ -f .env ]] && [[ "$NON_INTERACTIVE" -ne 1 ]]; then