363 lines
19 KiB
Python
363 lines
19 KiB
Python
import logging
|
||
import sqlite3
|
||
|
||
import telebot
|
||
from flask import Flask, request, jsonify, render_template, flash, redirect, url_for
|
||
from flask_ldap3_login.forms import LDAPLoginForm
|
||
from flask_login import login_manager, login_user, logout_user, UserMixin
|
||
|
||
from frontend.dashboard import bp_dashboard, bp_api
|
||
|
||
import backend_bot
|
||
import bot_database
|
||
import telezab
|
||
import utilities.telegram_utilities as telegram_util
|
||
from backend_locks import db_lock
|
||
from config import BASE_URL, DB_PATH
|
||
from utilities.telegram_utilities import extract_region_number, format_message, validate_chat_id, validate_telegram_id, validate_email
|
||
|
||
app = Flask(__name__, static_url_path='/telezab/static', template_folder='templates')
|
||
# app.register_blueprint(webui)
|
||
app.secret_key = "supersecretkey"
|
||
app.register_blueprint(bp_dashboard)
|
||
app.register_blueprint(bp_api)
|
||
#
|
||
# # Инициализация менеджеров
|
||
# ldap_manager = LDAP3LoginManager(app)
|
||
# login_manager = LoginManager(app)
|
||
# login_manager.login_view = "login"
|
||
|
||
# Пользовательский класс
|
||
class User(UserMixin):
|
||
def __init__(self, dn, username):
|
||
self.id = dn
|
||
self.username = username
|
||
|
||
|
||
# Настройка уровня логирования для Flask
|
||
app.logger.setLevel(logging.INFO)
|
||
|
||
|
||
@app.route(BASE_URL + '/webhook', methods=['POST'])
|
||
def webhook():
|
||
try:
|
||
# Получаем данные и логируем
|
||
data = request.get_json()
|
||
app.logger.info(f"Получены данные: {data}")
|
||
|
||
# # Генерация хеша события и логирование
|
||
# event_hash = bot_database.hash_data(data)
|
||
# app.logger.debug(f"Сгенерирован хеш для события: {event_hash}")
|
||
|
||
# Работа с базой данных в блоке синхронизации
|
||
with db_lock:
|
||
conn = sqlite3.connect(DB_PATH)
|
||
cursor = conn.cursor()
|
||
|
||
# Проверяем количество записей в таблице событий
|
||
cursor.execute('SELECT COUNT(*) FROM events')
|
||
count = cursor.fetchone()[0]
|
||
app.logger.debug(f"Текущее количество записей в таблице events: {count}")
|
||
|
||
# Если записей >= 200, удаляем самое старое событие
|
||
if count >= 200:
|
||
query = 'DELETE FROM events WHERE id = (SELECT MIN(id) FROM events)'
|
||
app.logger.debug(f"Удаление старого события: {query}")
|
||
cursor.execute(query)
|
||
|
||
# Извлечение номера региона из поля host
|
||
region_id = extract_region_number(data.get("host"))
|
||
if region_id is None:
|
||
app.logger.error(f"Не удалось извлечь номер региона из host: {data.get('host')}")
|
||
return jsonify({"status": "error", "message": "Invalid host format"}), 400
|
||
app.logger.debug(f"Извлечён номер региона: {region_id}")
|
||
|
||
# Запрос подписчиков для отправки уведомления в зависимости от уровня критичности
|
||
if data['severity'] == 'Disaster': # Авария
|
||
query = 'SELECT chat_id, username FROM subscriptions WHERE region_id = ? AND active = TRUE'
|
||
else: # Высокая критичность
|
||
query = 'SELECT chat_id, username FROM subscriptions WHERE region_id = ? AND active = TRUE AND disaster_only = FALSE'
|
||
|
||
app.logger.debug(f"Выполнение запроса: {query} для region_id={region_id}")
|
||
cursor.execute(query, (region_id,))
|
||
results = cursor.fetchall()
|
||
|
||
app.logger.debug(f"Найдено подписчиков: {len(results)} для региона {region_id}")
|
||
|
||
# Проверка статуса региона (активен или нет)
|
||
query = 'SELECT active FROM regions WHERE region_id = ?'
|
||
cursor.execute(query, (region_id,))
|
||
region_row = cursor.fetchone()
|
||
|
||
if region_row and region_row[0]: # Если регион активен
|
||
app.logger.debug(f"Регион {region_id} активен. Начинаем рассылку сообщений.")
|
||
message = format_message(data)
|
||
undelivered = False
|
||
|
||
# Отправляем сообщения подписчикам
|
||
for chat_id, username in results:
|
||
formatted_message = message.replace('\n', ' ').replace('\r', '')
|
||
|
||
app.logger.info(
|
||
f"Формирование сообщения для пользователя {username} (chat_id={chat_id}) [{formatted_message}]")
|
||
try:
|
||
from utilities.rabbitmq import send_to_queue
|
||
send_to_queue({'chat_id': chat_id, 'username': username, 'message': message})
|
||
app.logger.debug(f"Сообщение поставлено в очередь для {chat_id} (@{username})")
|
||
except Exception as e:
|
||
app.logger.error(f"Ошибка при отправке сообщения для {chat_id} (@{username}): {e}")
|
||
undelivered = True
|
||
|
||
# Сохранение события, если были проблемы с доставкой
|
||
if undelivered:
|
||
query = 'INSERT OR IGNORE INTO events (hash, data, delivered) VALUES (?, ?, ?)'
|
||
app.logger.debug(
|
||
f"Сохранение события в базе данных: {query} (delivered={False})")
|
||
cursor.execute(query, (str(data), False))
|
||
|
||
# Коммитим изменения в базе данных
|
||
conn.commit()
|
||
app.logger.debug("Изменения в базе данных успешно сохранены.")
|
||
conn.close()
|
||
|
||
# Возвращаем успешный ответ
|
||
return jsonify({"status": "success"}), 200
|
||
|
||
except sqlite3.OperationalError as e:
|
||
app.logger.error(f"Ошибка операции с базой данных: {e}")
|
||
return jsonify({"status": "error", "message": "Ошибка работы с базой данных"}), 500
|
||
|
||
except ValueError as e:
|
||
app.logger.error(f"Ошибка значения: {e}")
|
||
return jsonify({"status": "error", "message": "Некорректные данные"}), 400
|
||
|
||
except Exception as e:
|
||
app.logger.error(f"Неожиданная ошибка: {e}")
|
||
return jsonify({"status": "error", "message": "Внутренняя ошибка сервера"}), 500
|
||
|
||
|
||
@app.route(BASE_URL + '/users/add', methods=['POST'])
|
||
def add_user():
|
||
data = request.get_json()
|
||
|
||
telegram_id = data.get('telegram_id')
|
||
chat_id = data.get('chat_id')
|
||
user_email = data.get('user_email')
|
||
|
||
# DEBUG: Логирование полученных данных
|
||
app.logger.debug(f"Получены данные для добавления пользователя: {data}")
|
||
|
||
# Валидация данных
|
||
if not validate_chat_id(chat_id):
|
||
app.logger.warning(f"Ошибка валидации: некорректный chat_id: {chat_id}")
|
||
return jsonify({"status": "failure", "reason": "Invalid data chat_id must be digit"}), 400
|
||
|
||
if not validate_telegram_id(telegram_id):
|
||
app.logger.warning(f"Ошибка валидации: некорректный telegram_id: {telegram_id}")
|
||
return jsonify({"status": "failure", "reason": "Invalid data telegram id must start from '@'"}), 400
|
||
|
||
if not validate_email(user_email):
|
||
app.logger.warning(f"Ошибка валидации: некорректный email: {user_email}")
|
||
return jsonify({"status": "failure", "reason": "Invalid data email address must be from rtmis"}), 400
|
||
|
||
if telegram_id and chat_id and user_email:
|
||
try:
|
||
# INFO: Попытка отправить сообщение пользователю
|
||
app.logger.info(f"Отправка сообщения пользователю {telegram_id} с chat_id {chat_id}")
|
||
backend_bot.bot.send_message(chat_id, "Регистрация пройдена успешно.")
|
||
# DEBUG: Попытка добавления пользователя в whitelist
|
||
app.logger.debug(f"Добавление пользователя {telegram_id} в whitelist")
|
||
success = bot_database.rundeck_add_to_whitelist(chat_id, telegram_id, user_email)
|
||
if success:
|
||
# INFO: Пользователь успешно добавлен в whitelist
|
||
app.logger.info(f"Пользователь {telegram_id} добавлен в whitelist.")
|
||
telezab.state.set_state(chat_id, "MAIN_MENU")
|
||
|
||
# DEBUG: Показ основного меню пользователю
|
||
app.logger.debug(f"Отображение основного меню для пользователя с chat_id {chat_id}")
|
||
telegram_util.show_main_menu(chat_id)
|
||
return jsonify(
|
||
{"status": "success", "msg": f"User {telegram_id} with {user_email} added successfully"}), 200
|
||
else:
|
||
# INFO: Пользователь уже существует в системе
|
||
app.logger.info(f"Пользователь с chat_id {chat_id} уже существует.")
|
||
return jsonify({"status": "failure", "msg": "User already exists"}), 400
|
||
except telebot.apihelper.ApiTelegramException as e:
|
||
if e.result.status_code == 403:
|
||
# INFO: Пользователь заблокировал бота
|
||
app.logger.info(f"Пользователь {telegram_id} заблокировал бота")
|
||
return jsonify({"status": "failure", "msg": f"User {telegram_id} is blocked chat with bot"})
|
||
elif e.result.status_code == 400:
|
||
# WARNING: Пользователь неизвестен боту, возможно не нажал /start
|
||
app.logger.warning(
|
||
f"Пользователь {telegram_id} с chat_id {chat_id} неизвестен боту, возможно, не нажал /start")
|
||
return jsonify({"status": "failure",
|
||
"msg": f"User {telegram_id} with {chat_id} is unknown to the bot, did the user press /start button?"})
|
||
else:
|
||
# ERROR: Неизвестная ошибка при отправке сообщения
|
||
app.logger.error(f"Ошибка при отправке сообщения пользователю {telegram_id}: {str(e)}")
|
||
return jsonify({"status": "failure", "msg": f"{e}"})
|
||
else:
|
||
# ERROR: Ошибка валидации — недостаточно данных
|
||
app.logger.error("Получены некорректные данные для добавления пользователя.")
|
||
return jsonify({"status": "failure", "reason": "Invalid data"}), 400
|
||
|
||
|
||
@app.route(BASE_URL + '/users/del', methods=['POST'])
|
||
def delete_user():
|
||
data = request.get_json()
|
||
user_email = data.get('email')
|
||
conn = sqlite3.connect(DB_PATH)
|
||
try:
|
||
# DEBUG: Получен запрос и начинается обработка
|
||
app.logger.debug(f"Получен запрос на удаление пользователя. Данные: {data}")
|
||
|
||
if not user_email:
|
||
# WARNING: Ошибка валидации данных, email отсутствует
|
||
app.logger.warning(f"Ошибка валидации: отсутствует email")
|
||
return jsonify({"status": "failure", "message": "Email is required"}), 400
|
||
|
||
cursor = conn.cursor()
|
||
|
||
# DEBUG: Запрос на получение chat_id
|
||
app.logger.debug(f"Выполняется запрос на получение chat_id для email: {user_email}")
|
||
cursor.execute("SELECT chat_id FROM whitelist WHERE user_email = ?", (user_email,))
|
||
user = cursor.fetchone()
|
||
|
||
if user is None:
|
||
# WARNING: Пользователь с указанным email не найден
|
||
app.logger.warning(f"Пользователь с email {user_email} не найден")
|
||
return jsonify({"status": "failure", "message": "User not found"}), 404
|
||
chat_id = user[0]
|
||
|
||
# INFO: Удаление пользователя и его подписок начато
|
||
app.logger.info(f"Начато удаление пользователя с email {user_email} и всех его подписок")
|
||
|
||
# DEBUG: Удаление пользователя из whitelist
|
||
app.logger.debug(f"Удаление пользователя с email {user_email} из whitelist")
|
||
cursor.execute("DELETE FROM whitelist WHERE user_email = ?", (user_email,))
|
||
|
||
# DEBUG: Удаление подписок пользователя
|
||
app.logger.debug(f"Удаление подписок для пользователя с chat_id {chat_id}")
|
||
cursor.execute("DELETE FROM subscriptions WHERE chat_id = ?", (chat_id,))
|
||
|
||
conn.commit()
|
||
# INFO: Пользователь и подписки успешно удалены
|
||
app.logger.info(f"Пользователь с email {user_email} и все его подписки успешно удалены")
|
||
return jsonify(
|
||
{"status": "success", "message": f"User with email {user_email} and all subscriptions deleted."}), 200
|
||
except Exception as e:
|
||
conn.rollback()
|
||
# ERROR: Ошибка при удалении данных
|
||
app.logger.error(f"Ошибка при удалении пользователя с email {user_email}: {str(e)}")
|
||
return jsonify({"status": "failure", "message": str(e)}), 500
|
||
finally:
|
||
conn.close()
|
||
# DEBUG: Соединение с базой данных закрыто
|
||
app.logger.debug(f"Соединение с базой данных закрыто")
|
||
|
||
|
||
# @app.route(BASE_URL + '/users/get', methods=['GET'])
|
||
# def get_users():
|
||
# try:
|
||
# # INFO: Запрос на получение списка пользователей
|
||
# app.logger.info("Запрос на получение информации о пользователях получен")
|
||
#
|
||
# with db_lock:
|
||
# conn = sqlite3.connect(DB_PATH)
|
||
# cursor = conn.cursor()
|
||
#
|
||
# # DEBUG: Запрос данных из таблицы whitelist
|
||
# app.logger.debug("Запрос данных пользователей из таблицы whitelist")
|
||
# cursor.execute('SELECT * FROM whitelist')
|
||
# users = cursor.fetchall()
|
||
# app.logger.debug("Формирование словаря пользователей")
|
||
# users_dict = {user_id: {'id': user_id, 'username': username, 'email': email, 'events': [], 'worker': '',
|
||
# 'subscriptions': []}
|
||
# for user_id, username, email in users}
|
||
#
|
||
# # DEBUG: Запрос данных событий пользователей
|
||
# app.logger.debug("Запрос событий пользователей из таблицы user_events")
|
||
# cursor.execute('SELECT chat_id, username, action, timestamp FROM user_events')
|
||
# events = cursor.fetchall()
|
||
#
|
||
# # DEBUG: Обработка событий и добавление их в словарь пользователей
|
||
# for chat_id, username, action, timestamp in events:
|
||
# if chat_id in users_dict:
|
||
# event = {'type': action, 'date': timestamp}
|
||
# if "Subscribed to region" in action:
|
||
# region = action.split(": ")[-1]
|
||
# event['region'] = region
|
||
# users_dict[chat_id]['events'].append(event)
|
||
#
|
||
# # DEBUG: Запрос данных подписок пользователей
|
||
# app.logger.debug("Запрос активных подписок пользователей из таблицы subscriptions")
|
||
# cursor.execute('SELECT chat_id, region_id FROM subscriptions WHERE active = 1')
|
||
# subscriptions = cursor.fetchall()
|
||
#
|
||
# # DEBUG: Добавление подписок к пользователям
|
||
# for chat_id, region_id in subscriptions:
|
||
# if chat_id in users_dict:
|
||
# users_dict[chat_id]['subscriptions'].append(str(region_id))
|
||
#
|
||
# # INFO: Формирование результата
|
||
# app.logger.info("Формирование результата для ответа")
|
||
# result = []
|
||
# for user in users_dict.values():
|
||
# ordered_user = {
|
||
# 'email': user['email'],
|
||
# 'username': user['username'],
|
||
# 'id': user['id'],
|
||
# 'worker': user['worker'],
|
||
# 'events': user['events'],
|
||
# 'subscriptions': ', '.join(user['subscriptions'])
|
||
# }
|
||
# result.append(ordered_user)
|
||
#
|
||
# # INFO: Успешная отправка данных пользователей
|
||
# app.logger.info("Информация о пользователях успешно отправлена")
|
||
# return jsonify(result)
|
||
#
|
||
# except Exception as e:
|
||
# # ERROR: Ошибка при получении информации о пользователях
|
||
# app.logger.error(f"Ошибка при получении информации о пользователях: {str(e)}")
|
||
# return jsonify({'status': 'error', 'message': str(e)}), 500
|
||
|
||
|
||
@app.route(BASE_URL + '/debug/flask', methods=['POST'])
|
||
def toggle_flask_debug():
|
||
try:
|
||
data = request.get_json()
|
||
level = data.get('level').upper()
|
||
if level not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
|
||
return jsonify({'status': 'error', 'message': 'Invalid log level'}), 400
|
||
|
||
log_level = getattr(logging, level, logging.DEBUG)
|
||
app.logger.setLevel(log_level)
|
||
|
||
for handler in app.logger.handlers:
|
||
handler.setLevel(log_level)
|
||
|
||
return jsonify({'status': 'success', 'level': level})
|
||
except Exception as e:
|
||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||
|
||
|
||
@app.route(BASE_URL + '/debug/telebot', methods=['POST'])
|
||
def toggle_telebot_debug():
|
||
try:
|
||
data = request.get_json()
|
||
level = data.get('level').upper()
|
||
if level not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
|
||
return jsonify({'status': 'error', 'message': 'Invalid log level'}), 400
|
||
|
||
log_level = getattr(logging, level, logging.DEBUG)
|
||
telebot.logger.setLevel(log_level)
|
||
|
||
for handler in telebot.logger.handlers:
|
||
handler.setLevel(log_level)
|
||
|
||
return jsonify({'status': 'success', 'level': level})
|
||
except Exception as e:
|
||
return jsonify({'status': 'error', 'message': str(e)}), 500
|