ci: registry-based releases — multi-arch images built in CI, servers only pull
- release workflow: buildx multi-arch (amd64+arm64) build+push of bot and admin images to Gitea Container Registry (git.softuniq.eu/telegram-market/telegram-shop[-admin]) - docker-compose: bot/admin use registry images (IMAGE_TAG for pinning); tor-proxy stays local alpine build - install.sh: pull from registry instead of local docker build (no native compilation on weak hardware); interactive .env setup preserved - new buildx runner gitea-host-buildx (label buildx) on x86_64 host - REGISTRY_TOKEN replaced with package-scoped token (write:package) - README/VERSION/.env.example updated
This commit is contained in:
205
install.sh
205
install.sh
@@ -155,40 +155,138 @@ else
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 4. Проверка обязательных переменных
|
||||
# 4. Настройка переменных окружения (интерактивно)
|
||||
# ============================================================
|
||||
print_step "Проверка переменных окружения"
|
||||
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"
|
||||
# Функция для обновления переменной в .env (безопасно для спецсимволов в значении)
|
||||
update_env_var() {
|
||||
var_name="$1"
|
||||
var_value="$2"
|
||||
if grep -q "^${var_name}=" .env 2>/dev/null; then
|
||||
awk -v key="$var_name" -v val="$var_value" '
|
||||
BEGIN { FS = OFS = "="; done = 0 }
|
||||
/^[A-Za-z_][A-Za-z0-9_]*=/ {
|
||||
if ($1 == key) { print key "=" val; done = 1; next }
|
||||
}
|
||||
{ print }
|
||||
END { if (!done) print key "=" val }
|
||||
' .env > .env.tmp && mv .env.tmp .env
|
||||
else
|
||||
echo "${var_name}=${var_value}" >> .env
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$MISSING_COUNT" -ne 0 ]; then
|
||||
print_warn "Не заполнены или имеют placeholder значения:$MISSING_LIST"
|
||||
# Скрытый ввод секретов (POSIX sh, работает в Alpine ash)
|
||||
read_secret() {
|
||||
printf "%s" "$1"
|
||||
stty -echo 2>/dev/null
|
||||
read -r input
|
||||
stty echo 2>/dev/null
|
||||
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
|
||||
}
|
||||
|
||||
# Функция для генерации случайного hex-строки (32 байта)
|
||||
generate_random_hex() {
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex 32
|
||||
else
|
||||
od -An -tx32 /dev/urandom | tr -d ' \n'
|
||||
fi
|
||||
}
|
||||
|
||||
# BOT_TOKEN
|
||||
current_bot_token=$(grep -E "^BOT_TOKEN=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_bot_token" ] || [ "$current_bot_token" = "your_bot_token_here" ]; then
|
||||
print_warn "BOT_TOKEN не задан или содержит placeholder."
|
||||
read_secret "Введите BOT_TOKEN (от @BotFather): "
|
||||
update_env_var "BOT_TOKEN" "$input"
|
||||
bot_token="$input"
|
||||
else
|
||||
print_ok "Все обязательные переменные заполнены"
|
||||
bot_token="$current_bot_token"
|
||||
fi
|
||||
|
||||
# ADMIN_IDS
|
||||
current_admin_ids=$(grep -E "^ADMIN_IDS=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_admin_ids" ] || [ "$current_admin_ids" = "123456789,987654321" ]; then
|
||||
print_warn "ADMIN_IDS не задан или содержит placeholder."
|
||||
printf "Введите ADMIN_IDS (через запятую, например: 123456789,987654321): "
|
||||
read -r input
|
||||
update_env_var "ADMIN_IDS" "$input"
|
||||
admin_ids="$input"
|
||||
else
|
||||
admin_ids="$current_admin_ids"
|
||||
fi
|
||||
|
||||
# ENCRYPTION_KEY
|
||||
current_encryption_key=$(grep -E "^ENCRYPTION_KEY=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_encryption_key" ]; then
|
||||
encryption_key=$(generate_random_hex)
|
||||
update_env_var "ENCRYPTION_KEY" "$encryption_key"
|
||||
print_ok "ENCRYPTION_KEY сгенерирован автоматически"
|
||||
else
|
||||
encryption_key="$current_encryption_key"
|
||||
fi
|
||||
|
||||
# ADMIN_SECRET
|
||||
current_admin_secret=$(grep -E "^ADMIN_SECRET=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_admin_secret" ] || [ "$current_admin_secret" = "changeme" ]; then
|
||||
admin_secret=$(generate_random_hex)
|
||||
update_env_var "ADMIN_SECRET" "$admin_secret"
|
||||
print_ok "ADMIN_SECRET сгенерирован автоматически"
|
||||
else
|
||||
admin_secret="$current_admin_secret"
|
||||
fi
|
||||
|
||||
# SUPER_ADMIN_SECRET
|
||||
current_super_admin_secret=$(grep -E "^SUPER_ADMIN_SECRET=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_super_admin_secret" ] || [ "$current_super_admin_secret" = "changeme_super" ]; then
|
||||
# Генерация до тех пор, пока не получим значение, отличное от ADMIN_SECRET
|
||||
super_admin_secret=""
|
||||
while [ -z "$super_admin_secret" ] || [ "$super_admin_secret" = "$admin_secret" ]; do
|
||||
super_admin_secret=$(generate_random_hex)
|
||||
done
|
||||
update_env_var "SUPER_ADMIN_SECRET" "$super_admin_secret"
|
||||
print_ok "SUPER_ADMIN_SECRET сгенерирован автоматически"
|
||||
else
|
||||
super_admin_secret="$current_super_admin_secret"
|
||||
fi
|
||||
|
||||
# SHOP_ACTIVATED
|
||||
current_shop_activated=$(grep -E "^SHOP_ACTIVATED=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_shop_activated" ]; then
|
||||
shop_activated="false"
|
||||
update_env_var "SHOP_ACTIVATED" "$shop_activated"
|
||||
print_info "SHOP_ACTIVATED установлен в 'false' (магазин стартует заблокированным)"
|
||||
else
|
||||
shop_activated="$current_shop_activated"
|
||||
fi
|
||||
|
||||
# ADMIN_URL
|
||||
current_admin_url=$(grep -E "^ADMIN_URL=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
|
||||
if [ -z "$current_admin_url" ]; then
|
||||
# Попытка определить LAN IP
|
||||
lan_ip=$(hostname -I | awk '{print $1}')
|
||||
if [ -z "$lan_ip" ]; then
|
||||
lan_ip=$(ip route get 1 | awk '{print $7}' 2>/dev/null)
|
||||
fi
|
||||
if [ -n "$lan_ip" ]; then
|
||||
admin_url="http://${lan_ip}:3000"
|
||||
update_env_var "ADMIN_URL" "$admin_url"
|
||||
print_info "ADMIN_URL установлен в $admin_url (определен автоматически)"
|
||||
else
|
||||
print_warn "Не удалось определить LAN IP. ADMIN_URL не установлен."
|
||||
fi
|
||||
else
|
||||
admin_url="$current_admin_url"
|
||||
fi
|
||||
|
||||
# Ограничиваем доступ к .env (содержит секреты)
|
||||
if [ -f .env ]; then
|
||||
chmod 600 .env
|
||||
print_ok ".env защищён (chmod 600)"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
@@ -199,22 +297,24 @@ mkdir -p db uploads
|
||||
print_ok "db/, uploads/ готовы"
|
||||
|
||||
# ============================================================
|
||||
# 6. Сборка образа
|
||||
# 6. Загрузка образов из Gitea Container Registry
|
||||
# ============================================================
|
||||
print_step "Сборка Docker-образа (это может занять несколько минут)"
|
||||
print_step "Загрузка образов из Gitea Container Registry"
|
||||
print_info "Архитектура: $ARCH_NAME"
|
||||
print_info "Нативные модули: better-sqlite3, tiny-secp256k1 (компилируются в builder)"
|
||||
print_info "Образы собираются в CI (Gitea Actions, buildx multi-arch) — на сервере ничего не компилируется"
|
||||
|
||||
# Docker автоматически собирает под текущую архитектуру хоста
|
||||
# buildx нужен только для multi-arch push в registry, для локальной сборки — обычный build
|
||||
docker build -t telegram-shop:latest .
|
||||
print_ok "Образ telegram-shop:latest собран"
|
||||
# Релизная версия: по умолчанию latest, можно переопределить IMAGE_TAG=v1.2.9
|
||||
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||
print_info "Используемая версия образа: $IMAGE_TAG"
|
||||
|
||||
$COMPOSE_CMD pull telegram_shop_prod tg_shop_admin
|
||||
print_ok "Образы telegram-shop:${IMAGE_TAG} и telegram-shop-admin:${IMAGE_TAG} загружены"
|
||||
|
||||
# ============================================================
|
||||
# 7. Запуск контейнеров
|
||||
# ============================================================
|
||||
print_step "Запуск контейнеров"
|
||||
$COMPOSE_CMD up -d
|
||||
IMAGE_TAG="${IMAGE_TAG}" $COMPOSE_CMD up -d
|
||||
print_ok "Контейнеры запущены"
|
||||
|
||||
# ============================================================
|
||||
@@ -257,7 +357,7 @@ else
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 9. Tor Proxy — onion-адреса
|
||||
# 11. Tor Proxy — onion-адреса
|
||||
# ============================================================
|
||||
printf "\n"
|
||||
print_step "Проверка Tor-прокси"
|
||||
@@ -283,7 +383,7 @@ if [ "$TOR_RUNNING" -eq 1 ]; then
|
||||
printf "\n ${BOLD}Подключение:${NC}\n"
|
||||
printf " SSH: torify ssh root@%s\n" "$SSH_ONION"
|
||||
printf " Admin: откройте http://%s в Tor Browser\n" "$ADMIN_ONION"
|
||||
|
||||
|
||||
if [ -f "./tor-proxy/get-onions.sh" ]; then
|
||||
sh ./tor-proxy/get-onions.sh 2>/dev/null || true
|
||||
fi
|
||||
@@ -295,6 +395,35 @@ else
|
||||
printf " docker logs tor-proxy\n"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 12. Финальная сводка
|
||||
# ============================================================
|
||||
printf "\n"
|
||||
print_step "Финальная сводка"
|
||||
|
||||
# LAN IP для админки (если не определен ранее)
|
||||
if [ -z "${admin_url:-}" ]; then
|
||||
lan_ip=$(hostname -I | awk '{print $1}')
|
||||
if [ -z "$lan_ip" ]; then
|
||||
lan_ip=$(ip route get 1 | awk '{print $7}' 2>/dev/null)
|
||||
fi
|
||||
if [ -n "$lan_ip" ]; then
|
||||
admin_url="http://${lan_ip}:3000"
|
||||
else
|
||||
admin_url="Не определен"
|
||||
fi
|
||||
fi
|
||||
|
||||
printf " ${CYAN}${BOLD}LAN URL админки:${NC} %s\n" "$admin_url"
|
||||
printf " ${CYAN}${BOLD}SSH onion:${NC} %s\n" "$SSH_ONION"
|
||||
printf " ${CYAN}${BOLD}Admin onion:${NC} %s\n" "$ADMIN_ONION"
|
||||
printf " ${CYAN}${BOLD}ADMIN_SECRET:${NC} %s\n" "$admin_secret"
|
||||
printf " ${CYAN}${BOLD}SUPER_ADMIN_SECRET:${NC} %s\n" "$super_admin_secret"
|
||||
printf "\n"
|
||||
printf "${YELLOW}⚠ Магазин стартует заблокированным (SHOP_ACTIVATED=false).${NC}\n"
|
||||
printf "${YELLOW} Войдите как супер-админ, добавьте комиссионные кошельки и активируйте магазин в Настройках.${NC}\n"
|
||||
printf "\n"
|
||||
|
||||
# ============================================================
|
||||
# Готово
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user