Files
telegram-shop/install.sh
NW 4aea49811c feat: multi-architecture Docker setup (x86_64 + ARM64) with one-command install
- Multi-stage Dockerfile: builder compiles native modules (better-sqlite3,
  tiny-secp256k1) under target architecture, runtime is minimal Alpine
- install.sh: POSIX sh installer (Alpine ash compatible) with architecture
  detection, Docker install, .env validation, health-check retry loop
- docker-compose.yml: removed platform locks, .env read-only mount,
  127.0.0.1 port binding, 384m mem limit (Orange Pi Zero 2 safe)
- .dockerignore: excludes node_modules, secrets, tests, .kilo
- README.md: complete rewrite with deployment docs for any device
- Verified: POSIX sh syntax (dash), Dockerfile (docker build --check),
  docker-compose (docker compose config)
2026-06-24 02:06:07 +01:00

274 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -euo pipefail
# ============================================================
# Telegram Shop — установщик
# Скрипт для развёртывания на x86_64 и ARM64 (Orange Pi, RPi)
# Совместим с POSIX sh (Alpine ash)
# ============================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
print_header() {
printf "${CYAN}${BOLD}\n"
printf "╔═══════════════════════════════════════════════════════════════╗\n"
printf "║ Telegram Shop — Установщик v1.1 ║\n"
printf "║ Поддержка x86_64 и ARM64 (Orange Pi, Raspberry Pi) ║\n"
printf "╚═══════════════════════════════════════════════════════════════╝\n"
printf "${NC}\n"
}
print_ok() { printf "${GREEN}${NC} %s\n" "$1"; }
print_warn() { printf "${YELLOW}${NC} %s\n" "$1"; }
print_err() { printf "${RED}${NC} %s\n" "$1"; }
print_info() { printf "${BLUE}${NC} %s\n" "$1"; }
print_step() { printf "\n${CYAN}${BOLD}▶ %s${NC}\n" "$1"; }
trap 'print_err "Установка прервана ошибкой (строка $LINENO)"; exit 1' ERR
# ============================================================
# 0. Определение окружения
# ============================================================
print_header
print_step "Определение окружения"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH_NAME="x86_64 (Intel/AMD)" ;;
aarch64|arm64) ARCH_NAME="ARM64 (Orange Pi / RPi)" ;;
armv7l) ARCH_NAME="ARMv7 (Raspberry Pi 2)" ;;
*)
print_err "Неподдерживаемая архитектура: $ARCH"
exit 1
;;
esac
print_ok "Архитектура: $ARCH_NAME"
print_info "Docker автоматически соберёт образ под текущую архитектуру"
# ============================================================
# 1. Проверка / установка Docker
# ============================================================
print_step "Проверка Docker"
DOCKER_MISSING=0
if ! command -v docker >/dev/null 2>&1; then
DOCKER_MISSING=1
elif ! docker version >/dev/null 2>&1; then
DOCKER_MISSING=1
fi
if [ "$DOCKER_MISSING" -eq 1 ]; then
print_warn "Docker не установлен или не запущен"
printf "\n"
printf "Установить Docker сейчас? (y/N): "
read -r INSTALL_DOCKER
case "$INSTALL_DOCKER" in
[Yy]|[Yy][Ee][Ss])
;;
*)
print_err "Docker обязателен. Установите вручную: https://docs.docker.com/engine/install/"
exit 1
;;
esac
print_info "Установка Docker..."
if command -v apk >/dev/null 2>&1; then
# Alpine
apk add --no-cache docker docker-cli-compose
rc-service docker start 2>/dev/null || service docker start 2>/dev/null || true
if ! docker version >/dev/null 2>&1; then
print_err "Docker не запустился. Запустите вручную: rc-service docker start"
exit 1
fi
elif command -v apt-get >/dev/null 2>&1; then
# Debian / Ubuntu / Armbian
apt-get update
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
printf "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") $(. /etc/os-release && echo "$VERSION_CODENAME") stable\n" \
> /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
service docker start 2>/dev/null || systemctl start docker 2>/dev/null || true
if ! docker version >/dev/null 2>&1; then
print_err "Docker не запустился. Запустите вручную: systemctl start docker"
exit 1
fi
else
print_err "Неизвестный пакетный менеджер. Установите Docker вручную."
exit 1
fi
print_ok "Docker установлен"
else
print_ok "Docker установлен: $(docker --version)"
fi
# ============================================================
# 2. Проверка docker compose
# ============================================================
print_step "Проверка docker compose"
COMPOSE_CMD=""
if docker compose version >/dev/null 2>&1; then
COMPOSE_CMD="docker compose"
COMPOSE_VER=$(docker compose version --short 2>/dev/null || echo "v2")
print_ok "docker compose v2 доступен: $COMPOSE_VER"
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE_CMD="docker-compose"
print_ok "docker-compose v1 доступен: $(docker-compose --version)"
else
print_err "docker compose не найден. Установите docker-compose-plugin или docker-compose."
exit 1
fi
# ============================================================
# 3. Подготовка файлов окружения
# ============================================================
print_step "Подготовка .env"
if [ ! -f ".env" ]; then
if [ -f ".env.example" ]; then
cp .env.example .env
# Убираем CRLF если файл скопирован из Windows
if command -v sed >/dev/null 2>&1; then
sed -i 's/\r$//' .env
fi
print_ok ".env создан из .env.example"
else
print_err ".env.example не найден!"
exit 1
fi
else
print_ok ".env уже существует — пропускаю создание"
# Убираем CRLF в существующем файле тоже
if command -v sed >/dev/null 2>&1; then
sed -i 's/\r$//' .env
fi
fi
# ============================================================
# 4. Проверка обязательных переменных
# ============================================================
print_step "Проверка переменных окружения"
MISSING_COUNT=0
MISSING_LIST=""
for VAR in BOT_TOKEN ADMIN_IDS ENCRYPTION_KEY; do
VAL=$(grep -E "^${VAR}=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d '\r' || true)
if [ -z "$VAL" ] || [ "$VAL" = "your_bot_token_here" ] || [ "$VAL" = "123456789,987654321" ]; then
MISSING_COUNT=$((MISSING_COUNT + 1))
MISSING_LIST="$MISSING_LIST $VAR"
fi
done
if [ "$MISSING_COUNT" -ne 0 ]; then
print_warn "Не заполнены или имеют placeholder значения:$MISSING_LIST"
printf "\n"
printf "${YELLOW}Откройте .env в редакторе и заполните:${NC}\n"
for V in $MISSING_LIST; do
printf " - %s\n" "$V"
done
printf "\n"
printf "Продолжить всё равно? (y/N): "
read -r CONTINUE
case "$CONTINUE" in
[Yy]|[Yy][Ee][Ss])
;;
*)
print_info "Откройте .env, заполните значения и запустите install.sh снова."
exit 0
;;
esac
else
print_ok "Все обязательные переменные заполнены"
fi
# ============================================================
# 5. Создание директорий
# ============================================================
print_step "Создание директорий"
mkdir -p db uploads
print_ok "db/, uploads/ готовы"
# ============================================================
# 6. Сборка образа
# ============================================================
print_step "Сборка Docker-образа (это может занять несколько минут)"
print_info "Архитектура: $ARCH_NAME"
print_info "Нативные модули: better-sqlite3, tiny-secp256k1 (компилируются в builder)"
# Docker автоматически собирает под текущую архитектуру хоста
# buildx нужен только для multi-arch push в registry, для локальной сборки — обычный build
docker build -t telegram-shop:latest .
print_ok "Образ telegram-shop:latest собран"
# ============================================================
# 7. Запуск контейнеров
# ============================================================
print_step "Запуск контейнеров"
$COMPOSE_CMD up -d
print_ok "Контейнеры запущены"
# ============================================================
# 8. Проверка статуса
# ============================================================
print_step "Проверка статуса"
sleep 3
$COMPOSE_CMD ps
# ============================================================
# 9. Показ логов
# ============================================================
print_step "Последние логи"
$COMPOSE_CMD logs --tail=20
# ============================================================
# 10. Health-check с повторными попытками
# ============================================================
print_step "Проверка работоспособности"
HEALTH_URL="http://localhost:3001/health"
print_info "Запрос: $HEALTH_URL"
HEALTH_OK=0
for ATTEMPT in 1 2 3 4 5 6; do
sleep 5
if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then
HEALTH_OK=1
break
fi
print_info "Попытка $ATTEMPT/6 — сервис ещё запускается..."
done
if [ "$HEALTH_OK" -eq 1 ]; then
RESPONSE=$(curl -sf "$HEALTH_URL" 2>/dev/null || echo "ok")
print_ok "Сервис отвечает!"
printf " ${GREEN}Ответ: %s${NC}\n" "$RESPONSE"
else
print_warn "Health-check не ответил за 30 секунд. Проверьте логи:"
printf " curl %s\n" "$HEALTH_URL"
printf " %s logs -f\n" "$COMPOSE_CMD"
fi
# ============================================================
# Готово
# ============================================================
printf "\n"
printf "${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}\n"
printf "${GREEN}${BOLD}║ Установка завершена! ║${NC}\n"
printf "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}\n"
printf "\n"
printf "${BOLD}Полезные команды:${NC}\n"
printf " docker compose ps # статус контейнеров\n"
printf " docker compose logs -f # логи в реальном времени\n"
printf " docker compose restart # перезапустить\n"
printf " docker compose down # остановить\n"
printf " docker compose up -d --build # пересобрать и запустить\n"
printf "\n"
printf "${BOLD}Health-check:${NC} %s\n" "$HEALTH_URL"