sh scripts

This commit is contained in:
user 2025-07-02 23:30:23 +03:00
parent 2c1ca4bf45
commit 444b5af31a
7 changed files with 2543 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,328 @@
#!/bin/bash
# =============================================================================
# СОЗДАНИЕ NGINX КОНФИГУРАЦИИ ДЛЯ MY UPLOADER BOT
# =============================================================================
if [ $# -lt 2 ]; then
echo "Usage: $0 <domain> <project_dir>"
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"

View File

@ -0,0 +1,568 @@
#!/bin/bash
# =============================================================================
# СОЗДАНИЕ SYSTEMD СЕРВИСОВ ДЛЯ MY UPLOADER BOT
# =============================================================================
if [ $# -lt 1 ]; then
echo "Usage: $0 <project_dir> [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 ""

View File

@ -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!'

442
scripts/setup_modules.sh Normal file
View File

@ -0,0 +1,442 @@
#!/bin/bash
# =============================================================================
# НАСТРОЙКА ДОПОЛНИТЕЛЬНЫХ МОДУЛЕЙ MY UPLOADER BOT
# =============================================================================
if [ $# -lt 1 ]; then
echo "Usage: $0 <project_dir>"
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(`
<!DOCTYPE html>
<html>
<head>
<title>MY Uploader - Web Client</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.header { text-align: center; margin-bottom: 40px; }
.upload-form { border: 2px dashed #ccc; padding: 40px; text-align: center; margin-bottom: 40px; }
.btn { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; }
.status { margin-top: 20px; }
</style>
</head>
<body>
<div class="header">
<h1>🚀 MY Uploader Bot</h1>
<p>Web Client для загрузки контента в MY Network</p>
</div>
<div class="upload-form">
<h3>📁 Загрузка файла</h3>
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" id="fileInput" name="file" required>
<br><br>
<button type="submit" class="btn">Загрузить</button>
</form>
<div id="status" class="status"></div>
</div>
<div>
<h3>📊 Статус системы</h3>
<p>Веб клиент: <span style="color: green;">Активен</span></p>
<p>API: <span id="apiStatus">Проверка...</span></p>
</div>
<script>
// Проверка статуса API
fetch('/api/health')
.then(response => response.json())
.then(data => {
document.getElementById('apiStatus').innerHTML = '<span style="color: green;">Активен</span>';
})
.catch(error => {
document.getElementById('apiStatus').innerHTML = '<span style="color: red;">Недоступен</span>';
});
// Обработка загрузки файлов
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData();
const fileInput = document.getElementById('fileInput');
formData.append('file', fileInput.files[0]);
const status = document.getElementById('status');
status.innerHTML = 'Загрузка...';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
status.innerHTML = \`\${result.message}\`;
} catch (error) {
status.innerHTML = \`❌ Ошибка: \${error.message}\`;
}
});
</script>
</body>
</html>
`);
});
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 ""

View File

@ -0,0 +1,188 @@
#!/bin/bash
# =============================================================================
# НАСТРОЙКА PRODUCTION ОКРУЖЕНИЯ ДЛЯ MY UPLOADER BOT
# =============================================================================
if [ $# -lt 2 ]; then
echo "Usage: $0 <domain> <email>"
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 ""

348
setup_production_server.sh Normal file
View File

@ -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 <domain> <email>"
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