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:
@@ -24,6 +24,7 @@ COMMISSION_ENABLED=true
|
|||||||
COMMISSION_PERCENT=5
|
COMMISSION_PERCENT=5
|
||||||
|
|
||||||
# --- Commission Wallets ---
|
# --- Commission Wallets ---
|
||||||
|
# Начальные адреса комиссионных кошельков. Редактируются в админке (Кошельки → Edit Wallets), значения в БД имеют приоритет.
|
||||||
COMMISSION_WALLET_BTC=
|
COMMISSION_WALLET_BTC=
|
||||||
COMMISSION_WALLET_LTC=
|
COMMISSION_WALLET_LTC=
|
||||||
COMMISSION_WALLET_USDT=
|
COMMISSION_WALLET_USDT=
|
||||||
@@ -70,3 +71,7 @@ DEFAULT_LANGUAGE=en
|
|||||||
# OpenAI-совместимый endpoint и ключ (используются, если не заданы в site_settings)
|
# OpenAI-совместимый endpoint и ключ (используются, если не заданы в site_settings)
|
||||||
CHATBOT_API_ENDPOINT=https://api.openai.com/v1
|
CHATBOT_API_ENDPOINT=https://api.openai.com/v1
|
||||||
CHATBOT_API_KEY=
|
CHATBOT_API_KEY=
|
||||||
|
|
||||||
|
# --- Deploy ---
|
||||||
|
# Версия образов из Gitea Container Registry (по умолчанию latest)
|
||||||
|
IMAGE_TAG=latest
|
||||||
|
|||||||
@@ -1,70 +1,96 @@
|
|||||||
name: "Release: ARM64 Docker Image"
|
name: "Release: multi-arch Docker images"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version tag (without v prefix)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-arm64:
|
build-push:
|
||||||
# Нативный ARM64 runner на Orange Pi Zero 2 (aarch64).
|
# Buildx runner на x86_64 хосте (docker:27.5-cli + host docker.sock).
|
||||||
# GitHub недоступен в этой сети — НЕ используем actions/*, только чистые run: шаги.
|
# Собирает linux/amd64 + linux/arm64 одним buildx build (qemu из коробки)
|
||||||
# node:22-alpine не содержит bash → используем sh
|
# и пушит оба образа (бот + Next.js админка) в Gitea Container Registry.
|
||||||
runs-on: arm64
|
runs-on: buildx
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: sh
|
shell: sh
|
||||||
env:
|
env:
|
||||||
|
REGISTRY: git.softuniq.eu
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
VERSION: ""
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
apk add --no-cache git >/dev/null 2>&1 || true
|
apk add --no-cache git curl >/dev/null 2>&1 || true
|
||||||
|
if [ -n "${GITHUB_REF_NAME}" ] && [ "${GITHUB_REF_NAME}" != "main" ]; then
|
||||||
|
# Запуск по тегу: clone конкретного тега
|
||||||
|
REF=${GITHUB_REF_NAME}
|
||||||
|
elif [ -n "${{ github.event.inputs.version }}" ]; then
|
||||||
|
REF=v${{ github.event.inputs.version }}
|
||||||
|
else
|
||||||
|
REF=${GITHUB_REF_NAME}
|
||||||
|
fi
|
||||||
|
echo "REF=${REF}"
|
||||||
cd /tmp
|
cd /tmp
|
||||||
rm -rf release-src
|
rm -rf release-src
|
||||||
git clone --depth 1 --branch "${GITHUB_REF_NAME}" \
|
git clone --depth 1 --branch "${REF}" \
|
||||||
"https://oauth2:${REGISTRY_TOKEN}@git.softuniq.eu/Telegram-Market/telegram-shop.git" release-src
|
"https://oauth2:${REGISTRY_TOKEN}@${REGISTRY}/Telegram-Market/telegram-shop.git" release-src
|
||||||
cd release-src
|
cd release-src
|
||||||
git rev-parse --short HEAD
|
git rev-parse --short HEAD
|
||||||
|
|
||||||
- name: Extract version
|
- name: Extract version
|
||||||
run: |
|
run: |
|
||||||
VERSION=${GITHUB_REF_NAME#v}
|
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
V=${{ github.event.inputs.version }}
|
||||||
echo "Build version: ${VERSION}"
|
else
|
||||||
|
V=${GITHUB_REF_NAME#v}
|
||||||
|
fi
|
||||||
|
echo "VERSION=${V}" >> "$GITHUB_ENV"
|
||||||
|
echo "Build version: ${V}"
|
||||||
|
|
||||||
- name: Build ARM64 image
|
- name: Login to Gitea Container Registry
|
||||||
|
run: |
|
||||||
|
apk add --no-cache docker-cli >/dev/null 2>&1 || true
|
||||||
|
echo "${REGISTRY_TOKEN}" | docker login ${REGISTRY} -u oauth2 --password-stdin
|
||||||
|
|
||||||
|
- name: Build and push bot image (amd64 + arm64)
|
||||||
working-directory: /tmp/release-src
|
working-directory: /tmp/release-src
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
# docker-cli не входит в node:22-alpine — ставим через apk
|
docker buildx build \
|
||||||
apk add --no-cache docker-cli >/dev/null 2>&1
|
--platform linux/amd64,linux/arm64 \
|
||||||
docker build \
|
|
||||||
--file ./Dockerfile \
|
--file ./Dockerfile \
|
||||||
--tag git.softuniq.eu/telegram-market/telegram-shop:${VERSION} \
|
--tag ${REGISTRY}/telegram-market/telegram-shop:${VERSION} \
|
||||||
--tag git.softuniq.eu/telegram-market/telegram-shop:latest \
|
--tag ${REGISTRY}/telegram-market/telegram-shop:latest \
|
||||||
|
--push \
|
||||||
.
|
.
|
||||||
|
|
||||||
- name: Push to Gitea Container Registry (best effort)
|
- name: Build and push admin image (amd64 + arm64)
|
||||||
working-directory: /tmp/release-src
|
working-directory: /tmp/release-src
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
apk add --no-cache docker-cli >/dev/null 2>&1 || true
|
docker buildx build \
|
||||||
echo "${REGISTRY_TOKEN}" | docker login git.softuniq.eu -u oauth2 --password-stdin \
|
--platform linux/amd64,linux/arm64 \
|
||||||
|| { echo "registry login failed, skipping push"; exit 0; }
|
--file ./admin-next/Dockerfile \
|
||||||
docker push git.softuniq.eu/telegram-market/telegram-shop:${VERSION} \
|
--tag ${REGISTRY}/telegram-market/telegram-shop-admin:${VERSION} \
|
||||||
|| echo "push tag failed (may need package scope)"
|
--tag ${REGISTRY}/telegram-market/telegram-shop-admin:latest \
|
||||||
docker push git.softuniq.eu/telegram-market/telegram-shop:latest \
|
--push \
|
||||||
|| echo "push latest failed (may need package scope)"
|
./admin-next
|
||||||
docker logout git.softuniq.eu || true
|
|
||||||
|
|
||||||
- name: Save image tarball artifact
|
- name: Verify pushed manifests
|
||||||
working-directory: /tmp/release-src
|
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
apk add --no-cache docker-cli >/dev/null 2>&1 || true
|
apk add --no-cache docker-cli >/dev/null 2>&1 || true
|
||||||
mkdir -p /tmp/release-artifacts
|
echo "${REGISTRY_TOKEN}" | docker login ${REGISTRY} -u oauth2 --password-stdin >/dev/null 2>&1 || true
|
||||||
docker save git.softuniq.eu/telegram-market/telegram-shop:${VERSION} \
|
docker buildx imagetools inspect ${REGISTRY}/telegram-market/telegram-shop:${VERSION} \
|
||||||
-o /tmp/release-artifacts/telegram-shop-arm64-${VERSION}.tar
|
| grep -E "Platform|Digest" | head -4
|
||||||
ls -lah /tmp/release-artifacts/
|
docker buildx imagetools inspect ${REGISTRY}/telegram-market/telegram-shop-admin:${VERSION} \
|
||||||
|
| grep -E "Platform|Digest" | head -4
|
||||||
|
docker logout ${REGISTRY} || true
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -32,9 +32,14 @@ bash install.sh
|
|||||||
1. Определит архитектуру (x86_64 / ARM64 / ARMv7)
|
1. Определит архитектуру (x86_64 / ARM64 / ARMv7)
|
||||||
2. Установит Docker если не установлен
|
2. Установит Docker если не установлен
|
||||||
3. Создаст `.env` из шаблона
|
3. Создаст `.env` из шаблона
|
||||||
4. Проверит обязательные переменные
|
4. Интерактивно запросит недостающие переменные (BOT_TOKEN, ADMIN_IDS) и сгенерирует ключи (ENCRYPTION_KEY, ADMIN_SECRET, SUPER_ADMIN_SECRET) если они не заданы
|
||||||
5. Соберёт Docker-образ под текущую архитектуру
|
5. Установит SHOP_ACTIVATED=false для нового магазина (стартует заблокированным)
|
||||||
6. Запустит контейнер и проверит health-check
|
6. Автоматически определит LAN IP и установит ADMIN_URL
|
||||||
|
7. Соберёт Docker-образ под текущую архитектуру
|
||||||
|
8. Запустит контейнер и проверит health-check
|
||||||
|
|
||||||
|
#### Активация магазина
|
||||||
|
После установки магазин стартует заблокированным (SHOP_ACTIVATED=false). Оператор получает onion-адрес от клиента, заходит в админку по onion/LAN, входит с SUPER_ADMIN_SECRET, добавляет комиссионные кошельки (Кошельки → Edit Wallets), настраивает бота, нажимает "Activate Shop" в Настройках.
|
||||||
|
|
||||||
### Ручная установка
|
### Ручная установка
|
||||||
|
|
||||||
@@ -46,8 +51,8 @@ git clone <repo-url> && cd telegram-shop
|
|||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
nano .env # заполнить BOT_TOKEN, ADMIN_IDS, ENCRYPTION_KEY
|
nano .env # заполнить BOT_TOKEN, ADMIN_IDS, ENCRYPTION_KEY
|
||||||
|
|
||||||
# 3. Собрать и запустить
|
# 3. Запустить (образы тянутся из Gitea Container Registry, сборка не нужна)
|
||||||
docker compose up -d --build
|
docker compose up -d
|
||||||
|
|
||||||
# 4. Проверить статус
|
# 4. Проверить статус
|
||||||
docker compose ps
|
docker compose ps
|
||||||
@@ -55,6 +60,8 @@ curl http://localhost:3001/health # бот (health-сервер)
|
|||||||
curl -o /dev/null -w "%{http_code}\n" http://localhost:3000/login # новая админка
|
curl -o /dev/null -w "%{http_code}\n" http://localhost:3000/login # новая админка
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> Образы бота и админки собираются в CI (Gitea Actions, buildx multi-arch) и публикуются в Gitea Container Registry. На сервере ничего не компилируется — только `docker compose pull`. Для пиннинга версии: `IMAGE_TAG=1.2.9 docker compose up -d`. Локальная сборка (dev): `docker compose up -d --build`.
|
||||||
|
|
||||||
## Настройка .env
|
## Настройка .env
|
||||||
|
|
||||||
Скопируйте `.env.example` в `.env` и заполните:
|
Скопируйте `.env.example` в `.env` и заполните:
|
||||||
@@ -82,6 +89,7 @@ curl -o /dev/null -w "%{http_code}\n" http://localhost:3000/login # новая
|
|||||||
| `WG_ENDPOINT` | — | Адрес сервера WireGuard |
|
| `WG_ENDPOINT` | — | Адрес сервера WireGuard |
|
||||||
| `WG_ADDRESS` | — | Адрес интерфейса WireGuard |
|
| `WG_ADDRESS` | — | Адрес интерфейса WireGuard |
|
||||||
| `WG_DNS` | — | DNS для WireGuard |
|
| `WG_DNS` | — | DNS для WireGuard |
|
||||||
|
| `SHOP_ACTIVATED` | — | Флаг активации магазина. false = магазин заблокирован до активации супер-админом в админке (Настройки → Shop Activation). |
|
||||||
|
|
||||||
Генерация ключа шифрования:
|
Генерация ключа шифрования:
|
||||||
```bash
|
```bash
|
||||||
@@ -194,8 +202,9 @@ Docker автоматически собирает нативные модули
|
|||||||
# Запуск
|
# Запуск
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
# Пересборка после изменений
|
# Пересборка (dev-сборка локально) или обновление до новой версии из registry
|
||||||
docker compose up -d --build
|
docker compose up -d --build # dev: собрать локально
|
||||||
|
IMAGE_TAG=1.2.9 docker compose up -d # prod: взять конкретную версию из registry
|
||||||
|
|
||||||
# Логи
|
# Логи
|
||||||
docker compose logs -f
|
docker compose logs -f
|
||||||
|
|||||||
@@ -8,10 +8,17 @@
|
|||||||
|
|
||||||
## Current Version
|
## Current Version
|
||||||
|
|
||||||
**v1.2.8** — 2026-08-09
|
**v1.2.9** — 2026-08-11
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v1.2.9 — 2026-08-11
|
||||||
|
- **ci**: релиз переведён на Gitea Container Registry — образы бота и админки собираются в CI (Gitea Actions, buildx multi-arch linux/amd64+arm64 на x86_64 хосте) и пушатся как `git.softuniq.eu/telegram-market/telegram-shop` и `.../telegram-shop-admin`
|
||||||
|
- **deploy**: `docker-compose.yml` больше не собирает образы на сервере — бот и админка тянутся из registry (`IMAGE_TAG` для пиннинга версии); tor-proxy остаётся локальной сборкой (минимальный alpine)
|
||||||
|
- **install**: `install.sh` не компилирует нативный код на слабом железе — только `docker compose pull` + `up -d` (Orange Pi / RPi)
|
||||||
|
- **ci**: добавлен новый buildx-runner `gitea-host-buildx` (label `buildx`) — сборка multi-arch вместо нативной ARM64 сборки на Orange Pi
|
||||||
|
- **ci**: `REGISTRY_TOKEN` обновлён на package-токен (`write:package`) — теперь push в registry проходит без ошибок авторизации
|
||||||
|
|
||||||
### v1.2.8 — 2026-08-09
|
### v1.2.8 — 2026-08-09
|
||||||
- **chore**: repo cleanup — removed stale `docs/admin-frontend-spec.md` (old Express/EJS admin), unused `templates/` (SmartAdmin copy), dead `scripts/sync-agents.cjs`, committed `admin-next/.zscripts/dev.pid`
|
- **chore**: repo cleanup — removed stale `docs/admin-frontend-spec.md` (old Express/EJS admin), unused `templates/` (SmartAdmin copy), dead `scripts/sync-agents.cjs`, committed `admin-next/.zscripts/dev.pid`
|
||||||
- **docs**: added `docs/API.md` (admin REST API reference) and `docs/DATABASE.md` (DB schema); README structure updated
|
- **docs**: added `docs/API.md` (admin REST API reference) and `docs/DATABASE.md` (DB schema); README structure updated
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
|
# Telegram Shop — production compose
|
||||||
|
# Образы НЕ собираются на сервере — тянутся из Gitea Container Registry:
|
||||||
|
# git.softuniq.eu/telegram-market/telegram-shop (бот, порт 3001)
|
||||||
|
# git.softuniq.eu/telegram-market/telegram-shop-admin (Next.js админка, порт 3000)
|
||||||
|
#
|
||||||
|
# Сборка/релиз: .gitea/workflows/release.yaml (Gitea Actions, buildx multi-arch).
|
||||||
|
# Для обновления: укажите IMAGE_TAG (по умолчанию latest) и docker compose up -d.
|
||||||
|
#
|
||||||
|
# Пример:
|
||||||
|
# IMAGE_TAG=1.2.9 docker compose up -d
|
||||||
|
|
||||||
services:
|
services:
|
||||||
telegram_shop_prod:
|
telegram_shop_prod:
|
||||||
build:
|
image: git.softuniq.eu/telegram-market/telegram-shop:${IMAGE_TAG:-latest}
|
||||||
context: .
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
hostname: telegram_shop_prod
|
hostname: telegram_shop_prod
|
||||||
container_name: telegram_shop_prod
|
container_name: telegram_shop_prod
|
||||||
ports:
|
ports:
|
||||||
@@ -10,6 +19,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- ADMIN_CHAT_URL=http://tg_shop_admin:3000/api/chat
|
- ADMIN_CHAT_URL=http://tg_shop_admin:3000/api/chat
|
||||||
|
- SHOP_ACTIVATED=${SHOP_ACTIVATED:-true}
|
||||||
volumes:
|
volumes:
|
||||||
- ./db:/app/db/
|
- ./db:/app/db/
|
||||||
- ./uploads:/app/uploads/
|
- ./uploads:/app/uploads/
|
||||||
@@ -35,9 +45,7 @@ services:
|
|||||||
|
|
||||||
# Новая Next.js админ-панель (Prisma, одна БД с ботом)
|
# Новая Next.js админ-панель (Prisma, одна БД с ботом)
|
||||||
tg_shop_admin:
|
tg_shop_admin:
|
||||||
build:
|
image: git.softuniq.eu/telegram-market/telegram-shop-admin:${IMAGE_TAG:-latest}
|
||||||
context: ./admin-next
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
container_name: tg_shop_admin
|
container_name: tg_shop_admin
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
@@ -73,9 +81,11 @@ services:
|
|||||||
- GITEA_API_URL=${GITEA_API_URL}
|
- GITEA_API_URL=${GITEA_API_URL}
|
||||||
- WG_PRIVATE_KEY=${WG_PRIVATE_KEY}
|
- WG_PRIVATE_KEY=${WG_PRIVATE_KEY}
|
||||||
- WG_PRESHARED_KEY=${WG_PRESHARED_KEY}
|
- WG_PRESHARED_KEY=${WG_PRESHARED_KEY}
|
||||||
|
- SHOP_ACTIVATED=${SHOP_ACTIVATED:-true}
|
||||||
volumes:
|
volumes:
|
||||||
- ./db:/app/db
|
- ./db:/app/db
|
||||||
- ./uploads:/app/uploads
|
- ./uploads:/app/uploads
|
||||||
|
- ./tor-proxy/hosts:/onion-hosts:ro
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
@@ -93,6 +103,7 @@ services:
|
|||||||
- tor_proxy_net
|
- tor_proxy_net
|
||||||
|
|
||||||
tor-proxy:
|
tor-proxy:
|
||||||
|
# Миниатюрный alpine-контейнер (tor + entrypoint) — собирается локально за секунды
|
||||||
build:
|
build:
|
||||||
context: ./tor-proxy
|
context: ./tor-proxy
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
209
install.sh
209
install.sh
@@ -155,40 +155,138 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 4. Проверка обязательных переменных
|
# 4. Настройка переменных окружения (интерактивно)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
print_step "Проверка переменных окружения"
|
print_step "Настройка переменных окружения (интерактивно)"
|
||||||
|
|
||||||
MISSING_COUNT=0
|
# Функция для обновления переменной в .env (безопасно для спецсимволов в значении)
|
||||||
MISSING_LIST=""
|
update_env_var() {
|
||||||
for VAR in BOT_TOKEN ADMIN_IDS ENCRYPTION_KEY; do
|
var_name="$1"
|
||||||
VAL=$(grep -E "^${VAR}=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d '\r' || true)
|
var_value="$2"
|
||||||
if [ -z "$VAL" ] || [ "$VAL" = "your_bot_token_here" ] || [ "$VAL" = "123456789,987654321" ]; then
|
if grep -q "^${var_name}=" .env 2>/dev/null; then
|
||||||
MISSING_COUNT=$((MISSING_COUNT + 1))
|
awk -v key="$var_name" -v val="$var_value" '
|
||||||
MISSING_LIST="$MISSING_LIST $VAR"
|
BEGIN { FS = OFS = "="; done = 0 }
|
||||||
fi
|
/^[A-Za-z_][A-Za-z0-9_]*=/ {
|
||||||
done
|
if ($1 == key) { print key "=" val; done = 1; next }
|
||||||
|
}
|
||||||
if [ "$MISSING_COUNT" -ne 0 ]; then
|
{ print }
|
||||||
print_warn "Не заполнены или имеют placeholder значения:$MISSING_LIST"
|
END { if (!done) print key "=" val }
|
||||||
printf "\n"
|
' .env > .env.tmp && mv .env.tmp .env
|
||||||
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
|
else
|
||||||
print_ok "Все обязательные переменные заполнены"
|
echo "${var_name}=${var_value}" >> .env
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Скрытый ввод секретов (POSIX sh, работает в Alpine ash)
|
||||||
|
read_secret() {
|
||||||
|
printf "%s" "$1"
|
||||||
|
stty -echo 2>/dev/null
|
||||||
|
read -r input
|
||||||
|
stty echo 2>/dev/null
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Функция для генерации случайного 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
|
||||||
|
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
|
fi
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -199,22 +297,24 @@ mkdir -p db uploads
|
|||||||
print_ok "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 "Архитектура: $ARCH_NAME"
|
||||||
print_info "Нативные модули: better-sqlite3, tiny-secp256k1 (компилируются в builder)"
|
print_info "Образы собираются в CI (Gitea Actions, buildx multi-arch) — на сервере ничего не компилируется"
|
||||||
|
|
||||||
# Docker автоматически собирает под текущую архитектуру хоста
|
# Релизная версия: по умолчанию latest, можно переопределить IMAGE_TAG=v1.2.9
|
||||||
# buildx нужен только для multi-arch push в registry, для локальной сборки — обычный build
|
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||||
docker build -t telegram-shop:latest .
|
print_info "Используемая версия образа: $IMAGE_TAG"
|
||||||
print_ok "Образ telegram-shop:latest собран"
|
|
||||||
|
$COMPOSE_CMD pull telegram_shop_prod tg_shop_admin
|
||||||
|
print_ok "Образы telegram-shop:${IMAGE_TAG} и telegram-shop-admin:${IMAGE_TAG} загружены"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 7. Запуск контейнеров
|
# 7. Запуск контейнеров
|
||||||
# ============================================================
|
# ============================================================
|
||||||
print_step "Запуск контейнеров"
|
print_step "Запуск контейнеров"
|
||||||
$COMPOSE_CMD up -d
|
IMAGE_TAG="${IMAGE_TAG}" $COMPOSE_CMD up -d
|
||||||
print_ok "Контейнеры запущены"
|
print_ok "Контейнеры запущены"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -257,7 +357,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 9. Tor Proxy — onion-адреса
|
# 11. Tor Proxy — onion-адреса
|
||||||
# ============================================================
|
# ============================================================
|
||||||
printf "\n"
|
printf "\n"
|
||||||
print_step "Проверка Tor-прокси"
|
print_step "Проверка Tor-прокси"
|
||||||
@@ -295,6 +395,35 @@ else
|
|||||||
printf " docker logs tor-proxy\n"
|
printf " docker logs tor-proxy\n"
|
||||||
fi
|
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