feat: интерактивный установщик install.sh (Docker / Ubuntu, админ, БД)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+257
@@ -0,0 +1,257 @@
|
||||
#!/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 "============================================"
|
||||
Reference in New Issue
Block a user