Telezab/backend_flask.py

153 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
import sqlite3
from flask import Flask, request, jsonify, redirect, url_for
from flask_login import LoginManager
import config
from frontend.dashboard import bp_dashboard
from backend.api import bp_api
from backend.auth import bp_auth, User
from backend_locks import db_lock
from config import DB_PATH, TZ
from utilities.database import db
from utilities.telegram_utilities import extract_region_number, format_message
login_manager = LoginManager()
def create_app():
app = Flask(__name__, static_url_path='/telezab/static', template_folder='templates')
app.config['SECRET_KEY'] = config.SECRET_KEY # Замените на надежный секретный ключ
app.config['SESSION_COOKIE_SECURE'] = config.SESSION_COOKIE_SECURE # Убедитесь, что установлено значение True
app.config['SESSION_COOKIE_HTTPONLY'] = config.SESSION_COOKIE_HTTPONLY # Убедитесь, что установлено значение True
app.config['SESSION_COOKIE_SAMESITE'] = config.SESSION_COOKIE_SAMESITE
app.config['SESSION_REFRESH_EACH_REQUEST'] = False
app.config['PERMANENT_SESSION_LIFETIME'] = config.PERMANENT_SESSION_LIFETIME
app.config['SESSION_COOKIE_MAX_AGE'] = 3600
app.config['TIMEZONE'] = TZ
@login_manager.unauthorized_handler
def unauthorized():
logging.debug("Unauthorized access detected")
if request.path.startswith('/telezab/rest/api'):
return jsonify({'error': 'Не авторизован'}), 401
else:
return redirect(url_for('auth.login'))
app.register_blueprint(bp_dashboard)
app.register_blueprint(bp_auth)
app.register_blueprint(bp_api)
app.config['SQLALCHEMY_DATABASE_URI'] = config.SQLALCHEMY_DATABASE_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
with app.app_context():
db.create_all()
login_manager.init_app(app) # Инициализация login_manager
@login_manager.user_loader
def load_user(user_id):
return User(user_id)
return app
app = create_app()
@app.route('/telezab/webhook', methods=['POST'])
def webhook():
try:
# Получаем данные и логируем
data = request.get_json()
app.logger.info(f"Получены данные: {data}")
# Работа с базой данных в блоке синхронизации
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