#!/bin/bash # Интерактивный установщик Shop # bash scripts/install.sh # sudo bash scripts/install.sh (нативная установка на Ubuntu) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # --- ввод --- read_default() { local prompt="$1" local default="$2" local value if [ -n "$default" ]; then read -rp "$prompt [$default]: " value echo "${value:-$default}" else read -rp "$prompt: " value echo "$value" fi } read_secret() { local prompt="$1" local value read -rsp "$prompt" value echo "" echo "$value" } read_secret_confirm() { local prompt="$1" local a b while true; do a=$(read_secret "$prompt") b=$(read_secret "Повторите: ") if [ "$a" = "$b" ]; then echo "$a" return fi echo "Пароли не совпадают. Попробуйте снова." done } gen_secret() { if command -v openssl >/dev/null; then openssl rand -hex 32 else head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' fi } # Безопасная запись значения в .env (одинарные кавычки) env_quote() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")" } email_ok() { [[ "$1" =~ ^[^\s@]+@[^\s@]+\.[^\s@]+$ ]] } # --- главная --- clear 2>/dev/null || true echo "============================================" echo " Shop — интерактивная установка" echo "============================================" echo "" # Каталог установки if [ -f "$REPO_ROOT/package.json" ]; then INSTALL_DIR=$(read_default "Каталог установки" "$REPO_ROOT") else INSTALL_DIR=$(read_default "Каталог установки" "/opt/shop") if [ ! -f "$INSTALL_DIR/package.json" ]; then GIT_URL=$(read_default "URL git-репозитория" "") if [ -z "$GIT_URL" ]; then echo "Ошибка: укажите URL репозитория или запустите установщик из клона." exit 1 fi echo "Клонирование $GIT_URL -> $INSTALL_DIR ..." mkdir -p "$(dirname "$INSTALL_DIR")" git clone "$GIT_URL" "$INSTALL_DIR" fi fi cd "$INSTALL_DIR" export SHOP_ROOT="$INSTALL_DIR" # Режим echo "" echo "Способ установки:" echo " 1) Docker Compose (PostgreSQL + приложение в контейнерах)" echo " 2) Без Docker (Ubuntu: Node.js + PostgreSQL + systemd)" echo "" MODE=$(read_default "Выберите [1/2]" "1") # Администратор echo "" echo "--- Администратор магазина (единственный admin) ---" ADMIN_EMAIL=$(read_default "Email администратора" "admin@site.com") while ! email_ok "$ADMIN_EMAIL"; do echo "Некорректный email." ADMIN_EMAIL=$(read_default "Email администратора" "admin@site.com") done ADMIN_NAME=$(read_default "Имя администратора" "Администратор") ADMIN_PASSWORD=$(read_secret_confirm "Пароль администратора: ") # База данных echo "" echo "--- PostgreSQL ---" PG_USER=$(read_default "Пользователь БД" "shop") PG_PASS=$(read_secret_confirm "Пароль БД: ") PG_DB=$(read_default "Имя базы данных" "shop") if [ "$MODE" = "1" ]; then PG_HOST="postgres" PG_PORT="5432" APP_PORT=$(read_default "Порт сайта на хосте" "3000") TRUST_PROXY="0" echo "" read -rp "Включить Caddy (HTTPS, порты 80/443)? [y/N]: " USE_CADDY if [[ "${USE_CADDY,,}" == "y" || "${USE_CADDY,,}" == "yes" ]]; then TRUST_PROXY="1" USE_CADDY=1 else USE_CADDY=0 fi else PG_HOST=$(read_default "Хост PostgreSQL" "127.0.0.1") PG_PORT=$(read_default "Порт PostgreSQL" "5432") APP_PORT="3000" TRUST_PROXY=$(read_default "За reverse proxy (Caddy)? TRUST_PROXY [1/0]" "1") USE_CADDY=0 fi # Сайт и секрет echo "" echo "--- Прочие настройки ---" if [ "$MODE" = "1" ] && [ "$USE_CADDY" = "1" ]; then SITE_DEFAULT="https://shop.example.com" else SITE_DEFAULT="http://localhost:${APP_PORT}" fi SITE_URL=$(read_default "URL сайта (SITE_URL)" "$SITE_DEFAULT") SESSION_SECRET=$(read_default "SESSION_SECRET (Enter = сгенерировать)" "") SESSION_SECRET=${SESSION_SECRET:-$(gen_secret)} echo "" read -rp "Настроить SMTP для писем? [y/N]: " SET_SMTP SMTP_BLOCK="" if [[ "${SET_SMTP,,}" == "y" || "${SET_SMTP,,}" == "yes" ]]; then SMTP_HOST=$(read_default "SMTP_HOST" "smtp.example.com") SMTP_PORT=$(read_default "SMTP_PORT" "587") SMTP_USER=$(read_default "SMTP_USER" "") SMTP_PASS=$(read_secret "SMTP_PASS: ") SMTP_FROM=$(read_default "SMTP_FROM" "shop@example.com") SMTP_BLOCK="# SMTP SMTP_HOST=${SMTP_HOST} SMTP_PORT=${SMTP_PORT} SMTP_SECURE=false SMTP_USER=${SMTP_USER} SMTP_PASS=${SMTP_PASS} SMTP_FROM=${SMTP_FROM} " fi DATABASE_URL="postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}" # --- запись .env --- ENV_FILE="$INSTALL_DIR/.env" APP_HOST=$([ "$MODE" = "1" ] && echo "0.0.0.0" || echo "127.0.0.1") { echo "# Создано scripts/install.sh $(date -Iseconds)" echo "" echo "PORT=${APP_PORT}" echo "HOST=${APP_HOST}" echo "NODE_ENV=production" echo "TRUST_PROXY=${TRUST_PROXY}" echo "SESSION_SECRET=$(env_quote "$SESSION_SECRET")" echo "" echo "ADMIN_EMAIL=$(env_quote "$ADMIN_EMAIL")" echo "ADMIN_PASSWORD=$(env_quote "$ADMIN_PASSWORD")" echo "ADMIN_NAME=$(env_quote "$ADMIN_NAME")" echo "" echo "SITE_URL=$(env_quote "$SITE_URL")" echo "" if [ -n "$SMTP_BLOCK" ]; then echo "$SMTP_BLOCK" fi echo "# PostgreSQL" echo "POSTGRES_USER=$(env_quote "$PG_USER")" echo "POSTGRES_PASSWORD=$(env_quote "$PG_PASS")" echo "POSTGRES_DB=$(env_quote "$PG_DB")" echo "DATABASE_URL=$(env_quote "$DATABASE_URL")" echo "PGHOST=$(env_quote "$PG_HOST")" echo "PGPORT=${PG_PORT}" echo "PGUSER=$(env_quote "$PG_USER")" echo "PGPASSWORD=$(env_quote "$PG_PASS")" echo "PGDATABASE=$(env_quote "$PG_DB")" } > "$ENV_FILE" chmod 600 "$ENV_FILE" 2>/dev/null || true echo "" echo "Сохранено: $ENV_FILE" # --- установка --- echo "" if [ "$MODE" = "1" ]; then echo "=== Установка через Docker ===" if ! command -v docker >/dev/null; then echo "Ошибка: Docker не установлен. Установите Docker и повторите." exit 1 fi if ! docker compose version >/dev/null 2>&1; then echo "Ошибка: нужен Docker Compose v2 (docker compose)." exit 1 fi COMPOSE_CMD=(docker compose) if [ "$USE_CADDY" = "1" ]; then echo "Запуск: postgres + app + caddy ..." "${COMPOSE_CMD[@]}" --profile proxy up -d --build else echo "Запуск: postgres + app ..." "${COMPOSE_CMD[@]}" up -d --build fi echo "Ожидание health..." sleep 5 curl -sf "http://127.0.0.1:${APP_PORT}/health" && echo "" || echo "Проверьте: docker compose logs app" else echo "=== Установка без Docker (Ubuntu) ===" if [ "$(id -u)" -ne 0 ]; then echo "Запустите с root: sudo bash scripts/install.sh" exit 1 fi bash "$SCRIPT_DIR/install-postgresql-ubuntu.sh" export DB_USER="$PG_USER" DB_PASS="$PG_PASS" DB_NAME="$PG_DB" bash "$SCRIPT_DIR/setup-postgres-ubuntu.sh" npm install --omit=dev bash "$SCRIPT_DIR/install-shop-service.sh" fi echo "" echo "============================================" echo " Установка завершена" echo "============================================" echo " Каталог: $INSTALL_DIR" echo " Сайт: $SITE_URL" echo " Админ: $ADMIN_EMAIL" if [ "$MODE" = "1" ]; then echo " Порт: $APP_PORT" echo " Логи: docker compose -f $INSTALL_DIR/docker-compose.yml logs -f" else echo " Служба: systemctl status shop" echo " Health: curl http://127.0.0.1:3000/health" fi echo " Обновление: bash $INSTALL_DIR/scripts/server-update.sh" echo "============================================"