1831 lines
69 KiB
Bash
Executable File
1831 lines
69 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# MY Network v3.0 - Автоматическая установка и запуск ноды
|
||
# Версия: 3.0.0
|
||
|
||
set -e
|
||
|
||
# Цвета для вывода
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
PURPLE='\033[0;35m'
|
||
CYAN='\033[0;36m'
|
||
WHITE='\033[1;37m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Переменные по умолчанию
|
||
SCRIPT_VERSION="3.0.0"
|
||
PROJECT_DIR="/opt/my-network"
|
||
STORAGE_DIR="/opt/my-network/storage"
|
||
CONFIG_DIR="/opt/my-network/config"
|
||
LOGS_DIR="/opt/my-network/logs"
|
||
|
||
# Параметры ноды
|
||
NODE_TYPE=""
|
||
NETWORK_MODE=""
|
||
ALLOW_INCOMING="false"
|
||
DOCKER_SOCK_PATH="/var/run/docker.sock"
|
||
BOOTSTRAP_CONFIG=""
|
||
TELEGRAM_API_KEY=""
|
||
CLIENT_TELEGRAM_API_KEY=""
|
||
NODE_VERSION="3.0.0"
|
||
ENABLE_SSL="false"
|
||
DOMAIN=""
|
||
EMAIL=""
|
||
ENABLE_WEB_CLIENT="true"
|
||
|
||
# Функция логирования
|
||
log() {
|
||
echo -e "${WHITE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||
}
|
||
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# Показать заставку
|
||
show_banner() {
|
||
clear
|
||
echo -e "${PURPLE}"
|
||
cat << "EOF"
|
||
╔══════════════════════════════════════════════════════════════╗
|
||
║ MY Network v3.0 ║
|
||
║ Децентрализованная сеть контента ║
|
||
║ ║
|
||
║ • Мгновенная трансляция без расшифровки ║
|
||
║ • Блокчейн интеграция для uploader-bot ║
|
||
║ • Полная децентрализация без консенсуса ║
|
||
║ • Автоматическая конвертация через Docker ║
|
||
╚══════════════════════════════════════════════════════════════╝
|
||
EOF
|
||
echo -e "${NC}"
|
||
echo ""
|
||
log_info "Запуск автоматической установки MY Network v3.0..."
|
||
echo ""
|
||
}
|
||
|
||
# Проверка прав root
|
||
check_root() {
|
||
if [[ $EUID -ne 0 ]]; then
|
||
log_error "Этот скрипт должен запускаться с правами root (sudo)"
|
||
echo "Использование: sudo bash start.sh"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Определение операционной системы
|
||
detect_os() {
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
OS=$NAME
|
||
VER=$VERSION_ID
|
||
OS_ID=$ID
|
||
else
|
||
log_error "Не удалось определить операционную систему"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Обнаружена ОС: $OS $VER"
|
||
|
||
# Проверка поддерживаемых ОС
|
||
case $OS_ID in
|
||
ubuntu|debian|centos|rhel|fedora)
|
||
log_success "Операционная система поддерживается"
|
||
;;
|
||
*)
|
||
log_warn "Операционная система может быть не полностью поддержана"
|
||
if check_interactive; then
|
||
echo -n "Продолжить установку? [y/N]: " >&2
|
||
read -r REPLY < /dev/tty
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
exit 1
|
||
fi
|
||
else
|
||
log_info "Неинтерактивный режим: продолжаем с неподдерживаемой ОС"
|
||
fi
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Проверка доступности TTY для интерактивного ввода
|
||
check_interactive() {
|
||
if [ -t 0 ] && [ -t 1 ]; then
|
||
return 0 # TTY доступен
|
||
else
|
||
return 1 # TTY недоступен
|
||
fi
|
||
}
|
||
|
||
# Безопасное чтение ввода
|
||
safe_read() {
|
||
local prompt="$1"
|
||
local default="$2"
|
||
local var_name="$3"
|
||
|
||
if check_interactive; then
|
||
# Интерактивный режим - используем /dev/tty
|
||
echo -n "$prompt" >&2
|
||
read -r input < /dev/tty
|
||
if [ -n "$input" ]; then
|
||
eval "$var_name='$input'"
|
||
else
|
||
eval "$var_name='$default'"
|
||
fi
|
||
else
|
||
# Неинтерактивный режим - используем значения по умолчанию
|
||
log_info "Неинтерактивный режим: $prompt -> используется значение по умолчанию: $default"
|
||
eval "$var_name='$default'"
|
||
fi
|
||
}
|
||
|
||
# Интерактивная настройка
|
||
interactive_setup() {
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${WHITE} НАСТРОЙКА MY NETWORK v3.0 ${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
|
||
# Проверяем режим
|
||
if ! check_interactive; then
|
||
log_warn "Обнаружен неинтерактивный режим (curl | bash)"
|
||
log_info "Используются настройки по умолчанию. Для интерактивной настройки скачайте и запустите скрипт локально:"
|
||
log_info "wget https://git.projscale.dev/my-dev/uploader-bot/raw/branch/main/start.sh && chmod +x start.sh && sudo ./start.sh"
|
||
echo ""
|
||
fi
|
||
|
||
# 1. Тип сети
|
||
echo -e "${PURPLE}❓ Выберите режим работы сети:${NC}"
|
||
echo " 1) Создать новую сеть (Bootstrap нода)"
|
||
echo " 2) Подключиться к существующей сети"
|
||
echo ""
|
||
|
||
if check_interactive; then
|
||
while true; do
|
||
echo -n "Введите номер [1-2]: " >&2
|
||
read -r network_choice < /dev/tty
|
||
case $network_choice in
|
||
1)
|
||
NETWORK_MODE="bootstrap"
|
||
NODE_TYPE="bootstrap"
|
||
log_info "Режим: Создание новой сети (Bootstrap нода)"
|
||
break
|
||
;;
|
||
2)
|
||
NETWORK_MODE="existing"
|
||
log_info "Режим: Подключение к существующей сети"
|
||
break
|
||
;;
|
||
*)
|
||
log_error "Неверный выбор. Введите 1 или 2."
|
||
;;
|
||
esac
|
||
done
|
||
else
|
||
# Неинтерактивный режим - создаем новую сеть по умолчанию
|
||
NETWORK_MODE="bootstrap"
|
||
NODE_TYPE="bootstrap"
|
||
log_info "Неинтерактивный режим: используется режим создания новой сети (Bootstrap нода)"
|
||
fi
|
||
|
||
# 2. Тип ноды (если подключаемся к существующей)
|
||
if [ "$NETWORK_MODE" = "existing" ]; then
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Выберите тип ноды:${NC}"
|
||
echo " 1) Публичная нода (принимает входящие соединения)"
|
||
echo " 2) Приватная нода (только исходящие соединения)"
|
||
echo ""
|
||
|
||
if check_interactive; then
|
||
while true; do
|
||
echo -n "Введите номер [1-2]: " >&2
|
||
read -r node_choice < /dev/tty
|
||
case $node_choice in
|
||
1)
|
||
NODE_TYPE="public"
|
||
ALLOW_INCOMING="true"
|
||
log_info "Тип ноды: Публичная (открытые порты)"
|
||
break
|
||
;;
|
||
2)
|
||
NODE_TYPE="private"
|
||
ALLOW_INCOMING="false"
|
||
log_info "Тип ноды: Приватная (закрытые порты)"
|
||
break
|
||
;;
|
||
*)
|
||
log_error "Неверный выбор. Введите 1 или 2."
|
||
;;
|
||
esac
|
||
done
|
||
else
|
||
# Неинтерактивный режим - публичная нода по умолчанию
|
||
NODE_TYPE="public"
|
||
ALLOW_INCOMING="true"
|
||
log_info "Неинтерактивный режим: используется публичная нода по умолчанию"
|
||
fi
|
||
else
|
||
ALLOW_INCOMING="true" # Bootstrap нода всегда принимает подключения
|
||
fi
|
||
|
||
# 3. Bootstrap конфигурация
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Конфигурация bootstrap узлов:${NC}"
|
||
if [ "$NETWORK_MODE" = "bootstrap" ]; then
|
||
log_info "Bootstrap нода будет создавать новую сеть"
|
||
BOOTSTRAP_CONFIG="new"
|
||
else
|
||
if check_interactive; then
|
||
echo -n "Путь до bootstrap.json [Enter для дефолтного]: " >&2
|
||
read -r custom_bootstrap < /dev/tty
|
||
else
|
||
custom_bootstrap=""
|
||
log_info "Неинтерактивный режим: используется дефолтный bootstrap.json"
|
||
fi
|
||
|
||
if [ -n "$custom_bootstrap" ] && [ -f "$custom_bootstrap" ]; then
|
||
BOOTSTRAP_CONFIG="$custom_bootstrap"
|
||
log_success "Использован кастомный bootstrap.json: $custom_bootstrap"
|
||
else
|
||
BOOTSTRAP_CONFIG="default"
|
||
log_info "Используется дефолтный bootstrap.json"
|
||
fi
|
||
fi
|
||
|
||
# 4. Docker socket
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Настройка Docker для конвертации:${NC}"
|
||
if check_interactive; then
|
||
echo -n "Путь до docker.sock [$DOCKER_SOCK_PATH]: " >&2
|
||
read -r custom_docker_sock < /dev/tty
|
||
else
|
||
custom_docker_sock=""
|
||
log_info "Неинтерактивный режим: используется дефолтный путь $DOCKER_SOCK_PATH"
|
||
fi
|
||
|
||
if [ -n "$custom_docker_sock" ]; then
|
||
DOCKER_SOCK_PATH="$custom_docker_sock"
|
||
fi
|
||
|
||
if [ -S "$DOCKER_SOCK_PATH" ]; then
|
||
log_success "Docker socket найден: $DOCKER_SOCK_PATH"
|
||
else
|
||
log_warn "Docker socket не найден: $DOCKER_SOCK_PATH"
|
||
log_info "Docker будет установлен автоматически"
|
||
fi
|
||
|
||
# 5. Настройка веб-клиента
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Настройка веб-интерфейса:${NC}"
|
||
if check_interactive; then
|
||
echo -n "Развернуть веб-клиент для управления нодой? [Y/n]: " >&2
|
||
read -r web_choice < /dev/tty
|
||
else
|
||
web_choice="Y"
|
||
log_info "Неинтерактивный режим: веб-клиент включен по умолчанию"
|
||
fi
|
||
|
||
if [[ ! $web_choice =~ ^[Nn]$ ]]; then
|
||
ENABLE_WEB_CLIENT="true"
|
||
log_success "Веб-клиент будет развернут"
|
||
else
|
||
ENABLE_WEB_CLIENT="false"
|
||
log_info "Веб-клиент отключен"
|
||
fi
|
||
|
||
# 6. SSL и домен (только для публичных нод с веб-клиентом)
|
||
if [ "$ALLOW_INCOMING" = "true" ] && [ "$ENABLE_WEB_CLIENT" = "true" ]; then
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Настройка SSL сертификата:${NC}"
|
||
if check_interactive; then
|
||
echo -n "Настроить SSL сертификат? [y/N]: " >&2
|
||
read -r ssl_choice < /dev/tty
|
||
else
|
||
ssl_choice="N"
|
||
log_info "Неинтерактивный режим: SSL отключен (требует ручной настройки)"
|
||
fi
|
||
|
||
if [[ $ssl_choice =~ ^[Yy]$ ]]; then
|
||
echo -n "Доменное имя: " >&2
|
||
read -r DOMAIN < /dev/tty
|
||
if [ -n "$DOMAIN" ]; then
|
||
echo -n "Email для уведомлений SSL: " >&2
|
||
read -r EMAIL < /dev/tty
|
||
if [ -n "$EMAIL" ]; then
|
||
ENABLE_SSL="true"
|
||
log_success "SSL будет настроен для домена: $DOMAIN"
|
||
else
|
||
log_error "Email не указан. SSL отключен"
|
||
fi
|
||
else
|
||
log_error "Домен не указан. SSL отключен"
|
||
fi
|
||
fi
|
||
else
|
||
log_info "SSL недоступен для данной конфигурации"
|
||
fi
|
||
|
||
# 7. Telegram API ключи
|
||
echo ""
|
||
echo -e "${PURPLE}❓ Настройка Telegram ботов (необязательно):${NC}"
|
||
if check_interactive; then
|
||
echo -n "TELEGRAM_API_KEY (основной бот) [Enter для пропуска]: " >&2
|
||
read -r TELEGRAM_API_KEY < /dev/tty
|
||
else
|
||
TELEGRAM_API_KEY=""
|
||
log_info "Неинтерактивный режим: Telegram боты отключены"
|
||
fi
|
||
|
||
if [ -n "$TELEGRAM_API_KEY" ]; then
|
||
log_success "TELEGRAM_API_KEY настроен"
|
||
if check_interactive; then
|
||
echo -n "CLIENT_TELEGRAM_API_KEY (клиентский бот) [Enter для пропуска]: " >&2
|
||
read -r CLIENT_TELEGRAM_API_KEY < /dev/tty
|
||
else
|
||
CLIENT_TELEGRAM_API_KEY=""
|
||
fi
|
||
|
||
if [ -n "$CLIENT_TELEGRAM_API_KEY" ]; then
|
||
log_success "CLIENT_TELEGRAM_API_KEY настроен"
|
||
else
|
||
log_info "Клиентский Telegram бот будет отключен"
|
||
fi
|
||
else
|
||
log_info "Telegram боты будут отключены"
|
||
fi
|
||
|
||
# 6. Подтверждение настроек
|
||
echo ""
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${WHITE} ПОДТВЕРЖДЕНИЕ НАСТРОЕК ${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo -e "${WHITE}Режим сети:${NC} $NETWORK_MODE"
|
||
echo -e "${WHITE}Тип ноды:${NC} $NODE_TYPE"
|
||
echo -e "${WHITE}Входящие соединения:${NC} $ALLOW_INCOMING"
|
||
echo -e "${WHITE}Docker socket:${NC} $DOCKER_SOCK_PATH"
|
||
echo -e "${WHITE}Bootstrap config:${NC} $BOOTSTRAP_CONFIG"
|
||
echo -e "${WHITE}Веб-клиент:${NC} $([ "$ENABLE_WEB_CLIENT" = "true" ] && echo "включен" || echo "отключен")"
|
||
echo -e "${WHITE}SSL сертификат:${NC} $([ "$ENABLE_SSL" = "true" ] && echo "включен для $DOMAIN" || echo "отключен")"
|
||
echo -e "${WHITE}Telegram основной:${NC} $([ -n "$TELEGRAM_API_KEY" ] && echo "настроен" || echo "отключен")"
|
||
echo -e "${WHITE}Telegram клиентский:${NC} $([ -n "$CLIENT_TELEGRAM_API_KEY" ] && echo "настроен" || echo "отключен")"
|
||
echo ""
|
||
|
||
if check_interactive; then
|
||
echo -n "Продолжить установку с этими настройками? [Y/n]: " >&2
|
||
read -r REPLY < /dev/tty
|
||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||
log_info "Установка отменена пользователем"
|
||
exit 0
|
||
fi
|
||
else
|
||
log_info "Неинтерактивный режим: продолжаем установку с настройками по умолчанию"
|
||
sleep 3
|
||
fi
|
||
}
|
||
|
||
# Установка зависимостей
|
||
install_dependencies() {
|
||
log_info "📦 Установка системных зависимостей..."
|
||
|
||
# Настройка неинтерактивного режима для apt
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
export NEEDRESTART_MODE=a
|
||
export NEEDRESTART_SUSPEND=1
|
||
|
||
# Обновление пакетов
|
||
case $OS_ID in
|
||
ubuntu|debian)
|
||
# Настройка для автоматического согласия с дефолтными настройками
|
||
echo 'DPkg::Options {"--force-confdef";"--force-confold";}' > /etc/apt/apt.conf.d/50unattended-upgrades-local
|
||
|
||
apt-get update -qq
|
||
apt-get upgrade -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
||
apt-get install -y -qq curl wget git unzip htop nano ufw fail2ban \
|
||
python3 python3-pip python3-venv build-essential \
|
||
postgresql-client jq netcat-openbsd
|
||
;;
|
||
centos|rhel|fedora)
|
||
if command -v dnf &> /dev/null; then
|
||
dnf update -y -q
|
||
dnf install -y -q curl wget git unzip htop nano firewalld \
|
||
python3 python3-pip python3-devel gcc gcc-c++ \
|
||
postgresql jq nc
|
||
else
|
||
yum update -y -q
|
||
yum install -y -q curl wget git unzip htop nano firewalld \
|
||
python3 python3-pip python3-devel gcc gcc-c++ \
|
||
postgresql jq nc
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
# Очистка переменных окружения
|
||
unset DEBIAN_FRONTEND
|
||
unset NEEDRESTART_MODE
|
||
unset NEEDRESTART_SUSPEND
|
||
|
||
log_success "Системные зависимости установлены"
|
||
}
|
||
|
||
# Установка Docker и Docker Compose
|
||
install_docker() {
|
||
if command -v docker &> /dev/null; then
|
||
log_info "Docker уже установлен: $(docker --version)"
|
||
else
|
||
log_info "🐳 Установка Docker..."
|
||
|
||
# Установка Docker через официальный скрипт
|
||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||
sh get-docker.sh
|
||
rm get-docker.sh
|
||
|
||
# Запуск и автозагрузка Docker
|
||
systemctl start docker
|
||
systemctl enable docker
|
||
|
||
log_success "Docker установлен: $(docker --version)"
|
||
fi
|
||
|
||
# Проверка Docker Compose
|
||
if command -v docker-compose &> /dev/null; then
|
||
log_info "Docker Compose уже установлен: $(docker-compose --version)"
|
||
else
|
||
log_info "🐳 Установка Docker Compose..."
|
||
|
||
# Установка последней версии Docker Compose
|
||
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
|
||
curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||
chmod +x /usr/local/bin/docker-compose
|
||
|
||
log_success "Docker Compose установлен: $(docker-compose --version)"
|
||
fi
|
||
|
||
# Проверка доступности Docker socket
|
||
if [ ! -S "$DOCKER_SOCK_PATH" ]; then
|
||
log_error "Docker socket недоступен: $DOCKER_SOCK_PATH"
|
||
log_info "Перезапуск Docker..."
|
||
systemctl restart docker
|
||
sleep 5
|
||
|
||
if [ ! -S "$DOCKER_SOCK_PATH" ]; then
|
||
log_error "Не удалось настроить Docker socket"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
log_success "Docker socket доступен: $DOCKER_SOCK_PATH"
|
||
}
|
||
|
||
# Создание директорий проекта
|
||
create_directories() {
|
||
log_info "📁 Создание структуры директорий..."
|
||
|
||
# Основные директории
|
||
mkdir -p "$PROJECT_DIR"
|
||
mkdir -p "$STORAGE_DIR"
|
||
mkdir -p "$CONFIG_DIR"
|
||
mkdir -p "$LOGS_DIR"
|
||
|
||
# Поддиректории для хранения
|
||
mkdir -p "$STORAGE_DIR"
|
||
|
||
# Права доступа
|
||
chmod 755 "$PROJECT_DIR"
|
||
chmod 755 "$STORAGE_DIR"
|
||
chmod 700 "$CONFIG_DIR"
|
||
chmod 755 "$LOGS_DIR"
|
||
|
||
log_success "Структура директорий создана"
|
||
log_info "Проект: $PROJECT_DIR"
|
||
log_info "Хранилище: $STORAGE_DIR"
|
||
log_info "Конфигурация: $CONFIG_DIR"
|
||
log_info "Логи: $LOGS_DIR"
|
||
}
|
||
|
||
# Клонирование репозиториев
|
||
clone_repositories() {
|
||
log_info "📥 Клонирование репозиториев MY Network v3.0..."
|
||
|
||
cd "$PROJECT_DIR"
|
||
|
||
# Если проект уже существует, удаляем старую версию
|
||
if [ -d "my-network" ]; then
|
||
log_info "Удаление существующего проекта..."
|
||
rm -rf my-network
|
||
fi
|
||
|
||
# Создаем структуру проекта
|
||
mkdir -p my-network
|
||
cd my-network
|
||
|
||
# Клонирование всех репозиториев
|
||
log_info "Клонирование uploader-bot..."
|
||
if git clone https://git.projscale.dev/my-dev/uploader-bot.git .; then
|
||
log_success "uploader-bot клонирован"
|
||
else
|
||
log_error "Ошибка клонирования uploader-bot"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Клонирование web2-client..."
|
||
if git clone https://git.projscale.dev/my-dev/web2-client.git web2-client; then
|
||
log_success "web2-client клонирован"
|
||
else
|
||
log_error "Ошибка клонирования web2-client"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Клонирование converter-module..."
|
||
if git clone https://git.projscale.dev/my-dev/converter-module.git converter-module; then
|
||
log_success "converter-module клонирован"
|
||
else
|
||
log_error "Ошибка клонирования converter-module"
|
||
exit 1
|
||
fi
|
||
|
||
log_success "Все репозитории клонированы в $PROJECT_DIR/my-network"
|
||
}
|
||
|
||
# Создание файлов проекта
|
||
create_project_files() {
|
||
log_info "📝 Создание файлов проекта..."
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
# Создание docker-compose.yml
|
||
cat > docker-compose.yml << 'EOF'
|
||
version: '3.8'
|
||
|
||
services:
|
||
app:
|
||
build: .
|
||
container_name: my-network-app
|
||
restart: unless-stopped
|
||
ports:
|
||
- "15100:15100"
|
||
volumes:
|
||
- ${STORAGE_PATH:-./storage}:/app/storage
|
||
- ${DOCKER_SOCK_PATH:-/var/run/docker.sock}:/var/run/docker.sock
|
||
- ./logs:/app/logs
|
||
environment:
|
||
- DATABASE_URL=${DATABASE_URL}
|
||
- REDIS_URL=${REDIS_URL}
|
||
- NODE_ID=${NODE_ID}
|
||
- NODE_TYPE=${NODE_TYPE}
|
||
- NODE_VERSION=${NODE_VERSION}
|
||
- NETWORK_MODE=${NETWORK_MODE}
|
||
- ALLOW_INCOMING_CONNECTIONS=${ALLOW_INCOMING_CONNECTIONS}
|
||
- SECRET_KEY=${SECRET_KEY}
|
||
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
|
||
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
|
||
- STORAGE_PATH=/app/storage
|
||
- API_HOST=${API_HOST}
|
||
- API_PORT=${API_PORT}
|
||
- DOCKER_SOCK_PATH=/var/run/docker.sock
|
||
- TELEGRAM_API_KEY=${TELEGRAM_API_KEY}
|
||
- CLIENT_TELEGRAM_API_KEY=${CLIENT_TELEGRAM_API_KEY}
|
||
- LOG_LEVEL=${LOG_LEVEL}
|
||
- LOG_PATH=/app/logs
|
||
- BOOTSTRAP_CONFIG=${BOOTSTRAP_CONFIG}
|
||
- MAX_PEER_CONNECTIONS=${MAX_PEER_CONNECTIONS}
|
||
- SYNC_INTERVAL=${SYNC_INTERVAL}
|
||
- CONVERT_MAX_PARALLEL=${CONVERT_MAX_PARALLEL}
|
||
- CONVERT_TIMEOUT=${CONVERT_TIMEOUT}
|
||
depends_on:
|
||
- postgres
|
||
- redis
|
||
networks:
|
||
- my-network
|
||
|
||
postgres:
|
||
image: postgres:15-alpine
|
||
container_name: my-network-postgres
|
||
restart: unless-stopped
|
||
environment:
|
||
- POSTGRES_DB=${POSTGRES_DB}
|
||
- POSTGRES_USER=${POSTGRES_USER}
|
||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||
volumes:
|
||
- postgres_data:/var/lib/postgresql/data
|
||
- ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
|
||
networks:
|
||
- my-network
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
container_name: my-network-redis
|
||
restart: unless-stopped
|
||
command: redis-server --appendonly yes
|
||
volumes:
|
||
- redis_data:/data
|
||
networks:
|
||
- my-network
|
||
|
||
volumes:
|
||
postgres_data:
|
||
redis_data:
|
||
|
||
networks:
|
||
my-network:
|
||
driver: bridge
|
||
EOF
|
||
|
||
# Создание Dockerfile
|
||
cat > Dockerfile << 'EOF'
|
||
FROM python:3.11-slim
|
||
|
||
WORKDIR /app
|
||
|
||
# Установка системных зависимостей
|
||
RUN apt-get update && apt-get install -y \
|
||
gcc \
|
||
g++ \
|
||
curl \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
|
||
# Копирование requirements и установка Python зависимостей
|
||
COPY requirements.txt .
|
||
RUN pip install --no-cache-dir -r requirements.txt
|
||
|
||
# Копирование кода приложения
|
||
COPY app/ ./app/
|
||
COPY alembic/ ./alembic/
|
||
COPY alembic.ini .
|
||
COPY bootstrap.json .
|
||
|
||
# Создание директорий
|
||
RUN mkdir -p /app/storage /app/logs
|
||
|
||
# Права доступа
|
||
RUN chmod +x /app/app/main.py
|
||
|
||
EXPOSE 15100
|
||
|
||
CMD ["python", "-m", "app.main"]
|
||
EOF
|
||
|
||
# Создание requirements.txt
|
||
cat > requirements.txt << 'EOF'
|
||
fastapi==0.104.1
|
||
uvicorn[standard]==0.24.0
|
||
pydantic==2.4.2
|
||
pydantic-settings==2.0.3
|
||
sqlalchemy==2.0.23
|
||
alembic==1.12.1
|
||
asyncpg==0.29.0
|
||
redis==5.0.1
|
||
aioredis==2.0.1
|
||
aiofiles==23.2.1
|
||
cryptography==41.0.7
|
||
python-jose[cryptography]==3.3.0
|
||
python-multipart==0.0.6
|
||
httpx==0.25.2
|
||
websockets==12.0
|
||
docker==6.1.3
|
||
base58==2.1.1
|
||
passlib[bcrypt]==1.7.4
|
||
python-telegram-bot==20.7
|
||
APScheduler==3.10.4
|
||
psutil==5.9.6
|
||
requests==2.31.0
|
||
PyYAML==6.0.1
|
||
python-dotenv==1.0.0
|
||
Pillow==10.1.0
|
||
ffmpeg-python==0.2.0
|
||
python-magic==0.4.27
|
||
jinja2==3.1.2
|
||
starlette==0.27.0
|
||
structlog==23.2.0
|
||
EOF
|
||
|
||
# Создание init_db.sql
|
||
cat > init_db.sql << 'EOF'
|
||
-- MY Network v3.0 Database Initialization
|
||
|
||
-- Extension for UUID generation
|
||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||
|
||
-- Create enum types
|
||
DO $$ BEGIN
|
||
CREATE TYPE content_status AS ENUM ('pending', 'processing', 'completed', 'failed');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
-- Create stored_content table (compatible with DEPRECATED-uploader-bot)
|
||
CREATE TABLE IF NOT EXISTS stored_content (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
hash VARCHAR(255) UNIQUE NOT NULL,
|
||
original_filename VARCHAR(255) NOT NULL,
|
||
file_type VARCHAR(100) NOT NULL,
|
||
file_size BIGINT NOT NULL,
|
||
content_type VARCHAR(255),
|
||
storage_path TEXT NOT NULL,
|
||
decrypted_path TEXT,
|
||
encrypted_path TEXT NOT NULL,
|
||
thumbnail_path TEXT,
|
||
converted_formats JSONB DEFAULT '{}',
|
||
metadata JSONB DEFAULT '{}',
|
||
encryption_key TEXT NOT NULL,
|
||
upload_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
last_accessed TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
access_count INTEGER DEFAULT 0,
|
||
status content_status DEFAULT 'pending',
|
||
uploader_id VARCHAR(255),
|
||
tags TEXT[],
|
||
description TEXT,
|
||
is_public BOOLEAN DEFAULT false,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
);
|
||
|
||
-- Create indexes for performance
|
||
CREATE INDEX IF NOT EXISTS idx_stored_content_hash ON stored_content(hash);
|
||
CREATE INDEX IF NOT EXISTS idx_stored_content_status ON stored_content(status);
|
||
CREATE INDEX IF NOT EXISTS idx_stored_content_upload_date ON stored_content(upload_date);
|
||
CREATE INDEX IF NOT EXISTS idx_stored_content_uploader_id ON stored_content(uploader_id);
|
||
CREATE INDEX IF NOT EXISTS idx_stored_content_file_type ON stored_content(file_type);
|
||
|
||
-- Create nodes table for network management
|
||
CREATE TABLE IF NOT EXISTS nodes (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
node_id VARCHAR(255) UNIQUE NOT NULL,
|
||
address INET NOT NULL,
|
||
port INTEGER NOT NULL,
|
||
public_key TEXT,
|
||
node_type VARCHAR(50) NOT NULL,
|
||
version VARCHAR(20) NOT NULL,
|
||
last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
trust_score DECIMAL(3,2) DEFAULT 1.0,
|
||
is_active BOOLEAN DEFAULT true,
|
||
metadata JSONB DEFAULT '{}',
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
);
|
||
|
||
-- Create indexes for nodes
|
||
CREATE INDEX IF NOT EXISTS idx_nodes_node_id ON nodes(node_id);
|
||
CREATE INDEX IF NOT EXISTS idx_nodes_address ON nodes(address);
|
||
CREATE INDEX IF NOT EXISTS idx_nodes_is_active ON nodes(is_active);
|
||
CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON nodes(last_seen);
|
||
|
||
-- Create content_sync table for decentralized synchronization
|
||
CREATE TABLE IF NOT EXISTS content_sync (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
content_hash VARCHAR(255) NOT NULL,
|
||
node_id VARCHAR(255) NOT NULL,
|
||
sync_status VARCHAR(50) DEFAULT 'pending',
|
||
attempts INTEGER DEFAULT 0,
|
||
last_attempt TIMESTAMP WITH TIME ZONE,
|
||
error_message TEXT,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||
);
|
||
|
||
-- Create indexes for content_sync
|
||
CREATE INDEX IF NOT EXISTS idx_content_sync_hash ON content_sync(content_hash);
|
||
CREATE INDEX IF NOT EXISTS idx_content_sync_node_id ON content_sync(node_id);
|
||
CREATE INDEX IF NOT EXISTS idx_content_sync_status ON content_sync(sync_status);
|
||
|
||
-- Create conversion_jobs table
|
||
CREATE TABLE IF NOT EXISTS conversion_jobs (
|
||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
content_id UUID REFERENCES stored_content(id),
|
||
target_format VARCHAR(50) NOT NULL,
|
||
status content_status DEFAULT 'pending',
|
||
priority INTEGER DEFAULT 5,
|
||
attempts INTEGER DEFAULT 0,
|
||
max_attempts INTEGER DEFAULT 3,
|
||
error_message TEXT,
|
||
conversion_params JSONB DEFAULT '{}',
|
||
output_path TEXT,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||
started_at TIMESTAMP WITH TIME ZONE,
|
||
completed_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- Create indexes for conversion_jobs
|
||
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_content_id ON conversion_jobs(content_id);
|
||
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_status ON conversion_jobs(status);
|
||
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_priority ON conversion_jobs(priority);
|
||
|
||
-- Update trigger for updated_at columns
|
||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
RETURNS TRIGGER AS $$
|
||
BEGIN
|
||
NEW.updated_at = NOW();
|
||
RETURN NEW;
|
||
END;
|
||
$$ language 'plpgsql';
|
||
|
||
-- Apply update triggers
|
||
DROP TRIGGER IF EXISTS update_stored_content_updated_at ON stored_content;
|
||
CREATE TRIGGER update_stored_content_updated_at
|
||
BEFORE UPDATE ON stored_content
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
DROP TRIGGER IF EXISTS update_nodes_updated_at ON nodes;
|
||
CREATE TRIGGER update_nodes_updated_at
|
||
BEFORE UPDATE ON nodes
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
DROP TRIGGER IF EXISTS update_content_sync_updated_at ON content_sync;
|
||
CREATE TRIGGER update_content_sync_updated_at
|
||
BEFORE UPDATE ON content_sync
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
DROP TRIGGER IF EXISTS update_conversion_jobs_updated_at ON conversion_jobs;
|
||
CREATE TRIGGER update_conversion_jobs_updated_at
|
||
BEFORE UPDATE ON conversion_jobs
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
EOF
|
||
|
||
# Создание alembic.ini
|
||
cat > alembic.ini << 'EOF'
|
||
[alembic]
|
||
script_location = alembic
|
||
prepend_sys_path = .
|
||
version_path_separator = os
|
||
sqlalchemy.url =
|
||
|
||
[post_write_hooks]
|
||
|
||
[loggers]
|
||
keys = root,sqlalchemy,alembic
|
||
|
||
[handlers]
|
||
keys = console
|
||
|
||
[formatters]
|
||
keys = generic
|
||
|
||
[logger_root]
|
||
level = WARN
|
||
handlers = console
|
||
qualname =
|
||
|
||
[logger_sqlalchemy]
|
||
level = WARN
|
||
handlers =
|
||
qualname = sqlalchemy.engine
|
||
|
||
[logger_alembic]
|
||
level = INFO
|
||
handlers =
|
||
qualname = alembic
|
||
|
||
[handler_console]
|
||
class = StreamHandler
|
||
args = (sys.stderr,)
|
||
level = NOTSET
|
||
formatter = generic
|
||
|
||
[formatter_generic]
|
||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||
datefmt = %H:%M:%S
|
||
EOF
|
||
|
||
log_success "Файлы проекта созданы"
|
||
}
|
||
|
||
# Загрузка и настройка проекта
|
||
setup_project() {
|
||
clone_repositories
|
||
create_project_files
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
log_success "Проект настроен в $PROJECT_DIR/my-network"
|
||
}
|
||
|
||
# Функция для проверки доступности Docker registry
|
||
check_docker_registry() {
|
||
log_info "Проверка доступности Docker registry..."
|
||
|
||
# Проверяем доступность registry.docker.io
|
||
if timeout 30 curl -s --connect-timeout 10 https://registry.docker.io/v2/ >/dev/null 2>&1; then
|
||
log_success "Docker registry доступен"
|
||
return 0
|
||
else
|
||
log_warn "Docker registry недоступен или медленно отвечает"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Функция для настройки Docker daemon timeout
|
||
configure_docker_timeout() {
|
||
log_info "Настройка Docker timeout для сетевых операций..."
|
||
|
||
# Создаем или обновляем Docker daemon config
|
||
local docker_config="/etc/docker/daemon.json"
|
||
local temp_config="/tmp/daemon.json.tmp"
|
||
|
||
if [ -f "$docker_config" ]; then
|
||
# Читаем существующий конфиг
|
||
cp "$docker_config" "$temp_config"
|
||
else
|
||
# Создаем новый конфиг
|
||
echo '{}' > "$temp_config"
|
||
fi
|
||
|
||
# Добавляем настройки timeout с помощью jq если доступен
|
||
if command -v jq >/dev/null 2>&1; then
|
||
jq '. + {
|
||
"registry-mirrors": [],
|
||
"insecure-registries": [],
|
||
"max-concurrent-downloads": 3,
|
||
"max-concurrent-uploads": 3,
|
||
"default-runtime": "runc"
|
||
}' "$temp_config" > "${temp_config}.new" && mv "${temp_config}.new" "$temp_config"
|
||
|
||
if sudo cp "$temp_config" "$docker_config" 2>/dev/null; then
|
||
log_info "Docker daemon конфигурация обновлена"
|
||
# Перезапускаем Docker только если это безопасно
|
||
if ! docker ps >/dev/null 2>&1 || [ "$(docker ps -q | wc -l)" -eq 0 ]; then
|
||
sudo systemctl reload docker 2>/dev/null || true
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
rm -f "$temp_config" 2>/dev/null
|
||
}
|
||
|
||
# Сборка converter образа
|
||
build_converter_image() {
|
||
log_info "🔧 Сборка converter образа из converter-module..."
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
# Проверяем наличие клонированного converter-module
|
||
if [ ! -d "converter-module" ]; then
|
||
log_error "converter-module не найден. Проверьте клонирование репозиториев."
|
||
return 1
|
||
fi
|
||
|
||
cd converter-module
|
||
|
||
# Проверяем наличие Dockerfile в папке converter
|
||
if [ ! -f "converter/Dockerfile" ]; then
|
||
log_error "Dockerfile не найден в converter-module/converter/"
|
||
return 1
|
||
fi
|
||
|
||
# Переходим в папку converter для сборки
|
||
cd converter
|
||
|
||
# Настраиваем Docker timeout
|
||
configure_docker_timeout
|
||
|
||
# Проверяем доступность registry
|
||
if ! check_docker_registry; then
|
||
log_warn "Docker registry недоступен, пробуем продолжить с увеличенным timeout"
|
||
fi
|
||
|
||
# Сборка converter образа из оригинального репозитория с retry логикой
|
||
log_info "Сборка Docker образа для converter..."
|
||
|
||
# Попытки сборки с retry
|
||
local max_attempts=3
|
||
local attempt=1
|
||
local success=false
|
||
|
||
while [ $attempt -le $max_attempts ] && [ "$success" = false ]; do
|
||
log_info "Попытка сборки $attempt из $max_attempts..."
|
||
|
||
# Сборка с увеличенными таймаутами и дополнительными параметрами
|
||
if docker build \
|
||
--network=host \
|
||
--build-arg BUILDKIT_PROGRESS=plain \
|
||
--build-arg HTTP_TIMEOUT=300 \
|
||
--build-arg HTTPS_TIMEOUT=300 \
|
||
-t my-network-converter:latest . ; then
|
||
log_success "Converter образ собран: my-network-converter:latest"
|
||
success=true
|
||
else
|
||
log_warn "Попытка $attempt неудачна"
|
||
|
||
if [ $attempt -lt $max_attempts ]; then
|
||
log_info "Ожидание 15 секунд перед следующей попыткой..."
|
||
sleep 15
|
||
|
||
# Очистка Docker build cache и системы при неудачной попытке
|
||
log_info "Очистка Docker cache..."
|
||
docker builder prune -f >/dev/null 2>&1 || true
|
||
docker system prune -f >/dev/null 2>&1 || true
|
||
|
||
# Попытка сброса сетевых настроек Docker
|
||
if [ $attempt -eq 2 ]; then
|
||
log_info "Перезапуск Docker daemon для сброса сетевых настроек..."
|
||
systemctl restart docker >/dev/null 2>&1 || true
|
||
sleep 10
|
||
fi
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
fi
|
||
done
|
||
|
||
if [ "$success" = false ]; then
|
||
log_error "Не удалось собрать converter образ после $max_attempts попыток"
|
||
log_warn "Возможные причины:"
|
||
log_warn "1. Проблемы с подключением к Docker Hub"
|
||
log_warn "2. Сетевые проблемы на сервере"
|
||
log_warn "3. Временные проблемы Docker Registry"
|
||
log_info "Установка продолжится без converter образа"
|
||
log_info "Converter можно собрать позже командой:"
|
||
log_info "cd $PROJECT_DIR/my-network/converter-module/converter && docker build -t my-network-converter:latest ."
|
||
fi
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
}
|
||
|
||
# Установка и настройка nginx
|
||
setup_nginx() {
|
||
if [ "$ENABLE_WEB_CLIENT" = "true" ] || [ "$ENABLE_SSL" = "true" ]; then
|
||
log_info "🌐 Установка и настройка nginx..."
|
||
|
||
# Установка nginx
|
||
case $OS_ID in
|
||
ubuntu|debian)
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
export NEEDRESTART_MODE=a
|
||
export NEEDRESTART_SUSPEND=1
|
||
|
||
apt-get update -qq
|
||
apt-get install -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
|
||
nginx certbot python3-certbot-nginx
|
||
|
||
unset DEBIAN_FRONTEND
|
||
unset NEEDRESTART_MODE
|
||
unset NEEDRESTART_SUSPEND
|
||
;;
|
||
centos|rhel|fedora)
|
||
if command -v dnf &> /dev/null; then
|
||
dnf install -y -q nginx certbot python3-certbot-nginx
|
||
else
|
||
yum install -y -q nginx certbot python3-certbot-nginx
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
# Развертывание web2-client
|
||
if [ "$ENABLE_WEB_CLIENT" = "true" ]; then
|
||
log_info "Развертывание web2-client из репозитория..."
|
||
|
||
# Проверяем наличие клонированного web2-client
|
||
if [ ! -d "$PROJECT_DIR/my-network/web2-client" ]; then
|
||
log_error "web2-client не найден. Проверьте клонирование репозиториев."
|
||
return 1
|
||
fi
|
||
|
||
# Создаем директорию для nginx
|
||
mkdir -p /var/www/my-network-web
|
||
|
||
# Копируем файлы web2-client
|
||
cd "$PROJECT_DIR/my-network/web2-client"
|
||
|
||
# Если есть сборка (build process), выполняем её
|
||
if [ -f "package.json" ]; then
|
||
log_info "Установка зависимостей web2-client..."
|
||
npm install || log_warn "Не удалось установить зависимости npm"
|
||
|
||
# Если есть build скрипт, выполняем сборку
|
||
if npm run build 2>/dev/null; then
|
||
log_success "Сборка web2-client завершена"
|
||
# Копируем собранные файлы
|
||
if [ -d "build" ]; then
|
||
cp -r build/* /var/www/my-network-web/
|
||
elif [ -d "dist" ]; then
|
||
cp -r dist/* /var/www/my-network-web/
|
||
else
|
||
cp -r . /var/www/my-network-web/
|
||
fi
|
||
else
|
||
log_warn "Сборка не требуется, копируем исходные файлы"
|
||
cp -r . /var/www/my-network-web/
|
||
fi
|
||
else
|
||
# Копируем файлы как есть
|
||
log_info "Копирование статических файлов web2-client..."
|
||
cp -r . /var/www/my-network-web/
|
||
fi
|
||
|
||
# Настройка прав доступа
|
||
chown -R www-data:www-data /var/www/my-network-web/
|
||
chmod -R 755 /var/www/my-network-web/
|
||
|
||
log_success "Web2-client развернут в /var/www/my-network-web"
|
||
fi
|
||
|
||
# Создание конфигурации nginx
|
||
cat > /etc/nginx/sites-available/my-network << EOF
|
||
# MY Network v3.0 nginx configuration
|
||
|
||
# Upstream для API
|
||
upstream my_network_api {
|
||
server 127.0.0.1:15100;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name ${DOMAIN:-localhost};
|
||
|
||
# Максимальный размер для chunked uploads
|
||
client_max_body_size 10G;
|
||
client_body_timeout 300s;
|
||
client_header_timeout 300s;
|
||
|
||
# Proxy buffering для больших файлов
|
||
proxy_buffering off;
|
||
proxy_request_buffering off;
|
||
proxy_max_temp_file_size 0;
|
||
|
||
# Статический контент (веб-интерфейс)
|
||
location / {
|
||
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " root /var/www/my-network-web;" || echo " return 404;")
|
||
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " index index.html;" || echo "")
|
||
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " try_files \$uri \$uri/ =404;" || echo "")
|
||
}
|
||
|
||
# API proxy
|
||
location /api/ {
|
||
proxy_pass http://my_network_api;
|
||
proxy_set_header Host \$host;
|
||
proxy_set_header X-Real-IP \$remote_addr;
|
||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||
|
||
# Для chunked uploads
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
proxy_buffering off;
|
||
proxy_cache off;
|
||
|
||
# Таймауты для больших файлов
|
||
proxy_connect_timeout 300s;
|
||
proxy_send_timeout 300s;
|
||
proxy_read_timeout 300s;
|
||
}
|
||
|
||
# Health check
|
||
location /health {
|
||
proxy_pass http://my_network_api;
|
||
proxy_set_header Host \$host;
|
||
proxy_set_header X-Real-IP \$remote_addr;
|
||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||
}
|
||
|
||
# Мониторинг
|
||
location /monitor {
|
||
proxy_pass http://my_network_api;
|
||
proxy_set_header Host \$host;
|
||
proxy_set_header X-Real-IP \$remote_addr;
|
||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||
}
|
||
}
|
||
EOF
|
||
|
||
# Активация сайта
|
||
if [ ! -L "/etc/nginx/sites-enabled/my-network" ]; then
|
||
ln -s /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
|
||
fi
|
||
|
||
# Отключение дефолтного сайта
|
||
if [ -L "/etc/nginx/sites-enabled/default" ]; then
|
||
rm /etc/nginx/sites-enabled/default
|
||
fi
|
||
|
||
# Тест конфигурации nginx
|
||
if nginx -t; then
|
||
log_success "Конфигурация nginx корректна"
|
||
else
|
||
log_error "Ошибка в конфигурации nginx"
|
||
return 1
|
||
fi
|
||
|
||
# Запуск nginx
|
||
systemctl enable nginx
|
||
systemctl restart nginx
|
||
|
||
log_success "Nginx настроен и запущен"
|
||
|
||
# Настройка SSL если нужно
|
||
if [ "$ENABLE_SSL" = "true" ] && [ -n "$DOMAIN" ] && [ -n "$EMAIL" ]; then
|
||
install_ssl_certificates
|
||
fi
|
||
else
|
||
log_info "Nginx пропущен (веб-клиент и SSL отключены)"
|
||
fi
|
||
}
|
||
|
||
# Установка SSL сертификатов
|
||
install_ssl_certificates() {
|
||
log_info "🔒 Установка SSL сертификата для $DOMAIN..."
|
||
|
||
# Проверка DNS записи
|
||
if ! host "$DOMAIN" > /dev/null 2>&1; then
|
||
log_warn "DNS запись для $DOMAIN не найдена"
|
||
log_info "Убедитесь что домен указывает на этот сервер"
|
||
if check_interactive; then
|
||
echo -n "Продолжить установку SSL? [y/N]: " >&2
|
||
read -r ssl_continue < /dev/tty
|
||
if [[ ! $ssl_continue =~ ^[Yy]$ ]]; then
|
||
log_info "Установка SSL пропущена"
|
||
return 0
|
||
fi
|
||
else
|
||
log_info "Неинтерактивный режим: пропускаем SSL (требует ручной настройки)"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# Проверка и остановка запущенных процессов certbot
|
||
log_info "Проверка запущенных процессов certbot..."
|
||
if pgrep -f certbot > /dev/null; then
|
||
log_warn "Обнаружен запущенный процесс certbot, завершаем..."
|
||
pkill -f certbot 2>/dev/null || true
|
||
sleep 5
|
||
fi
|
||
|
||
# Очистка временных файлов certbot
|
||
rm -rf /tmp/tmp*/log 2>/dev/null || true
|
||
|
||
# Проверка существующих сертификатов и их очистка при конфликте
|
||
log_info "Проверка существующих сертификатов..."
|
||
if [ -d "/etc/letsencrypt/live/$DOMAIN" ]; then
|
||
log_warn "Обнаружен существующий сертификат для $DOMAIN, удаляем для предотвращения конфликтов..."
|
||
certbot delete --cert-name "$DOMAIN" --non-interactive 2>/dev/null || true
|
||
sleep 2
|
||
fi
|
||
|
||
# Получение сертификата через certbot с явным указанием типа ключа
|
||
log_info "Запуск certbot для получения SSL сертификата..."
|
||
if certbot --nginx -d "$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive --redirect --key-type rsa --cert-name "$DOMAIN"; then
|
||
log_success "SSL сертификат установлен для $DOMAIN"
|
||
|
||
# Настройка автообновления
|
||
if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then
|
||
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
|
||
log_success "Автообновление SSL настроено"
|
||
fi
|
||
|
||
# Обновление firewall для HTTPS
|
||
case $OS_ID in
|
||
ubuntu|debian)
|
||
ufw allow 443/tcp 2>/dev/null || true
|
||
;;
|
||
centos|rhel|fedora)
|
||
firewall-cmd --permanent --add-service=https 2>/dev/null || true
|
||
firewall-cmd --reload 2>/dev/null || true
|
||
;;
|
||
esac
|
||
|
||
log_success "HTTPS порт открыт в firewall"
|
||
else
|
||
log_error "Ошибка установки SSL сертификата"
|
||
log_warn "Возможные причины:"
|
||
log_warn "1. DNS запись $DOMAIN не указывает на этот сервер"
|
||
log_warn "2. Порт 80 заблокирован или недоступен из интернета"
|
||
log_warn "3. Nginx не запущен или неправильно настроен"
|
||
log_warn "4. Достигнут лимит запросов Let's Encrypt"
|
||
log_warn "5. Другой процесс certbot уже запущен"
|
||
log_info "Система продолжит работу без SSL. SSL можно настроить позже вручную:"
|
||
log_info "certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect"
|
||
# НЕ завершаем скрипт, SSL не критичен для работы системы
|
||
fi
|
||
}
|
||
|
||
# Генерация конфигурации
|
||
generate_config() {
|
||
log_info "⚙️ Генерация конфигурации..."
|
||
|
||
# Генерация уникальных ключей
|
||
SECRET_KEY=$(openssl rand -hex 32)
|
||
JWT_SECRET_KEY=$(openssl rand -hex 32)
|
||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||
DB_PASSWORD=$(openssl rand -hex 16)
|
||
|
||
# Генерация NODE_ID
|
||
NODE_ID="node-$(date +%s)-$(shuf -i 1000-9999 -n 1)"
|
||
|
||
# Создание .env файла
|
||
cat > "$CONFIG_DIR/.env" << EOF
|
||
# MY Network v3.0 Configuration
|
||
# Generated: $(date)
|
||
|
||
# Node Configuration
|
||
NODE_ID=$NODE_ID
|
||
NODE_TYPE=$NODE_TYPE
|
||
NODE_VERSION=$NODE_VERSION
|
||
NETWORK_MODE=$NETWORK_MODE
|
||
ALLOW_INCOMING_CONNECTIONS=$ALLOW_INCOMING
|
||
|
||
# Database Configuration
|
||
DATABASE_URL=postgresql://myuser:$DB_PASSWORD@postgres:5432/mynetwork
|
||
POSTGRES_DB=mynetwork
|
||
POSTGRES_USER=myuser
|
||
POSTGRES_PASSWORD=$DB_PASSWORD
|
||
|
||
# Redis Configuration
|
||
REDIS_URL=redis://redis:6379/0
|
||
|
||
# Security
|
||
SECRET_KEY=$SECRET_KEY
|
||
JWT_SECRET_KEY=$JWT_SECRET_KEY
|
||
ENCRYPTION_KEY=$ENCRYPTION_KEY
|
||
|
||
# Storage
|
||
STORAGE_PATH=$STORAGE_DIR
|
||
|
||
# API Configuration
|
||
API_HOST=0.0.0.0
|
||
API_PORT=15100
|
||
|
||
# Docker Configuration
|
||
DOCKER_SOCK_PATH=$DOCKER_SOCK_PATH
|
||
|
||
# Telegram Bots
|
||
TELEGRAM_API_KEY=$TELEGRAM_API_KEY
|
||
CLIENT_TELEGRAM_API_KEY=$CLIENT_TELEGRAM_API_KEY
|
||
|
||
# Logging
|
||
LOG_LEVEL=INFO
|
||
LOG_PATH=$LOGS_DIR
|
||
|
||
# Network Configuration
|
||
BOOTSTRAP_CONFIG=$BOOTSTRAP_CONFIG
|
||
MAX_PEER_CONNECTIONS=50
|
||
SYNC_INTERVAL=300
|
||
|
||
# Converter Configuration
|
||
CONVERT_MAX_PARALLEL=3
|
||
CONVERT_TIMEOUT=300
|
||
EOF
|
||
|
||
# Создание bootstrap.json
|
||
if [ "$BOOTSTRAP_CONFIG" = "new" ]; then
|
||
cat > "$CONFIG_DIR/bootstrap.json" << EOF
|
||
{
|
||
"version": "$NODE_VERSION",
|
||
"network_id": "my-network-$(date +%s)",
|
||
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||
"bootstrap_nodes": [
|
||
{
|
||
"node_id": "$NODE_ID",
|
||
"address": "$(curl -s ifconfig.me || echo 'localhost')",
|
||
"port": 15100,
|
||
"public_key": "",
|
||
"trusted": true,
|
||
"node_type": "bootstrap"
|
||
}
|
||
],
|
||
"network_settings": {
|
||
"protocol_version": "3.0",
|
||
"max_peers": 50,
|
||
"sync_interval": 300,
|
||
"individual_decisions": true,
|
||
"no_consensus": true
|
||
}
|
||
}
|
||
EOF
|
||
log_success "Создан новый bootstrap.json для новой сети"
|
||
elif [ "$BOOTSTRAP_CONFIG" != "default" ]; then
|
||
cp "$BOOTSTRAP_CONFIG" "$CONFIG_DIR/bootstrap.json"
|
||
log_success "Скопирован кастомный bootstrap.json"
|
||
fi
|
||
|
||
# Копирование конфигурации в проект
|
||
cp "$CONFIG_DIR/.env" "$PROJECT_DIR/my-network/.env"
|
||
if [ -f "$CONFIG_DIR/bootstrap.json" ]; then
|
||
cp "$CONFIG_DIR/bootstrap.json" "$PROJECT_DIR/my-network/bootstrap.json"
|
||
fi
|
||
|
||
log_success "Конфигурация сгенерирована"
|
||
}
|
||
|
||
# Настройка firewall
|
||
setup_firewall() {
|
||
if [ "$ALLOW_INCOMING" = "true" ]; then
|
||
log_info "🔥 Настройка firewall для публичной ноды..."
|
||
|
||
case $OS_ID in
|
||
ubuntu|debian)
|
||
# UFW для Ubuntu/Debian
|
||
ufw default deny incoming
|
||
ufw default allow outgoing
|
||
ufw allow ssh
|
||
ufw allow 15100/tcp # API порт MY Network
|
||
ufw --force enable
|
||
;;
|
||
centos|rhel|fedora)
|
||
# Firewalld для CentOS/RHEL/Fedora
|
||
systemctl start firewalld
|
||
systemctl enable firewalld
|
||
firewall-cmd --permanent --add-service=ssh
|
||
firewall-cmd --permanent --add-port=15100/tcp
|
||
firewall-cmd --reload
|
||
;;
|
||
esac
|
||
|
||
log_success "Firewall настроен (порт 15100 открыт)"
|
||
else
|
||
log_info "Приватная нода - firewall настройка пропущена"
|
||
fi
|
||
}
|
||
|
||
# Сборка и запуск контейнеров
|
||
build_and_start() {
|
||
log_info "🐳 Сборка и запуск MY Network v3.0..."
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
# Остановка существующих контейнеров
|
||
docker-compose down 2>/dev/null || true
|
||
|
||
# Сборка образов
|
||
log_info "Сборка Docker образов..."
|
||
docker-compose build --no-cache
|
||
|
||
# Запуск сервисов
|
||
log_info "Запуск сервисов..."
|
||
docker-compose up -d
|
||
|
||
# Ожидание готовности сервисов
|
||
log_info "Ожидание готовности сервисов..."
|
||
sleep 30
|
||
|
||
# Проверка статуса контейнеров
|
||
if docker-compose ps | grep -q "Up"; then
|
||
log_success "Контейнеры запущены"
|
||
else
|
||
log_error "Ошибка запуска контейнеров"
|
||
docker-compose logs
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Инициализация базы данных
|
||
init_database() {
|
||
log_info "🗄️ Инициализация базы данных..."
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
# Ожидание готовности PostgreSQL
|
||
log_info "Ожидание готовности PostgreSQL..."
|
||
for i in {1..30}; do
|
||
if docker-compose exec -T postgres pg_isready -U myuser -d mynetwork > /dev/null 2>&1; then
|
||
break
|
||
fi
|
||
echo -n "."
|
||
sleep 2
|
||
done
|
||
echo ""
|
||
|
||
# Выполнение миграций
|
||
log_info "Выполнение миграций базы данных..."
|
||
docker-compose exec -T app alembic upgrade head
|
||
|
||
if [ $? -eq 0 ]; then
|
||
log_success "База данных инициализирована"
|
||
else
|
||
log_error "Ошибка инициализации базы данных"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Подключение к сети
|
||
connect_to_network() {
|
||
log_info "🌐 Подключение к MY Network..."
|
||
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
# Ожидание готовности API
|
||
log_info "Ожидание готовности API..."
|
||
for i in {1..60}; do
|
||
if curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
|
||
break
|
||
fi
|
||
echo -n "."
|
||
sleep 2
|
||
done
|
||
echo ""
|
||
|
||
if ! curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
|
||
log_error "API недоступно"
|
||
return 1
|
||
fi
|
||
|
||
log_success "API готово: http://localhost:15100"
|
||
|
||
# Статистика ноды
|
||
log_info "Получение статистики ноды..."
|
||
node_stats=$(curl -s "http://localhost:15100/api/v3/node/status" 2>/dev/null || echo "{}")
|
||
echo "$node_stats" | jq '.' 2>/dev/null || echo "Статистика недоступна"
|
||
|
||
# Подключение к bootstrap нодам (если не bootstrap)
|
||
if [ "$NODE_TYPE" != "bootstrap" ]; then
|
||
log_info "Попытка подключения к bootstrap нодам..."
|
||
|
||
# Автообнаружение пиров
|
||
curl -X POST "http://localhost:15100/api/v3/node/connect" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"auto_discover": true}' > /dev/null 2>&1
|
||
|
||
sleep 10
|
||
|
||
# Проверка подключений
|
||
peers_response=$(curl -s "http://localhost:15100/api/v3/node/peers" 2>/dev/null || echo '{"count": 0}')
|
||
peer_count=$(echo "$peers_response" | jq -r '.count // 0' 2>/dev/null || echo "0")
|
||
|
||
if [ "$peer_count" -gt 0 ]; then
|
||
log_success "Подключено к $peer_count пир(ам)"
|
||
else
|
||
log_warn "Пока не удалось подключиться к другим нодам"
|
||
log_info "Нода будет продолжать попытки подключения в фоне"
|
||
fi
|
||
else
|
||
log_info "Bootstrap нода готова принимать подключения"
|
||
fi
|
||
|
||
# Статистика сети
|
||
network_stats=$(curl -s "http://localhost:15100/api/v3/network/stats" 2>/dev/null || echo '{}')
|
||
log_info "Статистика сети:"
|
||
echo "$network_stats" | jq '.' 2>/dev/null || echo "Статистика сети недоступна"
|
||
}
|
||
|
||
# Создание systemd сервиса
|
||
create_systemd_service() {
|
||
log_info "⚙️ Создание systemd сервиса..."
|
||
|
||
cat > /etc/systemd/system/my-network.service << EOF
|
||
[Unit]
|
||
Description=MY Network v3.0 Node
|
||
Requires=docker.service
|
||
After=docker.service
|
||
|
||
[Service]
|
||
Type=oneshot
|
||
RemainAfterExit=yes
|
||
WorkingDirectory=$PROJECT_DIR/my-network
|
||
ExecStart=/usr/local/bin/docker-compose up -d
|
||
ExecStop=/usr/local/bin/docker-compose down
|
||
TimeoutStartSec=300
|
||
User=root
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
# Активация сервиса
|
||
systemctl daemon-reload
|
||
systemctl enable my-network
|
||
|
||
log_success "Systemd сервис создан и активирован"
|
||
}
|
||
|
||
# Финальный отчет
|
||
final_report() {
|
||
clear
|
||
echo -e "${GREEN}"
|
||
cat << "EOF"
|
||
╔══════════════════════════════════════════════════════════════╗
|
||
║ УСТАНОВКА ЗАВЕРШЕНА! ║
|
||
║ MY Network v3.0 ║
|
||
╚══════════════════════════════════════════════════════════════╝
|
||
EOF
|
||
echo -e "${NC}"
|
||
|
||
echo ""
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${WHITE} СТАТУС СИСТЕМЫ ${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
|
||
# Проверка сервисов
|
||
cd "$PROJECT_DIR/my-network"
|
||
|
||
echo ""
|
||
echo -e "${WHITE}🐳 Docker контейнеры:${NC}"
|
||
docker-compose ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}"
|
||
|
||
echo ""
|
||
echo -e "${WHITE}⚙️ Systemd сервис:${NC}"
|
||
if systemctl is-active my-network >/dev/null 2>&1; then
|
||
echo -e " ${GREEN}✅ my-network: активен${NC}"
|
||
else
|
||
echo -e " ${RED}❌ my-network: неактивен${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${WHITE}🌐 Сетевая конфигурация:${NC}"
|
||
echo -e " Тип ноды: ${YELLOW}$NODE_TYPE${NC}"
|
||
echo -e " Режим сети: ${YELLOW}$NETWORK_MODE${NC}"
|
||
echo -e " Входящие соединения: ${YELLOW}$ALLOW_INCOMING${NC}"
|
||
|
||
if [ "$ALLOW_INCOMING" = "true" ]; then
|
||
PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "неизвестен")
|
||
echo -e " Публичный IP: ${YELLOW}$PUBLIC_IP${NC}"
|
||
if netstat -tlnp 2>/dev/null | grep -q ":15100 "; then
|
||
echo -e " API порт 15100: ${GREEN}✅ открыт${NC}"
|
||
else
|
||
echo -e " API порт 15100: ${RED}❌ недоступен${NC}"
|
||
fi
|
||
else
|
||
echo -e " Режим: ${YELLOW}Приватная нода (только исходящие)${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${WHITE}📡 API и интерфейсы:${NC}"
|
||
if curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
|
||
echo -e " API: ${GREEN}✅ http://localhost:15100${NC}"
|
||
echo -e " Health: ${GREEN}✅ http://localhost:15100/health${NC}"
|
||
echo -e " Мониторинг: ${GREEN}✅ http://localhost:15100/api/my/monitor/${NC}"
|
||
echo -e " Статус ноды: ${GREEN}✅ http://localhost:15100/api/v3/node/status${NC}"
|
||
else
|
||
echo -e " API: ${RED}❌ недоступно${NC}"
|
||
fi
|
||
|
||
# Веб-клиент и SSL информация
|
||
if [ "$ENABLE_WEB_CLIENT" = "true" ]; then
|
||
echo ""
|
||
echo -e "${WHITE}🌐 Веб-интерфейс:${NC}"
|
||
if systemctl is-active nginx >/dev/null 2>&1; then
|
||
if [ "$ENABLE_SSL" = "true" ] && [ -n "$DOMAIN" ]; then
|
||
echo -e " Веб-интерфейс: ${GREEN}✅ https://$DOMAIN${NC}"
|
||
echo -e " SSL сертификат: ${GREEN}✅ активен для $DOMAIN${NC}"
|
||
echo -e " HTTP redirect: ${GREEN}✅ автоматический переход на HTTPS${NC}"
|
||
else
|
||
if [ "$ALLOW_INCOMING" = "true" ]; then
|
||
PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "localhost")
|
||
echo -e " Веб-интерфейс: ${GREEN}✅ http://$PUBLIC_IP${NC}"
|
||
else
|
||
echo -e " Веб-интерфейс: ${GREEN}✅ http://localhost${NC}"
|
||
fi
|
||
echo -e " SSL: ${YELLOW}⚠ не настроен${NC}"
|
||
fi
|
||
echo -e " Nginx: ${GREEN}✅ работает${NC}"
|
||
echo -e " Chunked upload: ${GREEN}✅ поддерживается (до 10GB)${NC}"
|
||
else
|
||
echo -e " Веб-интерфейс: ${RED}❌ Nginx не запущен${NC}"
|
||
fi
|
||
else
|
||
echo -e " Веб-интерфейс: ${YELLOW}⚠ отключен${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${WHITE}🤖 Telegram боты:${NC}"
|
||
if [ -n "$TELEGRAM_API_KEY" ]; then
|
||
echo -e " Основной бот: ${GREEN}✅ настроен${NC}"
|
||
else
|
||
echo -e " Основной бот: ${YELLOW}⚠ отключен${NC}"
|
||
fi
|
||
|
||
if [ -n "$CLIENT_TELEGRAM_API_KEY" ]; then
|
||
echo -e " Клиентский бот: ${GREEN}✅ настроен${NC}"
|
||
else
|
||
echo -e " Клиентский бот: ${YELLOW}⚠ отключен${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${WHITE}💾 Хранилище и конвертация:${NC}"
|
||
echo -e " Путь хранения: ${YELLOW}$STORAGE_DIR${NC}"
|
||
echo -e " Docker socket: ${YELLOW}$DOCKER_SOCK_PATH${NC}"
|
||
if [ -S "$DOCKER_SOCK_PATH" ]; then
|
||
echo -e " Converter: ${GREEN}✅ готов к работе${NC}"
|
||
# Проверка наличия converter образа
|
||
if docker images | grep -q "my-network-converter"; then
|
||
echo -e " Converter образ: ${GREEN}✅ my-network-converter:latest${NC}"
|
||
else
|
||
echo -e " Converter образ: ${YELLOW}⚠ не найден${NC}"
|
||
fi
|
||
else
|
||
echo -e " Converter: ${RED}❌ Docker socket недоступен${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${WHITE} КОМАНДЫ УПРАВЛЕНИЯ ${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo -e "${WHITE}🔧 Управление сервисом:${NC}"
|
||
echo -e " ${BLUE}systemctl start my-network${NC} # Запуск"
|
||
echo -e " ${BLUE}systemctl stop my-network${NC} # Остановка"
|
||
echo -e " ${BLUE}systemctl restart my-network${NC} # Перезапуск"
|
||
echo -e " ${BLUE}systemctl status my-network${NC} # Статус"
|
||
echo ""
|
||
echo -e "${WHITE}📊 Мониторинг:${NC}"
|
||
echo -e " ${BLUE}docker-compose -f $PROJECT_DIR/my-network/docker-compose.yml logs -f${NC}"
|
||
echo -e " ${BLUE}curl http://localhost:15100/api/v3/node/status | jq${NC}"
|
||
echo -e " ${BLUE}curl http://localhost:15100/api/v3/network/stats | jq${NC}"
|
||
echo ""
|
||
echo -e "${WHITE}📁 Важные файлы:${NC}"
|
||
echo -e " Конфигурация: ${YELLOW}$CONFIG_DIR/.env${NC}"
|
||
echo -e " Bootstrap: ${YELLOW}$CONFIG_DIR/bootstrap.json${NC}"
|
||
echo -e " Логи: ${YELLOW}$LOGS_DIR/${NC}"
|
||
echo -e " Проект: ${YELLOW}$PROJECT_DIR/my-network/${NC}"
|
||
|
||
echo ""
|
||
echo -e "${GREEN}🎉 MY Network v3.0 успешно установлен и запущен!${NC}"
|
||
echo ""
|
||
echo -e "${WHITE}Особенности v3.0:${NC}"
|
||
echo -e " ✅ Полная децентрализация без консенсуса"
|
||
echo -e " ✅ Мгновенная трансляция контента"
|
||
echo -e " ✅ Автоматическая конвертация через Docker"
|
||
echo -e " ✅ Блокчейн интеграция для uploader-bot"
|
||
echo -e " ✅ Поддержка приватных и публичных нод"
|
||
echo ""
|
||
|
||
# Сохранение отчета
|
||
cat > "$PROJECT_DIR/installation-report.txt" << EOF
|
||
MY Network v3.0 Installation Report
|
||
Generated: $(date)
|
||
|
||
Node Configuration:
|
||
- Node ID: $NODE_ID
|
||
- Node Type: $NODE_TYPE
|
||
- Network Mode: $NETWORK_MODE
|
||
- Version: $NODE_VERSION
|
||
- Allow Incoming: $ALLOW_INCOMING
|
||
|
||
Paths:
|
||
- Project: $PROJECT_DIR/my-network
|
||
- Storage: $STORAGE_DIR
|
||
- Config: $CONFIG_DIR
|
||
- Logs: $LOGS_DIR
|
||
- Docker Socket: $DOCKER_SOCK_PATH
|
||
|
||
API Endpoints:
|
||
- Health: http://localhost:15100/health
|
||
- Node Status: http://localhost:15100/api/v3/node/status
|
||
- Network Stats: http://localhost:15100/api/v3/network/stats
|
||
- Monitoring: http://localhost:15100/api/my/monitor/
|
||
|
||
Management Commands:
|
||
- Start: systemctl start my-network
|
||
- Stop: systemctl stop my-network
|
||
- Status: systemctl status my-network
|
||
- Logs: docker-compose -f $PROJECT_DIR/my-network/docker-compose.yml logs -f
|
||
|
||
Telegram Bots:
|
||
- Main Bot: $([ -n "$TELEGRAM_API_KEY" ] && echo "enabled" || echo "disabled")
|
||
- Client Bot: $([ -n "$CLIENT_TELEGRAM_API_KEY" ] && echo "enabled" || echo "disabled")
|
||
EOF
|
||
|
||
log_success "Отчет об установке сохранен: $PROJECT_DIR/installation-report.txt"
|
||
}
|
||
|
||
# Основная функция
|
||
main() {
|
||
show_banner
|
||
check_root
|
||
detect_os
|
||
interactive_setup
|
||
install_dependencies
|
||
install_docker
|
||
create_directories
|
||
setup_project
|
||
build_converter_image
|
||
setup_nginx
|
||
generate_config
|
||
setup_firewall
|
||
build_and_start
|
||
init_database
|
||
connect_to_network
|
||
create_systemd_service
|
||
final_report
|
||
}
|
||
|
||
# Обработка ошибок
|
||
error_handler() {
|
||
log_error "Ошибка на строке $1. Код выхода: $2"
|
||
log_error "Установка прервана"
|
||
|
||
# Показать логи для диагностики
|
||
if [ -d "$PROJECT_DIR/my-network" ]; then
|
||
log_info "Логи для диагностики:"
|
||
cd "$PROJECT_DIR/my-network"
|
||
docker-compose logs --tail=50
|
||
fi
|
||
|
||
exit $2
|
||
}
|
||
|
||
trap 'error_handler $LINENO $?' ERR
|
||
|
||
# Запуск установки
|
||
main "$@" |