This commit is contained in:
Дмитрий Мамедов
2026-06-10 17:17:29 +03:00
parent 4eb9c82b72
commit cd93ddc640

View File

@@ -6,6 +6,7 @@ GIT_HOST="git.lendry.ru"
BRANCH="main" BRANCH="main"
INSTALL_DIR="${INSTALL_DIR:-$HOME/sso-mvk}" INSTALL_DIR="${INSTALL_DIR:-$HOME/sso-mvk}"
SKIP_PREREQS="${SKIP_PREREQS:-false}" SKIP_PREREQS="${SKIP_PREREQS:-false}"
PROGRESS_FILE="$INSTALL_DIR/.install-progress"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${GREEN}${NC} $1"; } info() { echo -e "${GREEN}${NC} $1"; }
@@ -13,6 +14,10 @@ warn() { echo -e "${YELLOW}⚠${NC} $1"; }
error() { echo -e "${RED}${NC} $1"; exit 1; } error() { echo -e "${RED}${NC} $1"; exit 1; }
step() { echo -e "\n${CYAN}━━━ $1 ━━━${NC}"; } step() { echo -e "\n${CYAN}━━━ $1 ━━━${NC}"; }
# ─── Mark progress ──────────────────────────────────────────────
mark_done() { echo "$1" >> "$PROGRESS_FILE"; }
is_done() { grep -qx "$1" "$PROGRESS_FILE" 2>/dev/null; }
echo -e "${CYAN}" echo -e "${CYAN}"
echo " ╔══════════════════════════════════════════════╗" echo " ╔══════════════════════════════════════════════╗"
echo " ║ SSO Service — Auto Installer ║" echo " ║ SSO Service — Auto Installer ║"
@@ -47,11 +52,7 @@ install_docker() {
info "Docker already installed ($($SUDO docker --version 2>/dev/null || docker --version))" info "Docker already installed ($($SUDO docker --version 2>/dev/null || docker --version))"
else else
warn "Docker not found — installing..." warn "Docker not found — installing..."
if command -v apt-get &>/dev/null; then if command -v apt-get &>/dev/null || command -v yum &>/dev/null || command -v dnf &>/dev/null; then
curl -fsSL https://get.docker.com | $SUDO sh
elif command -v yum &>/dev/null; then
curl -fsSL https://get.docker.com | $SUDO sh
elif command -v dnf &>/dev/null; then
curl -fsSL https://get.docker.com | $SUDO sh curl -fsSL https://get.docker.com | $SUDO sh
else else
error "Unsupported package manager. Install Docker manually: https://docs.docker.com/engine/install/" error "Unsupported package manager. Install Docker manually: https://docs.docker.com/engine/install/"
@@ -60,14 +61,25 @@ install_docker() {
info "Docker installed." info "Docker installed."
fi fi
# Ensure Docker daemon is running # Ждём запуска Docker daemon (до 30 секунд)
if ! $SUDO docker info &>/dev/null; then if ! $SUDO docker info &>/dev/null; then
warn "Starting Docker daemon..." warn "Docker daemon not running — starting..."
$SUDO systemctl enable --now docker 2>/dev/null || $SUDO dockerd &>/dev/null & $SUDO systemctl enable --now docker 2>/dev/null || $SUDO dockerd &>/dev/null &
sleep 3 for i in $(seq 1 15); do
sleep 2
if $SUDO docker info &>/dev/null; then
info "Docker daemon started"
break
fi fi
if [ "$i" -eq 15 ]; then
error "Docker daemon did not start. Check: $SUDO systemctl status docker"
fi
done
else
info "Docker daemon is running" info "Docker daemon is running"
fi
# Docker Compose plugin
if ! $SUDO docker compose version &>/dev/null; then if ! $SUDO docker compose version &>/dev/null; then
warn "Docker Compose not found — installing plugin..." warn "Docker Compose not found — installing plugin..."
$SUDO mkdir -p /usr/local/lib/docker/cli-plugins $SUDO mkdir -p /usr/local/lib/docker/cli-plugins
@@ -82,15 +94,13 @@ install_node() {
step "2/8 Installing Node.js" step "2/8 Installing Node.js"
if command -v node &>/dev/null; then if command -v node &>/dev/null; then
NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1) NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_MAJOR" -lt 22 ]; then if [ "$NODE_MAJOR" -ge 22 ]; then
warn "Node.js $(node -v) is too old — upgrading to v22..."
else
info "Node.js $(node -v) detected" info "Node.js $(node -v) detected"
return 0 return 0
fi fi
warn "Node.js $(node -v) is too old — upgrading to v22..."
fi fi
if ! command -v node &>/dev/null || [ "$NODE_MAJOR" -lt 22 ]; then
warn "Installing Node.js 22 LTS..." warn "Installing Node.js 22 LTS..."
if command -v apt-get &>/dev/null; then if command -v apt-get &>/dev/null; then
curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO bash - curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO bash -
@@ -109,11 +119,9 @@ install_node() {
nvm install 22 nvm install 22
nvm alias default 22 nvm alias default 22
fi fi
fi
info "Node.js $(node -v) ready" info "Node.js $(node -v) ready"
} }
# ─── 3. Install prerequisites if needed ──────────────────────────
if [ "$SKIP_PREREQS" != "true" ]; then if [ "$SKIP_PREREQS" != "true" ]; then
install_docker install_docker
install_node install_node
@@ -121,12 +129,13 @@ else
step "1-2/8 Skipping prerequisites (SKIP_PREREQS=true)" step "1-2/8 Skipping prerequisites (SKIP_PREREQS=true)"
fi fi
# ─── 4. Download project ───────────────────────────────────────── # ─── 3. Download project ─────────────────────────────────────────
step "3/8 Downloading SSO Service" step "3/8 Downloading SSO Service"
if [ -d "$INSTALL_DIR/.git" ]; then if ! is_done "download"; then
if [ -d "$INSTALL_DIR/.git" ]; then
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
git pull --ff-only 2>/dev/null && info "Updated existing installation" || warn "Could not git pull, continuing" git pull --ff-only 2>/dev/null && info "Updated existing installation" || warn "Could not git pull, continuing"
else else
mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR"
if command -v git &>/dev/null; then if command -v git &>/dev/null; then
git clone --depth 1 "https://$GIT_HOST/$REPO.git" "$INSTALL_DIR" 2>&1 git clone --depth 1 "https://$GIT_HOST/$REPO.git" "$INSTALL_DIR" 2>&1
@@ -135,14 +144,19 @@ else
curl -fsSL "https://$GIT_HOST/$REPO/archive/$BRANCH.tar.gz" | tar xz --strip=1 -C "$INSTALL_DIR" curl -fsSL "https://$GIT_HOST/$REPO/archive/$BRANCH.tar.gz" | tar xz --strip=1 -C "$INSTALL_DIR"
fi fi
info "Downloaded to $INSTALL_DIR" info "Downloaded to $INSTALL_DIR"
fi
cd "$INSTALL_DIR"
mark_done "download"
else
cd "$INSTALL_DIR"
info "Already downloaded"
fi fi
cd "$INSTALL_DIR"
# ─── 5. .env ───────────────────────────────────────────────────── # ─── 4. .env ─────────────────────────────────────────────────────
step "4/8 Configuring environment" step "4/8 Configuring environment"
if [ ! -f .env ]; then if ! is_done "env"; then
if [ ! -f .env ]; then
cp .env.example .env cp .env.example .env
# Генерация случайных JWT-секретов (32 байта hex = 64 символа)
JWT_ACCESS=$(openssl rand -hex 32 2>/dev/null || node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))") JWT_ACCESS=$(openssl rand -hex 32 2>/dev/null || node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))")
JWT_REFRESH=$(openssl rand -hex 32 2>/dev/null || node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))") JWT_REFRESH=$(openssl rand -hex 32 2>/dev/null || node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))")
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
@@ -153,29 +167,47 @@ if [ ! -f .env ]; then
sed -i "s/change-me-refresh-secret-at-least-32-chars/$JWT_REFRESH/" .env sed -i "s/change-me-refresh-secret-at-least-32-chars/$JWT_REFRESH/" .env
fi fi
info "Created .env with random JWT secrets" info "Created .env with random JWT secrets"
else else
info ".env already exists, keeping it" info ".env already exists, keeping it"
fi
mark_done "env"
else
info "Environment already configured"
fi fi
set -a; source .env; set +a set -a; source .env; set +a
# ─── 6. Docker infrastructure ──────────────────────────────────── # ─── 5. Docker infrastructure ────────────────────────────────────
step "5/8 Starting infrastructure (PostgreSQL, Redis, RabbitMQ)" step "5/8 Starting infrastructure (PostgreSQL, Redis, RabbitMQ)"
if ! $SUDO docker compose up -d; then
$SUDO docker compose logs 2>/dev/null | tail -20 # Проверка занятости портов
error "Docker containers failed to start. Check '${INSTALL_DIR}/docker-compose.yml' and run '${SUDO}docker compose logs' manually." for port in 5432 6379 5672 15672; do
if ss -tlnp "sport = :$port" 2>/dev/null | grep -q .; then
warn "Port $port is already in use — service may conflict with existing postgres/redis/rabbitmq"
fi
done
# Остановить предыдущие контейнеры (если есть), потом запустить заново
$SUDO docker compose down --remove-orphans 2>/dev/null || true
if ! $SUDO docker compose up -d --force-recreate --remove-orphans; then
warn "Docker Compose output:"
$SUDO docker compose logs --tail=30 2>/dev/null || true
error "Containers failed to start. Check ports and run: ${SUDO}docker compose logs"
fi fi
info "Waiting for PostgreSQL..." info "Waiting for PostgreSQL (up to 60s)..."
DB_READY=false
for i in $(seq 1 30); do for i in $(seq 1 30); do
if $SUDO docker compose exec -T postgres pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" &>/dev/null; then if $SUDO docker compose exec -T postgres pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" &>/dev/null; then
DB_READY=true
info "PostgreSQL is ready" info "PostgreSQL is ready"
break break
fi fi
if [ "$i" -eq 30 ]; then if [ "$i" -eq 30 ]; then
warn "PostgreSQL did not become ready — showing last logs:" warn "PostgreSQL logs (last 20 lines):"
$SUDO docker compose logs --tail=20 postgres 2>/dev/null || true $SUDO docker compose logs --tail=20 postgres 2>/dev/null || true
error "PostgreSQL did not start in time. Fix the issue and re-run the script." error "PostgreSQL did not start. Check ${INSTALL_DIR}/.env for DB credentials and port 5432 availability."
fi fi
sleep 2 sleep 2
done done
@@ -187,35 +219,41 @@ for i in $(seq 1 15); do
break break
fi fi
if [ "$i" -eq 15 ]; then if [ "$i" -eq 15 ]; then
warn "Redis may not be ready yet, continuing..." warn "Redis did not respond — continuing anyway..."
fi fi
sleep 2 sleep 2
done done
# ─── 7. Dependencies & Prisma ──────────────────────────────────── # ─── 6. Dependencies & Prisma ────────────────────────────────────
step "6/8 Installing dependencies" step "6/8 Installing dependencies"
npm ci --no-audit --no-fund || error "'npm ci' failed. Check network / ${INSTALL_DIR}/package-lock.json" npm ci --no-audit --no-fund || error "'npm ci' failed. Check network / ${INSTALL_DIR}/package-lock.json"
info "npm dependencies installed" info "npm dependencies installed"
npx prisma generate || error "'prisma generate' failed. Check Prisma schema." npx prisma generate || error "'prisma generate' failed."
info "Prisma client generated" info "Prisma client generated"
export DATABASE_URL export DATABASE_URL
npx prisma db push --skip-generate || error "'prisma db push' failed. Is PostgreSQL running? Check connection details in ${INSTALL_DIR}/.env" npx prisma db push --skip-generate || error "'prisma db push' failed. Is PostgreSQL accessible? Check ${INSTALL_DIR}/.env DATABASE_URL"
info "Database schema applied" info "Database schema applied"
# ─── 8. Seed ───────────────────────────────────────────────────── # ─── 7. Seed ─────────────────────────────────────────────────────
step "7/8 Seeding database" step "7/8 Seeding database"
npx ts-node -P tsconfig.json prisma/seed.ts || error "Seed failed. Check database connection." npx ts-node -P tsconfig.json prisma/seed.ts || error "Seed failed."
info "Seed data created" info "Seed data created"
# ─── 9. Build & Start ──────────────────────────────────────────── # ─── 8. Build & Start ────────────────────────────────────────────
step "8/8 Building and starting SSO Service" step "8/8 Building and starting SSO Service"
npm run build || error "'npm run build' failed. Check for TypeScript errors above." npm run build || error "'npm run build' failed. Check TypeScript errors above."
info "Application built" info "Application built"
# Остановить предыдущий процесс, если был
if [ -f "$INSTALL_DIR/.sso.pid" ]; then
kill "$(cat "$INSTALL_DIR/.sso.pid")" 2>/dev/null || true
rm -f "$INSTALL_DIR/.sso.pid"
fi
pm2 delete sso-service 2>/dev/null || true
if command -v pm2 &>/dev/null; then if command -v pm2 &>/dev/null; then
pm2 delete sso-service 2>/dev/null || true
pm2 start npm --name "sso-service" -- run start:prod pm2 start npm --name "sso-service" -- run start:prod
pm2 save 2>/dev/null || true pm2 save 2>/dev/null || true
info "Started with PM2" info "Started with PM2"