From 4ef050e3c801756bc5233ad3cedb1d01cd355ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=20=D0=97=D0=B2=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B2?= Date: Thu, 19 Sep 2024 02:55:34 +0500 Subject: [PATCH] Update logging system --- log_manager.py | 19 +++-- region_api.py | 46 ++++++++-- telezab.py | 224 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 218 insertions(+), 71 deletions(-) diff --git a/log_manager.py b/log_manager.py index af2265d..d517fbb 100644 --- a/log_manager.py +++ b/log_manager.py @@ -51,7 +51,12 @@ class LogManager: }, }, 'handlers': { - 'console': { + 'telebot_console': { + 'class': 'log_manager.UTF8StreamHandler', + 'stream': 'ext://sys.stdout', + 'formatter': 'default', + }, + 'flask_console': { 'class': 'log_manager.UTF8StreamHandler', 'stream': 'ext://sys.stdout', 'formatter': 'default', @@ -94,19 +99,19 @@ class LogManager: 'loggers': { 'flask': { 'level': 'INFO', - 'handlers': ['flask_file', 'flask_error_file', 'console'], + 'handlers': ['flask_file', 'flask_error_file', 'flask_console'], 'propagate': False, }, 'telebot': { 'level': 'INFO', - 'handlers': ['app_file', 'app_error_file', 'console'], + 'handlers': ['app_file', 'app_error_file', 'telebot_console'], 'propagate': False, }, - }, - 'root': { - 'level': 'DEBUG', - 'handlers': ['console'], } + # 'root': { + # 'level': 'DEBUG', + # 'handlers': ['flask_console', 'telebot_console'], + # } }) def archive_old_logs(self): diff --git a/region_api.py b/region_api.py index cbb8b78..10ac49d 100644 --- a/region_api.py +++ b/region_api.py @@ -1,73 +1,107 @@ -# region_api.py - import sqlite3 +import logging from threading import Lock db_lock = Lock() +# Инициализируем логгер +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) # Устанавливаем уровень логирования + + class RegionAPI: def __init__(self, db_path): self.db_path = db_path def add_region(self, region_id: int, region_name: str): - if not region_id.isdigit() == True: + logger.info(f"Запрос на добавление региона: id={region_id}, name={region_name}") + + # Проверка валидности region_id + if not str(region_id).isdigit(): + logger.error(f"region_id {region_id} не является числом.") return {"status": "failure", "message": "Region_id must be digit only"} + with db_lock, sqlite3.connect(self.db_path) as conn: cursor = conn.cursor() + logger.debug(f"Проверка существования региона с id={region_id}") cursor.execute('SELECT COUNT(*) FROM regions WHERE region_id = ?', (region_id,)) count = cursor.fetchone()[0] + if count == 0: - cursor.execute('INSERT INTO regions (region_id, region_name, active) VALUES (?, ?, 1)', (region_id, region_name)) + # Добавляем новый регион + cursor.execute('INSERT INTO regions (region_id, region_name, active) VALUES (?, ?, 1)', + (region_id, region_name)) conn.commit() + logger.info(f"Регион с id={region_id} успешно добавлен.") return {"status": "success", "message": "Region added successfully"} else: + logger.warning(f"Регион с id={region_id} уже существует.") return {"status": "error", "message": "Region already exists"} def remove_region(self, region_id): + logger.info(f"Запрос на удаление региона: id={region_id}") + with db_lock, sqlite3.connect(self.db_path) as conn: cursor = conn.cursor() + logger.debug(f"Проверка существования региона с id={region_id}") cursor.execute('SELECT COUNT(*) FROM regions WHERE region_id = ?', (region_id,)) count = cursor.fetchone()[0] + if count == 0: + logger.warning(f"Регион с id={region_id} не найден.") return {"status": "error", "message": "Region not found"} else: cursor.execute('DELETE FROM regions WHERE region_id = ?', (region_id,)) conn.commit() + logger.info(f"Регион с id={region_id} успешно удалён.") return {"status": "success", "message": "Region removed successfully"} def get_regions(self): + logger.info("Запрос на получение списка регионов.") + with db_lock, sqlite3.connect(self.db_path) as conn: cursor = conn.cursor() + logger.debug("Извлечение данных из таблицы regions.") cursor.execute('SELECT region_id, region_name, active FROM regions') regions = cursor.fetchall() + logger.info(f"Получено {len(regions)} регионов.") return [{"region_id": r[0], "region_name": r[1], "regions_active": r[2]} for r in regions] def change_region_status(self, region_id, active): + logger.info(f"Запрос на изменение статуса региона: id={region_id}, статус={active}") + with db_lock, sqlite3.connect(self.db_path) as conn: cursor = conn.cursor() + logger.debug(f"Проверка существования региона с id={region_id}") cursor.execute('SELECT COUNT(*) FROM regions WHERE region_id = ?', (region_id,)) count = cursor.fetchone()[0] if count == 0: + logger.warning(f"Регион с id={region_id} не найден.") return {"status": "error", "message": "Region not found"} else: cursor.execute('UPDATE regions SET active = ? WHERE region_id = ?', (active, region_id)) conn.commit() + logger.info(f"Статус региона с id={region_id} успешно изменён.") return {"status": "success", "message": "Region status updated successfully"} def update_region_status(self, region_id, active): + logger.info(f"Запрос на обновление статуса региона: id={region_id}, активность={active}") + with db_lock, sqlite3.connect(self.db_path) as conn: cursor = conn.cursor() # Проверяем существование региона + logger.debug(f"Проверка существования региона с id={region_id}") cursor.execute("SELECT region_name FROM regions WHERE region_id = ?", (region_id,)) result = cursor.fetchone() if not result: + logger.warning(f"Регион с id={region_id} не найден.") return {"status": "error", "message": "Регион не найден"} # Обновляем статус активности региона cursor.execute("UPDATE regions SET active = ? WHERE region_id = ?", (int(active), region_id)) conn.commit() - action = "Активирован" if active else "Отключён" - return {"status": "success", "message": f"Регион {region_id} {action}"} \ No newline at end of file + logger.info(f"Регион с id={region_id} {action}.") + return {"status": "success", "message": f"Регион {region_id} {action}"} diff --git a/telezab.py b/telezab.py index 424ab1c..b333992 100644 --- a/telezab.py +++ b/telezab.py @@ -339,9 +339,8 @@ def handle_help(message): help_text = ( '/start - Показать меню бота\n' 'Настройки - Перейти в режим настроек и управления подписками\n' - 'Активные триггеры - Получение всех нерешённых событий мониторинга по сервисам в выбранном регионе\n' - 'Помощь - Описание всех возможностей бота' - + 'Активные события - Получение всех нерешённых событий мониторинга по выбранным сервисам в выбранного региона\n' + 'Помощь - Описание всех возможностей бота' ) bot.send_message(message.chat.id, help_text, parse_mode="html") show_main_menu(message.chat.id) @@ -374,7 +373,7 @@ def show_main_menu(chat_id): markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) if is_whitelisted(chat_id): user_state_manager.set_state(chat_id, "MAIN_MENU") - markup.add('Настройки', 'Помощь', 'Активные триггеры') + markup.add('Настройки', 'Помощь', 'Активные события') else: user_state_manager.set_state(chat_id, "REGISTRATION") markup.add('Регистрация') @@ -453,7 +452,7 @@ def handle_main_menu(message, chat_id, text): show_settings_menu(chat_id) elif text == 'Помощь': handle_help(message) - elif text == 'Активные триггеры': + elif text == 'Активные события': handle_active_triggers(message) else: bot.send_message(chat_id, "Команда не распознана.") @@ -496,7 +495,11 @@ def handle_subscribe_button(message): bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") return - username = message.from_user.username or "N/A" + username = message.from_user.username + if username: + username = f"@{username}" + else: + username = "N/A" regions_list = format_regions_list(get_sorted_regions()) markup = telebot.types.InlineKeyboardMarkup() markup.add(telebot.types.InlineKeyboardButton(text="Отмена", @@ -511,6 +514,7 @@ def handle_subscribe_button(message): def process_subscription_button(message, chat_id, username): subbed_regions = [] invalid_regions = [] + if message.text.lower() == 'отмена': bot.send_message(chat_id, "Действие отменено.") user_state_manager.set_state(chat_id, "SETTINGS_MENU") @@ -928,10 +932,11 @@ async def consume_from_queue(): for method_frame, properties, body in channel.consume(RABBITMQ_QUEUE): message = json.loads(body) chat_id = message['chat_id'] + username = message['username'] message_text = message['message'] try: - await send_notification_message(chat_id, message_text) + await send_notification_message(chat_id, message_text, username) channel.basic_ack(method_frame.delivery_tag) except Exception as e: telebot.logger.error(f"Error sending message from queue: {e}") @@ -962,7 +967,8 @@ async def send_message(chat_id, message, is_notification=False): telebot.logger.error(f"Failed to send message to {chat_id}: {e}") telebot.logger.error(f"Detailed Error: {e}", exc_info=True) # Добавлено логирование исключения except Exception as e: - telebot.logger.error(f"Unexpected error while sending message to {chat_id}: {e}", exc_info=True) + username = f"@{message.from_user.username}" if message.from_user.username else "N/A" + telebot.logger.error(f"Unexpected error while sending message to {username} {chat_id}: {e}", exc_info=True) await check_telegram_api() finally: if is_notification: @@ -970,9 +976,10 @@ async def send_message(chat_id, message, is_notification=False): -async def send_notification_message(chat_id, message): +async def send_notification_message(chat_id, message, username): await send_message(chat_id, message, is_notification=True) - telebot.logger.info(f'Send {message} notification to {chat_id}') + formatted_message = message.replace('\n', ' ').replace('\r', '') + telebot.logger.info(f'Send notification to {username} {chat_id} from RabbitMQ [{formatted_message}]') async def run_in_executor(func, *args): @@ -1037,7 +1044,7 @@ def handle_notification_mode_selection(call): cursor.execute(query, (disaster_only, chat_id)) conn.commit() - mode_text = "Только Авария" if disaster_only else "Все события" + mode_text = "Критические события" if disaster_only else "Все события" bot.send_message(chat_id, f"Режим уведомлений успешно изменён на: {mode_text}") user_state_manager.set_state(chat_id, "SETTINGS_MENU") show_settings_menu(chat_id) @@ -1051,14 +1058,14 @@ def handle_notification_mode_selection(call): -# Фаза 1: Запрос активных триггеров и выбор региона с постраничным переключением +# Фаза 1: Запрос активных событий и выбор региона с постраничным переключением def handle_active_triggers(message): chat_id = message.chat.id regions = get_sorted_regions() # Используем функцию get_regions для получения регионов start_index = 0 markup = create_region_keyboard(regions, start_index) - bot.send_message(chat_id, "Выберите регион для получения активных триггеров:", reply_markup=markup) + bot.send_message(chat_id, "Выберите регион для получения активных событий:", reply_markup=markup) def create_region_keyboard(regions, start_index, regions_per_page=10): @@ -1141,7 +1148,7 @@ def handle_region_selection(call, region_id): markup.add(types.InlineKeyboardButton(text=group['name'], callback_data=f"group_{group['groupid']}")) markup.add(types.InlineKeyboardButton(text="Все группы региона\n(Долгое выполнение)", callback_data=f"all_groups_{region_id}")) - bot.send_message(chat_id, "Выберите группу хостов или получите триггеры по всем группам региона:", reply_markup=markup) + bot.send_message(chat_id, "Выберите группу хостов или получите события по всем группам региона:", reply_markup=markup) except Exception as e: bot.send_message(chat_id, f"Ошибка при подключении к Zabbix API.\n{str(e)}") show_main_menu(chat_id) @@ -1159,22 +1166,22 @@ def handle_group_or_all_groups(call): # Если выбрана конкретная группа if call.data.startswith("group_"): group_id = call.data.split("_")[1] - get_triggers_for_group(chat_id, group_id) # Сразу получаем триггеры для группы + get_triggers_for_group(chat_id, group_id) # Сразу получаем события для группы show_main_menu(chat_id) # Если выбраны все группы региона elif call.data.startswith("all_groups_"): region_id = call.data.split("_")[2] - get_triggers_for_all_groups(chat_id, region_id) # Сразу получаем триггеры для всех групп региона + get_triggers_for_all_groups(chat_id, region_id) # Сразу получаем события для всех групп региона show_main_menu(chat_id) -# Вспомогательная функция: получение триггеров для группы +# Вспомогательная функция: получение событий для группы def get_triggers_for_group(chat_id, group_id): - triggers = get_zabbix_triggers(group_id) # Получаем все активные триггеры без периода + triggers = get_zabbix_triggers(group_id) # Получаем все активные события без периода if not triggers: - bot.send_message(chat_id, f"Нет активных триггеров.") + bot.send_message(chat_id, f"Нет активных событий.") show_main_menu(chat_id) else: send_triggers_to_user(triggers, chat_id) @@ -1197,15 +1204,15 @@ def get_triggers_for_all_groups(chat_id, region_id): if all_triggers: send_triggers_to_user(all_triggers, chat_id) else: - bot.send_message(chat_id, f"Нет активных триггеров.") + bot.send_message(chat_id, f"Нет активных событий.") show_main_menu(chat_id) except Exception as e: - bot.send_message(chat_id, f"Ошибка при получении триггеров.\n{str(e)}") + bot.send_message(chat_id, f"Ошибка при получении событий.\n{str(e)}") show_main_menu(chat_id) -# Вспомогательная функция: отправка триггеров пользователю +# Вспомогательная функция: отправка событий пользователю def send_triggers_to_user(triggers, chat_id): for trigger in triggers: bot.send_message(chat_id, trigger, parse_mode="html") @@ -1240,7 +1247,7 @@ def get_zabbix_triggers(group_id): zapi.login(api_token=ZABBIX_API_TOKEN) telebot.logger.info(f"Fetching triggers for group {group_id}") - # Получение триггеров + # Получение событий triggers = zapi.trigger.get( output=["triggerid", "description", "priority"], selectHosts=["hostid", "name"], @@ -1313,77 +1320,96 @@ def get_zabbix_triggers(group_id): @app.route(BASE_URL + '/webhook', methods=['POST']) def webhook(): try: + # Получаем данные и логируем data = request.get_json() - app.logger.info(f"Received data: {data}") + app.logger.info(f"Получены данные: {data}") + # Генерация хеша события и логирование event_hash = 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"Executing query: {query}") + 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"Failed to extract region number from host: {data.get('host')}") + app.logger.error(f"Не удалось извлечь номер региона из host: {data.get('host')}") return jsonify({"status": "error", "message": "Invalid host format"}), 400 + app.logger.info(f"Извлечён номер региона: {region_id}") - # Fetch chat_ids to send the alert based on disaster_only flag + # Запрос подписчиков для отправки уведомления в зависимости от уровня опасности if data['severity'] == '5': # Авария - 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' + 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"Executing query: {query} with region_id={region_id}") + app.logger.debug(f"Выполнение запроса: {query} для region_id={region_id}") cursor.execute(query, (region_id,)) results = cursor.fetchall() + app.logger.info(f"Найдено подписчиков: {len(results)} для региона {region_id}") - # Check if the region is active + # Проверка статуса региона (активен или нет) query = 'SELECT active FROM regions WHERE region_id = ?' cursor.execute(query, (region_id,)) region_row = cursor.fetchone() - if region_row and region_row[0]: # Check if region_row is not None and if active + if region_row and region_row[0]: # Если регион активен + app.logger.info(f"Регион {region_id} активен. Начинаем рассылку сообщений.") message = format_message(data) undelivered = False + + # Отправляем сообщения подписчикам for chat_id, username in results: - app.logger.info(f"Queueing message: {message} to chat_id={chat_id}, username={username}") + formatted_message = message.replace('\n',' ').replace('\r','') + app.logger.info(f"Отправка сообщения пользователю @{username} (chat_id={chat_id}) [{formatted_message}]") try: - send_to_queue({'chat_id': chat_id, 'message': message}) - app.logger.info(f"Queued alert for {chat_id} ({username}) for region {region_id}") + send_to_queue({'chat_id': chat_id, 'username': username, 'message': message}) + app.logger.info(f"Сообщение поставлено в очередь для {chat_id} (@{username})") except Exception as e: - app.logger.error(f"Failed to send message to {chat_id} ({username}): {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"Executing query: {query} with hash={event_hash}, data={data}, delivered={False}") + app.logger.debug(f"Сохранение события в базе данных: {query} (hash={event_hash}, delivered={False})") cursor.execute(query, (event_hash, str(data), False)) + # Коммитим изменения в базе данных conn.commit() + app.logger.info("Изменения в базе данных успешно сохранены.") conn.close() + # Возвращаем успешный ответ return jsonify({"status": "success"}), 200 except sqlite3.OperationalError as e: - app.logger.error(f"Database operation error: {e}") - return jsonify({"status": "error", "message": "Database operation error"}), 500 + app.logger.error(f"Ошибка операции с базой данных: {e}") + return jsonify({"status": "error", "message": "Ошибка работы с базой данных"}), 500 except ValueError as e: - app.logger.error(f"Value error: {e}") - return jsonify({"status": "error", "message": "Invalid data"}), 400 + app.logger.error(f"Ошибка значения: {e}") + return jsonify({"status": "error", "message": "Некорректные данные"}), 400 except Exception as e: - app.logger.error(f"Unexpected error: {e}") - return jsonify({"status": "error", "message": "Internal server error"}), 500 + app.logger.error(f"Неожиданная ошибка: {e}") + return jsonify({"status": "error", "message": "Внутренняя ошибка сервера"}), 500 + def format_message(data): @@ -1436,79 +1462,149 @@ def validate_email(email): @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: - success = rundeck_add_to_whitelist(chat_id, telegram_id, user_email) - if success: - app.logger.info(f"User {telegram_id} added to whitelist.") - user_state_manager.set_state(chat_id, "MAIN_MENU") + try: + # INFO: Попытка отправить сообщение пользователю + app.logger.info(f"Отправка сообщения пользователю {telegram_id} с chat_id {chat_id}") bot.send_message(chat_id, "Регистрация пройдена успешно.") - show_main_menu(chat_id) - return jsonify({"status": "success", "msg": f"User {telegram_id} with {user_email} added successfully"}), 200 - else: - app.logger.info(f"User with chat_id {chat_id} already exists.") - return jsonify({"status": "failure", "msg": "User already exists"}), 400 + # DEBUG: Попытка добавления пользователя в whitelist + app.logger.debug(f"Добавление пользователя {telegram_id} в whitelist") + success = rundeck_add_to_whitelist(chat_id, telegram_id, user_email) + if success: + # INFO: Пользователь успешно добавлен в whitelist + app.logger.info(f"Пользователь {telegram_id} добавлен в whitelist.") + user_state_manager.set_state(chat_id, "MAIN_MENU") + + # DEBUG: Показ основного меню пользователю + app.logger.debug(f"Отображение основного меню для пользователя с chat_id {chat_id}") + 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: - app.logger.error("Invalid data received for adding user.") + # 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: - data = request.get_json() - user_email = data.get('email') + # 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() - return jsonify({"status": "success", "message": f"User with email {user_email} and all subscriptions deleted."}), 200 + # 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() + # DEBUG: Формирование словаря пользователей + app.logger.debug("Формирование словаря пользователей") users_dict = {id: {'id': id, 'username': username, 'email': email, 'events': [], 'worker': '', 'subscriptions': []} for 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} @@ -1517,12 +1613,18 @@ def get_users(): 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 = { @@ -1535,11 +1637,17 @@ def get_users(): } 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 + # Маршрут для отображения HTML-страницы с информацией о пользователях @app.route(BASE_URL + '/users', methods=['GET']) def view_users():