fix: PostgreSQL compatibility and Telegram bot token validation

- Fixed PostgreSQL 18+ compatibility by using postgres:17 image
- Added token validation for Telegram bot
- Improved error handling in telegram_bot.py
- Added scripts for bot token management
- Cleaned up old QR codes and added new utility files
This commit is contained in:
2025-11-23 21:23:51 +09:00
parent 7ce0c6c62a
commit 37d7fc74b8
89 changed files with 251 additions and 19 deletions

2
bin/migrate.sh Normal file → Executable file
View File

@@ -1,3 +1,3 @@
#!/usr/bin/env bash
# Run migrations
docker exec ${} python3 smartsoltech/manage.py migrate
docker exec ${django_app} python3 smartsoltech/manage.py migrate

View File

@@ -2,8 +2,8 @@ version: '3.8'
services:
db:
image: postgres:latest
postgres_db:
image: postgres:17
container_name: postgres_db
env_file: .env
volumes:
@@ -23,7 +23,7 @@ services:
container_name: pgadmin
env_file: .env
depends_on:
- db
- postgres_db
ports:
- "8080:80"
environment:
@@ -59,7 +59,7 @@ services:
ports:
- "8000:8000"
depends_on:
- db
- postgres_db
networks:
- web_db_network

48
endpoint_test.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
BASE_URL="http://localhost:8002/auth"
EMAIL="testuser@example.com"
PASSWORD="secret123"
echo "1⃣ Регистрация пользователя..."
curl -s -X POST "$BASE_URL/register" \
-H "Content-Type: application/json" \
-d "{\"email\": \"$EMAIL\", \"password\": \"$PASSWORD\"}" | tee response_register.json
echo -e "\n"
USER_ID=$(jq .id response_register.json)
echo "2⃣ Аутентификация..."
curl -s -X POST "$BASE_URL/login" \
-H "Content-Type: application/json" \
-d "{\"email\": \"$EMAIL\", \"password\": \"$PASSWORD\"}" | tee response_login.json
echo -e "\n"
TOKEN=$(jq -r .access_token response_login.json)
echo "🔐 Получен токен: $TOKEN"
AUTH_HEADER="Authorization: Bearer $TOKEN"
echo "3⃣ Получение текущего пользователя (/me)..."
curl -s -X GET "$BASE_URL/me" -H "$AUTH_HEADER" | tee response_me.json
echo -e "\n"
echo "4⃣ Получение списка всех пользователей..."
curl -s -X GET "$BASE_URL/users" | tee response_users.json
echo -e "\n"
echo "5⃣ Получение пользователя по ID ($USER_ID)..."
curl -s -X GET "$BASE_URL/users/$USER_ID" | tee response_user.json
echo -e "\n"
echo "6⃣ Обновление пользователя..."
curl -s -X PUT "$BASE_URL/users/$USER_ID" \
-H "Content-Type: application/json" \
-d "{\"email\": \"updated_$EMAIL\", \"role\": \"admin\"}" | tee response_update.json
echo -e "\n"
echo "7⃣ Удаление пользователя..."
curl -s -X DELETE "$BASE_URL/users/$USER_ID" | tee response_delete.json
echo -e "\n"
echo "✅ Тест завершён."

1
response_delete.json Normal file
View File

@@ -0,0 +1 @@
{"detail":"User 4 deleted"}

1
response_login.json Normal file
View File

@@ -0,0 +1 @@
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE3NTI0OTc2NDV9.S_tquLFIPnyG6XlfwIw97hJv0l9oKpTcYw_XG0mDd6w","token_type":"bearer"}

1
response_me.json Normal file
View File

@@ -0,0 +1 @@
{"id":4,"email":"testuser@example.com","role":"user"}

1
response_register.json Normal file
View File

@@ -0,0 +1 @@
{"id":4,"email":"testuser@example.com","role":"user"}

1
response_update.json Normal file
View File

@@ -0,0 +1 @@
{"id":4,"email":"updated_testuser@example.com","role":"admin"}

1
response_user.json Normal file
View File

@@ -0,0 +1 @@
{"id":4,"email":"testuser@example.com","role":"user"}

1
response_users.json Normal file
View File

@@ -0,0 +1 @@
[{"id":1,"email":"user1@example.com","role":"user"},{"id":4,"email":"testuser@example.com","role":"user"}]

View File

@@ -14,12 +14,35 @@ class TelegramBot:
def __init__(self):
# Получение настроек бота из базы данных
bot_settings = TelegramSettings.objects.first()
if bot_settings:
TELEGRAM_BOT_TOKEN = bot_settings.bot_token
self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN)
logging.info("[TelegramBot] Бот инициализирован с токеном.")
if bot_settings and bot_settings.bot_token:
TELEGRAM_BOT_TOKEN = bot_settings.bot_token.strip()
# Проверяем валидность токена
if self._validate_token(TELEGRAM_BOT_TOKEN):
self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN)
logging.info(f"[TelegramBot] Бот инициализирован с токеном для {bot_settings.bot_name}.")
else:
logging.error(f"[TelegramBot] Токен невалиден: {TELEGRAM_BOT_TOKEN[:10]}...")
raise Exception(f"Невалидный токен Telegram бота. Обновите токен в базе данных.")
else:
raise Exception("Telegram bot settings not found")
raise Exception("Telegram bot settings not found or token is empty")
def _validate_token(self, token):
"""Проверяет валидность токена через Telegram API"""
url = f"https://api.telegram.org/bot{token}/getMe"
try:
response = requests.get(url, timeout=10)
result = response.json()
if result.get('ok'):
bot_info = result.get('result', {})
logging.info(f"[TelegramBot] Токен валиден. Бот: @{bot_info.get('username', 'unknown')}")
return True
else:
logging.error(f"[TelegramBot] Ошибка Telegram API: {result.get('description', 'Unknown error')}")
return False
except requests.RequestException as e:
logging.error(f"[TelegramBot] Ошибка при проверке токена: {e}")
return False
def start_bot_polling(self):
logging.info("[TelegramBot] Бот начал работу в режиме polling.")

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@@ -29,9 +29,11 @@ SECRET_KEY = config('SECRET_KEY')
DEBUG = True
# Allowed hosts and CSRF trusted origins
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='').split(',')
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost').split(',')
CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='').split(',')
print(f"ALLOWED_HOSTS: {ALLOWED_HOSTS}")
print(f"CSRF_TRUSTED_ORIGINS: {CSRF_TRUSTED_ORIGINS}")
# Application definition

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -7,7 +7,7 @@
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Contact us</h2>
<p class="w-lg-50">Curae hendrerit donec commodo hendrerit egestas tempus, turpis facilisis nostra nunc. Vestibulum dui eget ultrices.</p>
<p class="w-lg-50"></p>
</div>
</div>
<div class="row d-flex justify-content-center">
@@ -28,7 +28,7 @@
</svg></div>
<div class="px-2">
<h6 class="mb-0">Email</h6>
<p class="mb-0"><a href="mailto:a.choi@smartsoltech.kr">a.choI@smartsoltech.kr</a></p>
<p class="mb-0"><a href="mailto:a.choi@smartsoltech.kr">a.choi@smartsoltech.kr</a></p>
</div>
</div>
<div class="d-flex align-items-center p-3">

View File

@@ -9,7 +9,6 @@
<link rel="manifest" href="/static/manifest.json">
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<link rel="stylesheet" href="{% static 'assets/css/modal-styles.css' %}">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<title>{% block title %}Smartsoltech{% endblock %}</title>

View File

@@ -10,10 +10,10 @@
<a class="nav-link" href="{% url 'home' %}">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Услуги</a>
<a class="nav-link" href="{% url 'services' %}">Услуги</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Контакты</a>
<a class="nav-link" href="{% url 'about' %}">Контакты</a>
</li>
</ul>
</div>

View File

@@ -6,9 +6,8 @@
</svg></span><span>SmartSolTech</span></a><button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navcol-5"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div id="navcol-5" class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link active" href="/services/">Услуги</a></li>
<li class="nav-item"><a class="nav-link" href="#">Наши проекты</a></li>
<li class="nav-item"><a class="nav-link" href="/about/">О нас</a></li>
<li class="nav-item"><a class="nav-link active" href="{% url 'services' %}">Услуги</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'about_view' %}">О нас</a></li>
</ul>
</div>
</div>

50
update_bot_token.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# Скрипт для обновления токена Telegram бота
# Использование: ./update_bot_token.sh "НОВЫЙ_ТОКЕН"
if [ $# -eq 0 ]; then
echo "❌ Ошибка: Необходимо указать токен"
echo "Использование: $0 \"НОВЫЙ_ТОКЕН\""
echo ""
echo "Пример: $0 \"1234567890:ABCDEFghijklmnopqrstuvwxyz\""
echo ""
echo "Получите токен от @BotFather в Telegram:"
echo "1. Отправьте /mybots"
echo "2. Выберите своего бота"
echo "3. Нажмите 'API Token'"
exit 1
fi
NEW_TOKEN="$1"
echo "🔄 Обновление токена Telegram бота..."
# Проверяем валидность токена
echo "🔍 Проверка валидности токена..."
RESPONSE=$(curl -s "https://api.telegram.org/bot${NEW_TOKEN}/getMe")
if echo "$RESPONSE" | grep -q '"ok":true'; then
BOT_USERNAME=$(echo "$RESPONSE" | grep -o '"username":"[^"]*"' | cut -d'"' -f4)
echo "✅ Токен валиден! Бот: @${BOT_USERNAME}"
else
echo "❌ Ошибка: Токен невалиден!"
echo "Ответ API: $RESPONSE"
exit 1
fi
# Обновляем токен в базе данных
echo "💾 Обновление токена в базе данных..."
docker exec postgres_db psql -U trevor -d 2st_db -c "
UPDATE comunication_telegramsettings
SET bot_token = '$NEW_TOKEN', bot_name = '@${BOT_USERNAME}'
WHERE id = 1;
"
if [ $? -eq 0 ]; then
echo "✅ Токен успешно обновлен в базе данных!"
echo "🔄 Перезапуск контейнера telegram_bot..."
docker restart telegram_bot
echo "🎉 Готово! Проверьте логи: docker logs telegram_bot"
else
echo "❌ Ошибка при обновлении базы данных"
exit 1
fi

104
update_telegram_token.py Normal file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""
Скрипт для обновления токена Telegram бота в базе данных
"""
import os
import sys
import django
import requests
# Настройка Django
sys.path.append('/app/smartsoltech')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartsoltech.settings')
django.setup()
from comunication.models import TelegramSettings
def validate_token(token):
"""Проверяет валидность токена через Telegram API"""
url = f"https://api.telegram.org/bot{token}/getMe"
try:
response = requests.get(url, timeout=10)
return response.json().get('ok', False)
except requests.RequestException:
return False
def update_telegram_token(new_token, bot_name=None):
"""Обновляет токен Telegram бота в базе данных"""
# Проверяем валидность токена
if not validate_token(new_token):
print(f"❌ Ошибка: Токен {new_token} невалиден!")
return False
# Получаем информацию о боте
url = f"https://api.telegram.org/bot{new_token}/getMe"
response = requests.get(url)
bot_info = response.json()
if bot_info.get('ok'):
bot_username = bot_info['result']['username']
print(f"✅ Токен валиден. Бот: @{bot_username}")
# Обновляем настройки в базе данных
telegram_settings, created = TelegramSettings.objects.get_or_create(
id=1,
defaults={
'bot_name': f"@{bot_username}",
'bot_token': new_token,
'use_polling': True
}
)
if not created:
telegram_settings.bot_token = new_token
telegram_settings.bot_name = bot_name or f"@{bot_username}"
telegram_settings.save()
print(f"✅ Токен обновлен в базе данных для бота {telegram_settings.bot_name}")
else:
print(f"✅ Создана новая запись для бота @{bot_username}")
return True
else:
print(f"❌ Ошибка при получении информации о боте")
return False
if __name__ == "__main__":
print("🤖 Скрипт обновления токена Telegram бота")
print("=" * 50)
# Проверяем текущий токен
try:
current_settings = TelegramSettings.objects.first()
if current_settings:
print(f"Текущий бот: {current_settings.bot_name}")
print(f"Текущий токен: {current_settings.bot_token[:10]}...")
if not validate_token(current_settings.bot_token):
print("❌ Текущий токен невалиден")
else:
print("✅ Текущий токен валиден")
else:
print("⚠️ Настройки Telegram бота не найдены")
except Exception as e:
print(f"❌ Ошибка при проверке текущих настроек: {e}")
print("\n" + "=" * 50)
print("Для обновления токена:")
print("1. Идите к @BotFather в Telegram")
print("2. Создайте нового бота или используйте /token для существующего")
print("3. Скопируйте токен и введите его ниже")
print("=" * 50)
new_token = input("\nВведите новый токен бота (или 'exit' для выхода): ").strip()
if new_token.lower() == 'exit':
print("Выход...")
sys.exit(0)
if update_telegram_token(new_token):
print("\n🎉 Токен успешно обновлен!")
print("Теперь перезапустите контейнер telegram_bot:")
print("docker restart telegram_bot")
else:
print("\nНе удалось обновить токен")