Files
aiform_prod/backend/app/config.py

295 lines
13 KiB
Python
Raw Normal View History

"""
Конфигурация приложения
"""
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
import os
import json
from pathlib import Path
from pydantic_settings import BaseSettings
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
from typing import List, Optional
BASE_DIR = Path(__file__).resolve().parents[2]
ENV_PATH = BASE_DIR / ".env"
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
# Список CORS, обновляется при изменении .env (чтобы не перезапускать бэкенд)
_cors_origins_live: List[str] = []
_settings_cache: Optional["Settings"] = None
_env_mtime_cache: float = 0
class Settings(BaseSettings):
# ============================================
# APPLICATION
# ============================================
app_name: str = "Ticket Form Intake Platform"
app_env: str = "development"
debug: bool = True
# API
api_v1_prefix: str = "/api/v1"
backend_url: str = "http://localhost:8200"
frontend_url: str = "http://localhost:5175"
# ============================================
# DATABASE (PostgreSQL)
# ============================================
postgres_host: str = "147.45.189.234"
postgres_port: int = 5432
postgres_db: str = "default_db"
postgres_user: str = "gen_user"
postgres_password: str = "2~~9_^kVsU?2\\S"
# ============================================
# MYSQL (для проверки полисов ERV)
# ============================================
mysql_host: str = "localhost"
mysql_port: int = 3306
mysql_db: str = "u2768571_crm_db"
mysql_user: str = "root"
mysql_password: str = ""
# ============================================
# MYSQL CRM (vtiger CRM)
# ============================================
mysql_crm_host: str = "localhost" # В режиме network_mode: host используем localhost # Доступ к хосту из Docker контейнера
mysql_crm_port: int = 3306
mysql_crm_db: str = "ci20465_72new"
mysql_crm_user: str = "ci20465_72new"
mysql_crm_password: str = "EcY979Rn"
@property
def database_url(self) -> str:
"""Формирует URL для подключения к PostgreSQL"""
return f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
# ============================================
# REDIS (внешний — события, буферы, SMS и т.д.)
# ============================================
redis_host: str = "localhost"
redis_port: int = 6379
redis_password: str = "CRM_Redis_Pass_2025_Secure!"
redis_db: int = 0
redis_prefix: str = "ticket_form:"
# Redis для сессий (локальный в Docker — miniapp_redis; снаружи — localhost:6383 или свой)
redis_session_host: str = "localhost"
redis_session_port: int = 6383
redis_session_password: str = ""
redis_session_db: int = 0
@property
def redis_url(self) -> str:
"""Формирует URL для подключения к Redis (внешний)"""
if self.redis_password:
return f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}"
return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}"
@property
def redis_session_url(self) -> str:
"""URL для локального Redis сессий"""
if self.redis_session_password:
return f"redis://:{self.redis_session_password}@{self.redis_session_host}:{self.redis_session_port}/{self.redis_session_db}"
return f"redis://{self.redis_session_host}:{self.redis_session_port}/{self.redis_session_db}"
# ============================================
# RABBITMQ
# ============================================
rabbitmq_host: str = "185.197.75.249"
rabbitmq_port: int = 5672
rabbitmq_user: str = "admin"
rabbitmq_password: str = "tyejvtej"
rabbitmq_vhost: str = "/"
@property
def rabbitmq_url(self) -> str:
"""Формирует URL для подключения к RabbitMQ"""
return f"amqp://{self.rabbitmq_user}:{self.rabbitmq_password}@{self.rabbitmq_host}:{self.rabbitmq_port}{self.rabbitmq_vhost}"
# ============================================
# S3 STORAGE (Timeweb Cloud Storage)
# ============================================
s3_endpoint: str = "https://s3.timeweb.com"
s3_bucket: str = "erv-platform-files"
s3_access_key: str = "your_access_key_here"
s3_secret_key: str = "your_secret_key_here"
s3_region: str = "ru-1"
# ============================================
# OCR SERVICE
# ============================================
ocr_api_url: str = "http://147.45.146.17:8001"
ocr_api_key: str = ""
# ============================================
# AI SERVICE (OpenRouter)
# ============================================
openrouter_api_key: str = "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"
openrouter_base_url: str = "https://openrouter.ai/api/v1"
openrouter_model: str = "google/gemini-2.0-flash-001"
# ============================================
# FLIGHT APIs
# ============================================
# FlightAware
flightaware_api_key: str = "Puz0cdxAHzAEqMRZwtdeqBUSm9naJfwK"
flightaware_base_url: str = "https://aeroapi.flightaware.com/aeroapi"
# AviationStack (резервный)
aviationstack_api_key: str = ""
aviationstack_base_url: str = "http://api.aviationstack.com/v1"
# ============================================
# NSPK BANKS API (и альтернативный BANK_IP из .env)
# ============================================
nspk_banks_api_url: str = "https://qr.nspk.ru/proxyapp/c2bmembers.json"
bank_ip: str = "http://212.193.27.93/api/payouts/dictionaries/nspk-banks"
bank_api_url: str = "http://212.193.27.93/api/payouts/dictionaries/nspk-banks"
# ============================================
# DADATA (подсказки адресов в форме профиля)
# ============================================
forma_dadata_api_key: str = "" # FORMA_DADATA_API_KEY
forma_dadata_secret: str = "" # FORMA_DADATA_SECRET
# ============================================
# SMS SERVICE (SigmaSMS)
# ============================================
sms_api_url: str = "https://online.sigmasms.ru/api/"
sms_login: str = ""
sms_password: str = ""
sms_token: str = ""
sms_sender: str = "lexpriority"
sms_enabled: bool = True
# ============================================
# VTIGER CRM (PHP Bridge)
# ============================================
crm_webservice_url: str = "http://crm.clientright.ru/webservice.php"
crm_webform_url: str = "https://crm.clientright.ru/modules/Webforms/capture.php"
crm_token: str = ""
# ============================================
# RATE LIMITING
# ============================================
rate_limit_per_minute: int = 60
rate_limit_per_hour: int = 1000
# ============================================
# FILE UPLOAD
# ============================================
max_upload_size_mb: int = 50
allowed_file_extensions: str = "pdf,jpg,jpeg,png,heic,heif,webp"
@property
def allowed_extensions_list(self) -> List[str]:
"""Список разрешенных расширений файлов"""
return [ext.strip() for ext in self.allowed_file_extensions.split(",")]
# ============================================
# CORS
# ============================================
cors_origins: str = "http://localhost:5175,http://127.0.0.1:5175,http://147.45.146.17:5175"
@property
def cors_origins_list(self) -> List[str]:
"""Список CORS origins"""
if isinstance(self.cors_origins, str):
return [origin.strip() for origin in self.cors_origins.split(",")]
return self.cors_origins
# ============================================
# N8N API & WEBHOOKS
# ============================================
n8n_url: str = "https://n8n.clientright.pro"
n8n_api_key: str = "" # Нужно задать в .env
n8n_policy_check_webhook: str = ""
n8n_file_upload_webhook: str = ""
n8n_create_contact_webhook: str = "https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27"
n8n_create_claim_webhook: str = "https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d"
n8n_description_webhook: str = "https://n8n.clientright.ru/webhook/ticket_form_description" # Webhook для описания проблемы (переопределяется через N8N_DESCRIPTION_WEBHOOK в .env)
# Консультации: тикеты из CRM (MySQL) — тот же payload, что и у других хуков
n8n_ticket_form_consultation_webhook: str = "" # N8N_TICKET_FORM_CONSULTATION_WEBHOOK в .env
# Подробнее по тикету: session + ticket_id → ответ вебхука (HTML/JSON)
n8n_ticket_form_podrobnee_webhook: str = "" # N8N_TICKET_FORM_PODROBNEE_WEBHOOK в .env
# Wizard и финальная отправка заявки (create) — один webhook, меняется через .env
n8n_ticket_form_final_webhook: str = "https://n8n.clientright.pro/webhook/ecc93306-fadc-489a-afdb-d3e981013df3"
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
n8n_tg_auth_webhook: str = "" # Webhook для авторизации пользователей Telegram WebApp (Mini App)
# Контактные данные из CRM для раздела «Профиль» (массив или пусто)
n8n_contact_webhook: str = "" # N8N_CONTACT_WEBHOOK в .env
n8n_profile_update_webhook: str = "" # N8N_PROFILE_UPDATE_WEBHOOK в .env — обновление профиля (verification=0)
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
# ============================================
# TELEGRAM BOT
# ============================================
telegram_bot_token: str = "" # Токен бота для проверки initData WebApp
2026-02-20 09:31:13 +03:00
2026-02-27 07:48:16 +03:00
def get_telegram_bot_tokens(self) -> List[tuple]:
"""Список (bot_id, token) для проверки подписи Telegram initData. Один токен — [('default', token)]."""
token = (self.telegram_bot_token or "").strip()
if token:
return [("default", token)]
return []
2026-02-20 09:31:13 +03:00
# ============================================
# MAX (мессенджер) — Mini App auth
# ============================================
max_bot_token: str = "" # Токен бота MAX (один бот)
max_bot_tokens: str = "" # Мультибот: JSON {"bot_id": "token", ...}. Если задан — используется вместо max_bot_token.
2026-02-27 07:48:16 +03:00
def get_max_bot_tokens(self) -> List[tuple]:
"""Список (bot_id, token) для проверки подписи MAX initData. Из MAX_BOT_TOKENS (JSON) или [('default', MAX_BOT_TOKEN)]."""
s = (self.max_bot_tokens or os.environ.get("MAX_BOT_TOKENS") or "").strip()
if s:
try:
d = json.loads(s)
out = [(k, str(v).strip()) for k, v in d.items() if v and str(v).strip()]
if out:
return out
except Exception:
pass
token = (self.max_bot_token or os.environ.get("MAX_BOT_TOKEN") or "").strip()
2026-02-27 07:48:16 +03:00
if token:
return [("default", token)]
return []
2026-02-20 09:31:13 +03:00
n8n_max_auth_webhook: str = "" # Webhook n8n: max_user_id → unified_id, contact_id, has_drafts
n8n_auth_webhook: str = "" # Универсальный auth: channel + channel_user_id + init_data → unified_id, phone, contact_id, has_drafts
2026-02-20 09:31:13 +03:00
# ============================================
# LOGGING
# ============================================
log_level: str = "INFO"
log_file: str = "/app/logs/ticket_form_backend.log"
class Config:
env_file = str(ENV_PATH)
case_sensitive = False
extra = "ignore" # Игнорируем лишние поля из .env
def get_settings() -> Settings:
feat: Telegram Mini App integration and UX improvements - Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK) - Отдельный компактный дизайн для Telegram Mini App - Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации) - Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию - Telegram Mini App: кнопка "Выход" просто закрывает приложение - Telegram Mini App: заявки "В работе" скрыты из списка - Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot) - Telegram Mini App: кнопки действий в черновиках расположены вертикально - Веб-версия: убрано отображение номера телефона в приветствии - Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации) - Заблокировано удаление и редактирование заявок со статусом "В работе" - Добавлена документация по Telegram Mini App интеграции
2026-01-29 16:12:48 +03:00
"""Текущие настройки. При изменении .env подхватываются без перезапуска."""
global _settings_cache, _env_mtime_cache, _cors_origins_live
mtime = os.path.getmtime(ENV_PATH) if ENV_PATH.exists() else 0.0
if _settings_cache is None or mtime > _env_mtime_cache:
_settings_cache = Settings()
_env_mtime_cache = mtime
_cors_origins_live.clear()
_cors_origins_live.extend(_settings_cache.cors_origins_list)
return _settings_cache
def get_cors_origins_live() -> List[str]:
"""
Список CORS origins для middleware; обновляется при изменении .env без перезапуска.
Обработчики, которые используют get_settings() при каждом запросе, тоже видят новые значения.
"""
get_settings() # обновить кеш и _cors_origins_live при изменении .env
return _cors_origins_live
settings = get_settings()