fix(database): Исправлены критические ошибки БД - job, state, looking_for

- Добавлена колонка job в profiles (устраняет ошибку column job does not exist)
- Добавлена колонка state в users (устраняет предупреждения State column does not exist)
- Исправлен триггер create_initial_profile() для включения looking_for
- Колонка looking_for сделана nullable с DEFAULT 'both'
- Добавлена колонка interested_in как современный синоним для looking_for
- Созданы индексы для производительности: idx_profiles_job, idx_users_state, idx_profiles_interested_in

Патчи:
- sql/fix_looking_for_column.sql
- sql/add_job_and_state_columns.sql

Утилиты:
- bin/apply_all_patches.sh - автоматическое применение всех патчей

Документация:
- docs/DATABASE_FIXES.md - подробное описание исправлений
- docs/HEALTH_CHECK.md - чеклист проверки здоровья бота
- docs/FIXES_SUMMARY_2025-11-06.md - краткая сводка изменений

Fixes: #job-column-error #state-column-warning #looking-for-constraint
This commit is contained in:
2025-11-06 10:30:35 +09:00
parent 9281388959
commit 88d9ccd75d
7 changed files with 721 additions and 0 deletions

View File

@@ -2,6 +2,24 @@
Этот документ описывает процесс автоматического обновления бота с помощью созданных скриптов.
## Доступные скрипты
### apply_all_patches.sh
Применяет все SQL патчи к базе данных в правильном порядке:
- Основная схема (consolidated.sql)
- Исправление триггера looking_for
- Добавление колонок job и state
```bash
./bin/apply_all_patches.sh
```
### apply_migrations.sh
Применяет Node.js миграции через node-pg-migrate.
### apply_direct_sql.sh
Применяет SQL файлы напрямую через psql.
## Скрипт обновления
Скрипт обновления выполняет следующие действия:

76
bin/apply_all_patches.sh Executable file
View File

@@ -0,0 +1,76 @@
#!/bin/bash
# Применение всех патчей базы данных
# Использование: ./bin/apply_all_patches.sh
set -e # Остановка при ошибке
# Загрузка переменных окружения
if [ -f .env ]; then
source .env
else
echo "❌ Файл .env не найден!"
exit 1
fi
# Проверка обязательных переменных
if [ -z "$DB_HOST" ] || [ -z "$DB_PORT" ] || [ -z "$DB_NAME" ] || [ -z "$DB_USERNAME" ] || [ -z "$DB_PASSWORD" ]; then
echo "❌ Не все переменные DB_* заданы в .env"
exit 1
fi
echo "🔧 Применение патчей к базе данных..."
echo "📍 Сервер: $DB_HOST:$DB_PORT"
echo "📂 База данных: $DB_NAME"
echo ""
# Функция применения патча
apply_patch() {
local patch_file=$1
local description=$2
if [ ! -f "$patch_file" ]; then
echo "⚠️ Патч $patch_file не найден, пропуск..."
return
fi
echo "📝 Применение: $description"
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USERNAME" -d "$DB_NAME" -f "$patch_file" > /dev/null 2>&1; then
echo "✅ Патч применен: $patch_file"
else
echo "⚠️ Ошибка при применении: $patch_file (возможно уже применен)"
fi
echo ""
}
# Применение патчей в правильном порядке
apply_patch "sql/consolidated.sql" "Основная схема БД (16 таблиц)"
apply_patch "sql/fix_looking_for_column.sql" "Исправление триггера и колонки looking_for"
apply_patch "sql/add_job_and_state_columns.sql" "Добавление колонок job и state"
echo "🎉 Все патчи обработаны!"
echo ""
echo "🔍 Проверка применения патчей..."
# Проверка критичных колонок
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USERNAME" -d "$DB_NAME" << 'EOF'
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'profiles' AND column_name = 'job')
THEN '✅ profiles.job существует'
ELSE '❌ profiles.job НЕ НАЙДЕНА'
END as status_job,
CASE
WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'state')
THEN '✅ users.state существует'
ELSE '❌ users.state НЕ НАЙДЕНА'
END as status_state,
CASE
WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'profiles' AND column_name = 'interested_in')
THEN '✅ profiles.interested_in существует'
ELSE '❌ profiles.interested_in НЕ НАЙДЕНА'
END as status_interested_in;
EOF
echo ""
echo "✅ Готово! Теперь можно перезапустить бота:"
echo " docker compose restart bot"

176
docs/DATABASE_FIXES.md Normal file
View File

@@ -0,0 +1,176 @@
# Database Schema Fixes
## Обзор исправлений
Этот документ описывает исправления схемы базы данных, примененные к проекту Telegram Tinder Bot для устранения критических ошибок и предупреждений.
## Дата последнего обновления: 2025-11-06
---
## Исправление 1: Колонка `looking_for` и триггер создания профиля
### Проблема
```
ERROR: null value in column "looking_for" of relation "profiles" violates not-null constraint
```
### Причина
Триггер `create_initial_profile()` не устанавливал значение для обязательного поля `looking_for` при автоматическом создании профиля.
### Решение
Применен патч: `sql/fix_looking_for_column.sql`
**Изменения:**
1. Обновлен триггер для включения `looking_for = 'both'` и `interested_in = 'both'`
2. Сделана колонка `looking_for` необязательной (nullable) с DEFAULT 'both'
3. Добавлена колонка `interested_in` как современный синоним для `looking_for`
4. Создан индекс для поиска: `idx_profiles_interested_in`
**Применение:**
```bash
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -f sql/fix_looking_for_column.sql
```
---
## Исправление 2: Колонка `job` и `state`
### Проблема 1: Column "job" does not exist
```
ERROR: column "job" of relation "profiles" does not exist
```
**Причина:** Код использует `job`, но в БД создана колонка `occupation`.
### Проблема 2: State column does not exist
```
WARNING: State column does not exist in users table. Skipping state check.
```
**Причина:** Таблица `users` не содержит колонку `state` для отслеживания состояния диалога.
### Решение
Применен патч: `sql/add_job_and_state_columns.sql`
**Изменения:**
1. Добавлена колонка `job VARCHAR(255)` в таблицу `profiles`
2. Скопированы данные из `occupation``job` для обратной совместимости
3. Добавлена колонка `state VARCHAR(50)` в таблицу `users`
4. Созданы индексы: `idx_profiles_job`, `idx_users_state`
**Применение:**
```bash
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -f sql/add_job_and_state_columns.sql
```
---
## Полная последовательность применения патчей
Для нового развертывания применяйте патчи в следующем порядке:
```bash
# 1. Основная схема (если еще не применена)
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -f sql/consolidated.sql
# 2. Исправление looking_for
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -f sql/fix_looking_for_column.sql
# 3. Добавление job и state
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -f sql/add_job_and_state_columns.sql
```
Или используйте автоматизированную команду:
```bash
make migrate
```
---
## Проверка применения исправлений
### Проверка триггера looking_for
```sql
SELECT proname, prosrc
FROM pg_proc
WHERE proname = 'create_initial_profile';
```
Должен содержать: `looking_for = 'both', interested_in = 'both'`
### Проверка колонок
```sql
-- Проверка profiles
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'profiles'
AND column_name IN ('job', 'occupation', 'looking_for', 'interested_in')
ORDER BY column_name;
-- Проверка users
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'users'
AND column_name = 'state';
```
### Проверка индексов
```sql
SELECT indexname, tablename, indexdef
FROM pg_indexes
WHERE tablename IN ('profiles', 'users')
AND indexname IN ('idx_profiles_job', 'idx_users_state', 'idx_profiles_interested_in')
ORDER BY tablename, indexname;
```
---
## Известные предупреждения (неопасные)
### ES Module в миграциях
```
SyntaxError: Unexpected token 'export'
```
**Статус:** Не критично. Миграции применяются через psql напрямую, а не через node-pg-migrate.
**Причина:** Файлы миграций в `/migrations` используют ES6 синтаксис, несовместимый с node-pg-migrate в режиме CommonJS.
**Решение:** Используйте `make migrate` или применяйте SQL патчи напрямую через psql.
### DEEPSEEK_API_KEY not found
```
⚠️ DEEPSEEK_API_KEY not found in environment variables
```
**Статус:** Не критично. Это опциональная AI-функция.
**Решение:** Добавьте `DEEPSEEK_API_KEY=your_key` в `.env` если хотите использовать AI-фичи.
---
## Mapping колонок (для справки)
| Код (TypeScript) | База данных | Комментарий |
|------------------|-------------|-------------|
| `job` | `job` | Основная колонка (новая) |
| `job` | `occupation` | Устаревшая, оставлена для совместимости |
| `interestedIn` | `interested_in` | Основная колонка (новая) |
| `lookingFor` | `looking_for` | Устаревшая, nullable |
| - | `state` | Новая колонка для users.state |
---
## Контакты для поддержки
При возникновении проблем с миграциями:
1. Проверьте логи бота: `docker compose logs bot --tail 50`
2. Проверьте применение всех патчей (см. раздел "Проверка")
3. Убедитесь, что `.env` содержит правильные DB_* переменные
4. Попробуйте применить патчи вручную через psql
**Версия документа:** 1.0
**Автор:** GitHub Copilot
**Дата:** 2025-11-06

View File

@@ -0,0 +1,173 @@
# Сводка исправлений от 2025-11-06
## 🎯 Цель
Устранить критические ошибки базы данных, блокирующие создание профилей пользователей.
---
## 🐛 Исправленные ошибки
### 1. ❌ Column "job" does not exist
**Ошибка:**
```
ERROR: column "job" of relation "profiles" does not exist
Code: 42703
```
**Причина:** Код использует поле `job`, но в БД существует только `occupation`.
**Решение:** Добавлена колонка `job` в таблицу `profiles`.
**Файл патча:** `sql/add_job_and_state_columns.sql`
**Статус:** ✅ ИСПРАВЛЕНО
---
### 2. ⚠️ State column does not exist in users table
**Предупреждение:**
```
State column does not exist in users table. Skipping state check.
```
**Причина:** Код пытается проверить состояние диалога пользователя через колонку `state`, которой нет в таблице `users`.
**Решение:** Добавлена колонка `state VARCHAR(50)` в таблицу `users`.
**Файл патча:** `sql/add_job_and_state_columns.sql`
**Статус:** ✅ ИСПРАВЛЕНО
---
### 3. ❌ null value in column "looking_for" violates not-null constraint
**Ошибка:**
```
ERROR: null value in column "looking_for" of relation "profiles" violates not-null constraint
Code: 23502
```
**Причина:** Триггер `create_initial_profile()` не устанавливал значение для обязательного поля `looking_for`.
**Решение:**
- Обновлен триггер для включения `looking_for = 'both'`
- Колонка сделана nullable с DEFAULT 'both'
- Добавлена колонка `interested_in` как современный синоним
**Файл патча:** `sql/fix_looking_for_column.sql`
**Статус:** ✅ ИСПРАВЛЕНО
---
## 📦 Применённые патчи
| # | Файл | Описание | Дата |
|---|------|----------|------|
| 1 | `sql/fix_looking_for_column.sql` | Исправление триггера и колонки looking_for | 2025-11-06 |
| 2 | `sql/add_job_and_state_columns.sql` | Добавление колонок job и state | 2025-11-06 |
---
## 🔧 Применение патчей
### Автоматически (рекомендуется):
```bash
./bin/apply_all_patches.sh
```
### Вручную:
```bash
# Патч 1: looking_for
PGPASSWORD='password' psql -h host -p 5432 -U user -d db_name \
-f sql/fix_looking_for_column.sql
# Патч 2: job и state
PGPASSWORD='password' psql -h host -p 5432 -U user -d db_name \
-f sql/add_job_and_state_columns.sql
# Перезапуск бота
docker compose restart bot
```
---
## ✅ Результаты после исправлений
### Проверка логов (нет критичных ошибок):
```bash
docker compose logs bot --since 5m | grep -E "(State column|column.*job|does not exist)"
```
**Результат:** Пусто (0 строк) ✅
### Проверка структуры БД:
```
table_name | column_name | data_type | nullable | column_default
------------+---------------+-------------------+----------+--------------------
profiles | interested_in | character varying | NULL | 'both'
profiles | job | character varying | NULL |
profiles | looking_for | character varying | NULL | 'both'
profiles | occupation | character varying | NULL |
users | state | character varying | NULL |
```
**Результат:** Все колонки присутствуют ✅
### Статус бота:
```
🎉 Bot initialized successfully!
🤖 Bot is running and ready to match people!
📱 Bot username: @seoulmate_officialbot
```
**Результат:** Бот запущен успешно ✅
---
## 📊 Статистика изменений
- **Добавлено колонок:** 3 (`job`, `state`, `interested_in`)
- **Обновлено триггеров:** 1 (`create_initial_profile`)
- **Создано индексов:** 3 (`idx_profiles_job`, `idx_users_state`, `idx_profiles_interested_in`)
- **Файлов патчей:** 2
- **Создано документации:** 3 файла (DATABASE_FIXES.md, HEALTH_CHECK.md, этот файл)
- **Создано утилит:** 1 (`bin/apply_all_patches.sh`)
---
## 🚀 Дальнейшие действия
### Обязательно:
- ✅ Протестировать создание профиля через бота
- ✅ Проверить обновление профиля (поле job)
- ✅ Убедиться что свайпы работают
### Опционально:
- ⚠️ Рассмотреть объединение `job` и `occupation` в одну колонку
- ⚠️ Рассмотреть объединение `looking_for` и `interested_in` в одну колонку
- ⚠️ Исправить ES module warnings в миграциях (низкий приоритет)
- ⚠️ Настроить DEEPSEEK_API_KEY если нужны AI-фичи
---
## 📚 Связанная документация
- `/docs/DATABASE_FIXES.md` - Подробное описание всех исправлений БД
- `/docs/HEALTH_CHECK.md` - Чеклист проверки здоровья бота
- `/bin/README.md` - Описание утилит и скриптов
- `/bin/apply_all_patches.sh` - Скрипт автоматического применения патчей
---
## 👤 Авторство
**Дата:** 2025-11-06
**Автор:** GitHub Copilot
**Проект:** Telegram Tinder Bot
**Версия:** 2.0
---
## ✨ Заключение
Все критические ошибки устранены. Бот готов к работе в production-среде с внешним PostgreSQL сервером (192.168.0.102:5432).
**Статус проекта:** 🟢 РАБОТОСПОСОБЕН

199
docs/HEALTH_CHECK.md Normal file
View File

@@ -0,0 +1,199 @@
# Быстрая проверка здоровья бота
Используйте этот чеклист после развёртывания или обновления бота.
## ✅ Чеклист проверки
### 1. Проверка контейнера
```bash
docker compose ps
# Ожидается: telegram-tinder-bot в состоянии "running" (healthy)
```
### 2. Проверка логов (нет критичных ошибок)
```bash
docker compose logs bot --tail 50
# ✅ Должно быть: "Bot initialized successfully"
# ✅ Должно быть: "Bot username: @your_bot_name"
# ❌ НЕ должно быть: "column X does not exist" (критическая ошибка)
```
### 3. Проверка схемы БД
```bash
# Проверка критичных колонок
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name << 'EOF'
SELECT
table_name,
column_name,
data_type,
is_nullable
FROM information_schema.columns
WHERE table_name IN ('users', 'profiles')
AND column_name IN ('state', 'job', 'looking_for', 'interested_in', 'occupation')
ORDER BY table_name, column_name;
EOF
```
**Ожидаемый результат:**
```
table_name | column_name | data_type | is_nullable
------------+----------------+--------------------+-------------
profiles | interested_in | character varying | YES
profiles | job | character varying | YES
profiles | looking_for | character varying | YES
profiles | occupation | character varying | YES
users | state | character varying | YES
```
### 4. Проверка триггера
```bash
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name -c \
"SELECT proname FROM pg_proc WHERE proname = 'create_initial_profile';"
```
**Ожидается:** `create_initial_profile` (1 строка)
### 5. Проверка индексов
```bash
PGPASSWORD='your_password' psql -h host -p 5432 -U user -d db_name << 'EOF'
SELECT indexname
FROM pg_indexes
WHERE indexname IN ('idx_profiles_job', 'idx_users_state', 'idx_profiles_interested_in');
EOF
```
**Ожидается:** 3 строки с названиями индексов
---
## 🔧 Типичные проблемы и решения
### Проблема: "column job does not exist"
**Решение:**
```bash
./bin/apply_all_patches.sh
docker compose restart bot
```
### Проблема: "State column does not exist" (много раз)
**Решение:**
```bash
PGPASSWORD='password' psql -h host -p 5432 -U user -d db_name \
-c "ALTER TABLE users ADD COLUMN IF NOT EXISTS state VARCHAR(50);"
docker compose restart bot
```
### Проблема: "looking_for violates not-null constraint"
**Решение:**
```bash
PGPASSWORD='password' psql -h host -p 5432 -U user -d db_name \
-f sql/fix_looking_for_column.sql
docker compose restart bot
```
### Проблема: Бот не запускается (exit code 1)
**Диагностика:**
```bash
docker compose logs bot --tail 100
# Ищите строки с ERROR или "does not exist"
```
**Решения:**
1. Проверьте `.env` - все переменные DB_* заданы?
2. Проверьте подключение к БД: `PGPASSWORD='password' psql -h host -p 5432 -U user -d db_name -c "SELECT 1;"`
3. Примените все патчи: `./bin/apply_all_patches.sh`
4. Пересоберите контейнер: `make update`
---
## 📊 Быстрая диагностика одной командой
```bash
# Создайте alias для удобства
alias bot-health='docker compose ps && echo "=== LOGS ===" && docker compose logs bot --tail 20'
# Использование
bot-health
```
---
## 🚀 Команды для разработки
```bash
# Полное обновление (pull + rebuild + migrate + restart)
make update
# Применение миграций
make migrate
# Только перезапуск
docker compose restart bot
# Пересборка с нуля
make clean && make install
# Проверка синтаксиса TypeScript
npm run build
# Запуск в режиме разработки (локально)
npm run dev
```
---
## 📝 Переменные окружения (.env)
Обязательные:
```env
DB_HOST=192.168.0.102
DB_PORT=5432
DB_NAME=telegram_tinder_bot
DB_USERNAME=trevor
DB_PASSWORD=your_secure_password
TELEGRAM_BOT_TOKEN=your_bot_token
JWT_SECRET=your_jwt_secret
APP_SECRET=your_app_secret
NODE_ENV=production
PORT=3000
```
Опциональные:
```env
DEEPSEEK_API_KEY=your_deepseek_key # Для AI фич
LOG_LEVEL=info # debug | info | warn | error
```
---
## 🆘 Экстренное восстановление
Если бот полностью сломан:
```bash
# 1. Остановить всё
docker compose down
# 2. Сделать бэкап БД
./bin/backup_db.sh
# 3. Откатить к последнему коммиту
git reset --hard HEAD
# 4. Применить все патчи заново
./bin/apply_all_patches.sh
# 5. Пересобрать
make install
# 6. Запустить
docker compose up -d
```
---
**Версия:** 1.0
**Дата:** 2025-11-06
**Для:** Telegram Tinder Bot v2.0

View File

@@ -0,0 +1,36 @@
-- Добавление колонок job и state
-- Дата: 2025-11-06
-- Исправляет ошибки: "column job does not exist" и "State column does not exist"
-- 1. Добавляем колонку job в таблицу profiles (синоним для occupation)
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS job VARCHAR(255);
-- 2. Копируем существующие данные из occupation в job
UPDATE profiles SET job = occupation WHERE occupation IS NOT NULL AND job IS NULL;
-- 3. Добавляем колонку state в таблицу users для отслеживания состояния диалога
ALTER TABLE users ADD COLUMN IF NOT EXISTS state VARCHAR(50);
-- 4. Создаём индексы для производительности
CREATE INDEX IF NOT EXISTS idx_profiles_job ON profiles(job);
CREATE INDEX IF NOT EXISTS idx_users_state ON users(state);
-- 5. Добавляем комментарии для документации
COMMENT ON COLUMN profiles.job IS 'Профессия/работа пользователя (синоним для occupation)';
COMMENT ON COLUMN profiles.occupation IS 'Профессия/работа пользователя (устаревшее, используйте job)';
COMMENT ON COLUMN users.state IS 'Текущее состояние пользователя в диалоге с ботом';
-- Проверка результата
SELECT
'profiles.job' as column_name,
CASE WHEN EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'profiles' AND column_name = 'job'
) THEN '✅ Существует' ELSE 'Не найдена' END as status
UNION ALL
SELECT
'users.state' as column_name,
CASE WHEN EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'state'
) THEN '✅ Существует' ELSE 'Не найдена' END as status;

View File

@@ -0,0 +1,43 @@
-- Исправление триггера create_initial_profile и колонки looking_for
-- Дата: 2025-11-06
-- 1. Удаляем старую функцию триггера
DROP FUNCTION IF EXISTS create_initial_profile() CASCADE;
-- 2. Создаём исправленную функцию с полем looking_for
CREATE OR REPLACE FUNCTION create_initial_profile()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO profiles (user_id, name, age, gender, looking_for, interested_in)
VALUES (NEW.id, COALESCE(NEW.first_name, 'User'), 18, 'other', 'both', 'both');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 3. Пересоздаём триггер
DROP TRIGGER IF EXISTS create_profile_on_user_insert ON users;
DROP TRIGGER IF EXISTS create_profile_trigger ON users;
CREATE TRIGGER create_profile_on_user_insert
AFTER INSERT ON users
FOR EACH ROW
EXECUTE FUNCTION create_initial_profile();
-- 4. Делаем looking_for необязательным с дефолтным значением
ALTER TABLE profiles ALTER COLUMN looking_for DROP NOT NULL;
ALTER TABLE profiles ALTER COLUMN looking_for SET DEFAULT 'both';
-- 5. Добавляем interested_in как синоним для looking_for
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS interested_in VARCHAR(20) DEFAULT 'both'
CHECK (interested_in IN ('male', 'female', 'both'));
-- 6. Обновляем существующие записи
UPDATE profiles SET looking_for = 'both' WHERE looking_for IS NULL;
UPDATE profiles SET interested_in = COALESCE(looking_for, 'both') WHERE interested_in IS NULL;
-- 7. Создаём индекс для поиска по interested_in
CREATE INDEX IF NOT EXISTS idx_profiles_interested_in ON profiles(interested_in);
COMMENT ON COLUMN profiles.looking_for IS 'Предпочитаемый пол для знакомства (устаревшее, используйте interested_in)';
COMMENT ON COLUMN profiles.interested_in IS 'Предпочитаемый пол для знакомства: male, female, both';