pre-deploy commit

This commit is contained in:
2025-09-18 14:19:49 +09:00
parent 5ea3e8c1f3
commit 713eadc643
50 changed files with 2238 additions and 569 deletions

View File

@@ -1,30 +1,58 @@
# Telegram Bot Configuration
# Telegram Tinder Bot Configuration
# Rename this file to .env before starting the application
# === REQUIRED SETTINGS ===
# Telegram Bot Token (Get from @BotFather)
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
# Database Configuration
# For local development (when running the bot directly)
DB_HOST=localhost
DB_PORT=5432
DB_NAME=telegram_tinder_bot
DB_USERNAME=postgres
DB_PASSWORD=your_password_here
# Application Settings
# === APPLICATION SETTINGS ===
# Environment (development, production)
NODE_ENV=development
# Port for health checks
PORT=3000
# Optional: Redis for caching (if using)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# === FILE UPLOAD SETTINGS ===
# Optional: File upload settings
# Path for storing uploaded files
UPLOAD_PATH=./uploads
# Maximum file size for uploads (in bytes, default: 5MB)
MAX_FILE_SIZE=5242880
# Optional: External services
GOOGLE_MAPS_API_KEY=your_google_maps_key
CLOUDINARY_URL=your_cloudinary_url
# === LOGGING ===
# Security
# Log level (error, warn, info, debug)
LOG_LEVEL=info
# Path for storing log files
LOG_PATH=./logs
# === SECURITY ===
# Secret key for JWT tokens
JWT_SECRET=your_jwt_secret_here
# Encryption key for sensitive data
ENCRYPTION_KEY=your_encryption_key_here
# === ADVANCED SETTINGS ===
# Notification check interval in milliseconds (default: 60000 - 1 minute)
NOTIFICATION_CHECK_INTERVAL=60000
# Number of matches to show per page
MATCHES_PER_PAGE=10
# Number of profiles to load at once
PROFILES_BATCH_SIZE=5

68
.env.production Normal file
View File

@@ -0,0 +1,68 @@
# Конфигурация Telegram Tinder Bot для Production
# === НЕОБХОДИМЫЕ НАСТРОЙКИ ===
# Токен Telegram бота (получить у @BotFather)
TELEGRAM_BOT_TOKEN=your_bot_token_here
# Настройки базы данных PostgreSQL
DB_HOST=db
DB_PORT=5432
DB_NAME=telegram_tinder_bot
DB_USERNAME=postgres
DB_PASSWORD=your_secure_password_here
# === НАСТРОЙКИ ПРИЛОЖЕНИЯ ===
# Окружение
NODE_ENV=production
# Порт для проверок работоспособности
PORT=3000
# === НАСТРОЙКИ ЗАГРУЗКИ ФАЙЛОВ ===
# Путь для хранения загруженных файлов
UPLOAD_PATH=./uploads
# Максимальный размер загружаемого файла (в байтах, по умолчанию: 5MB)
MAX_FILE_SIZE=5242880
# Разрешенные типы файлов
ALLOWED_FILE_TYPES=image/jpeg,image/png,image/gif
# === ЛОГИРОВАНИЕ ===
# Уровень логирования (error, warn, info, debug)
LOG_LEVEL=info
# Путь для хранения лог-файлов
LOG_PATH=./logs
# === БЕЗОПАСНОСТЬ ===
# Секретный ключ для JWT токенов
JWT_SECRET=your_jwt_secret_here
# Ключ шифрования для чувствительных данных
ENCRYPTION_KEY=your_encryption_key_here
# === РАСШИРЕННЫЕ НАСТРОЙКИ ===
# Интервал проверки уведомлений в миллисекундах (по умолчанию: 60000 - 1 минута)
NOTIFICATION_CHECK_INTERVAL=60000
# Количество матчей для отображения на странице
MATCHES_PER_PAGE=10
# Количество профилей для загрузки за один раз
PROFILES_BATCH_SIZE=5
# === НАСТРОЙКИ DOCKER ===
# Имя хоста для доступа извне
EXTERNAL_HOSTNAME=your_domain.com
# Настройки кеширования (Redis, если используется)
CACHE_HOST=redis
CACHE_PORT=6379

View File

@@ -8,11 +8,12 @@ WORKDIR /app
COPY package*.json ./
COPY tsconfig.json ./
# Install dependencies
RUN npm ci --only=production && npm cache clean --force
# Install all dependencies (including devDependencies for build)
RUN npm ci && npm cache clean --force
# Copy source code
COPY src/ ./src/
COPY .env.example ./
# Build the application
RUN npm run build
@@ -31,11 +32,19 @@ RUN npm ci --only=production && npm cache clean --force
# Copy built application from builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/.env.example ./.env.example
# Copy configuration files
COPY config/ ./config/
# Copy database migrations
COPY src/database/migrations/ ./dist/database/migrations/
# Create uploads directory
# Copy locales
COPY src/locales/ ./dist/locales/
# Copy scripts
COPY scripts/startup.sh ./startup.sh
RUN chmod +x ./startup.sh
# Create directories
RUN mkdir -p uploads logs
# Create non-root user for security
@@ -53,7 +62,7 @@ EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })" || exit 1
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Start the application
CMD ["node", "dist/bot.js"]
# Start the application with migration script
CMD ["./startup.sh"]

View File

@@ -268,8 +268,32 @@ npm run dev
- Node.js 16+
- PostgreSQL 12+
- Telegram Bot Token (получить у [@BotFather](https://t.me/BotFather))
- Docker и Docker Compose (опционально)
### 2. Установка
### 2. Установка и запуск
#### С использованием стартовых скриптов (рекомендуется)
```bash
# Клонировать репозиторий
git clone <repository-url>
cd telegram-tinder-bot
# На Windows:
.\start.bat
# На Linux/macOS:
chmod +x start.sh
./start.sh
```
Скрипт автоматически:
- Проверит наличие файла .env и создаст его из шаблона при необходимости
- Предложит выбор между запуском с локальной БД или подключением к внешней
- Настроит все необходимые параметры окружения
- Запустит контейнеры Docker
#### Без Docker
```bash
# Клонировать репозиторий
@@ -279,24 +303,17 @@ cd telegram-tinder-bot
# Установить зависимости
npm install
# Скомпилировать TypeScript
npm run build
```
# Скопировать файл конфигурации
cp .env.example .env
# Отредактируйте файл .env и укажите свой TELEGRAM_BOT_TOKEN
### 3. Настройка базы данных
```bash
# Создать базу данных PostgreSQL
createdb telegram_tinder_bot
# Запустить миграции
psql -d telegram_tinder_bot -f src/database/migrations/init.sql
```
npm run migrate:up
### 4. Запуск бота
```bash
# Компиляция TypeScript
# Скомпилировать TypeScript
npm run build
# Запуск бота

54
bin/backup_db.sh Normal file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
# backup_db.sh - Script for backing up the PostgreSQL database
echo "📦 Backing up PostgreSQL database..."
# Default backup directory
BACKUP_DIR="${BACKUP_DIR:-/var/backups/tg_tinder_bot}"
BACKUP_FILENAME="tg_tinder_bot_$(date +%Y%m%d_%H%M%S).sql"
BACKUP_PATH="$BACKUP_DIR/$BACKUP_FILENAME"
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Check if running in docker-compose environment
if [ -f /.dockerenv ] || [ -f /proc/self/cgroup ] && grep -q docker /proc/self/cgroup; then
echo "🐳 Running in Docker environment, using docker-compose exec..."
docker-compose exec -T db pg_dump -U postgres telegram_tinder_bot > "$BACKUP_PATH"
else
# Check if PGPASSWORD is set in environment
if [ -z "$PGPASSWORD" ]; then
# If .env file exists, try to get password from there
if [ -f .env ]; then
DB_PASSWORD=$(grep DB_PASSWORD .env | cut -d '=' -f2)
export PGPASSWORD="$DB_PASSWORD"
else
echo "⚠️ No DB_PASSWORD found in environment or .env file."
echo "Please enter PostgreSQL password:"
read -s PGPASSWORD
export PGPASSWORD
fi
fi
echo "💾 Backing up database to $BACKUP_PATH..."
pg_dump -h localhost -U postgres -d telegram_tinder_bot > "$BACKUP_PATH"
fi
# Check if backup was successful
if [ $? -eq 0 ]; then
echo "✅ Backup completed successfully: $BACKUP_PATH"
echo "📊 Backup size: $(du -h $BACKUP_PATH | cut -f1)"
# Compress the backup
gzip -f "$BACKUP_PATH"
echo "🗜️ Compressed backup: $BACKUP_PATH.gz"
# Keep only the last 7 backups
echo "🧹 Cleaning up old backups..."
find "$BACKUP_DIR" -name "tg_tinder_bot_*.sql.gz" -type f -mtime +7 -delete
echo "🎉 Backup process completed!"
else
echo "❌ Backup failed!"
exit 1
fi

72
bin/create_release.sh Normal file
View File

@@ -0,0 +1,72 @@
#!/bin/bash
# Скрипт для создания релиза Telegram Tinder Bot
# Получение версии из package.json
VERSION=$(grep -m1 "version" package.json | cut -d'"' -f4)
RELEASE_NAME="tg-tinder-bot-v$VERSION"
RELEASE_DIR="bin/releases/$RELEASE_NAME"
echo "🚀 Создание релиза $RELEASE_NAME"
# Создание директории релиза
mkdir -p "$RELEASE_DIR"
# Очистка временных файлов
echo "🧹 Очистка временных файлов..."
rm -rf dist node_modules
# Установка зависимостей
echo "📦 Установка зависимостей production..."
npm ci --only=production
# Сборка проекта
echo "🔧 Сборка проекта..."
npm run build
# Копирование файлов релиза
echo "📋 Копирование файлов..."
cp -r dist "$RELEASE_DIR/"
cp -r src/locales "$RELEASE_DIR/dist/"
cp package.json package-lock.json .env.example "$RELEASE_DIR/"
cp -r bin/start_bot.* bin/install_ubuntu.sh "$RELEASE_DIR/"
cp README.md LICENSE "$RELEASE_DIR/" 2>/dev/null || echo "Файлы документации не найдены"
cp sql/consolidated.sql "$RELEASE_DIR/"
cp docker-compose.yml Dockerfile "$RELEASE_DIR/"
cp deploy.sh "$RELEASE_DIR/" && chmod +x "$RELEASE_DIR/deploy.sh"
# Создание README для релиза
cat > "$RELEASE_DIR/RELEASE.md" << EOL
# Telegram Tinder Bot v$VERSION
Эта папка содержит релиз Telegram Tinder Bot версии $VERSION.
## Содержимое
- \`dist/\` - Скомпилированный код
- \`package.json\` - Зависимости и скрипты
- \`.env.example\` - Пример конфигурации
- \`docker-compose.yml\` и \`Dockerfile\` - Для запуска через Docker
- \`consolidated.sql\` - SQL-скрипт для инициализации базы данных
- \`deploy.sh\` - Скрипт для простого деплоя
## Быстрый старт
1. Создайте файл \`.env\` на основе \`.env.example\`
2. Запустите бота одним из способов:
- Через Docker: \`./deploy.sh\`
- Через Node.js: \`node dist/bot.js\`
## Дата релиза
$(date "+%d.%m.%Y %H:%M")
EOL
# Архивирование релиза
echo "📦 Создание архива..."
cd bin/releases
zip -r "$RELEASE_NAME.zip" "$RELEASE_NAME"
cd ../..
echo "✅ Релиз создан успешно!"
echo "📂 Релиз доступен в: bin/releases/$RELEASE_NAME"
echo "📦 Архив релиза: bin/releases/$RELEASE_NAME.zip"

58
bin/install_docker.sh Normal file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
# install_docker.sh - Script for installing Docker and Docker Compose
echo "🚀 Installing Docker and Docker Compose..."
# Check if script is run as root
if [ "$(id -u)" -ne 0 ]; then
echo "❌ This script must be run as root. Please run with sudo."
exit 1
fi
# Update package lists
echo "📦 Updating package lists..."
apt update
# Install required packages
echo "📦 Installing required packages..."
apt install -y apt-transport-https ca-certificates curl software-properties-common
# Add Docker GPG key
echo "🔑 Adding Docker GPG key..."
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# Add Docker repository
echo "📁 Adding Docker repository..."
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Update package lists again
apt update
# Install Docker
echo "🐳 Installing Docker..."
apt install -y docker-ce docker-ce-cli containerd.io
# Enable and start Docker service
systemctl enable docker
systemctl start docker
# Install Docker Compose
echo "🐳 Installing Docker Compose..."
curl -L "https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# Check versions
echo "✅ Installation complete!"
echo "Docker version:"
docker --version
echo "Docker Compose version:"
docker-compose --version
# Add current user to docker group if not root
if [ -n "$SUDO_USER" ]; then
echo "👤 Adding user $SUDO_USER to docker group..."
usermod -aG docker $SUDO_USER
echo "⚠️ Please log out and log back in for group changes to take effect."
fi
echo "🎉 Docker installation completed successfully!"

View File

@@ -1,14 +0,0 @@
import { query } from './src/database/connection';
async function checkSchema() {
try {
const result = await query('SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1', ['messages']);
console.log(result.rows);
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
}
checkSchema();

55
deploy.sh Normal file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
# deploy.sh - Скрипт для деплоя Telegram Tinder Bot
echo "🚀 Деплой Telegram Tinder Bot..."
# Проверяем наличие Docker
if ! command -v docker &> /dev/null || ! command -v docker-compose &> /dev/null; then
echo "❌ Docker и Docker Compose должны быть установлены!"
echo "Для установки на Ubuntu выполните:"
echo "sudo apt update && sudo apt install -y docker.io docker-compose"
exit 1
fi
# Определяем рабочую директорию
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$SCRIPT_DIR"
# Получаем последние изменения
echo "📥 Получение последних изменений..."
git pull origin main
# Проверяем наличие .env файла
if [ ! -f .env ]; then
echo "📝 Создание .env файла из .env.production..."
cp .env.production .env
echo "⚠️ Пожалуйста, отредактируйте файл .env и укажите свои настройки!"
exit 1
fi
# Запускаем Docker Compose
echo "🐳 Сборка и запуск контейнеров Docker..."
docker-compose down
docker-compose build
docker-compose up -d
# Проверяем статус контейнеров
echo "🔍 Проверка статуса контейнеров..."
docker-compose ps
echo "✅ Деплой успешно завершен! Бот должен быть доступен через Telegram."
echo ""
echo "📊 Полезные команды:"
echo "- Просмотр логов: docker-compose logs -f"
echo "- Перезапуск сервисов: docker-compose restart"
echo "- Остановка всех сервисов: docker-compose down"
echo "- Доступ к базе данных: docker-compose exec db psql -U postgres -d telegram_tinder_bot"
echo "- Проверка состояния бота: curl http://localhost:3000/health"
echo ""
echo "🌟 Для администрирования базы данных:"
echo "Adminer доступен по адресу: http://ваш_сервер:8080"
echo " - Система: PostgreSQL"
echo " - Сервер: db"
echo " - Пользователь: postgres"
echo " - Пароль: (из переменной DB_PASSWORD в .env)"
echo " - База данных: telegram_tinder_bot"

View File

@@ -0,0 +1,69 @@
# Используем версию Docker Compose для локальной разработки
version: '3.8'
services:
bot:
build:
context: .
dockerfile: Dockerfile
args:
- NODE_ENV=development
environment:
- NODE_ENV=development
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=telegram_tinder_bot
- DB_USERNAME=postgres
- DB_PASSWORD=dev_password
volumes:
# Монтируем исходный код для горячей перезагрузки
- ./src:/app/src
- ./dist:/app/dist
- ./.env:/app/.env
ports:
# Открываем порт для отладки
- "9229:9229"
command: npm run dev
networks:
- bot-network
depends_on:
- db
db:
# Используем последнюю версию PostgreSQL для разработки
image: postgres:16-alpine
environment:
- POSTGRES_DB=telegram_tinder_bot
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=dev_password
volumes:
# Хранение данных локально для быстрого сброса
- postgres_data_dev:/var/lib/postgresql/data
# Монтируем скрипты инициализации
- ./sql:/docker-entrypoint-initdb.d
ports:
# Открываем порт для доступа к БД напрямую
- "5433:5432"
networks:
- bot-network
adminer:
image: adminer:latest
ports:
- "8080:8080"
networks:
- bot-network
depends_on:
- db
environment:
- ADMINER_DEFAULT_SERVER=db
- ADMINER_DEFAULT_USER=postgres
- ADMINER_DEFAULT_PASSWORD=dev_password
volumes:
postgres_data_dev:
networks:
bot-network:
driver: bridge

View File

@@ -6,19 +6,26 @@ services:
container_name: telegram-tinder-bot
restart: unless-stopped
depends_on:
- db
db:
condition: service_healthy
env_file: .env
environment:
- NODE_ENV=production
- DB_HOST={DB_HOST}
- DB_PORT={DB_PORT}
- DB_NAME={DB_NAME}
- DB_USERNAME={DB_USERNAME}
- DB_PASSWORD={DB_PASSWORD}
- TELEGRAM_BOT_TOKEN={TELEGRAM_BOT_TOKEN}
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=telegram_tinder_bot
- DB_USERNAME=postgres
volumes:
- ./uploads:/app/uploads
- ./logs:/app/logs
networks:
- bot-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
db:
image: postgres:15-alpine
@@ -27,14 +34,18 @@ services:
environment:
- POSTGRES_DB=telegram_tinder_bot
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password123
- POSTGRES_PASSWORD=${DB_PASSWORD:-password123}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./src/database/migrations/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5433:5432"
networks:
- bot-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
adminer:
image: adminer:latest

View File

@@ -0,0 +1,264 @@
# Инструкция по развертыванию Telegram Tinder Bot в Production
Это подробное руководство по развертыванию Telegram Tinder Bot в production-окружении с использованием Docker и Docker Compose.
## 📋 Требования
- **Операционная система**: Ubuntu 20.04 или выше (рекомендуется) / Windows Server с Docker
- **Программное обеспечение**:
- Docker (последняя версия)
- Docker Compose (последняя версия)
- Git
## 🚀 Быстрое развертывание
### 1. Клонирование репозитория
```bash
git clone https://github.com/your-username/telegram-tinder-bot.git
cd telegram-tinder-bot
```
### 2. Настройка конфигурации
```bash
# Создание файла конфигурации из шаблона
cp .env.production .env
# Редактирование конфигурационного файла
nano .env
```
Важно указать следующие параметры:
- `TELEGRAM_BOT_TOKEN`: токен от @BotFather
- `DB_PASSWORD`: надежный пароль для базы данных
- `JWT_SECRET`: случайная строка для JWT
- `ENCRYPTION_KEY`: случайная строка для шифрования
### 3. Запуск деплоя
```bash
# Сделайте скрипт исполняемым
chmod +x deploy.sh
# Запустите деплой
./deploy.sh
```
## 🔧 Подробное руководство по установке
### Подготовка сервера Ubuntu
```bash
# Обновление системы
sudo apt update && sudo apt upgrade -y
# Установка необходимых пакетов
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common git
# Установка Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Добавление текущего пользователя в группу docker
sudo usermod -aG docker ${USER}
# Установка Docker Compose
sudo apt install -y docker-compose
```
### Клонирование и настройка проекта
```bash
# Создание директории для проекта
mkdir -p /opt/telegram-tinder
cd /opt/telegram-tinder
# Клонирование репозитория
git clone https://github.com/your-username/telegram-tinder-bot.git .
# Настройка .env файла
cp .env.production .env
nano .env
# Создание директорий для данных и логов
mkdir -p uploads logs
chmod 777 uploads logs
```
### Запуск проекта
```bash
# Запуск в фоновом режиме
docker-compose up -d
# Проверка статуса контейнеров
docker-compose ps
# Просмотр логов
docker-compose logs -f
```
## 🔄 Обновление бота
Для обновления бота выполните:
```bash
cd /путь/к/telegram-tinder-bot
./deploy.sh
```
Скрипт автоматически выполнит:
1. Получение последних изменений из репозитория
2. Перезапуск контейнеров с новой версией кода
3. Применение миграций базы данных
## 🛡️ Обеспечение безопасности
### Настройка файрвола
```bash
# Разрешение только необходимых портов
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```
### Настройка HTTPS с Let's Encrypt (опционально)
Для использования HTTPS с Let's Encrypt и Nginx:
```bash
# Установка Certbot
sudo apt install -y certbot python3-certbot-nginx
# Получение SSL-сертификата
sudo certbot --nginx -d your-domain.com
```
## 📊 Мониторинг и управление
### Просмотр логов
```bash
# Логи всех контейнеров
docker-compose logs -f
# Логи конкретного контейнера (например, бота)
docker-compose logs -f bot
# Последние 100 строк логов
docker-compose logs --tail=100 bot
```
### Управление сервисами
```bash
# Остановка всех контейнеров
docker-compose down
# Перезапуск всех контейнеров
docker-compose restart
# Перезапуск только бота
docker-compose restart bot
```
### Доступ к базе данных
```bash
# Вход в консоль PostgreSQL
docker-compose exec db psql -U postgres -d telegram_tinder_bot
# Резервное копирование базы данных
docker-compose exec db pg_dump -U postgres telegram_tinder_bot > backup_$(date +%Y%m%d).sql
# Восстановление базы из резервной копии
cat backup.sql | docker-compose exec -T db psql -U postgres -d telegram_tinder_bot
```
## 🔍 Устранение неполадок
### Проверка работоспособности
```bash
# Проверка API бота
curl http://localhost:3000/health
# Проверка подключения к базе данных
docker-compose exec bot node -e "const { Client } = require('pg'); const client = new Client({ host: 'db', port: 5432, database: 'telegram_tinder_bot', user: 'postgres', password: process.env.DB_PASSWORD }); client.connect().then(() => { console.log('Connected to DB!'); client.end(); }).catch(e => console.error(e));"
```
### Общие проблемы и решения
**Проблема**: Бот не отвечает в Telegram
**Решение**:
- Проверьте валидность токена бота
- Проверьте логи на наличие ошибок: `docker-compose logs -f bot`
**Проблема**: Ошибки подключения к базе данных
**Решение**:
- Проверьте настройки подключения в `.env`
- Убедитесь, что контейнер с базой данных запущен: `docker-compose ps`
- Проверьте логи базы данных: `docker-compose logs db`
**Проблема**: Недостаточно свободного места на диске
**Решение**:
- Очистите неиспользуемые Docker образы: `docker image prune -a`
- Очистите неиспользуемые Docker тома: `docker volume prune`
## 🔁 Настройка автоматического обновления
### Настройка автообновления через Cron
```bash
# Редактирование crontab
crontab -e
# Добавление задачи (обновление каждую ночь в 3:00)
0 3 * * * cd /путь/к/telegram-tinder-bot && ./deploy.sh > /tmp/tg-tinder-update.log 2>&1
```
## 📝 Рекомендации по обслуживанию
1. **Регулярное резервное копирование**:
```bash
# Ежедневное резервное копирование через cron
0 2 * * * docker-compose exec -T db pg_dump -U postgres telegram_tinder_bot > /path/to/backups/tg_$(date +\%Y\%m\%d).sql
```
2. **Мониторинг использования ресурсов**:
```bash
# Просмотр использования ресурсов контейнерами
docker stats
```
3. **Обновление Docker образов**:
```bash
# Обновление образов
docker-compose pull
docker-compose up -d
```
4. **Проверка журналов на наличие ошибок**:
```bash
# Поиск ошибок в логах
docker-compose logs | grep -i error
docker-compose logs | grep -i exception
```
---
## 📋 Контрольный список деплоя
- [ ] Установлены Docker и Docker Compose
- [ ] Клонирован репозиторий
- [ ] Настроен файл .env с реальными данными
- [ ] Запущены контейнеры через docker-compose
- [ ] Проверено подключение бота к Telegram API
- [ ] Настроено резервное копирование
- [ ] Настроен файрвол и безопасность сервера
- [ ] Проверены и настроены логи
- [ ] (Опционально) Настроен SSL для веб-интерфейса
- [ ] (Опционально) Настроено автоматическое обновление

View File

@@ -1,90 +0,0 @@
# Исправления ошибок в коде
## Проблема: Несоответствие имен столбцов в таблице swipes
В коде обнаружены несоответствия в названиях столбцов при работе с таблицей `swipes`. Используются два разных варианта именования:
1. `user_id` и `target_user_id`
2. `swiper_id` и `swiped_id`
Судя по ошибкам в консоли и анализу кода, корректными именами столбцов являются `user_id` и `target_user_id`.
## Необходимые исправления
### 1. В файле `profileService.ts` - метод `deleteProfile`:
```typescript
// Неверно:
await client.query('DELETE FROM swipes WHERE swiper_id = $1 OR swiped_id = $1', [userId]);
// Исправить на:
await client.query('DELETE FROM swipes WHERE user_id = $1 OR target_user_id = $1', [userId]);
```
### 2. В файле `matchingService.ts` - метод `performSwipe`:
```typescript
// Неверно:
const reciprocalSwipe = await client.query(`
SELECT * FROM swipes
WHERE swiper_id = $1 AND swiped_id = $2 AND direction IN ('right', 'super')
`, [targetUserId, userId]);
// Исправить на:
const reciprocalSwipe = await client.query(`
SELECT * FROM swipes
WHERE user_id = $1 AND target_user_id = $2 AND direction IN ('right', 'super')
`, [targetUserId, userId]);
```
### 3. В файле `matchingService.ts` - метод `getRecentLikes`:
```typescript
// Неверно (если используется метод mapEntityToSwipe):
private mapEntityToSwipe(entity: any): Swipe {
return new Swipe({
id: entity.id,
userId: entity.swiper_id,
targetUserId: entity.swiped_id,
type: this.convertDirectionToSwipeType(entity.direction),
timestamp: entity.created_at,
isMatch: entity.is_match
});
}
// Исправить на:
private mapEntityToSwipe(entity: any): Swipe {
return new Swipe({
id: entity.id,
userId: entity.user_id,
targetUserId: entity.target_user_id,
type: this.convertDirectionToSwipeType(entity.direction),
timestamp: entity.created_at,
isMatch: entity.is_match
});
}
```
### 4. В файле `matchingService.ts` - метод `getDailySwipeStats`:
```typescript
// Неверно:
const result = await query(`
SELECT direction, COUNT(*) as count
FROM swipes
WHERE swiper_id = $1 AND created_at >= $2
GROUP BY direction
`, [userId, today]);
// Исправить на:
const result = await query(`
SELECT direction, COUNT(*) as count
FROM swipes
WHERE user_id = $1 AND created_at >= $2
GROUP BY direction
`, [userId, today]);
```
## Примечание
После внесения исправлений рекомендуется проверить все остальные места в коде, где могут использоваться эти имена столбцов, и убедиться в их согласованности.

View File

@@ -1,174 +0,0 @@
require('dotenv').config();
const { Pool } = require('pg');
const { v4: uuidv4 } = require('uuid');
// Функция для запуска скрипта
async function initializeDatabase() {
console.log('Starting database initialization script...');
const pool = new Pool({
host: 'localhost', // Используем localhost
port: 5432, // Используем стандартный порт 5432
database: 'telegram_tinder_bot',
user: 'postgres',
password: '',
max: 5,
connectionTimeoutMillis: 5000
});
console.log('DB Connection Details:');
console.log('- Host: localhost');
console.log('- Port: 5432');
console.log('- Database: telegram_tinder_bot');
console.log('- User: postgres');
try {
// Проверяем подключение
console.log('Testing connection...');
const client = await pool.connect();
try {
console.log('✅ Connected to database successfully!');
// 1. Создаем расширение для генерации UUID если его нет
console.log('Creating UUID extension...');
await client.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`);
// 2. Создаем таблицу для шаблонов уведомлений
console.log('Creating notification_templates table...');
await client.query(`
CREATE TABLE IF NOT EXISTS notification_templates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
type VARCHAR(50) NOT NULL UNIQUE,
title TEXT NOT NULL,
message_template TEXT NOT NULL,
button_template JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`);
// 3. Вставляем базовые шаблоны
console.log('Inserting notification templates...');
const templates = [
{
id: uuidv4(),
type: 'new_like',
title: 'Новый лайк!',
message_template: '❤️ *{{name}}* поставил(а) вам лайк!\n\nВозраст: {{age}}\n{{city}}\n\nОтветьте взаимностью или посмотрите профиль.',
button_template: JSON.stringify({
inline_keyboard: [
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
[
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
],
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
]
})
},
{
id: uuidv4(),
type: 'super_like',
title: 'Супер-лайк!',
message_template: '⭐️ *{{name}}* отправил(а) вам супер-лайк!\n\nВозраст: {{age}}\n{{city}}\n\nВы произвели особое впечатление! Ответьте взаимностью или посмотрите профиль.',
button_template: JSON.stringify({
inline_keyboard: [
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
[
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
],
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
]
})
},
{
id: uuidv4(),
type: 'new_match',
title: 'Новый матч!',
message_template: '🎊 *Ура! Это взаимно!* 🎊\n\nВы и *{{name}}* понравились друг другу!\nВозраст: {{age}}\n{{city}}\n\nСделайте первый шаг - напишите сообщение!',
button_template: JSON.stringify({
inline_keyboard: [
[{ text: '💬 Начать общение', callback_data: 'open_native_chat_{{matchId}}' }],
[
{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' },
{ text: '📋 Все матчи', callback_data: 'native_chats' }
]
]
})
},
{
id: uuidv4(),
type: 'new_message',
title: 'Новое сообщение!',
message_template: '💌 *Новое сообщение!*\n\nОт: *{{name}}*\n\n"{{message}}"\n\nОтветьте на сообщение прямо сейчас!',
button_template: JSON.stringify({
inline_keyboard: [
[{ text: '📩 Ответить', callback_data: 'open_native_chat_{{matchId}}' }],
[
{ text: '👤 Профиль', callback_data: 'view_profile:{{userId}}' },
{ text: '📋 Все чаты', callback_data: 'native_chats' }
]
]
})
}
];
// Вставляем шаблоны с проверкой на конфликты
for (const template of templates) {
await client.query(`
INSERT INTO notification_templates
(id, type, title, message_template, button_template, created_at)
VALUES ($1, $2, $3, $4, $5, NOW())
ON CONFLICT (type) DO UPDATE
SET title = $3,
message_template = $4,
button_template = $5
`, [template.id, template.type, template.title, template.message_template, template.button_template]);
}
console.log('✅ Notification templates created/updated successfully');
// 4. Создаем таблицу для хранения логов уведомлений если её нет
console.log('Creating notifications table...');
await client.query(`
CREATE TABLE IF NOT EXISTS notifications (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL,
type VARCHAR(50) NOT NULL,
data JSONB NOT NULL,
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
)
`);
console.log('✅ Notifications table created successfully');
// 5. Проверяем, что таблицы созданы
const tablesResult = await client.query(`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN ('notification_templates', 'notifications')
`);
console.log('Created tables:');
tablesResult.rows.forEach(row => {
console.log(`- ${row.table_name}`);
});
} finally {
client.release();
await pool.end();
}
console.log('✅ Database initialization completed successfully');
} catch (error) {
console.error('❌ Database initialization error:', error);
}
}
// Запускаем скрипт
initializeDatabase();

View File

@@ -1,153 +0,0 @@
// Патч для учета просмотренных профилей в функциональности бота
// 1. Добавляем функцию recordProfileView в ProfileController
import { Profile, ProfileData } from '../models/Profile';
import { ProfileService } from '../services/profileService';
export class ProfileController {
constructor(private profileService: ProfileService) {}
// Существующие методы...
// Новый метод для записи просмотра профиля
async recordProfileView(viewerTelegramId: string, viewedTelegramId: string, viewType: string = 'browse'): Promise<boolean> {
try {
// Получаем внутренние ID пользователей
const viewerId = await this.profileService.getUserIdByTelegramId(viewerTelegramId);
const viewedId = await this.profileService.getUserIdByTelegramId(viewedTelegramId);
if (!viewerId || !viewedId) {
console.error('Не удалось найти пользователей для записи просмотра профиля');
return false;
}
// Проверяем существование таблицы profile_views
const checkTableResult = await this.profileService.checkTableExists('profile_views');
if (checkTableResult) {
// Записываем просмотр
await this.profileService.recordProfileView(viewerId, viewedId, viewType);
console.log(`Просмотр профиля записан: ${viewerTelegramId} просмотрел ${viewedTelegramId}`);
return true;
} else {
console.log('Таблица profile_views не существует, просмотр не записан');
return false;
}
} catch (error) {
console.error('Ошибка при записи просмотра профиля:', error);
return false;
}
}
// Новый метод для получения списка просмотренных профилей
async getViewedProfiles(telegramId: string, limit: number = 50): Promise<string[]> {
try {
// Получаем внутренний ID пользователя
const userId = await this.profileService.getUserIdByTelegramId(telegramId);
if (!userId) {
console.error('Не удалось найти пользователя для получения списка просмотренных профилей');
return [];
}
// Проверяем существование таблицы profile_views
const checkTableResult = await this.profileService.checkTableExists('profile_views');
if (checkTableResult) {
// Получаем список просмотренных профилей
return await this.profileService.getViewedProfiles(userId, limit);
} else {
console.log('Таблица profile_views не существует, возвращаем пустой список');
return [];
}
} catch (error) {
console.error('Ошибка при получении списка просмотренных профилей:', error);
return [];
}
}
}
// 2. Добавляем функцию для проверки существования таблицы в ProfileService
async checkTableExists(tableName: string): Promise<boolean> {
try {
const result = await query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = $1
);
`, [tableName]);
return result.rows.length > 0 && result.rows[0].exists;
} catch (error) {
console.error(`Ошибка проверки существования таблицы ${tableName}:`, error);
return false;
}
}
// 3. Обновляем обработчик показа профиля, чтобы записывать просмотры
async function handleShowProfile(ctx: any) {
// Существующий код...
// После успешного отображения профиля записываем просмотр
const viewerTelegramId = ctx.from.id.toString();
const viewedTelegramId = candidateProfile.telegram_id.toString();
try {
const profileController = new ProfileController(new ProfileService());
await profileController.recordProfileView(viewerTelegramId, viewedTelegramId, 'browse');
} catch (error) {
console.error('Ошибка при записи просмотра профиля:', error);
}
// Остальной код...
}
// 4. Обновляем функцию getNextCandidate, чтобы учитывать просмотренные профили
async function getNextCandidate(ctx: any) {
const telegramId = ctx.from.id.toString();
const isNewUser = false; // Определяем, является ли пользователь новым
try {
// Сначала пытаемся получить профили, которые пользователь еще не просматривал
const matchingService = new MatchingService();
const profileService = new ProfileService();
const profileController = new ProfileController(profileService);
// Получаем UUID пользователя
const userId = await profileService.getUserIdByTelegramId(telegramId);
if (!userId) {
console.error('Не удалось найти пользователя для получения следующего кандидата');
return null;
}
// Получаем список просмотренных профилей
const viewedProfiles = await profileController.getViewedProfiles(telegramId);
// Получаем профиль пользователя
const userProfile = await profileService.getProfileByTelegramId(telegramId);
if (!userProfile) {
console.error('Не удалось найти профиль пользователя для получения следующего кандидата');
return null;
}
// Ищем подходящий профиль с учетом просмотренных
const nextCandidate = await matchingService.getNextCandidate(telegramId, isNewUser);
// Если найден кандидат, записываем просмотр
if (nextCandidate) {
const viewedTelegramId = await profileService.getTelegramIdByUserId(nextCandidate.userId);
if (viewedTelegramId) {
await profileController.recordProfileView(telegramId, viewedTelegramId, 'browse');
}
}
return nextCandidate;
} catch (error) {
console.error('Ошибка при получении следующего кандидата:', error);
return null;
}
}

View File

@@ -1,76 +1,49 @@
# Исправление проблем с уведомлениями в боте
# Структура скриптов в директории `/scripts`
Этот набор скриптов предназначен для исправления проблем с обработкой уведомлений в боте.
Эта директория содержит вспомогательные скрипты для работы с Telegram Tinder Bot.
## Описание проблемы
## Основные скрипты
После внедрения системы уведомлений и связанных с ней изменений в базе данных, возникла проблема с обработкой callback запросов. Бот перестал реагировать на все callback запросы, кроме тех, что связаны с уведомлениями.
- `startup.sh` - Скрипт запуска бота в Docker-контейнере
- `migrate-sync.js` - Синхронизация миграций базы данных
- `createNotificationTables.js` - Создание таблиц для системы уведомлений
- `add-hobbies-column.js` - Добавление колонки интересов в профиль
- `create_profile_fix.js` - Исправление профилей пользователей
- `createProfileViewsTable.js` - Создание таблицы для учета просмотров профилей
- `update_bot_with_notifications.js` - Обновление бота с поддержкой уведомлений
Проблема вызвана следующими факторами:
1. Отсутствие или неверная структура таблиц в базе данных для хранения уведомлений
2. Отсутствие необходимых полей `state` и `state_data` в таблице `users`
3. Отсутствие правильной регистрации обработчиков уведомлений в файле `bot.ts`
## Директории
## Решение
- `/legacy` - Устаревшие и тестовые скрипты, сохраненные для истории
Для решения проблемы были созданы следующие скрипты:
## Использование скриптов
### 1. `fix_notification_callbacks.js`
Проверяет и создает необходимые таблицы и столбцы в базе данных:
- Таблицы `notifications`, `scheduled_notifications`, `notification_templates`
- Столбцы `notification_settings`, `state`, `state_data` в таблице `users`
Скрипты JavaScript можно запускать с помощью Node.js:
### 2. `update_bot_with_notifications.js`
Обновляет файл `bot.ts`:
- Добавляет импорт класса `NotificationHandlers`
- Добавляет объявление поля `notificationHandlers` в класс `TelegramTinderBot`
- Добавляет создание экземпляра `NotificationHandlers` в конструкторе
- Добавляет регистрацию обработчиков уведомлений в методе `registerHandlers`
```bash
node scripts/script-name.js
```
### 3. `fix_all_notifications.js`
Запускает оба скрипта последовательно для полного исправления проблемы
Bash скрипты должны быть сделаны исполняемыми:
## Как использовать
```bash
chmod +x scripts/script-name.sh
./scripts/script-name.sh
```
1. Остановите бота, если он запущен:
```bash
# Нажмите Ctrl+C в терминале, где запущен бот
# или найдите процесс и завершите его
```
## Добавление новых скриптов
2. Запустите комплексный скрипт исправления:
```bash
node scripts/fix_all_notifications.js
```
При добавлении новых скриптов соблюдайте следующие правила:
1. Используйте понятное имя файла, отражающее его назначение
2. Добавьте комментарии в начало файла с описанием его функциональности
3. Добавьте запись об этом скрипте в текущий файл README.md
3. После успешного выполнения скрипта перезапустите бота:
```bash
npm run start
```
## Скрипты миграций
## Проверка результата
Миграции базы данных следует создавать с помощью команды:
После запуска бота убедитесь, что:
1. Бот отвечает на все callback запросы (включая кнопки, не связанные с уведомлениями)
2. Настройки уведомлений работают корректно (команда /notifications или кнопка в меню настроек)
3. Уведомления о лайках, супер-лайках и новых матчах приходят пользователям
```bash
npm run migrate:create your_migration_name
```
## Если проблемы остались
Если после выполнения всех шагов проблемы остались, выполните следующие проверки:
1. Проверьте логи бота на наличие ошибок
2. Проверьте структуру базы данных:
```sql
\dt -- Список всех таблиц
\d notifications -- Структура таблицы notifications
\d scheduled_notifications -- Структура таблицы scheduled_notifications
\d notification_templates -- Структура таблицы notification_templates
\d users -- Убедитесь, что поля state, state_data и notification_settings существуют
```
3. Проверьте код в файлах:
- `src/bot.ts`: должен содержать импорт, создание и регистрацию `NotificationHandlers`
- `src/handlers/callbackHandlers.ts`: должен правильно обрабатывать все callback-запросы
В случае обнаружения ошибок, исправьте их вручную и перезапустите бота.
Это создаст файл миграции в директории `/migrations`.

16
scripts/startup.sh Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
# startup.sh - Script to run migrations and start the bot
echo "🚀 Starting Telegram Tinder Bot..."
# Wait for database to be ready
echo "⏳ Waiting for database to be ready..."
sleep 5
# Run database migrations
echo "🔄 Running database migrations..."
node dist/database/migrateOnStartup.js
# Start the bot
echo "✅ Starting the bot..."
node dist/bot.js

Binary file not shown.

404
sql/consolidated.sql Normal file
View File

@@ -0,0 +1,404 @@
# Consolidated SQL файл для миграции базы данных Telegram Tinder Bot
# Этот файл содержит все необходимые SQL-запросы для создания базы данных с нуля
-- Создание расширения для UUID
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Создание перечислений
CREATE TYPE gender_type AS ENUM ('male', 'female', 'other');
CREATE TYPE swipe_action AS ENUM ('like', 'dislike', 'superlike');
-- Создание таблицы пользователей
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
telegram_id BIGINT UNIQUE NOT NULL,
username VARCHAR(255),
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255),
language_code VARCHAR(10),
is_premium BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Создание таблицы профилей
CREATE TABLE IF NOT EXISTS profiles (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
age INTEGER NOT NULL CHECK (age >= 18),
gender gender_type NOT NULL,
bio TEXT,
photos TEXT[], -- JSON array of photo file_ids
location VARCHAR(255),
job VARCHAR(255),
interests TEXT[], -- JSON array of interests
last_active TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
is_completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_user_profile UNIQUE (user_id)
);
-- Создание индекса для поиска по возрасту и полу
CREATE INDEX idx_profiles_age_gender ON profiles(age, gender);
-- Создание таблицы предпочтений поиска
CREATE TABLE IF NOT EXISTS search_preferences (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
age_min INTEGER NOT NULL DEFAULT 18 CHECK (age_min >= 18),
age_max INTEGER NOT NULL DEFAULT 99 CHECK (age_max >= age_min),
looking_for gender_type NOT NULL,
distance_max INTEGER, -- max distance in km, NULL means no limit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_user_preferences UNIQUE (user_id)
);
-- Создание таблицы действий (лайки/дизлайки)
CREATE TABLE IF NOT EXISTS swipes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
target_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
action swipe_action NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_swipe UNIQUE (user_id, target_id)
);
-- Создание индекса для быстрого поиска матчей
CREATE INDEX idx_swipes_user_target ON swipes(user_id, target_id);
-- Создание таблицы матчей
CREATE TABLE IF NOT EXISTS matches (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id_1 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
user_id_2 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
CONSTRAINT unique_match UNIQUE (user_id_1, user_id_2)
);
-- Создание индекса для быстрого поиска матчей по пользователю
CREATE INDEX idx_matches_user_id_1 ON matches(user_id_1);
CREATE INDEX idx_matches_user_id_2 ON matches(user_id_2);
-- Создание таблицы блокировок
CREATE TABLE IF NOT EXISTS blocks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
blocker_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
blocked_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_block UNIQUE (blocker_id, blocked_id)
);
-- Создание таблицы сообщений
CREATE TABLE IF NOT EXISTS messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
match_id UUID NOT NULL REFERENCES matches(id) ON DELETE CASCADE,
sender_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
text TEXT NOT NULL,
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Создание индекса для быстрого поиска сообщений
CREATE INDEX idx_messages_match_id ON messages(match_id);
-- Создание таблицы уведомлений
CREATE TABLE IF NOT EXISTS notifications (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL, -- new_match, new_message, etc.
content TEXT NOT NULL,
is_read BOOLEAN DEFAULT FALSE,
reference_id UUID, -- Can reference a match_id, message_id, etc.
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Создание индекса для быстрого поиска уведомлений
CREATE INDEX idx_notifications_user_id ON notifications(user_id);
-- Создание таблицы настроек
CREATE TABLE IF NOT EXISTS settings (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
notifications_enabled BOOLEAN DEFAULT TRUE,
show_online_status BOOLEAN DEFAULT TRUE,
visibility BOOLEAN DEFAULT TRUE, -- whether profile is visible in search
theme VARCHAR(20) DEFAULT 'light',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_user_settings UNIQUE (user_id)
);
-- Создание таблицы просмотров профиля
CREATE TABLE IF NOT EXISTS profile_views (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
viewer_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
viewed_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
view_count INTEGER DEFAULT 1,
last_viewed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_view UNIQUE (viewer_id, viewed_id)
);
-- Создание таблицы для премиум-пользователей
CREATE TABLE IF NOT EXISTS premium_features (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
is_premium BOOLEAN DEFAULT FALSE,
superlike_quota INTEGER DEFAULT 1,
spotlight_quota INTEGER DEFAULT 0,
see_likes BOOLEAN DEFAULT FALSE, -- Can see who liked their profile
unlimited_likes BOOLEAN DEFAULT FALSE,
expires_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_user_premium UNIQUE (user_id)
);
-- Функция для обновления поля updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Триггеры для обновления поля updated_at
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_profiles_updated_at
BEFORE UPDATE ON profiles
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_search_preferences_updated_at
BEFORE UPDATE ON search_preferences
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_settings_updated_at
BEFORE UPDATE ON settings
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_premium_features_updated_at
BEFORE UPDATE ON premium_features
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
-- Индекс для поиска пользователей по Telegram ID (часто используемый запрос)
CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);
-- Индекс для статуса профиля (активный/неактивный, завершенный/незавершенный)
CREATE INDEX IF NOT EXISTS idx_profiles_is_completed ON profiles(is_completed);
-- Представление для статистики
CREATE OR REPLACE VIEW user_statistics AS
SELECT
u.id,
u.telegram_id,
(SELECT COUNT(*) FROM swipes WHERE user_id = u.id AND action = 'like') AS likes_given,
(SELECT COUNT(*) FROM swipes WHERE user_id = u.id AND action = 'dislike') AS dislikes_given,
(SELECT COUNT(*) FROM swipes WHERE target_id = u.id AND action = 'like') AS likes_received,
(SELECT COUNT(*) FROM matches WHERE user_id_1 = u.id OR user_id_2 = u.id) AS matches_count,
(SELECT COUNT(*) FROM messages WHERE sender_id = u.id) AS messages_sent,
(SELECT COUNT(*) FROM profile_views WHERE viewed_id = u.id) AS profile_views
FROM users u;
-- Функция для создания матча при взаимных лайках
CREATE OR REPLACE FUNCTION create_match_on_mutual_like()
RETURNS TRIGGER AS $$
DECLARE
reverse_like_exists BOOLEAN;
BEGIN
-- Check if there is a reverse like
SELECT EXISTS (
SELECT 1
FROM swipes
WHERE user_id = NEW.target_id
AND target_id = NEW.user_id
AND action = 'like'
) INTO reverse_like_exists;
-- If there is a reverse like, create a match
IF reverse_like_exists AND NEW.action = 'like' THEN
INSERT INTO matches (user_id_1, user_id_2)
VALUES (
LEAST(NEW.user_id, NEW.target_id),
GREATEST(NEW.user_id, NEW.target_id)
)
ON CONFLICT (user_id_1, user_id_2) DO NOTHING;
-- Create notifications for both users
INSERT INTO notifications (user_id, type, content, reference_id)
VALUES (
NEW.user_id,
'new_match',
'У вас новый матч!',
(SELECT id FROM matches WHERE
(user_id_1 = LEAST(NEW.user_id, NEW.target_id) AND user_id_2 = GREATEST(NEW.user_id, NEW.target_id))
)
);
INSERT INTO notifications (user_id, type, content, reference_id)
VALUES (
NEW.target_id,
'new_match',
'У вас новый матч!',
(SELECT id FROM matches WHERE
(user_id_1 = LEAST(NEW.user_id, NEW.target_id) AND user_id_2 = GREATEST(NEW.user_id, NEW.target_id))
)
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Триггер для создания матча при взаимных лайках
CREATE TRIGGER create_match_trigger
AFTER INSERT ON swipes
FOR EACH ROW
EXECUTE FUNCTION create_match_on_mutual_like();
-- Функция для создания уведомления о новом сообщении
CREATE OR REPLACE FUNCTION notify_new_message()
RETURNS TRIGGER AS $$
DECLARE
recipient_id UUID;
match_record RECORD;
BEGIN
-- Get the match record
SELECT * INTO match_record FROM matches WHERE id = NEW.match_id;
-- Determine the recipient
IF match_record.user_id_1 = NEW.sender_id THEN
recipient_id := match_record.user_id_2;
ELSE
recipient_id := match_record.user_id_1;
END IF;
-- Create notification
INSERT INTO notifications (user_id, type, content, reference_id)
VALUES (
recipient_id,
'new_message',
'У вас новое сообщение!',
NEW.id
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Триггер для создания уведомления о новом сообщении
CREATE TRIGGER notify_new_message_trigger
AFTER INSERT ON messages
FOR EACH ROW
EXECUTE FUNCTION notify_new_message();
-- Функция для обновления времени последней активности пользователя
CREATE OR REPLACE FUNCTION update_last_active()
RETURNS TRIGGER AS $$
BEGIN
UPDATE profiles
SET last_active = CURRENT_TIMESTAMP
WHERE user_id = NEW.user_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Триггер для обновления времени последней активности при свайпах
CREATE TRIGGER update_last_active_on_swipe
AFTER INSERT ON swipes
FOR EACH ROW
EXECUTE FUNCTION update_last_active();
-- Триггер для обновления времени последней активности при отправке сообщений
CREATE TRIGGER update_last_active_on_message
AFTER INSERT ON messages
FOR EACH ROW
EXECUTE FUNCTION update_last_active();
-- Создание функции для автоматического создания профиля при создании пользователя
CREATE OR REPLACE FUNCTION create_initial_profile()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO profiles (user_id, name, age, gender)
VALUES (NEW.id, COALESCE(NEW.first_name, 'User'), 18, 'other');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Триггер для автоматического создания профиля при создании пользователя
CREATE TRIGGER create_profile_trigger
AFTER INSERT ON users
FOR EACH ROW
EXECUTE FUNCTION create_initial_profile();
-- Индексы для оптимизации частых запросов
CREATE INDEX IF NOT EXISTS idx_profiles_last_active ON profiles(last_active);
CREATE INDEX IF NOT EXISTS idx_swipes_action ON swipes(action);
CREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications(is_read);
CREATE INDEX IF NOT EXISTS idx_messages_is_read ON messages(is_read);
-- Добавление ограничений для проверки возраста
ALTER TABLE profiles DROP CONSTRAINT IF EXISTS age_check;
ALTER TABLE profiles ADD CONSTRAINT age_check CHECK (age >= 18 AND age <= 99);
-- Добавление ограничений для предпочтений
ALTER TABLE search_preferences DROP CONSTRAINT IF EXISTS age_range_check;
ALTER TABLE search_preferences ADD CONSTRAINT age_range_check CHECK (age_min >= 18 AND age_max >= age_min AND age_max <= 99);
-- Комментарии к таблицам для документации
COMMENT ON TABLE users IS 'Таблица пользователей Telegram';
COMMENT ON TABLE profiles IS 'Профили пользователей для знакомств';
COMMENT ON TABLE search_preferences IS 'Предпочтения поиска пользователей';
COMMENT ON TABLE swipes IS 'История лайков/дислайков';
COMMENT ON TABLE matches IS 'Совпадения (матчи) между пользователями';
COMMENT ON TABLE messages IS 'Сообщения между пользователями';
COMMENT ON TABLE notifications IS 'Уведомления для пользователей';
COMMENT ON TABLE settings IS 'Настройки пользователей';
COMMENT ON TABLE profile_views IS 'История просмотров профилей';
COMMENT ON TABLE premium_features IS 'Премиум-функции для пользователей';
-- Представление для быстрого получения активных матчей с информацией о пользователе
CREATE OR REPLACE VIEW active_matches AS
SELECT
m.id AS match_id,
m.created_at AS match_date,
CASE
WHEN m.user_id_1 = u1.id THEN u2.id
ELSE u1.id
END AS partner_id,
CASE
WHEN m.user_id_1 = u1.id THEN u2.telegram_id
ELSE u1.telegram_id
END AS partner_telegram_id,
CASE
WHEN m.user_id_1 = u1.id THEN p2.name
ELSE p1.name
END AS partner_name,
CASE
WHEN m.user_id_1 = u1.id THEN p2.photos[1]
ELSE p1.photos[1]
END AS partner_photo,
(SELECT COUNT(*) FROM messages WHERE match_id = m.id) AS message_count,
(SELECT COUNT(*) FROM messages WHERE match_id = m.id AND is_read = false AND sender_id != u1.id) AS unread_count,
m.is_active
FROM matches m
JOIN users u1 ON m.user_id_1 = u1.id
JOIN users u2 ON m.user_id_2 = u2.id
JOIN profiles p1 ON u1.id = p1.user_id
JOIN profiles p2 ON u2.id = p2.user_id
WHERE m.is_active = true;

View File

@@ -0,0 +1,108 @@
// Script to run migrations on startup
import { Pool } from 'pg';
import * as fs from 'fs';
import * as path from 'path';
import 'dotenv/config';
async function runMigrations() {
console.log('Starting database migration...');
// Create a connection pool
const pool = new Pool({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'telegram_tinder_bot',
user: process.env.DB_USERNAME || 'postgres',
password: process.env.DB_PASSWORD,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
try {
// Test connection
const testRes = await pool.query('SELECT NOW()');
console.log(`Database connection successful at ${testRes.rows[0].now}`);
// Create migrations table if not exists
await pool.query(`
CREATE TABLE IF NOT EXISTS migrations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Get list of executed migrations
const migrationRes = await pool.query('SELECT name FROM migrations');
const executedMigrations = migrationRes.rows.map(row => row.name);
console.log(`Found ${executedMigrations.length} executed migrations`);
// Get migration files
const migrationsPath = path.join(__dirname, 'migrations');
let migrationFiles = [];
try {
migrationFiles = fs.readdirSync(migrationsPath)
.filter(file => file.endsWith('.sql'))
.sort();
console.log(`Found ${migrationFiles.length} migration files`);
} catch (error: any) {
console.error(`Error reading migrations directory: ${error.message}`);
console.log('Continuing with built-in consolidated migration...');
// If no external files found, use consolidated.sql
const consolidatedSQL = fs.readFileSync(path.join(__dirname, 'migrations', 'consolidated.sql'), 'utf8');
console.log('Executing consolidated migration...');
await pool.query(consolidatedSQL);
if (!executedMigrations.includes('consolidated.sql')) {
await pool.query(
'INSERT INTO migrations (name) VALUES ($1)',
['consolidated.sql']
);
}
console.log('Consolidated migration completed successfully');
return;
}
// Run each migration that hasn't been executed yet
for (const file of migrationFiles) {
if (!executedMigrations.includes(file)) {
console.log(`Executing migration: ${file}`);
const sql = fs.readFileSync(path.join(migrationsPath, file), 'utf8');
try {
await pool.query('BEGIN');
await pool.query(sql);
await pool.query(
'INSERT INTO migrations (name) VALUES ($1)',
[file]
);
await pool.query('COMMIT');
console.log(`Migration ${file} completed successfully`);
} catch (error: any) {
await pool.query('ROLLBACK');
console.error(`Error executing migration ${file}: ${error.message}`);
throw error;
}
} else {
console.log(`Migration ${file} already executed, skipping`);
}
}
console.log('All migrations completed successfully!');
} catch (error: any) {
console.error(`Migration failed: ${error.message}`);
process.exit(1);
} finally {
await pool.end();
}
}
runMigrations().catch((error: any) => {
console.error('Unhandled error during migration:', error);
process.exit(1);
});

View File

@@ -0,0 +1,182 @@
-- Consolidated migrations for Telegram Tinder Bot
-- Create extension for UUID if not exists
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
----------------------------------------------
-- Core Tables
----------------------------------------------
-- Users table
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
telegram_id BIGINT UNIQUE NOT NULL,
username VARCHAR(255),
first_name VARCHAR(255),
last_name VARCHAR(255),
language_code VARCHAR(10) DEFAULT 'ru',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
last_active_at TIMESTAMP DEFAULT NOW(),
premium BOOLEAN DEFAULT FALSE,
state VARCHAR(255),
state_data JSONB DEFAULT '{}'::jsonb
);
-- Profiles table
CREATE TABLE IF NOT EXISTS profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
age INTEGER NOT NULL CHECK (age >= 18 AND age <= 100),
gender VARCHAR(10) NOT NULL CHECK (gender IN ('male', 'female', 'other')),
interested_in VARCHAR(10) NOT NULL CHECK (interested_in IN ('male', 'female', 'both')),
bio TEXT,
photos JSONB DEFAULT '[]',
interests JSONB DEFAULT '[]',
city VARCHAR(255),
education VARCHAR(255),
job VARCHAR(255),
height INTEGER,
location_lat DECIMAL(10, 8),
location_lon DECIMAL(11, 8),
search_min_age INTEGER DEFAULT 18,
search_max_age INTEGER DEFAULT 50,
search_max_distance INTEGER DEFAULT 50,
is_verified BOOLEAN DEFAULT FALSE,
is_visible BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
religion VARCHAR(255),
dating_goal VARCHAR(50),
smoking VARCHAR(20),
drinking VARCHAR(20),
has_kids BOOLEAN DEFAULT FALSE
);
-- Swipes table
CREATE TABLE IF NOT EXISTS swipes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
target_user_id UUID REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(20) NOT NULL CHECK (type IN ('like', 'pass', 'superlike')),
created_at TIMESTAMP DEFAULT NOW(),
is_match BOOLEAN DEFAULT FALSE,
UNIQUE(user_id, target_user_id)
);
-- Matches table
CREATE TABLE IF NOT EXISTS matches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id_1 UUID REFERENCES users(id) ON DELETE CASCADE,
user_id_2 UUID REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT NOW(),
last_message_at TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
is_super_match BOOLEAN DEFAULT FALSE,
unread_count_1 INTEGER DEFAULT 0,
unread_count_2 INTEGER DEFAULT 0,
UNIQUE(user_id_1, user_id_2)
);
-- Messages table
CREATE TABLE IF NOT EXISTS messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
match_id UUID REFERENCES matches(id) ON DELETE CASCADE,
sender_id UUID REFERENCES users(id) ON DELETE CASCADE,
receiver_id UUID REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
message_type VARCHAR(20) DEFAULT 'text' CHECK (message_type IN ('text', 'photo', 'gif', 'sticker')),
created_at TIMESTAMP DEFAULT NOW(),
is_read BOOLEAN DEFAULT FALSE
);
----------------------------------------------
-- Profile Views Table
----------------------------------------------
-- Table for tracking profile views
CREATE TABLE IF NOT EXISTS profile_views (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
viewer_id UUID REFERENCES users(id) ON DELETE CASCADE,
viewed_id UUID REFERENCES users(id) ON DELETE CASCADE,
view_type VARCHAR(20) DEFAULT 'browse' CHECK (view_type IN ('browse', 'search', 'recommended')),
viewed_at TIMESTAMP DEFAULT NOW(),
CONSTRAINT unique_profile_view UNIQUE (viewer_id, viewed_id, view_type)
);
-- Index for profile views
CREATE INDEX IF NOT EXISTS idx_profile_views_viewer ON profile_views(viewer_id);
CREATE INDEX IF NOT EXISTS idx_profile_views_viewed ON profile_views(viewed_id);
----------------------------------------------
-- Notification Tables
----------------------------------------------
-- Notifications table
CREATE TABLE IF NOT EXISTS notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL,
content JSONB NOT NULL DEFAULT '{}',
is_read BOOLEAN DEFAULT FALSE,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Notification settings table
CREATE TABLE IF NOT EXISTS notification_settings (
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
new_matches BOOLEAN DEFAULT TRUE,
new_messages BOOLEAN DEFAULT TRUE,
new_likes BOOLEAN DEFAULT TRUE,
reminders BOOLEAN DEFAULT TRUE,
daily_summary BOOLEAN DEFAULT FALSE,
time_preference VARCHAR(20) DEFAULT 'evening',
do_not_disturb BOOLEAN DEFAULT FALSE,
do_not_disturb_start TIME,
do_not_disturb_end TIME,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Scheduled notifications table
CREATE TABLE IF NOT EXISTS scheduled_notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL,
content JSONB NOT NULL DEFAULT '{}',
scheduled_at TIMESTAMP WITH TIME ZONE NOT NULL,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
----------------------------------------------
-- Indexes for better performance
----------------------------------------------
-- User Indexes
CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);
-- Profile Indexes
CREATE INDEX IF NOT EXISTS idx_profiles_user_id ON profiles(user_id);
CREATE INDEX IF NOT EXISTS idx_profiles_location ON profiles(location_lat, location_lon)
WHERE location_lat IS NOT NULL AND location_lon IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_profiles_age_gender ON profiles(age, gender, interested_in);
-- Swipe Indexes
CREATE INDEX IF NOT EXISTS idx_swipes_user ON swipes(user_id, target_user_id);
-- Match Indexes
CREATE INDEX IF NOT EXISTS idx_matches_users ON matches(user_id_1, user_id_2);
-- Message Indexes
CREATE INDEX IF NOT EXISTS idx_messages_match ON messages(match_id, created_at);
-- Notification Indexes
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_notifications_type ON notifications(type);
CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at);
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_user_id ON scheduled_notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_scheduled_at ON scheduled_notifications(scheduled_at);
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_processed ON scheduled_notifications(processed);

62
src/premium/README.md Normal file
View File

@@ -0,0 +1,62 @@
# Модуль премиум-функций Telegram Tinder Bot
Этот каталог содержит модули и скрипты для управления премиум-функциями бота.
## Содержимое
- `add-premium-columns.js` - Добавление колонок для премиум-функций в базу данных (версия JavaScript)
- `add-premium-columns.ts` - Добавление колонок для премиум-функций в базу данных (версия TypeScript)
- `add-premium-columns-direct.js` - Прямое добавление премиум-колонок без миграций
- `addPremiumColumn.js` - Добавление отдельной колонки премиум в таблицу пользователей
- `setPremiumStatus.js` - Обновление статуса премиум для пользователей
## Премиум-функции
В боте реализованы следующие премиум-функции:
1. **Неограниченные лайки** - снятие дневного лимита на количество лайков
2. **Супер-лайки** - возможность отправлять супер-лайки (повышенный приоритет)
3. **Просмотр лайков** - возможность видеть, кто поставил лайк вашему профилю
4. **Скрытый режим** - возможность скрывать свою активность
5. **Расширенные фильтры** - дополнительные параметры для поиска
## Использование
### Добавление премиум-колонок в базу данных
```bash
node src/premium/add-premium-columns.js
```
### Изменение премиум-статуса пользователя
```typescript
import { PremiumService } from '../services/premiumService';
// Установка премиум-статуса для пользователя
const premiumService = new PremiumService();
await premiumService.setPremiumStatus(userId, true, 30); // 30 дней премиума
```
## Интеграция в основной код
Проверка премиум-статуса должна выполняться следующим образом:
```typescript
// В классах контроллеров
const isPremium = await this.premiumService.checkUserPremium(userId);
if (isPremium) {
// Предоставить премиум-функцию
} else {
// Сообщить о необходимости премиум-подписки
}
```
## Период действия премиум-статуса
По умолчанию премиум-статус устанавливается на 30 дней. Для изменения срока используйте третий параметр в методе `setPremiumStatus`.
## Дополнительная информация
Более подробная информация о премиум-функциях содержится в документации проекта в каталоге `docs/VIP_FUNCTIONS.md`.

241
start.bat Normal file
View File

@@ -0,0 +1,241 @@
@echo off
:: start.bat - Скрипт для запуска Telegram Tinder Bot на Windows
:: Позволяет выбрать между локальной БД в контейнере или внешней БД
echo ================================================
echo Запуск Telegram Tinder Bot
echo ================================================
:: Проверка наличия Docker и Docker Compose
WHERE docker >nul 2>&1
IF %ERRORLEVEL% NEQ 0 (
echo [31mОШИБКА: Docker не установлен![0m
echo Установите Docker Desktop для Windows: https://docs.docker.com/desktop/install/windows-install/
exit /b 1
)
:: Проверяем наличие .env файла
IF NOT EXIST .env (
echo [33mФайл .env не найден. Создаем из шаблона...[0m
IF EXIST .env.example (
copy .env.example .env
echo [32mФайл .env создан из шаблона. Пожалуйста, отредактируйте его с вашими настройками.[0m
) ELSE (
echo [31mОШИБКА: Файл .env.example не найден. Создайте файл .env вручную.[0m
exit /b 1
)
)
:: Спрашиваем про запуск базы данных
set /p use_container_db="Запустить базу данных PostgreSQL в контейнере? (y/n): "
:: Функции для работы с docker-compose
IF /I "%use_container_db%" NEQ "y" (
:: Запрашиваем параметры подключения к внешней БД
echo [36mВведите параметры подключения к внешней базе данных:[0m
set /p db_host="Хост (например, localhost): "
set /p db_port="Порт (например, 5432): "
set /p db_name="Имя базы данных: "
set /p db_user="Имя пользователя: "
set /p db_password="Пароль: "
:: Модифицируем docker-compose.yml
echo [33mМодифицируем docker-compose.yml для работы с внешней базой данных...[0m
:: Сохраняем оригинальную версию файла
copy docker-compose.yml docker-compose.yml.bak
:: Создаем временный файл с модифицированным содержимым
(
echo version: '3.8'
echo.
echo services:
echo bot:
echo build: .
echo container_name: telegram-tinder-bot
echo restart: unless-stopped
echo env_file: .env
echo environment:
echo - NODE_ENV=production
echo - DB_HOST=%db_host%
echo - DB_PORT=%db_port%
echo - DB_NAME=%db_name%
echo - DB_USERNAME=%db_user%
echo - DB_PASSWORD=%db_password%
echo volumes:
echo - ./uploads:/app/uploads
echo - ./logs:/app/logs
echo networks:
echo - bot-network
echo healthcheck:
echo test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
echo interval: 30s
echo timeout: 5s
echo retries: 3
echo start_period: 10s
echo.
echo adminer:
echo image: adminer:latest
echo container_name: adminer-tinder
echo restart: unless-stopped
echo ports:
echo - "8080:8080"
echo networks:
echo - bot-network
echo.
echo volumes:
echo postgres_data:
echo.
echo networks:
echo bot-network:
echo driver: bridge
) > docker-compose.temp.yml
:: Заменяем оригинальный файл
move /y docker-compose.temp.yml docker-compose.yml
echo [32mdocker-compose.yml обновлен для работы с внешней базой данных[0m
:: Обновляем .env файл
echo [33mОбновляем файл .env с параметрами внешней базы данных...[0m
:: Создаем временный файл
type NUL > .env.temp
:: Читаем .env построчно и заменяем нужные строки
for /f "tokens=*" %%a in (.env) do (
set line=%%a
set line=!line:DB_HOST=*!
if "!line:~0,1!" == "*" (
echo DB_HOST=%db_host%>> .env.temp
) else (
set line=!line:DB_PORT=*!
if "!line:~0,1!" == "*" (
echo DB_PORT=%db_port%>> .env.temp
) else (
set line=!line:DB_NAME=*!
if "!line:~0,1!" == "*" (
echo DB_NAME=%db_name%>> .env.temp
) else (
set line=!line:DB_USERNAME=*!
if "!line:~0,1!" == "*" (
echo DB_USERNAME=%db_user%>> .env.temp
) else (
set line=!line:DB_PASSWORD=*!
if "!line:~0,1!" == "*" (
echo DB_PASSWORD=%db_password%>> .env.temp
) else (
echo %%a>> .env.temp
)
)
)
)
)
)
:: Заменяем оригинальный файл
move /y .env.temp .env
echo [32mФайл .env обновлен с параметрами внешней базы данных[0m
:: Запускаем только контейнер с ботом
echo [36mЗапускаем Telegram Bot без контейнера базы данных...[0m
docker-compose up -d bot adminer
echo [32mБот запущен и использует внешнюю базу данных: %db_host%:%db_port%/%db_name%[0m
echo [33mAdminer доступен по адресу: http://localhost:8080/[0m
echo [33mДанные для входа в Adminer:[0m
echo [33mСистема: PostgreSQL[0m
echo [33mСервер: %db_host%[0m
echo [33mПользователь: %db_user%[0m
echo [33mПароль: (введенный вами)[0m
echo [33mБаза данных: %db_name%[0m
) ELSE (
:: Восстанавливаем оригинальный docker-compose.yml если есть бэкап
if exist docker-compose.yml.bak (
move /y docker-compose.yml.bak docker-compose.yml
echo [32mdocker-compose.yml восстановлен из резервной копии[0m
)
echo [36mЗапускаем Telegram Bot с контейнером базы данных...[0m
:: Проверка, запущены ли контейнеры
docker ps -q -f name=telegram-tinder-bot > tmp_containers.txt
set /p containers=<tmp_containers.txt
del tmp_containers.txt
if not "%containers%" == "" (
set /p restart_containers="Контейнеры уже запущены. Перезапустить? (y/n): "
if /I "%restart_containers%" == "y" (
docker-compose down
docker-compose up -d
echo [32mКонтейнеры перезапущены[0m
) else (
echo [36mПродолжаем работу с уже запущенными контейнерами[0m
)
) else (
docker-compose up -d
echo [32mКонтейнеры запущены[0m
)
:: Проверка наличия пароля для БД в .env
findstr /C:"DB_PASSWORD=" .env > tmp_db_password.txt
set /p db_password_line=<tmp_db_password.txt
del tmp_db_password.txt
:: Проверяем, есть ли значение после DB_PASSWORD=
for /f "tokens=2 delims==" %%i in ("%db_password_line%") do (
set db_password=%%i
)
if "%db_password%" == "" (
:: Генерируем случайный пароль
set chars=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
set random_password=
for /L %%i in (1,1,16) do (
set /a random_index=!random! %% 62
for %%j in (!random_index!) do set random_password=!random_password!!chars:~%%j,1!
)
:: Обновляем .env файл с новым паролем
:: Создаем временный файл
type NUL > .env.temp
:: Читаем .env построчно и заменяем строку с паролем
for /f "tokens=*" %%a in (.env) do (
set line=%%a
set line=!line:DB_PASSWORD=*!
if "!line:~0,1!" == "*" (
echo DB_PASSWORD=%random_password%>> .env.temp
) else (
echo %%a>> .env.temp
)
)
:: Заменяем оригинальный файл
move /y .env.temp .env
echo [33mСгенерирован случайный пароль для базы данных и сохранен в .env[0m
)
echo [32mTelegram Bot запущен с локальной базой данных[0m
echo [33mAdminer доступен по адресу: http://localhost:8080/[0m
echo [33mДанные для входа в Adminer:[0m
echo [33mСистема: PostgreSQL[0m
echo [33mСервер: db[0m
echo [33mПользователь: postgres[0m
echo [33mПароль: (из переменной DB_PASSWORD в .env)[0m
echo [33mБаза данных: telegram_tinder_bot[0m
)
:: Проверка статуса контейнеров
echo [36mПроверка статуса контейнеров:[0m
docker-compose ps
echo ================================================
echo [32mПроцесс запуска Telegram Tinder Bot завершен![0m
echo ================================================
echo [33mДля просмотра логов используйте: docker-compose logs -f bot[0m
echo [33mДля остановки: docker-compose down[0m
pause

229
start.ps1 Normal file
View File

@@ -0,0 +1,229 @@
function createModifiedDockerCompose {
param (
[string]$dbHost,
[string]$dbPort,
[string]$dbName,
[string]$dbUser,
[string]$dbPassword
)
$dockerComposeContent = @"
version: '3.8'
services:
bot:
build: .
container_name: telegram-tinder-bot
restart: unless-stopped
env_file: .env
environment:
- NODE_ENV=production
- DB_HOST=$dbHost
- DB_PORT=$dbPort
- DB_NAME=$dbName
- DB_USERNAME=$dbUser
- DB_PASSWORD=$dbPassword
volumes:
- ./uploads:/app/uploads
- ./logs:/app/logs
networks:
- bot-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
adminer:
image: adminer:latest
container_name: adminer-tinder
restart: unless-stopped
ports:
- "8080:8080"
networks:
- bot-network
volumes:
postgres_data:
networks:
bot-network:
driver: bridge
"@
return $dockerComposeContent
}
function restoreDockerCompose {
if (Test-Path -Path "docker-compose.yml.bak") {
Copy-Item -Path "docker-compose.yml.bak" -Destination "docker-compose.yml" -Force
Write-Host "docker-compose.yml восстановлен из резервной копии" -ForegroundColor Green
}
}
function updateEnvFile {
param (
[string]$dbHost,
[string]$dbPort,
[string]$dbName,
[string]$dbUser,
[string]$dbPassword
)
$envContent = Get-Content -Path ".env" -Raw
$envContent = $envContent -replace "DB_HOST=.*", "DB_HOST=$dbHost"
$envContent = $envContent -replace "DB_PORT=.*", "DB_PORT=$dbPort"
$envContent = $envContent -replace "DB_NAME=.*", "DB_NAME=$dbName"
$envContent = $envContent -replace "DB_USERNAME=.*", "DB_USERNAME=$dbUser"
$envContent = $envContent -replace "DB_PASSWORD=.*", "DB_PASSWORD=$dbPassword"
Set-Content -Path ".env" -Value $envContent
Write-Host "Файл .env обновлен с параметрами внешней базы данных" -ForegroundColor Green
}
function generateRandomPassword {
$length = 16
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
$bytes = New-Object Byte[] $length
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
$rng.GetBytes($bytes)
$password = ""
for ($i = 0; $i -lt $length; $i++) {
$password += $chars[$bytes[$i] % $chars.Length]
}
return $password
}
# Начало основного скрипта
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " Запуск Telegram Tinder Bot" -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
# Проверка наличия Docker
try {
docker --version | Out-Null
} catch {
Write-Host "ОШИБКА: Docker не установлен!" -ForegroundColor Red
Write-Host "Установите Docker Desktop для Windows: https://docs.docker.com/desktop/install/windows-install/"
exit
}
# Проверяем наличие .env файла
if (-not (Test-Path -Path ".env")) {
Write-Host "Файл .env не найден. Создаем из шаблона..." -ForegroundColor Yellow
if (Test-Path -Path ".env.example") {
Copy-Item -Path ".env.example" -Destination ".env"
Write-Host "Файл .env создан из шаблона. Пожалуйста, отредактируйте его с вашими настройками." -ForegroundColor Green
} else {
Write-Host "ОШИБКА: Файл .env.example не найден. Создайте файл .env вручную." -ForegroundColor Red
exit
}
}
# Спрашиваем про запуск базы данных
$useContainerDb = Read-Host "Запустить базу данных PostgreSQL в контейнере? (y/n)"
if ($useContainerDb -ne "y") {
# Запрашиваем параметры подключения к внешней БД
Write-Host "Введите параметры подключения к внешней базе данных:" -ForegroundColor Cyan
$dbHost = Read-Host "Хост (например, localhost)"
$dbPort = Read-Host "Порт (например, 5432)"
$dbName = Read-Host "Имя базы данных"
$dbUser = Read-Host "Имя пользователя"
$dbPassword = Read-Host "Пароль" -AsSecureString
$dbPasswordText = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($dbPassword))
# Модифицируем docker-compose.yml
Write-Host "Модифицируем docker-compose.yml для работы с внешней базой данных..." -ForegroundColor Yellow
# Сохраняем оригинальную версию файла
Copy-Item -Path "docker-compose.yml" -Destination "docker-compose.yml.bak" -Force
# Создаем модифицированный docker-compose.yml
$dockerComposeContent = createModifiedDockerCompose -dbHost $dbHost -dbPort $dbPort -dbName $dbName -dbUser $dbUser -dbPassword $dbPasswordText
Set-Content -Path "docker-compose.yml" -Value $dockerComposeContent
Write-Host "docker-compose.yml обновлен для работы с внешней базой данных" -ForegroundColor Green
# Обновляем .env файл
Write-Host "Обновляем файл .env с параметрами внешней базы данных..." -ForegroundColor Yellow
updateEnvFile -dbHost $dbHost -dbPort $dbPort -dbName $dbName -dbUser $dbUser -dbPassword $dbPasswordText
# Запускаем только контейнер с ботом
Write-Host "Запускаем Telegram Bot без контейнера базы данных..." -ForegroundColor Cyan
docker-compose up -d bot adminer
Write-Host "Бот запущен и использует внешнюю базу данных: $dbHost`:$dbPort/$dbName" -ForegroundColor Green
Write-Host "Adminer доступен по адресу: http://localhost:8080/" -ForegroundColor Yellow
Write-Host "Данные для входа в Adminer:" -ForegroundColor Yellow
Write-Host "Система: PostgreSQL" -ForegroundColor Yellow
Write-Host "Сервер: $dbHost" -ForegroundColor Yellow
Write-Host "Пользователь: $dbUser" -ForegroundColor Yellow
Write-Host "Пароль: (введенный вами)" -ForegroundColor Yellow
Write-Host "База данных: $dbName" -ForegroundColor Yellow
} else {
# Восстанавливаем оригинальный docker-compose.yml если есть бэкап
restoreDockerCompose
Write-Host "Запускаем Telegram Bot с контейнером базы данных..." -ForegroundColor Cyan
# Проверка, запущены ли контейнеры
$containers = docker ps -q -f name=telegram-tinder-bot -f name=postgres-tinder
if ($containers) {
$restartContainers = Read-Host "Контейнеры уже запущены. Перезапустить? (y/n)"
if ($restartContainers -eq "y") {
docker-compose down
docker-compose up -d
Write-Host "Контейнеры перезапущены" -ForegroundColor Green
} else {
Write-Host "Продолжаем работу с уже запущенными контейнерами" -ForegroundColor Cyan
}
} else {
docker-compose up -d
Write-Host "Контейнеры запущены" -ForegroundColor Green
}
# Проверка наличия пароля для БД в .env
$envContent = Get-Content -Path ".env" -Raw
$match = [Regex]::Match($envContent, "DB_PASSWORD=(.*)(\r?\n|$)")
$dbPassword = if ($match.Success) { $match.Groups[1].Value.Trim() } else { "" }
if ([string]::IsNullOrWhiteSpace($dbPassword)) {
# Генерируем случайный пароль
$randomPassword = generateRandomPassword
# Обновляем .env файл
$envContent = $envContent -replace "DB_PASSWORD=.*", "DB_PASSWORD=$randomPassword"
Set-Content -Path ".env" -Value $envContent
Write-Host "Сгенерирован случайный пароль для базы данных и сохранен в .env" -ForegroundColor Yellow
}
Write-Host "Telegram Bot запущен с локальной базой данных" -ForegroundColor Green
Write-Host "Adminer доступен по адресу: http://localhost:8080/" -ForegroundColor Yellow
Write-Host "Данные для входа в Adminer:" -ForegroundColor Yellow
Write-Host "Система: PostgreSQL" -ForegroundColor Yellow
Write-Host "Сервер: db" -ForegroundColor Yellow
Write-Host "Пользователь: postgres" -ForegroundColor Yellow
Write-Host "Пароль: (из переменной DB_PASSWORD в .env)" -ForegroundColor Yellow
Write-Host "База данных: telegram_tinder_bot" -ForegroundColor Yellow
}
# Проверка статуса контейнеров
Write-Host "Проверка статуса контейнеров:" -ForegroundColor Cyan
docker-compose ps
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "Процесс запуска Telegram Tinder Bot завершен!" -ForegroundColor Green
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "Для просмотра логов используйте: docker-compose logs -f bot" -ForegroundColor Yellow
Write-Host "Для остановки: docker-compose down" -ForegroundColor Yellow
Read-Host "Нажмите Enter для выхода"

217
start.sh Executable file
View File

@@ -0,0 +1,217 @@
#!/bin/bash
# start.sh - Скрипт для запуска Telegram Tinder Bot
# Позволяет выбрать между локальной БД в контейнере или внешней БД
# Цвета для вывода
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${BLUE}==================================================${NC}"
echo -e "${BLUE} Запуск Telegram Tinder Bot ${NC}"
echo -e "${BLUE}==================================================${NC}"
# Проверка наличия Docker и Docker Compose
if ! command -v docker &> /dev/null || ! command -v docker-compose &> /dev/null; then
echo -e "${RED}ОШИБКА: Docker и/или Docker Compose не установлены!${NC}"
echo -e "Для установки Docker следуйте инструкции на: https://docs.docker.com/get-docker/"
exit 1
fi
# Проверяем наличие .env файла
if [ ! -f .env ]; then
echo -e "${YELLOW}Файл .env не найден. Создаем из шаблона...${NC}"
if [ -f .env.example ]; then
cp .env.example .env
echo -e "${GREEN}Файл .env создан из шаблона. Пожалуйста, отредактируйте его с вашими настройками.${NC}"
else
echo -e "${RED}ОШИБКА: Файл .env.example не найден. Создайте файл .env вручную.${NC}"
exit 1
fi
fi
# Спрашиваем про запуск базы данных
read -p "Запустить базу данных PostgreSQL в контейнере? (y/n): " use_container_db
# Функция для изменения docker-compose.yml
modify_docker_compose() {
local host=$1
local port=$2
local user=$3
local password=$4
local db_name=$5
echo -e "${YELLOW}Модифицируем docker-compose.yml для работы с внешней базой данных...${NC}"
# Сохраняем оригинальную версию файла
cp docker-compose.yml docker-compose.yml.bak
# Создаем временный файл с модифицированным содержимым
cat > docker-compose.temp.yml << EOL
version: '3.8'
services:
bot:
build: .
container_name: telegram-tinder-bot
restart: unless-stopped
env_file: .env
environment:
- NODE_ENV=production
- DB_HOST=${host}
- DB_PORT=${port}
- DB_NAME=${db_name}
- DB_USERNAME=${user}
- DB_PASSWORD=${password}
volumes:
- ./uploads:/app/uploads
- ./logs:/app/logs
networks:
- bot-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
adminer:
image: adminer:latest
container_name: adminer-tinder
restart: unless-stopped
ports:
- "8080:8080"
networks:
- bot-network
volumes:
postgres_data:
networks:
bot-network:
driver: bridge
EOL
# Заменяем оригинальный файл
mv docker-compose.temp.yml docker-compose.yml
echo -e "${GREEN}docker-compose.yml обновлен для работы с внешней базой данных${NC}"
}
# Функция для восстановления docker-compose.yml
restore_docker_compose() {
if [ -f docker-compose.yml.bak ]; then
mv docker-compose.yml.bak docker-compose.yml
echo -e "${GREEN}docker-compose.yml восстановлен из резервной копии${NC}"
fi
}
# Обработка выбора
if [[ "$use_container_db" =~ ^[Nn]$ ]]; then
# Запрашиваем параметры подключения к внешней БД
echo -e "${BLUE}Введите параметры подключения к внешней базе данных:${NC}"
read -p "Хост (например, localhost): " db_host
read -p "Порт (например, 5432): " db_port
read -p "Имя базы данных: " db_name
read -p "Имя пользователя: " db_user
read -p "Пароль: " db_password
# Модифицируем docker-compose.yml
modify_docker_compose "$db_host" "$db_port" "$db_user" "$db_password" "$db_name"
# Обновляем .env файл
echo -e "${YELLOW}Обновляем файл .env с параметрами внешней базы данных...${NC}"
# Используем sed для замены переменных в .env
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS требует другой синтаксис для sed
sed -i '' "s/DB_HOST=.*/DB_HOST=${db_host}/" .env
sed -i '' "s/DB_PORT=.*/DB_PORT=${db_port}/" .env
sed -i '' "s/DB_NAME=.*/DB_NAME=${db_name}/" .env
sed -i '' "s/DB_USERNAME=.*/DB_USERNAME=${db_user}/" .env
sed -i '' "s/DB_PASSWORD=.*/DB_PASSWORD=${db_password}/" .env
else
# Linux и другие системы
sed -i "s/DB_HOST=.*/DB_HOST=${db_host}/" .env
sed -i "s/DB_PORT=.*/DB_PORT=${db_port}/" .env
sed -i "s/DB_NAME=.*/DB_NAME=${db_name}/" .env
sed -i "s/DB_USERNAME=.*/DB_USERNAME=${db_user}/" .env
sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=${db_password}/" .env
fi
echo -e "${GREEN}Файл .env обновлен с параметрами внешней базы данных${NC}"
# Запускаем только контейнер с ботом
echo -e "${BLUE}Запускаем Telegram Bot без контейнера базы данных...${NC}"
docker-compose up -d bot adminer
echo -e "${GREEN}Бот запущен и использует внешнюю базу данных: ${db_host}:${db_port}/${db_name}${NC}"
echo -e "${YELLOW}Adminer доступен по адресу: http://localhost:8080/${NC}"
echo -e "${YELLOW}Данные для входа в Adminer:${NC}"
echo -e "${YELLOW}Система: PostgreSQL${NC}"
echo -e "${YELLOW}Сервер: ${db_host}${NC}"
echo -e "${YELLOW}Пользователь: ${db_user}${NC}"
echo -e "${YELLOW}Пароль: (введенный вами)${NC}"
echo -e "${YELLOW}База данных: ${db_name}${NC}"
else
# Восстанавливаем оригинальный docker-compose.yml если есть бэкап
restore_docker_compose
echo -e "${BLUE}Запускаем Telegram Bot с контейнером базы данных...${NC}"
# Проверка, запущены ли контейнеры
containers=$(docker ps -q -f name=telegram-tinder-bot -f name=postgres-tinder)
if [ -n "$containers" ]; then
echo -e "${YELLOW}Контейнеры уже запущены. Перезапустить? (y/n): ${NC}"
read restart_containers
if [[ "$restart_containers" =~ ^[Yy]$ ]]; then
docker-compose down
docker-compose up -d
echo -e "${GREEN}Контейнеры перезапущены${NC}"
else
echo -e "${BLUE}Продолжаем работу с уже запущенными контейнерами${NC}"
fi
else
docker-compose up -d
echo -e "${GREEN}Контейнеры запущены${NC}"
fi
# Проверка наличия пароля для БД в .env
db_password=$(grep DB_PASSWORD .env | cut -d '=' -f2)
if [ -z "$db_password" ]; then
# Генерируем случайный пароль
random_password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
# Обновляем .env файл
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS требует другой синтаксис для sed
sed -i '' "s/DB_PASSWORD=.*/DB_PASSWORD=${random_password}/" .env
else
# Linux и другие системы
sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=${random_password}/" .env
fi
echo -e "${YELLOW}Сгенерирован случайный пароль для базы данных и сохранен в .env${NC}"
fi
echo -e "${GREEN}Telegram Bot запущен с локальной базой данных${NC}"
echo -e "${YELLOW}Adminer доступен по адресу: http://localhost:8080/${NC}"
echo -e "${YELLOW}Данные для входа в Adminer:${NC}"
echo -e "${YELLOW}Система: PostgreSQL${NC}"
echo -e "${YELLOW}Сервер: db${NC}"
echo -e "${YELLOW}Пользователь: postgres${NC}"
echo -e "${YELLOW}Пароль: (из переменной DB_PASSWORD в .env)${NC}"
echo -e "${YELLOW}База данных: telegram_tinder_bot${NC}"
fi
# Проверка статуса контейнеров
echo -e "${BLUE}Проверка статуса контейнеров:${NC}"
docker-compose ps
echo -e "${BLUE}==================================================${NC}"
echo -e "${GREEN}Процесс запуска Telegram Tinder Bot завершен!${NC}"
echo -e "${BLUE}==================================================${NC}"
echo -e "${YELLOW}Для просмотра логов используйте: docker-compose logs -f bot${NC}"
echo -e "${YELLOW}Для остановки: docker-compose down${NC}"

View File

@@ -1,37 +0,0 @@
require('dotenv').config();
const { Pool } = require('pg');
// Используем параметры напрямую из .env
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
max: 5,
connectionTimeoutMillis: 5000
});
console.log('DB Connection Details:');
console.log(`- Host: ${process.env.DB_HOST}`);
console.log(`- Port: ${process.env.DB_PORT}`);
console.log(`- Database: ${process.env.DB_NAME}`);
console.log(`- User: ${process.env.DB_USERNAME}`);
async function testConnection() {
try {
const client = await pool.connect();
try {
const result = await client.query('SELECT NOW() as current_time');
console.log('✅ Connected to database successfully!');
console.log(`Current database time: ${result.rows[0].current_time}`);
} finally {
client.release();
}
await pool.end();
} catch (error) {
console.error('❌ Failed to connect to database:', error);
}
}
testConnection();

View File