sh scripts
This commit is contained in:
parent
2c1ca4bf45
commit
444b5af31a
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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 ""
|
||||
|
|
@ -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!'
|
||||
|
|
@ -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 ""
|
||||
|
|
@ -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 ""
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue