#!/usr/bin/env bash set -euo pipefail REPO="lendry/sso-mvk" GIT_HOST="git.lendry.ru" BRANCH="main" INSTALL_DIR="${INSTALL_DIR:-$HOME/sso-mvk}" SKIP_PREREQS="${SKIP_PREREQS:-false}" 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"; } warn() { echo -e "${YELLOW}⚠${NC} $1"; } error() { echo -e "${RED}✘${NC} $1"; exit 1; } step() { echo -e "\n${CYAN}━━━ $1 ━━━${NC}"; } echo -e "${CYAN}" echo " ╔══════════════════════════════════════════════╗" echo " ║ SSO Service — Auto Installer ║" echo " ║ https://${GIT_HOST}/${REPO} ║" echo " ╚══════════════════════════════════════════════╝" echo -e "${NC}" # ─── Проверка root ────────────────────────────────────────────── if [ "$(id -u)" -eq 0 ]; then SUDO="" else SUDO="sudo" fi # ─── Detecting OS ──────────────────────────────────────────────── step "Detecting operating system" OS="" if [ -f /etc/os-release ]; then . /etc/os-release OS="$ID" elif command -v lsb_release &>/dev/null; then OS="$(lsb_release -si | tr '[:upper:]' '[:lower:]')" else OS="$(uname -s)" fi info "Detected: ${OS} ($(uname -m))" # ─── 1. Docker ────────────────────────────────────────────────── install_docker() { step "1/8 Installing Docker" if command -v docker &>/dev/null; then info "Docker already installed ($($SUDO docker --version 2>/dev/null || docker --version))" else warn "Docker not found — installing..." if command -v apt-get &>/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 else error "Unsupported package manager. Install Docker manually: https://docs.docker.com/engine/install/" fi $SUDO usermod -aG docker "$USER" 2>/dev/null || true info "Docker installed." fi # Ensure Docker daemon is running if ! $SUDO docker info &>/dev/null; then warn "Starting Docker daemon..." $SUDO systemctl enable --now docker 2>/dev/null || $SUDO dockerd &>/dev/null & sleep 3 fi info "Docker daemon is running" if ! $SUDO docker compose version &>/dev/null; then warn "Docker Compose not found — installing plugin..." $SUDO mkdir -p /usr/local/lib/docker/cli-plugins $SUDO curl -fsSL "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/lib/docker/cli-plugins/docker-compose $SUDO chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi info "Docker Compose: $($SUDO docker compose version 2>&1)" } # ─── 2. Node.js ───────────────────────────────────────────────── install_node() { step "2/8 Installing Node.js" if command -v node &>/dev/null; then NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1) if [ "$NODE_MAJOR" -lt 22 ]; then warn "Node.js $(node -v) is too old — upgrading to v22..." else info "Node.js $(node -v) detected" return 0 fi fi if ! command -v node &>/dev/null || [ "$NODE_MAJOR" -lt 22 ]; then warn "Installing Node.js 22 LTS..." if command -v apt-get &>/dev/null; then curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO bash - $SUDO apt-get install -y nodejs elif command -v dnf &>/dev/null; then curl -fsSL https://rpm.nodesource.com/setup_22.x | $SUDO bash - $SUDO dnf install -y nodejs elif command -v yum &>/dev/null; then curl -fsSL https://rpm.nodesource.com/setup_22.x | $SUDO bash - $SUDO yum install -y nodejs else warn "Package manager not recognised — trying nvm..." curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" nvm install 22 nvm alias default 22 fi fi info "Node.js $(node -v) ready" } # ─── 3. Install prerequisites if needed ────────────────────────── if [ "$SKIP_PREREQS" != "true" ]; then install_docker install_node else step "1-2/8 Skipping prerequisites (SKIP_PREREQS=true)" fi # ─── 4. Download project ───────────────────────────────────────── step "3/8 Downloading SSO Service" if [ -d "$INSTALL_DIR/.git" ]; then cd "$INSTALL_DIR" git pull --ff-only 2>/dev/null && info "Updated existing installation" || warn "Could not git pull, continuing" else mkdir -p "$INSTALL_DIR" if command -v git &>/dev/null; then git clone --depth 1 "https://$GIT_HOST/$REPO.git" "$INSTALL_DIR" 2>&1 else warn "git not found, downloading archive..." curl -fsSL "https://$GIT_HOST/$REPO/archive/$BRANCH.tar.gz" | tar xz --strip=1 -C "$INSTALL_DIR" fi info "Downloaded to $INSTALL_DIR" fi cd "$INSTALL_DIR" # ─── 5. .env ───────────────────────────────────────────────────── step "4/8 Configuring environment" if [ ! -f .env ]; then 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_REFRESH=$(openssl rand -hex 32 2>/dev/null || node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))") if [ "$(uname -s)" = "Darwin" ]; then sed -i '' "s/change-me-access-secret-at-least-32-chars/$JWT_ACCESS/" .env sed -i '' "s/change-me-refresh-secret-at-least-32-chars/$JWT_REFRESH/" .env else sed -i "s/change-me-access-secret-at-least-32-chars/$JWT_ACCESS/" .env sed -i "s/change-me-refresh-secret-at-least-32-chars/$JWT_REFRESH/" .env fi info "Created .env with random JWT secrets" else info ".env already exists, keeping it" fi set -a; source .env; set +a # ─── 6. Docker infrastructure ──────────────────────────────────── 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." fi info "Waiting for PostgreSQL..." 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 info "PostgreSQL is ready" break fi if [ "$i" -eq 30 ]; then warn "PostgreSQL did not become ready — showing last logs:" $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." fi sleep 2 done info "Waiting for Redis..." for i in $(seq 1 15); do if $SUDO docker compose exec -T redis redis-cli ping 2>/dev/null | grep -q PONG; then info "Redis is ready" break fi if [ "$i" -eq 15 ]; then warn "Redis may not be ready yet, continuing..." fi sleep 2 done # ─── 7. Dependencies & Prisma ──────────────────────────────────── step "6/8 Installing dependencies" npm ci --no-audit --no-fund || error "'npm ci' failed. Check network / ${INSTALL_DIR}/package-lock.json" info "npm dependencies installed" npx prisma generate || error "'prisma generate' failed. Check Prisma schema." info "Prisma client generated" export DATABASE_URL npx prisma db push --skip-generate || error "'prisma db push' failed. Is PostgreSQL running? Check connection details in ${INSTALL_DIR}/.env" info "Database schema applied" # ─── 8. Seed ───────────────────────────────────────────────────── step "7/8 Seeding database" npx ts-node -P tsconfig.json prisma/seed.ts || error "Seed failed. Check database connection." info "Seed data created" # ─── 9. Build & Start ──────────────────────────────────────────── step "8/8 Building and starting SSO Service" npm run build || error "'npm run build' failed. Check for TypeScript errors above." info "Application built" 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 save 2>/dev/null || true info "Started with PM2" else PORT="${PORT:-3000}" nohup node dist/main > "$INSTALL_DIR/sso.log" 2>&1 & echo $! > "$INSTALL_DIR/.sso.pid" info "Started in background (PID $(cat "$INSTALL_DIR/.sso.pid")) — logs: $INSTALL_DIR/sso.log" fi sleep 3 if curl -sfo /dev/null "http://localhost:${PORT:-3000}/metrics" 2>/dev/null; then info "Service is running on http://localhost:${PORT:-3000}" else warn "Service may still be starting... check: $INSTALL_DIR/sso.log" fi # ─── Done ──────────────────────────────────────────────────────── echo "" echo -e "${GREEN}════════════════════════════════════════════════${NC}" echo -e "${GREEN} SSO Service installed and running!${NC}" echo -e "${GREEN}════════════════════════════════════════════════${NC}" echo "" echo -e " ${CYAN}Web panel:${NC} http://localhost:${PORT:-3000}" echo -e " ${CYAN}Admin login:${NC} admin@sso.local / admin123!" echo -e " ${CYAN}Metrics:${NC} http://localhost:${PORT:-3000}/metrics" echo -e " ${CYAN}RabbitMQ UI:${NC} http://localhost:15672 (${RABBITMQ_USER:-sso} / ${RABBITMQ_PASSWORD:-sso_secret})" echo "" echo -e " ${YELLOW}Install dir:${NC} $INSTALL_DIR" echo -e " ${YELLOW}Log file:${NC} $INSTALL_DIR/sso.log" echo "" echo -e " ${YELLOW}Stop:${NC} $(command -v pm2 &>/dev/null && echo 'pm2 stop sso-service' || echo "kill \$(cat $INSTALL_DIR/.sso.pid)")" echo -e " ${YELLOW}Update:${NC} cd $INSTALL_DIR && git pull && npm ci && npm run build && npm run start:prod" echo -e " ${YELLOW}Re-run:${NC} curl -fsSL https://${GIT_HOST}/${REPO}/raw/branch/${BRANCH}/install.sh | bash" echo "" echo -e " ${GREEN}Open http://localhost:${PORT:-3000} in your browser${NC}" echo ""