diff --git a/docker-compose.production.yml b/docker-compose.production.yml new file mode 100644 index 0000000..6bd3bd1 --- /dev/null +++ b/docker-compose.production.yml @@ -0,0 +1,286 @@ +version: '3.8' + +services: + # Основное приложение MY UPLOADER BOT + app: + build: + context: . + dockerfile: Dockerfile.simple + container_name: my-uploader-app + restart: unless-stopped + depends_on: + - postgres + - redis + ports: + - "127.0.0.1:15100:15100" + volumes: + - app_data:/app/data + - app_logs:/app/logs + - ./app/my_network/bootstrap.json:/app/app/my_network/bootstrap.json:ro + - /var/run/docker.sock:/var/run/docker.sock # Для управления converter контейнерами + - converter_shared:/shared/converter # Общая папка для converter заданий + environment: + # Основные настройки + - NODE_ENV=production + - DEBUG=false + + # Database + - DATABASE_URL=postgresql://my_user:${POSTGRES_PASSWORD}@postgres:5432/my_uploader_db + - POSTGRES_HOST=postgres + - POSTGRES_PORT=5432 + - POSTGRES_DB=my_uploader_db + - POSTGRES_USER=my_user + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + + # Redis + - REDIS_URL=redis://redis:6379/0 + - REDIS_HOST=redis + - REDIS_PORT=6379 + + # Security + - SECRET_KEY=${SECRET_KEY} + - JWT_SECRET=${JWT_SECRET} + - ENCRYPTION_KEY=${ENCRYPTION_KEY} + + # MY Network + - MY_NETWORK_NODE_ID=${MY_NETWORK_NODE_ID} + - MY_NETWORK_PORT=15100 + - MY_NETWORK_HOST=0.0.0.0 + - MY_NETWORK_DOMAIN=${MY_NETWORK_DOMAIN} + - MY_NETWORK_SSL_ENABLED=true + + # API Settings + - API_HOST=0.0.0.0 + - API_PORT=15100 + - API_WORKERS=4 + - MAX_UPLOAD_SIZE=100MB + + # Converter Settings (on-demand) + - CONVERTER_DOCKER_IMAGE=my-converter:latest + - CONVERTER_SHARED_PATH=/shared/converter + - CONVERTER_MAX_PARALLEL=3 + - CONVERTER_TIMEOUT=300 + + # Logging + - LOG_LEVEL=INFO + - LOG_FORMAT=json + + networks: + - uploader_network + healthcheck: + test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:15100/health')"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # PostgreSQL Database + postgres: + image: postgres:15-alpine + container_name: my-postgres + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql/data + - ./scripts/init-db-production.sql:/docker-entrypoint-initdb.d/01-init.sql:ro + environment: + - POSTGRES_DB=my_uploader_db + - POSTGRES_USER=my_user + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_INITDB_ARGS=--auth-host=md5 + ports: + - "127.0.0.1:5432:5432" + networks: + - uploader_network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U my_user -d my_uploader_db"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache + redis: + image: redis:7-alpine + container_name: my-redis + restart: unless-stopped + volumes: + - redis_data:/data + - ./redis.conf:/usr/local/etc/redis/redis.conf:ro + command: redis-server /usr/local/etc/redis/redis.conf + ports: + - "127.0.0.1:6379:6379" + networks: + - uploader_network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + + # Web2 Client + web2-client: + build: + context: ./modules/web2-client + dockerfile: Dockerfile + container_name: my-web2-client + restart: unless-stopped + depends_on: + - app + ports: + - "127.0.0.1:3000:3000" + volumes: + - web2_uploads:/app/uploads + environment: + - NODE_ENV=production + - API_URL=http://app:15100 + - UPLOAD_PATH=/app/uploads + networks: + - uploader_network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Nginx Reverse Proxy + nginx: + image: nginx:alpine + container_name: my-nginx + restart: unless-stopped + depends_on: + - app + - web2-client + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/sites-enabled:/etc/nginx/sites-enabled:ro + - ./ssl:/etc/nginx/ssl:ro + - nginx_logs:/var/log/nginx + - ./nginx/html:/usr/share/nginx/html:ro + networks: + - uploader_network + healthcheck: + test: ["CMD", "nginx", "-t"] + interval: 30s + timeout: 10s + retries: 3 + + # Monitoring Stack (Prometheus + Grafana) + prometheus: + image: prom/prometheus:latest + container_name: my-prometheus + restart: unless-stopped + ports: + - "127.0.0.1:9090:9090" + volumes: + - prometheus_data:/prometheus + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + networks: + - uploader_network + + grafana: + image: grafana/grafana:latest + container_name: my-grafana + restart: unless-stopped + ports: + - "127.0.0.1:3001:3000" + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana:/etc/grafana/provisioning:ro + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} + - GF_USERS_ALLOW_SIGN_UP=false + - GF_SERVER_DOMAIN=${MY_NETWORK_DOMAIN} + networks: + - uploader_network + + # Log Management + loki: + image: grafana/loki:latest + container_name: my-loki + restart: unless-stopped + ports: + - "127.0.0.1:3100:3100" + volumes: + - loki_data:/loki + - ./monitoring/loki.yml:/etc/loki/local-config.yaml:ro + command: -config.file=/etc/loki/local-config.yaml + networks: + - uploader_network + + # Log Collection + promtail: + image: grafana/promtail:latest + container_name: my-promtail + restart: unless-stopped + volumes: + - /var/log:/var/log:ro + - ./monitoring/promtail.yml:/etc/promtail/config.yml:ro + - app_logs:/var/log/app:ro + - nginx_logs:/var/log/nginx:ro + command: -config.file=/etc/promtail/config.yml + networks: + - uploader_network + +# ============================================================================= +# CONVERTER MODULE BUILD (собирается, но не запускается постоянно) +# ============================================================================= +# Этот сервис используется только для сборки image +# Основное приложение будет создавать контейнеры on-demand + + converter-build: + build: + context: ./modules/converter-module + dockerfile: Dockerfile + image: my-converter:latest + container_name: my-converter-build + profiles: ["build-only"] # Запускается только при сборке + volumes: + - converter_shared:/shared/converter + networks: + - uploader_network + +volumes: + # Application data + app_data: + driver: local + app_logs: + driver: local + + # Database + postgres_data: + driver: local + redis_data: + driver: local + + # Modules + converter_shared: # Общая папка для on-demand converter заданий + driver: local + web2_uploads: + driver: local + + # Infrastructure + nginx_logs: + driver: local + + # Monitoring + prometheus_data: + driver: local + grafana_data: + driver: local + loki_data: + driver: local + +networks: + uploader_network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 \ No newline at end of file diff --git a/scripts/create_nginx_config.sh b/scripts/create_nginx_config.sh new file mode 100644 index 0000000..18b403a --- /dev/null +++ b/scripts/create_nginx_config.sh @@ -0,0 +1,328 @@ +#!/bin/bash + +# ============================================================================= +# СОЗДАНИЕ NGINX КОНФИГУРАЦИИ ДЛЯ MY UPLOADER BOT +# ============================================================================= + +if [ $# -lt 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +DOMAIN="$1" +PROJECT_DIR="$2" +NGINX_CONFIG="/etc/nginx/sites-available/my-uploader-bot" + +echo "Создание Nginx конфигурации для $DOMAIN..." + +cat > "$NGINX_CONFIG" << EOF +# ============================================================================= +# MY UPLOADER BOT - NGINX CONFIGURATION +# ============================================================================= +# Domain: $DOMAIN +# Generated: $(date) +# ============================================================================= + +# Rate limiting zones +limit_req_zone \$binary_remote_addr zone=api_limit:10m rate=10r/s; +limit_req_zone \$binary_remote_addr zone=upload_limit:10m rate=2r/s; +limit_req_zone \$binary_remote_addr zone=general_limit:10m rate=20r/s; + +# Upstream для основного приложения +upstream my_uploader_app { + server 127.0.0.1:15100 max_fails=3 fail_timeout=30s; + keepalive 32; +} + +# Upstream для web2-client +upstream web2_client { + server 127.0.0.1:3000 max_fails=3 fail_timeout=30s; + keepalive 16; +} + +# Upstream для converter-module +upstream converter_module { + server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; + keepalive 16; +} + +# Redirect HTTP to HTTPS +server { + listen 80; + listen [::]:80; + server_name $DOMAIN www.$DOMAIN; + + # ACME challenge для Let's Encrypt + location /.well-known/acme-challenge/ { + root /var/www/html; + try_files \$uri =404; + } + + # Redirect всех остальных запросов на HTTPS + location / { + return 301 https://\$server_name\$request_uri; + } +} + +# HTTPS Main Server +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name $DOMAIN www.$DOMAIN; + + # SSL Configuration + ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/$DOMAIN/chain.pem; + + # SSL Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_stapling on; + ssl_stapling_verify on; + + # Security Headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; img-src 'self' data: https: blob:; connect-src 'self' https: wss: ws:; media-src 'self' https: blob:; object-src 'none'; base-uri 'self'; form-action 'self';" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + + # Basic Settings + client_max_body_size 100M; + client_body_timeout 60s; + client_header_timeout 60s; + keepalive_timeout 65s; + send_timeout 60s; + + # Gzip Compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/javascript + application/xml+rss + application/json + application/ld+json + image/svg+xml; + + # Logging + access_log /var/log/nginx/my-uploader-bot.access.log; + error_log /var/log/nginx/my-uploader-bot.error.log; + + # ========================================================================== + # ОСНОВНЫЕ МАРШРУТЫ + # ========================================================================== + + # API routes + location /api/ { + limit_req zone=api_limit burst=20 nodelay; + + proxy_pass http://my_uploader_app; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header X-Forwarded-Port \$server_port; + + proxy_cache_bypass \$http_upgrade; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # Специальные настройки для upload endpoints + location /api/upload/ { + limit_req zone=upload_limit burst=5 nodelay; + client_max_body_size 500M; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_pass http://my_uploader_app; + } + } + + # Health check endpoint + location /health { + proxy_pass http://my_uploader_app; + proxy_http_version 1.1; + 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; + + access_log off; + } + + # Telegram webhook endpoint + location /webhook/ { + limit_req zone=api_limit burst=50 nodelay; + + proxy_pass http://my_uploader_app; + proxy_http_version 1.1; + 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; + } + + # Static files for main app + location /static/ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files \$uri @app_static; + } + + location @app_static { + proxy_pass http://my_uploader_app; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_cache_valid 200 1d; + } + + # ========================================================================== + # WEB2-CLIENT (фронтенд интерфейс) + # ========================================================================== + + location /upload/ { + limit_req zone=general_limit burst=10 nodelay; + + proxy_pass http://web2_client/; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_cache_bypass \$http_upgrade; + } + + location /client/ { + limit_req zone=general_limit burst=10 nodelay; + + proxy_pass http://web2_client/; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_cache_bypass \$http_upgrade; + } + + # ========================================================================== + # CONVERTER-MODULE (конвертер медиа) + # ========================================================================== + + location /convert/ { + limit_req zone=upload_limit burst=3 nodelay; + client_max_body_size 500M; + + proxy_pass http://converter_module/; + proxy_http_version 1.1; + 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; + + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + + # ========================================================================== + # ОСНОВНОЙ МАРШРУТ (главная страница) + # ========================================================================== + + location / { + limit_req zone=general_limit burst=20 nodelay; + + proxy_pass http://my_uploader_app; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header X-Forwarded-Port \$server_port; + proxy_cache_bypass \$http_upgrade; + } + + # ========================================================================== + # СПЕЦИАЛЬНЫЕ ФАЙЛЫ + # ========================================================================== + + # robots.txt + location = /robots.txt { + return 200 "User-agent: *\nDisallow: /api/\nDisallow: /webhook/\nAllow: /\n"; + add_header Content-Type text/plain; + } + + # favicon.ico + location = /favicon.ico { + proxy_pass http://my_uploader_app; + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Защита от доступа к скрытым файлам + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Защита от доступа к конфигурационным файлам + location ~* \.(env|conf|config|ini|log|bak|backup|tmp)$ { + deny all; + access_log off; + log_not_found off; + } +} + +# ============================================================================= +# ДОПОЛНИТЕЛЬНАЯ БЕЗОПАСНОСТЬ +# ============================================================================= + +# Блокировка ботов и сканеров +map \$http_user_agent \$blocked_agent { + ~*malicious 1; + ~*bot 0; + ~*crawler 0; + ~*spider 0; + default 0; +} + +# Блокировка IP по геолокации (опционально) +# geo \$blocked_country { +# default 0; +# CN 1; # Китай +# RU 0; # Россия разрешена +# } +EOF + +echo "✅ Nginx конфигурация создана: $NGINX_CONFIG" + +# Создание директории для логов +mkdir -p /var/log/nginx + +# Проверка синтаксиса +echo "Проверка синтаксиса Nginx..." +nginx -t -c /etc/nginx/nginx.conf + +echo "✅ Nginx конфигурация готова для $DOMAIN" \ No newline at end of file diff --git a/scripts/create_systemd_services.sh b/scripts/create_systemd_services.sh new file mode 100644 index 0000000..4ef358d --- /dev/null +++ b/scripts/create_systemd_services.sh @@ -0,0 +1,568 @@ +#!/bin/bash + +# ============================================================================= +# СОЗДАНИЕ SYSTEMD СЕРВИСОВ ДЛЯ MY UPLOADER BOT +# ============================================================================= + +if [ $# -lt 1 ]; then + echo "Usage: $0 [user]" + exit 1 +fi + +PROJECT_DIR="$1" +SERVICE_USER="${2:-service}" + +echo "Создание systemd сервисов для MY Uploader Bot..." +echo "Проект: $PROJECT_DIR" +echo "Пользователь: $SERVICE_USER" + +# Проверка прав root +if [ "$EUID" -ne 0 ]; then + echo "❌ Этот скрипт должен запускаться с правами root" + exit 1 +fi + +# ============================================================================= +# ОСНОВНОЙ СЕРВИС MY-UPLOADER-BOT +# ============================================================================= + +echo "📝 Создание сервиса my-uploader-bot..." + +cat > /etc/systemd/system/my-uploader-bot.service << EOF +[Unit] +Description=MY Uploader Bot - Distributed Content Network +Documentation=https://github.com/your-org/my-uploader-bot +After=network.target postgresql.service redis.service docker.service +Wants=postgresql.service redis.service docker.service + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR +Environment=PATH=/usr/local/bin:/usr/bin:/bin +Environment=PYTHONPATH=$PROJECT_DIR +Environment=PYTHONUNBUFFERED=1 + +# Переменные окружения +EnvironmentFile=$PROJECT_DIR/.env + +# Команда запуска +ExecStart=/usr/bin/python3 -m app.main +ExecReload=/bin/kill -HUP \$MAINPID + +# Настройки перезапуска +Restart=always +RestartSec=10 +StartLimitInterval=60 +StartLimitBurst=3 + +# Безопасность +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=$PROJECT_DIR + +# Ресурсы +MemoryMax=512M +CPUQuota=50% + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-uploader-bot + +[Install] +WantedBy=multi-user.target +EOF + +echo "✅ Сервис my-uploader-bot создан" + +# ============================================================================= +# СЕРВИС MY NETWORK BOOTSTRAP NODE +# ============================================================================= + +echo "📝 Создание сервиса my-network-bootstrap..." + +cat > /etc/systemd/system/my-network-bootstrap.service << EOF +[Unit] +Description=MY Network Bootstrap Node +Documentation=https://github.com/your-org/my-uploader-bot +After=network.target +Requires=network.target + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR +Environment=PATH=/usr/local/bin:/usr/bin:/bin +Environment=PYTHONPATH=$PROJECT_DIR +Environment=PYTHONUNBUFFERED=1 + +# Переменные окружения +EnvironmentFile=$PROJECT_DIR/.env +Environment=MY_NETWORK_MODE=bootstrap +Environment=MY_NETWORK_PORT=15100 + +# Команда запуска bootstrap узла +ExecStart=/usr/bin/python3 -c " +import sys +sys.path.insert(0, '$PROJECT_DIR') +from app.my_network.bootstrap_node import start_bootstrap_node +start_bootstrap_node() +" + +# Настройки перезапуска +Restart=always +RestartSec=5 +StartLimitInterval=60 +StartLimitBurst=5 + +# Безопасность +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=$PROJECT_DIR + +# Ресурсы +MemoryMax=256M +CPUQuota=25% + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-network-bootstrap + +[Install] +WantedBy=multi-user.target +EOF + +echo "✅ Сервис my-network-bootstrap создан" + +# ============================================================================= +# СЕРВИС WEB2 CLIENT +# ============================================================================= + +if [ -d "$PROJECT_DIR/modules/web2-client" ]; then + echo "📝 Создание сервиса my-web2-client..." + + cat > /etc/systemd/system/my-web2-client.service << EOF +[Unit] +Description=MY Web2 Client - Web Interface +Documentation=https://github.com/your-org/my-uploader-bot +After=network.target +Requires=network.target + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR/modules/web2-client +Environment=PATH=/usr/local/bin:/usr/bin:/bin +Environment=NODE_ENV=production +Environment=API_URL=http://localhost:15100 + +# Команда запуска +ExecStart=/usr/bin/node server.js + +# Настройки перезапуска +Restart=always +RestartSec=10 +StartLimitInterval=60 +StartLimitBurst=3 + +# Безопасность +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=$PROJECT_DIR/modules/web2-client + +# Ресурсы +MemoryMax=256M +CPUQuota=25% + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-web2-client + +[Install] +WantedBy=multi-user.target +EOF + + echo "✅ Сервис my-web2-client создан" +fi + +# ============================================================================= +# СЕРВИС DOCKER COMPOSE УПРАВЛЕНИЯ +# ============================================================================= + +echo "📝 Создание сервиса my-docker-compose..." + +cat > /etc/systemd/system/my-docker-compose.service << EOF +[Unit] +Description=MY Uploader Bot - Docker Compose Stack +Documentation=https://github.com/your-org/my-uploader-bot +After=docker.service network.target +Requires=docker.service +Before=my-uploader-bot.service + +[Service] +Type=oneshot +RemainAfterExit=yes +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR +Environment=PATH=/usr/local/bin:/usr/bin:/bin + +# Команды запуска и остановки +ExecStart=/usr/local/bin/docker-compose -f docker-compose.production.yml up -d +ExecStop=/usr/local/bin/docker-compose -f docker-compose.production.yml down +ExecReload=/usr/local/bin/docker-compose -f docker-compose.production.yml restart + +# Настройки +TimeoutStartSec=300 +TimeoutStopSec=120 + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-docker-compose + +[Install] +WantedBy=multi-user.target +EOF + +echo "✅ Сервис my-docker-compose создан" + +# ============================================================================= +# СЕРВИС CONVERTER BUILDER (только сборка image) +# ============================================================================= + +echo "📝 Создание сервиса my-converter-builder..." + +cat > /etc/systemd/system/my-converter-builder.service << EOF +[Unit] +Description=MY Converter Builder - Build Docker Image +Documentation=https://github.com/your-org/my-uploader-bot +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR +Environment=PATH=/usr/local/bin:/usr/bin:/bin + +# Команда сборки converter image +ExecStart=/usr/local/bin/docker-compose -f docker-compose.production.yml build converter-build + +# Настройки +TimeoutStartSec=600 +RemainAfterExit=yes + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-converter-builder + +[Install] +WantedBy=multi-user.target +EOF + +echo "✅ Сервис my-converter-builder создан" + +# ============================================================================= +# СЕРВИС МОНИТОРИНГА MY NETWORK +# ============================================================================= + +echo "📝 Создание сервиса my-network-monitor..." + +cat > /etc/systemd/system/my-network-monitor.service << EOF +[Unit] +Description=MY Network Monitor - Health Checker +Documentation=https://github.com/your-org/my-uploader-bot +After=network.target my-uploader-bot.service +Wants=my-uploader-bot.service + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$PROJECT_DIR +Environment=PATH=/usr/local/bin:/usr/bin:/bin +Environment=PYTHONPATH=$PROJECT_DIR +Environment=PYTHONUNBUFFERED=1 + +# Переменные окружения +EnvironmentFile=$PROJECT_DIR/.env + +# Команда запуска мониторинга +ExecStart=/usr/bin/python3 -c " +import asyncio +import sys +import time +import logging +import subprocess +import docker +sys.path.insert(0, '$PROJECT_DIR') + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger('my-network-monitor') + +def check_converter_image(): + '''Проверяет наличие и обновляет converter image''' + try: + client = docker.from_env() + images = client.images.list('my-converter:latest') + if not images: + logger.warning('Converter image не найден, собираем...') + subprocess.run(['systemctl', 'start', 'my-converter-builder'], check=True) + else: + logger.info('Converter image готов к использованию') + except Exception as e: + logger.error(f'Ошибка проверки converter image: {e}') + +async def monitor_health(): + while True: + try: + # Проверка основного здоровья системы + logger.info('MY Network: Health check passed') + + # Проверка converter image каждые 10 минут + if int(time.time()) % 600 == 0: + check_converter_image() + + except Exception as e: + logger.error(f'MY Network: Health check failed: {e}') + await asyncio.sleep(30) + +if __name__ == '__main__': + asyncio.run(monitor_health()) +" + +# Настройки перезапуска +Restart=always +RestartSec=30 +StartLimitInterval=300 +StartLimitBurst=5 + +# Безопасность +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes + +# Ресурсы +MemoryMax=64M +CPUQuota=10% + +# Логирование +StandardOutput=journal +StandardError=journal +SyslogIdentifier=my-network-monitor + +[Install] +WantedBy=multi-user.target +EOF + +echo "✅ Сервис my-network-monitor создан" + +# ============================================================================= +# НАСТРОЙКА ЛОГРОТАЦИИ +# ============================================================================= + +echo "📝 Настройка logrotate для MY Network..." + +cat > /etc/logrotate.d/my-network << EOF +# Ротация логов MY Network +$PROJECT_DIR/logs/*.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + create 644 $SERVICE_USER $SERVICE_USER + postrotate + systemctl reload my-uploader-bot my-network-bootstrap my-web2-client || true + endscript +} + +# Ротация логов systemd для MY Network +/var/log/journal/my-uploader-bot.log +/var/log/journal/my-network-bootstrap.log +/var/log/journal/my-web2-client.log +/var/log/journal/my-network-monitor.log +/var/log/journal/my-docker-compose.log { + weekly + missingok + rotate 4 + compress + delaycompress + notifempty +} +EOF + +echo "✅ Logrotate настроен" + +# ============================================================================= +# ПРИМЕНЕНИЕ НАСТРОЕК +# ============================================================================= + +echo "🔄 Применение настроек systemd..." + +# Перезагрузка systemd +systemctl daemon-reload + +# Включение автозапуска сервисов +systemctl enable my-docker-compose.service +systemctl enable my-converter-builder.service +systemctl enable my-uploader-bot.service +systemctl enable my-network-bootstrap.service +systemctl enable my-network-monitor.service + +if [ -f "/etc/systemd/system/my-web2-client.service" ]; then + systemctl enable my-web2-client.service +fi + +echo "✅ Все сервисы включены для автозапуска" + +# ============================================================================= +# СОЗДАНИЕ УПРАВЛЯЮЩИХ СКРИПТОВ +# ============================================================================= + +echo "📝 Создание управляющих скриптов..." + +# Скрипт запуска всех сервисов +cat > "$PROJECT_DIR/start_all_services.sh" << EOF +#!/bin/bash +echo "🚀 Запуск всех сервисов MY Network..." + +# Сначала запуск Docker Compose stack +sudo systemctl start my-docker-compose + +# Сборка converter image если нужно +sudo systemctl start my-converter-builder + +# Запуск основных сервисов +sudo systemctl start my-uploader-bot +sudo systemctl start my-network-bootstrap +sudo systemctl start my-network-monitor + +if systemctl list-unit-files | grep -q my-web2-client; then + sudo systemctl start my-web2-client +fi + +echo "✅ Все сервисы запущены" +sudo systemctl status my-uploader-bot my-network-bootstrap --no-pager +EOF + +# Скрипт остановки всех сервисов +cat > "$PROJECT_DIR/stop_all_services.sh" << EOF +#!/bin/bash +echo "🛑 Остановка всех сервисов MY Network..." + +sudo systemctl stop my-uploader-bot +sudo systemctl stop my-network-bootstrap +sudo systemctl stop my-network-monitor + +if systemctl list-unit-files | grep -q my-web2-client; then + sudo systemctl stop my-web2-client +fi + +# Остановка Docker Compose stack +sudo systemctl stop my-docker-compose + +echo "✅ Все сервисы остановлены" +EOF + +# Скрипт проверки статуса +cat > "$PROJECT_DIR/check_services.sh" << EOF +#!/bin/bash +echo "📊 Статус сервисов MY Network:" +echo "================================" + +sudo systemctl status my-docker-compose --no-pager -l +echo "" +sudo systemctl status my-uploader-bot --no-pager -l +echo "" +sudo systemctl status my-network-bootstrap --no-pager -l +echo "" +sudo systemctl status my-network-monitor --no-pager -l + +if systemctl list-unit-files | grep -q my-web2-client; then + echo "" + sudo systemctl status my-web2-client --no-pager -l +fi + +echo "" +echo "🐳 Docker контейнеры:" +sudo docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" + +echo "" +echo "🖼️ Docker images:" +sudo docker images | grep my- + +echo "" +echo "🌐 Активные порты:" +sudo netstat -tlnp | grep -E ":(15100|3000|80|443)" +EOF + +# Скрипт пересборки converter +cat > "$PROJECT_DIR/rebuild_converter.sh" << EOF +#!/bin/bash +echo "🔄 Пересборка converter image..." + +cd $PROJECT_DIR +sudo systemctl stop my-converter-builder +sudo docker rmi my-converter:latest 2>/dev/null || true +sudo systemctl start my-converter-builder + +echo "✅ Converter image пересобран" +sudo docker images | grep my-converter +EOF + +# Установка прав доступа +chmod +x "$PROJECT_DIR/start_all_services.sh" +chmod +x "$PROJECT_DIR/stop_all_services.sh" +chmod +x "$PROJECT_DIR/check_services.sh" +chmod +x "$PROJECT_DIR/rebuild_converter.sh" +chown $SERVICE_USER:$SERVICE_USER "$PROJECT_DIR"/*.sh + +echo "✅ Управляющие скрипты созданы" + +echo "" +echo "🎉 SystemD сервисы успешно настроены!" +echo "" +echo "📋 Созданные сервисы:" +echo " • my-docker-compose.service - Docker Compose stack" +echo " • my-converter-builder.service - Сборка converter image" +echo " • my-uploader-bot.service - Основное приложение" +echo " • my-network-bootstrap.service - Bootstrap узел" +echo " • my-network-monitor.service - Мониторинг сети" +if [ -f "/etc/systemd/system/my-web2-client.service" ]; then + echo " • my-web2-client.service - Web интерфейс" +fi +echo "" +echo "🎛️ Управление сервисами:" +echo " Запуск всех: $PROJECT_DIR/start_all_services.sh" +echo " Остановка всех: $PROJECT_DIR/stop_all_services.sh" +echo " Проверка: $PROJECT_DIR/check_services.sh" +echo " Пересборка converter: $PROJECT_DIR/rebuild_converter.sh" +echo "" +echo "📝 Логи сервисов:" +echo " journalctl -u my-uploader-bot -f" +echo " journalctl -u my-network-bootstrap -f" +echo " journalctl -u my-docker-compose -f" +echo "" +echo "💡 Converter работает on-demand:" +echo " - Image собирается один раз при установке" +echo " - MY Uploader Bot создает контейнеры по мере необходимости" +echo " - Контейнеры автоматически удаляются после завершения задач" +echo "" \ No newline at end of file diff --git a/scripts/init-db-production.sql b/scripts/init-db-production.sql new file mode 100644 index 0000000..89192d7 --- /dev/null +++ b/scripts/init-db-production.sql @@ -0,0 +1,383 @@ +-- ============================================================================= +-- MY UPLOADER BOT - PRODUCTION DATABASE INITIALIZATION +-- ============================================================================= + +-- Создание расширений +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +CREATE EXTENSION IF NOT EXISTS "btree_gin"; + +-- Настройка SSL (если требуется) +-- ALTER SYSTEM SET ssl = on; +-- ALTER SYSTEM SET ssl_cert_file = '/var/lib/postgresql/server.crt'; +-- ALTER SYSTEM SET ssl_key_file = '/var/lib/postgresql/server.key'; + +-- ============================================================================= +-- СОЗДАНИЕ СХЕМ +-- ============================================================================= + +-- Основная схема приложения +CREATE SCHEMA IF NOT EXISTS app; + +-- Схема для MY Network +CREATE SCHEMA IF NOT EXISTS my_network; + +-- Схема для мониторинга +CREATE SCHEMA IF NOT EXISTS monitoring; + +-- ============================================================================= +-- ТАБЛИЦЫ ПОЛЬЗОВАТЕЛЕЙ +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS app.users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + salt VARCHAR(255) NOT NULL, + is_active BOOLEAN DEFAULT true, + is_verified BOOLEAN DEFAULT false, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + last_login TIMESTAMP WITH TIME ZONE, + + -- Индексы + CONSTRAINT users_username_check CHECK (length(username) >= 3), + CONSTRAINT users_email_check CHECK (email ~* '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$') +); + +-- Индексы для пользователей +CREATE INDEX IF NOT EXISTS idx_users_username ON app.users(username); +CREATE INDEX IF NOT EXISTS idx_users_email ON app.users(email); +CREATE INDEX IF NOT EXISTS idx_users_active ON app.users(is_active); +CREATE INDEX IF NOT EXISTS idx_users_created_at ON app.users(created_at); + +-- ============================================================================= +-- ТАБЛИЦЫ КОНТЕНТА +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS app.content ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID NOT NULL REFERENCES app.users(id) ON DELETE CASCADE, + filename VARCHAR(255) NOT NULL, + original_filename VARCHAR(255) NOT NULL, + content_type VARCHAR(100) NOT NULL, + file_size BIGINT NOT NULL, + file_hash VARCHAR(64) NOT NULL, + upload_path TEXT NOT NULL, + + -- Метаданные + metadata JSONB DEFAULT '{}', + tags TEXT[] DEFAULT '{}', + + -- MY Network данные + my_network_id VARCHAR(64) UNIQUE, + replication_status VARCHAR(20) DEFAULT 'pending', + replicated_nodes TEXT[] DEFAULT '{}', + + -- Статус + status VARCHAR(20) DEFAULT 'uploaded', + is_public BOOLEAN DEFAULT false, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + -- Ограничения + CONSTRAINT content_file_size_check CHECK (file_size > 0), + CONSTRAINT content_status_check CHECK (status IN ('uploaded', 'processing', 'ready', 'error', 'deleted')), + CONSTRAINT content_replication_status_check CHECK (replication_status IN ('pending', 'replicating', 'replicated', 'failed')) +); + +-- Индексы для контента +CREATE INDEX IF NOT EXISTS idx_content_user_id ON app.content(user_id); +CREATE INDEX IF NOT EXISTS idx_content_file_hash ON app.content(file_hash); +CREATE INDEX IF NOT EXISTS idx_content_my_network_id ON app.content(my_network_id); +CREATE INDEX IF NOT EXISTS idx_content_status ON app.content(status); +CREATE INDEX IF NOT EXISTS idx_content_replication_status ON app.content(replication_status); +CREATE INDEX IF NOT EXISTS idx_content_created_at ON app.content(created_at); +CREATE INDEX IF NOT EXISTS idx_content_metadata ON app.content USING GIN(metadata); +CREATE INDEX IF NOT EXISTS idx_content_tags ON app.content USING GIN(tags); + +-- ============================================================================= +-- MY NETWORK ТАБЛИЦЫ +-- ============================================================================= + +-- Узлы MY Network +CREATE TABLE IF NOT EXISTS my_network.nodes ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + node_id VARCHAR(64) UNIQUE NOT NULL, + hostname VARCHAR(255) NOT NULL, + port INTEGER NOT NULL DEFAULT 15100, + public_key TEXT, + + -- Статус узла + status VARCHAR(20) DEFAULT 'active', + last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + version VARCHAR(20), + + -- Статистика + total_content BIGINT DEFAULT 0, + total_storage BIGINT DEFAULT 0, + + -- Метаданные + metadata JSONB DEFAULT '{}', + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT nodes_status_check CHECK (status IN ('active', 'inactive', 'banned', 'maintenance')) +); + +-- Индексы для узлов +CREATE INDEX IF NOT EXISTS idx_nodes_node_id ON my_network.nodes(node_id); +CREATE INDEX IF NOT EXISTS idx_nodes_status ON my_network.nodes(status); +CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON my_network.nodes(last_seen); + +-- Репликация контента между узлами +CREATE TABLE IF NOT EXISTS my_network.content_replications ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + content_id UUID NOT NULL REFERENCES app.content(id) ON DELETE CASCADE, + source_node_id VARCHAR(64) NOT NULL, + target_node_id VARCHAR(64) NOT NULL, + + -- Статус репликации + status VARCHAR(20) DEFAULT 'pending', + started_at TIMESTAMP WITH TIME ZONE, + completed_at TIMESTAMP WITH TIME ZONE, + error_message TEXT, + + -- Метаданные + file_size BIGINT, + transfer_speed BIGINT, -- bytes per second + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT replications_status_check CHECK (status IN ('pending', 'in_progress', 'completed', 'failed', 'cancelled')), + CONSTRAINT replications_unique_transfer UNIQUE (content_id, target_node_id) +); + +-- Индексы для репликации +CREATE INDEX IF NOT EXISTS idx_replications_content_id ON my_network.content_replications(content_id); +CREATE INDEX IF NOT EXISTS idx_replications_status ON my_network.content_replications(status); +CREATE INDEX IF NOT EXISTS idx_replications_source_node ON my_network.content_replications(source_node_id); +CREATE INDEX IF NOT EXISTS idx_replications_target_node ON my_network.content_replications(target_node_id); + +-- ============================================================================= +-- МОНИТОРИНГ И ЛОГИ +-- ============================================================================= + +-- Системные события +CREATE TABLE IF NOT EXISTS monitoring.system_events ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_type VARCHAR(50) NOT NULL, + severity VARCHAR(20) DEFAULT 'info', + source VARCHAR(100) NOT NULL, + message TEXT NOT NULL, + + -- Дополнительные данные + details JSONB DEFAULT '{}', + user_id UUID REFERENCES app.users(id), + node_id VARCHAR(64), + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT events_severity_check CHECK (severity IN ('debug', 'info', 'warning', 'error', 'critical')) +); + +-- Индексы для событий +CREATE INDEX IF NOT EXISTS idx_events_type ON monitoring.system_events(event_type); +CREATE INDEX IF NOT EXISTS idx_events_severity ON monitoring.system_events(severity); +CREATE INDEX IF NOT EXISTS idx_events_source ON monitoring.system_events(source); +CREATE INDEX IF NOT EXISTS idx_events_created_at ON monitoring.system_events(created_at); +CREATE INDEX IF NOT EXISTS idx_events_user_id ON monitoring.system_events(user_id); + +-- Метрики производительности +CREATE TABLE IF NOT EXISTS monitoring.performance_metrics ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + metric_name VARCHAR(100) NOT NULL, + metric_value NUMERIC NOT NULL, + metric_unit VARCHAR(20), + + -- Контекст + source VARCHAR(100) NOT NULL, + tags JSONB DEFAULT '{}', + + measured_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + -- Партиционирование по времени + PARTITION BY RANGE (measured_at) +); + +-- Создание партиций для метрик (по месяцам) +CREATE TABLE IF NOT EXISTS monitoring.performance_metrics_current + PARTITION OF monitoring.performance_metrics + FOR VALUES FROM (date_trunc('month', NOW())) TO (date_trunc('month', NOW() + interval '1 month')); + +-- Индексы для метрик +CREATE INDEX IF NOT EXISTS idx_metrics_name_time ON monitoring.performance_metrics(metric_name, measured_at); +CREATE INDEX IF NOT EXISTS idx_metrics_source ON monitoring.performance_metrics(source); + +-- ============================================================================= +-- ФУНКЦИИ И ТРИГГЕРЫ +-- ============================================================================= + +-- Функция обновления updated_at +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Триггеры для обновления updated_at +CREATE TRIGGER trigger_users_updated_at + BEFORE UPDATE ON app.users + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER trigger_content_updated_at + BEFORE UPDATE ON app.content + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER trigger_nodes_updated_at + BEFORE UPDATE ON my_network.nodes + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- Функция логирования событий +CREATE OR REPLACE FUNCTION log_system_event( + p_event_type VARCHAR(50), + p_message TEXT, + p_severity VARCHAR(20) DEFAULT 'info', + p_source VARCHAR(100) DEFAULT 'system', + p_details JSONB DEFAULT '{}', + p_user_id UUID DEFAULT NULL, + p_node_id VARCHAR(64) DEFAULT NULL +) +RETURNS UUID AS $$ +DECLARE + event_id UUID; +BEGIN + INSERT INTO monitoring.system_events ( + event_type, message, severity, source, details, user_id, node_id + ) VALUES ( + p_event_type, p_message, p_severity, p_source, p_details, p_user_id, p_node_id + ) RETURNING id INTO event_id; + + RETURN event_id; +END; +$$ LANGUAGE plpgsql; + +-- ============================================================================= +-- ПРЕДСТАВЛЕНИЯ (VIEWS) +-- ============================================================================= + +-- Активные узлы +CREATE OR REPLACE VIEW my_network.active_nodes AS +SELECT + node_id, + hostname, + port, + status, + last_seen, + total_content, + total_storage, + version +FROM my_network.nodes +WHERE status = 'active' + AND last_seen > NOW() - INTERVAL '1 hour'; + +-- Статистика контента +CREATE OR REPLACE VIEW app.content_stats AS +SELECT + COUNT(*) as total_files, + SUM(file_size) as total_size, + COUNT(DISTINCT user_id) as unique_users, + COUNT(*) FILTER (WHERE status = 'ready') as ready_files, + COUNT(*) FILTER (WHERE replication_status = 'replicated') as replicated_files, + AVG(file_size) as avg_file_size +FROM app.content +WHERE status != 'deleted'; + +-- ============================================================================= +-- НАЧАЛЬНЫЕ ДАННЫЕ +-- ============================================================================= + +-- Создание администратора (пароль: admin123) +-- Хеш создан с использованием bcrypt +INSERT INTO app.users (username, email, password_hash, salt, is_active, is_verified) +VALUES ( + 'admin', + 'admin@mynetwork.local', + '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LowN5wVrtJr.pjdXW', + 'production_salt_2024', + true, + true +) ON CONFLICT (username) DO NOTHING; + +-- Регистрация локального узла +INSERT INTO my_network.nodes (node_id, hostname, port, status, version, metadata) +VALUES ( + 'local-production-node', + 'localhost', + 15100, + 'active', + '1.0.0', + '{"type": "production", "bootstrap": true}' +) ON CONFLICT (node_id) DO NOTHING; + +-- Начальное событие +SELECT log_system_event( + 'system_init', + 'Production database initialized successfully', + 'info', + 'database', + '{"version": "1.0.0", "environment": "production"}' +); + +-- ============================================================================= +-- НАСТРОЙКИ ПРОИЗВОДИТЕЛЬНОСТИ +-- ============================================================================= + +-- Оптимизация для production +ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements'; +ALTER SYSTEM SET track_activities = on; +ALTER SYSTEM SET track_counts = on; +ALTER SYSTEM SET track_io_timing = on; +ALTER SYSTEM SET log_statement = 'mod'; +ALTER SYSTEM SET log_min_duration_statement = 1000; +ALTER SYSTEM SET max_connections = 200; +ALTER SYSTEM SET shared_buffers = '256MB'; +ALTER SYSTEM SET effective_cache_size = '1GB'; +ALTER SYSTEM SET maintenance_work_mem = '64MB'; +ALTER SYSTEM SET checkpoint_completion_target = 0.9; +ALTER SYSTEM SET wal_buffers = '16MB'; + +-- Применение настроек +SELECT pg_reload_conf(); + +-- ============================================================================= +-- ПРАВА ДОСТУПА +-- ============================================================================= + +-- Права для пользователя приложения +GRANT USAGE ON SCHEMA app TO my_user; +GRANT USAGE ON SCHEMA my_network TO my_user; +GRANT USAGE ON SCHEMA monitoring TO my_user; + +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO my_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA my_network TO my_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA monitoring TO my_user; + +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA app TO my_user; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA my_network TO my_user; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA monitoring TO my_user; + +-- Права на функции +GRANT EXECUTE ON FUNCTION update_updated_at_column() TO my_user; +GRANT EXECUTE ON FUNCTION log_system_event(VARCHAR, TEXT, VARCHAR, VARCHAR, JSONB, UUID, VARCHAR) TO my_user; + +COMMIT; + +-- Финальное сообщение +\echo 'MY Uploader Bot production database initialized successfully!' +\echo 'Default admin user: admin / admin123' +\echo 'Please change the default password immediately!' \ No newline at end of file diff --git a/scripts/setup_modules.sh b/scripts/setup_modules.sh new file mode 100644 index 0000000..62e7360 --- /dev/null +++ b/scripts/setup_modules.sh @@ -0,0 +1,442 @@ +#!/bin/bash + +# ============================================================================= +# НАСТРОЙКА ДОПОЛНИТЕЛЬНЫХ МОДУЛЕЙ MY UPLOADER BOT +# ============================================================================= + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +PROJECT_DIR="$1" +MODULES_DIR="$PROJECT_DIR/modules" + +echo "Настройка дополнительных модулей в $MODULES_DIR..." + +# Создание директории для модулей +mkdir -p "$MODULES_DIR" +cd "$MODULES_DIR" + +# ============================================================================= +# CONVERTER-MODULE (Конвертер медиа файлов) +# ============================================================================= + +echo "📦 Настройка converter-module..." + +if [ ! -d "converter-module" ]; then + echo "Создание converter-module структуры..." + mkdir -p converter-module/{converter,logs,in_examples,out_examples,output} + + # Создание основного файла converter-module + cat > "converter-module/app.py" << 'EOF' +#!/usr/bin/env python3 +""" +MY UPLOADER BOT - CONVERTER MODULE +Конвертер медиа файлов для MY Network +""" + +import os +import sys +import asyncio +import logging +from pathlib import Path +from fastapi import FastAPI, File, UploadFile, HTTPException +from fastapi.responses import JSONResponse, FileResponse +from fastapi.middleware.cors import CORSMiddleware +import uvicorn + +# Настройка логирования +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('logs/converter.log'), + logging.StreamHandler() + ] +) + +logger = logging.getLogger(__name__) + +app = FastAPI( + title="MY Converter Module", + description="Конвертер медиа файлов для MY Network", + version="1.0.0" +) + +# CORS настройки +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # В продакшене ограничить + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Создание необходимых директорий +os.makedirs("output", exist_ok=True) +os.makedirs("logs", exist_ok=True) + +@app.get("/") +async def root(): + return {"message": "MY Converter Module", "status": "running"} + +@app.get("/health") +async def health(): + return {"status": "healthy", "module": "converter"} + +@app.post("/convert/") +async def convert_file(file: UploadFile = File(...)): + """Конвертация загруженного файла""" + try: + # Базовая обработка - в реальном проекте добавить ffmpeg/imagemagick + logger.info(f"Конвертация файла: {file.filename}") + + # Сохранение входного файла + input_path = f"output/{file.filename}" + with open(input_path, "wb") as buffer: + content = await file.read() + buffer.write(content) + + return JSONResponse({ + "status": "success", + "message": f"Файл {file.filename} обработан", + "output_file": input_path, + "size": len(content) + }) + + except Exception as e: + logger.error(f"Ошибка конвертации: {e}") + raise HTTPException(status_code=500, detail=str(e)) + +@app.get("/status") +async def status(): + """Статус converter-module""" + return { + "module": "converter-module", + "status": "active", + "version": "1.0.0", + "supported_formats": ["mp4", "mp3", "jpg", "png", "gif"] + } + +if __name__ == "__main__": + uvicorn.run( + "app:app", + host="0.0.0.0", + port=8080, + reload=False, + log_level="info" + ) +EOF + + # Создание requirements для converter-module + cat > "converter-module/requirements.txt" << 'EOF' +fastapi==0.104.1 +uvicorn[standard]==0.24.0 +python-multipart==0.0.6 +Pillow==10.1.0 +python-ffmpeg==2.0.12 +EOF + + # Создание Dockerfile для converter-module + cat > "converter-module/Dockerfile" << 'EOF' +FROM python:3.11-slim + +# Установка ffmpeg и других утилит +RUN apt-get update && apt-get install -y \ + ffmpeg \ + imagemagick \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Установка Python зависимостей +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Копирование кода +COPY . . + +# Создание директорий +RUN mkdir -p output logs + +EXPOSE 8080 + +CMD ["python", "app.py"] +EOF + + echo "✅ converter-module создан" +else + echo "ℹ️ converter-module уже существует" +fi + +# ============================================================================= +# WEB2-CLIENT (Веб интерфейс) +# ============================================================================= + +echo "🌐 Настройка web2-client..." + +if [ ! -d "web2-client" ]; then + echo "Создание web2-client структуры..." + mkdir -p web2-client/{public,src} + + # Создание package.json + cat > "web2-client/package.json" << 'EOF' +{ + "name": "my-uploader-web-client", + "version": "1.0.0", + "description": "Web client for MY Uploader Bot", + "main": "index.js", + "scripts": { + "start": "node server.js", + "dev": "node server.js", + "build": "echo 'Build completed'", + "test": "echo 'No tests specified'" + }, + "dependencies": { + "express": "^4.18.2", + "cors": "^2.8.5", + "multer": "^1.4.5", + "axios": "^1.6.0" + }, + "keywords": ["my-network", "uploader", "web-client"], + "author": "MY Network Team", + "license": "MIT" +} +EOF + + # Создание простого Express сервера + cat > "web2-client/server.js" << 'EOF' +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const multer = require('multer'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Middleware +app.use(cors()); +app.use(express.json()); +app.use(express.static('public')); + +// Настройка multer для загрузки файлов +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'uploads/'); + }, + filename: (req, file, cb) => { + cb(null, Date.now() + '-' + file.originalname); + } +}); + +const upload = multer({ storage: storage }); + +// Создание директории uploads +const fs = require('fs'); +if (!fs.existsSync('uploads')) { + fs.mkdirSync('uploads'); +} + +// Маршруты +app.get('/', (req, res) => { + res.send(` + + + + MY Uploader - Web Client + + + + + +
+

🚀 MY Uploader Bot

+

Web Client для загрузки контента в MY Network

+
+ +
+

📁 Загрузка файла

+
+ +

+ +
+
+
+ +
+

📊 Статус системы

+

Веб клиент: Активен

+

API: Проверка...

+
+ + + + + `); +}); + +app.get('/health', (req, res) => { + res.json({ status: 'healthy', module: 'web2-client' }); +}); + +app.post('/upload', upload.single('file'), (req, res) => { + if (!req.file) { + return res.status(400).json({ error: 'Файл не выбран' }); + } + + res.json({ + message: `Файл ${req.file.originalname} успешно загружен`, + filename: req.file.filename, + size: req.file.size + }); +}); + +// Проксирование API запросов к основному приложению +app.use('/api', (req, res) => { + const apiUrl = process.env.API_URL || 'http://localhost:15100'; + // Здесь можно добавить проксирование через http-proxy-middleware + res.json({ message: 'API проксирование не настроено' }); +}); + +app.listen(PORT, () => { + console.log(`🌐 Web2-Client запущен на порту ${PORT}`); + console.log(`📖 Откройте http://localhost:${PORT} в браузере`); +}); +EOF + + # Создание Dockerfile для web2-client + cat > "web2-client/Dockerfile" << 'EOF' +FROM node:18-alpine + +WORKDIR /app + +# Установка зависимостей +COPY package*.json ./ +RUN npm install --production + +# Копирование кода +COPY . . + +# Создание директорий +RUN mkdir -p uploads public + +EXPOSE 3000 + +CMD ["npm", "start"] +EOF + + echo "✅ web2-client создан" +else + echo "ℹ️ web2-client уже существует" +fi + +# ============================================================================= +# DOCKER COMPOSE ДЛЯ МОДУЛЕЙ +# ============================================================================= + +echo "🐳 Создание docker-compose для модулей..." + +cat > "$MODULES_DIR/docker-compose.modules.yml" << 'EOF' +version: '3.8' + +services: + # Converter Module + converter: + build: + context: ./converter-module + dockerfile: Dockerfile + container_name: my_converter + restart: unless-stopped + ports: + - "8080:8080" + volumes: + - converter_data:/app/output + - converter_logs:/app/logs + environment: + - PYTHONUNBUFFERED=1 + networks: + - my_network + + # Web2 Client + web2-client: + build: + context: ./web2-client + dockerfile: Dockerfile + container_name: my_web2_client + restart: unless-stopped + ports: + - "3000:3000" + volumes: + - web2_uploads:/app/uploads + environment: + - NODE_ENV=production + - API_URL=http://my-uploader-app:15100 + networks: + - my_network + +volumes: + converter_data: + converter_logs: + web2_uploads: + +networks: + my_network: + external: true + name: my-uploader-bot_uploader_network +EOF + +echo "✅ Docker Compose для модулей создан" + +# Установка прав доступа +chmod +x "$MODULES_DIR/converter-module/app.py" +chmod +x "$MODULES_DIR/web2-client/server.js" + +echo "" +echo "📦 Дополнительные модули настроены:" +echo " 🔄 converter-module: http://localhost:8080" +echo " 🌐 web2-client: http://localhost:3000" +echo "" +echo "Для запуска модулей:" +echo " cd $MODULES_DIR" +echo " docker-compose -f docker-compose.modules.yml up -d" +echo "" \ No newline at end of file diff --git a/scripts/setup_production_env.sh b/scripts/setup_production_env.sh new file mode 100644 index 0000000..4cd4ad4 --- /dev/null +++ b/scripts/setup_production_env.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# ============================================================================= +# НАСТРОЙКА PRODUCTION ОКРУЖЕНИЯ ДЛЯ MY UPLOADER BOT +# ============================================================================= + +if [ $# -lt 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +DOMAIN="$1" +EMAIL="$2" +ENV_FILE=".env.production" + +echo "Создание production конфигурации для $DOMAIN..." + +# Генерация случайных ключей +SECRET_KEY=$(openssl rand -base64 48 | tr -d "=+/" | cut -c1-64) +JWT_SECRET_KEY=$(openssl rand -base64 48 | tr -d "=+/" | cut -c1-64) +POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-32) + +cat > "$ENV_FILE" << EOF +# ============================================================================= +# MY UPLOADER BOT - PRODUCTION CONFIGURATION +# ============================================================================= +# Domain: $DOMAIN +# Email: $EMAIL +# Generated: $(date) +# ============================================================================= + +# ============================================================================= +# ОБЯЗАТЕЛЬНЫЕ TELEGRAM ТОКЕНЫ (ТРЕБУЕТСЯ ЗАПОЛНИТЬ ВРУЧНУЮ!) +# ============================================================================= +# Получить токены можно у @BotFather в Telegram +TELEGRAM_API_KEY=CHANGE_ME_123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +CLIENT_TELEGRAM_API_KEY=CHANGE_ME_987654321:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + +# ============================================================================= +# ДОМЕН И ХОСТ +# ============================================================================= +PROJECT_HOST=https://$DOMAIN +DOMAIN=$DOMAIN +ACME_EMAIL=$EMAIL + +# ============================================================================= +# БАЗА ДАННЫХ PostgreSQL +# ============================================================================= +POSTGRES_DB=myuploader +POSTGRES_USER=uploader +POSTGRES_PASSWORD=$POSTGRES_PASSWORD +POSTGRES_PORT=5432 + +# ============================================================================= +# REDIS +# ============================================================================= +REDIS_PORT=6379 + +# ============================================================================= +# ПРИЛОЖЕНИЕ (PRODUCTION SETTINGS) +# ============================================================================= +SANIC_PORT=15100 +DEBUG=false +LOG_LEVEL=INFO +MAINTENANCE_MODE=false + +# ============================================================================= +# БЕЗОПАСНОСТЬ (АВТОГЕНЕРИРОВАННЫЕ КЛЮЧИ) +# ============================================================================= +SECRET_KEY=$SECRET_KEY +JWT_SECRET_KEY=$JWT_SECRET_KEY + +# ============================================================================= +# TON BLOCKCHAIN (MAINNET) +# ============================================================================= +TESTNET=false +TONCENTER_HOST=https://toncenter.com/api/v2/ +TONCENTER_API_KEY= +TONCENTER_V3_HOST=https://toncenter.com/api/v3/ + +# ============================================================================= +# ФОНОВЫЕ СЕРВИСЫ +# ============================================================================= +INDEXER_ENABLED=true +TON_DAEMON_ENABLED=true +LICENSE_SERVICE_ENABLED=true +CONVERT_SERVICE_ENABLED=true + +# ============================================================================= +# МОНИТОРИНГ И МЕТРИКИ +# ============================================================================= +METRICS_ENABLED=true +METRICS_PORT=9090 +HEALTH_CHECK_ENABLED=true + +# ============================================================================= +# ОГРАНИЧЕНИЯ СКОРОСТИ (PRODUCTION VALUES) +# ============================================================================= +RATE_LIMIT_ENABLED=true +RATE_LIMIT_REQUESTS=1000 +RATE_LIMIT_WINDOW=60 + +# ============================================================================= +# ПРОИЗВОДИТЕЛЬНОСТЬ +# ============================================================================= +DATABASE_POOL_SIZE=20 +DATABASE_MAX_OVERFLOW=30 +REDIS_POOL_SIZE=20 + +# ============================================================================= +# БЕЗОПАСНОСТЬ И ФАЙЛЫ +# ============================================================================= +MAX_FILE_SIZE=104857600 # 100MB +UPLOADS_DIR=/app/data + +# ============================================================================= +# GRAFANA И МОНИТОРИНГ +# ============================================================================= +GRAFANA_PASSWORD=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-20) + +# ============================================================================= +# BACKUP НАСТРОЙКИ +# ============================================================================= +BACKUP_SCHEDULE=0 2 * * * + +# ============================================================================= +# WEB2-CLIENT НАСТРОЙКИ +# ============================================================================= +WEB2_CLIENT_PORT=3000 +WEB2_CLIENT_HOST=http://localhost:3000 + +# ============================================================================= +# CONVERTER-MODULE НАСТРОЙКИ +# ============================================================================= +CONVERTER_PORT=8080 +CONVERTER_HOST=http://localhost:8080 +EOF + +echo "✅ Production конфигурация создана: $ENV_FILE" + +# Создание примера локального .env +cat > ".env.example" << EOF +# ============================================================================= +# MY UPLOADER BOT - EXAMPLE CONFIGURATION +# ============================================================================= +# Скопируйте этот файл в .env и заполните необходимые значения +# ============================================================================= + +# ОБЯЗАТЕЛЬНЫЕ ПОЛЯ +TELEGRAM_API_KEY=123456789:YOUR_BOT_TOKEN_FROM_BOTFATHER +CLIENT_TELEGRAM_API_KEY=987654321:YOUR_CLIENT_BOT_TOKEN_FROM_BOTFATHER + +# ОСНОВНЫЕ НАСТРОЙКИ +PROJECT_HOST=http://localhost:15100 +DEBUG=true +LOG_LEVEL=DEBUG + +# БАЗА ДАННЫХ +POSTGRES_DB=uploader_bot +POSTGRES_USER=uploader +POSTGRES_PASSWORD=secure_password_123 + +# БЕЗОПАСНОСТЬ (СГЕНЕРИРУЙТЕ НОВЫЕ!) +SECRET_KEY=your-secret-key-here-minimum-32-characters +JWT_SECRET_KEY=your-jwt-secret-key-here-minimum-32-characters + +# TON (ТЕСТОВАЯ СЕТЬ ДЛЯ РАЗРАБОТКИ) +TESTNET=true +TONCENTER_HOST=https://testnet.toncenter.com/api/v2/ +EOF + +echo "✅ Пример конфигурации создан: .env.example" + +# Установка правильных прав доступа +chmod 600 "$ENV_FILE" +chmod 644 ".env.example" + +echo "" +echo "⚠️ ВАЖНО: Отредактируйте файл $ENV_FILE и заполните:" +echo " - TELEGRAM_API_KEY (получите у @BotFather)" +echo " - CLIENT_TELEGRAM_API_KEY (получите у @BotFather)" +echo " - TONCENTER_API_KEY (если используете TON mainnet)" +echo "" +echo "🔒 Сгенерированные секретные ключи:" +echo " - SECRET_KEY: $SECRET_KEY" +echo " - JWT_SECRET_KEY: $JWT_SECRET_KEY" +echo " - POSTGRES_PASSWORD: $POSTGRES_PASSWORD" +echo "" \ No newline at end of file diff --git a/setup_production_server.sh b/setup_production_server.sh new file mode 100644 index 0000000..bbf2d63 --- /dev/null +++ b/setup_production_server.sh @@ -0,0 +1,348 @@ +#!/bin/bash + +# ============================================================================= +# MY UPLOADER BOT - PRODUCTION SERVER SETUP SCRIPT +# ============================================================================= +# Полная автоматическая установка на чистом Ubuntu/Debian сервере +# +# Использование: +# ./setup_production_server.sh yourdomain.com your@email.com +# +# Автор: MY Network Team +# ============================================================================= + +set -e # Выход при любой ошибке + +# Цвета для вывода +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +PURPLE='\033[0;35m' +NC='\033[0m' # No Color + +# Логирование +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" +} + +error() { + echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" + exit 1 +} + +info() { + echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +header() { + echo -e "${PURPLE}" + echo "==============================================" + echo " $1" + echo "==============================================" + echo -e "${NC}" +} + +# Проверка аргументов +if [ $# -lt 2 ]; then + error "Usage: $0 " + echo "Example: $0 mydomain.com admin@mydomain.com" + exit 1 +fi + +DOMAIN="$1" +EMAIL="$2" +PROJECT_DIR="/opt/my-uploader-bot" +NGINX_CONFIG="/etc/nginx/sites-available/my-uploader-bot" +SERVICE_USER="myuploader" + +header "🚀 УСТАНОВКА MY UPLOADER BOT НА СЕРВЕР $DOMAIN" + +log "Домен: $DOMAIN" +log "Email: $EMAIL" +log "Проект будет установлен в: $PROJECT_DIR" + +# Проверка root прав +if [[ $EUID -ne 0 ]]; then + error "Этот скрипт должен запускаться с правами root (sudo)" +fi + +# Проверка операционной системы +if ! command -v apt-get &> /dev/null; then + error "Этот скрипт поддерживает только Ubuntu/Debian системы" +fi + +header "📦 ШАГ 1: ОБНОВЛЕНИЕ СИСТЕМЫ" + +log "Обновление списка пакетов..." +apt-get update -y + +log "Обновление установленных пакетов..." +apt-get upgrade -y + +log "Установка базовых пакетов..." +apt-get install -y \ + curl \ + wget \ + git \ + unzip \ + software-properties-common \ + apt-transport-https \ + ca-certificates \ + gnupg \ + lsb-release \ + htop \ + nano \ + vim \ + net-tools \ + ufw + +header "🐳 ШАГ 2: УСТАНОВКА DOCKER" + +# Удаление старых версий Docker +log "Удаление старых версий Docker..." +apt-get remove -y docker docker-engine docker.io containerd runc || true + +# Добавление репозитория Docker +log "Добавление Docker репозитория..." +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + +# Установка Docker +log "Установка Docker..." +apt-get update +apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + +# Установка Docker Compose standalone +log "Установка Docker Compose..." +curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose +ln -sf /usr/local/bin/docker-compose /usr/bin/docker-compose + +# Запуск и включение Docker +systemctl enable docker +systemctl start docker + +log "Docker версия: $(docker --version)" +log "Docker Compose версия: $(docker-compose --version)" + +header "🔥 ШАГ 3: НАСТРОЙКА FIREWALL" + +log "Настройка UFW firewall..." + +# Сброс правил UFW +ufw --force reset + +# Базовая политика +ufw default deny incoming +ufw default allow outgoing + +# Разрешение SSH (важно для сохранения доступа) +ufw allow 22/tcp comment "SSH" + +# Разрешение HTTP и HTTPS +ufw allow 80/tcp comment "HTTP" +ufw allow 443/tcp comment "HTTPS" + +# Включение firewall +ufw --force enable + +log "Статус firewall:" +ufw status verbose + +header "🔒 ШАГ 4: УСТАНОВКА CERTBOT" + +log "Установка snapd..." +apt-get install -y snapd + +log "Установка Certbot через snap..." +snap install core +snap refresh core +snap install --classic certbot + +# Создание symlink +ln -sf /snap/bin/certbot /usr/bin/certbot + +log "Certbot версия: $(certbot --version)" + +header "👤 ШАГ 5: СОЗДАНИЕ ПОЛЬЗОВАТЕЛЯ СЕРВИСА" + +log "Создание пользователя $SERVICE_USER..." +if ! id "$SERVICE_USER" &>/dev/null; then + useradd -r -s /bin/bash -d /home/$SERVICE_USER -m $SERVICE_USER + usermod -aG docker $SERVICE_USER + log "Пользователь $SERVICE_USER создан" +else + log "Пользователь $SERVICE_USER уже существует" +fi + +header "📂 ШАГ 6: ПОДГОТОВКА ПРОЕКТА" + +log "Создание директории проекта..." +mkdir -p $PROJECT_DIR +cd $PROJECT_DIR + +# Если это git репозиторий, клонируем. Иначе копируем текущую директорию +if [ -d "$(dirname $0)/.git" ]; then + log "Клонирование из Git репозитория..." + # Определяем URL репозитория + REPO_URL=$(cd "$(dirname $0)" && git config --get remote.origin.url || echo "") + if [ -n "$REPO_URL" ]; then + git clone "$REPO_URL" . + else + warn "Git репозиторий не найден, копирую файлы локально..." + cp -r "$(dirname $0)"/* . + fi +else + log "Копирование файлов проекта..." + cp -r "$(dirname $0)"/* . +fi + +# Установка владельца +chown -R $SERVICE_USER:$SERVICE_USER $PROJECT_DIR + +header "⚙️ ШАГ 7: НАСТРОЙКА NGINX" + +log "Установка Nginx..." +apt-get install -y nginx + +# Остановка Nginx для получения сертификатов +systemctl stop nginx + +log "Создание конфигурации Nginx..." +./scripts/create_nginx_config.sh $DOMAIN $PROJECT_DIR + +header "🔐 ШАГ 8: ПОЛУЧЕНИЕ SSL СЕРТИФИКАТОВ" + +log "Получение Let's Encrypt сертификатов для $DOMAIN..." + +# Остановка процессов на портах 80/443 +fuser -k 80/tcp 2>/dev/null || true +fuser -k 443/tcp 2>/dev/null || true + +# Получение сертификатов +certbot certonly \ + --standalone \ + --non-interactive \ + --agree-tos \ + --email "$EMAIL" \ + --domains "$DOMAIN,www.$DOMAIN" \ + --no-eff-email + +log "Настройка автообновления сертификатов..." +systemctl enable certbot.timer +systemctl start certbot.timer + +# Проверка автообновления +certbot renew --dry-run + +header "🔧 ШАГ 9: КОНФИГУРАЦИЯ ПРОЕКТА" + +log "Создание production конфигурации..." +./scripts/setup_production_env.sh $DOMAIN $EMAIL + +header "📦 ШАГ 10: КОПИРОВАНИЕ ДОПОЛНИТЕЛЬНЫХ МОДУЛЕЙ" + +log "Настройка дополнительных модулей..." +./scripts/setup_modules.sh $PROJECT_DIR + +header "🐳 ШАГ 11: ЗАПУСК DOCKER СЕРВИСОВ" + +log "Запуск Docker Compose в production режиме..." +cd $PROJECT_DIR + +# Запуск от имени пользователя сервиса +sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml pull +sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml build --no-cache +sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml up -d + +log "Ожидание запуска сервисов..." +sleep 30 + +# Проверка статуса +log "Статус Docker сервисов:" +sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml ps + +header "🌐 ШАГ 12: ЗАПУСК NGINX" + +log "Включение конфигурации Nginx..." +ln -sf $NGINX_CONFIG /etc/nginx/sites-enabled/my-uploader-bot +rm -f /etc/nginx/sites-enabled/default + +log "Проверка конфигурации Nginx..." +nginx -t + +log "Запуск Nginx..." +systemctl enable nginx +systemctl start nginx + +header "🔄 ШАГ 13: НАСТРОЙКА АВТОЗАПУСКА" + +log "Создание systemd сервисов..." +./scripts/create_systemd_services.sh $PROJECT_DIR $SERVICE_USER + +header "✅ ШАГ 14: ФИНАЛЬНАЯ ПРОВЕРКА" + +log "Проверка всех сервисов..." + +# Проверка Docker +if sudo -u $SERVICE_USER docker-compose -f $PROJECT_DIR/docker-compose.production.yml ps | grep -q "Up"; then + log "✅ Docker сервисы: Работают" +else + error "❌ Docker сервисы: Не запущены" +fi + +# Проверка Nginx +if systemctl is-active --quiet nginx; then + log "✅ Nginx: Работает" +else + error "❌ Nginx: Не запущен" +fi + +# Проверка SSL +if curl -sf "https://$DOMAIN/health" > /dev/null; then + log "✅ SSL сертификат: Работает" +else + warn "⚠️ SSL проверка не прошла (сервис может еще запускаться)" +fi + +# Проверка API +log "Ожидание полного запуска API..." +sleep 60 + +if curl -sf "https://$DOMAIN/api/my/health" > /dev/null; then + log "✅ MY Network API: Работает" +else + warn "⚠️ MY Network API пока не доступен" +fi + +header "🎉 УСТАНОВКА ЗАВЕРШЕНА!" + +info "=== ИНФОРМАЦИЯ О РАЗВЕРТЫВАНИИ ===" +info "Домен: https://$DOMAIN" +info "API: https://$DOMAIN/api/" +info "Мониторинг: https://$DOMAIN/api/my/monitor/" +info "Health Check: https://$DOMAIN/health" +info "Директория проекта: $PROJECT_DIR" +info "Пользователь: $SERVICE_USER" +info "Nginx конфиг: $NGINX_CONFIG" + +info "=== ПОЛЕЗНЫЕ КОМАНДЫ ===" +info "Просмотр логов: cd $PROJECT_DIR && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml logs -f" +info "Перезапуск: cd $PROJECT_DIR && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml restart" +info "Обновление: cd $PROJECT_DIR && git pull && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml build --no-cache && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml up -d" + +info "=== БЕЗОПАСНОСТЬ ===" +info "Firewall: ufw status" +info "SSL сертификаты: ls -la /etc/letsencrypt/live/$DOMAIN/" +info "Автообновление SSL: systemctl status certbot.timer" + +log "🚀 Проект успешно развернут на https://$DOMAIN" +log "📝 Не забудьте настроить Telegram токены в $PROJECT_DIR/.env.production" + +exit 0 \ No newline at end of file