2025-10-24 12:02:17 +03:00
"""
Конфигурация приложения
"""
2026-01-29 16:12:48 +03:00
import os
Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)
- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
/api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).
Подробности в CHANGELOG_PROFILE_VALIDATION.md.
2026-02-27 18:31:41 +03:00
import json
2025-11-14 19:06:36 +03:00
from pathlib import Path
2025-10-24 12:02:17 +03:00
from pydantic_settings import BaseSettings
2026-01-29 16:12:48 +03:00
from typing import List , Optional
2025-10-24 12:02:17 +03:00
2025-11-14 19:06:36 +03:00
BASE_DIR = Path ( __file__ ) . resolve ( ) . parents [ 2 ]
ENV_PATH = BASE_DIR / " .env "
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
2025-11-14 19:06:36 +03:00
2025-10-24 12:02:17 +03:00
class Settings ( BaseSettings ) :
2025-10-24 16:19:58 +03:00
# ============================================
# APPLICATION
# ============================================
2025-11-14 19:06:36 +03:00
app_name : str = " Ticket Form Intake Platform "
2025-10-24 12:02:17 +03:00
app_env : str = " development "
debug : bool = True
# API
api_v1_prefix : str = " /api/v1 "
2025-11-14 19:06:36 +03:00
backend_url : str = " http://localhost:8200 "
frontend_url : str = " http://localhost:5175 "
2025-10-24 12:02:17 +03:00
2025-10-24 16:19:58 +03:00
# ============================================
# DATABASE (PostgreSQL)
# ============================================
2025-10-24 12:02:17 +03:00
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 "
2025-10-24 20:27:10 +03:00
# ============================================
# MYSQL (для проверки полисов ERV)
# ============================================
mysql_host : str = " localhost "
mysql_port : int = 3306
mysql_db : str = " u2768571_crm_db "
mysql_user : str = " root "
mysql_password : str = " "
2025-12-04 12:22:23 +03:00
# ============================================
# 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 "
2025-10-24 16:19:58 +03:00
@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 } "
# ============================================
2026-02-24 16:17:59 +03:00
# REDIS (внешний — события, буферы, SMS и т.д.)
2025-10-24 16:19:58 +03:00
# ============================================
2025-10-24 12:02:17 +03:00
redis_host : str = " localhost "
redis_port : int = 6379
redis_password : str = " CRM_Redis_Pass_2025_Secure! "
redis_db : int = 0
2025-11-14 19:06:36 +03:00
redis_prefix : str = " ticket_form: "
2026-02-24 16:17:59 +03:00
# 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
2025-10-24 12:02:17 +03:00
2025-10-24 16:19:58 +03:00
@property
def redis_url ( self ) - > str :
2026-02-24 16:17:59 +03:00
""" Формирует URL для подключения к Redis (внешний) """
2025-10-24 16:19:58 +03:00
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 } "
2026-02-24 16:17:59 +03:00
@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 } "
2025-10-24 16:19:58 +03:00
# ============================================
# RABBITMQ
# ============================================
2025-10-24 12:02:17 +03:00
rabbitmq_host : str = " 185.197.75.249 "
rabbitmq_port : int = 5672
rabbitmq_user : str = " admin "
rabbitmq_password : str = " tyejvtej "
rabbitmq_vhost : str = " / "
2025-10-24 16:19:58 +03:00
@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
# ============================================
2025-10-24 12:02:17 +03:00
ocr_api_url : str = " http://147.45.146.17:8001 "
2025-10-24 16:19:58 +03:00
ocr_api_key : str = " "
2025-10-24 12:02:17 +03:00
2025-10-24 16:19:58 +03:00
# ============================================
# AI SERVICE (OpenRouter)
# ============================================
2025-10-24 12:02:17 +03:00
openrouter_api_key : str = " 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 "
2025-10-24 16:19:58 +03:00
openrouter_base_url : str = " https://openrouter.ai/api/v1 "
2025-10-24 12:02:17 +03:00
openrouter_model : str = " google/gemini-2.0-flash-001 "
2025-10-24 16:19:58 +03:00
# ============================================
# FLIGHT APIs
# ============================================
2025-10-24 12:02:17 +03:00
# FlightAware
flightaware_api_key : str = " Puz0cdxAHzAEqMRZwtdeqBUSm9naJfwK "
2025-10-24 16:19:58 +03:00
flightaware_base_url : str = " https://aeroapi.flightaware.com/aeroapi "
# AviationStack (резервный)
aviationstack_api_key : str = " "
aviationstack_base_url : str = " http://api.aviationstack.com/v1 "
# ============================================
Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)
- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
/api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).
Подробности в CHANGELOG_PROFILE_VALIDATION.md.
2026-02-27 18:31:41 +03:00
# NSPK BANKS API (и альтернативный BANK_IP из .env)
2025-10-24 16:19:58 +03:00
# ============================================
nspk_banks_api_url : str = " https://qr.nspk.ru/proxyapp/c2bmembers.json "
Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)
- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
/api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).
Подробности в CHANGELOG_PROFILE_VALIDATION.md.
2026-02-27 18:31:41 +03:00
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
2025-10-24 16:19:58 +03:00
# ============================================
# 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
2025-10-24 12:02:17 +03:00
2025-10-24 16:19:58 +03:00
# ============================================
# 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 ( " , " ) ]
# ============================================
2025-10-24 12:02:17 +03:00
# CORS
2025-10-24 16:19:58 +03:00
# ============================================
2025-11-14 19:06:36 +03:00
cors_origins : str = " http://localhost:5175,http://127.0.0.1:5175,http://147.45.146.17:5175 "
2025-10-24 16:19:58 +03:00
@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
2025-10-29 16:49:03 +03:00
# ============================================
2025-12-29 01:19:19 +03:00
# N8N API & WEBHOOKS
2025-10-29 16:49:03 +03:00
# ============================================
2025-12-29 01:19:19 +03:00
n8n_url : str = " https://n8n.clientright.pro "
n8n_api_key : str = " " # Нужно задать в .env
2025-10-29 16:49:03 +03:00
n8n_policy_check_webhook : str = " "
n8n_file_upload_webhook : str = " "
2026-01-02 17:37:37 +03:00
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 "
2026-02-24 16:17:59 +03:00
n8n_description_webhook : str = " https://n8n.clientright.ru/webhook/ticket_form_description " # Webhook для описания проблемы (переопределяется через N8N_DESCRIPTION_WEBHOOK в .env)
# Wizard и финальная отправка заявки (create) — один webhook, меняется через .env
n8n_ticket_form_final_webhook : str = " https://n8n.clientright.pro/webhook/ecc93306-fadc-489a-afdb-d3e981013df3 "
2026-01-29 16:12:48 +03:00
n8n_tg_auth_webhook : str = " " # Webhook для авторизации пользователей Telegram WebApp (Mini App)
2026-02-24 16:17:59 +03:00
# Контактные данные из CRM для раздела «Профиль» (массив или пусто)
n8n_contact_webhook : str = " " # N8N_CONTACT_WEBHOOK в .env
2026-02-27 08:34:27 +03:00
n8n_profile_update_webhook : str = " " # N8N_PROFILE_UPDATE_WEBHOOK в .env — обновление профиля (verification=0)
2026-02-24 16:17:59 +03:00
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
# ============================================
Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)
- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
/api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).
Подробности в CHANGELOG_PROFILE_VALIDATION.md.
2026-02-27 18:31:41 +03:00
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 ] :
Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)
- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
/api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).
Подробности в CHANGELOG_PROFILE_VALIDATION.md.
2026-02-27 18:31:41 +03:00
""" Список (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
2026-02-24 16:17:59 +03:00
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
2025-10-24 16:19:58 +03:00
# ============================================
# LOGGING
# ============================================
log_level : str = " INFO "
2025-11-14 19:06:36 +03:00
log_file : str = " /app/logs/ticket_form_backend.log "
2025-10-24 12:02:17 +03:00
class Config :
2025-11-14 19:06:36 +03:00
env_file = str ( ENV_PATH )
2025-10-24 12:02:17 +03:00
case_sensitive = False
2025-10-24 16:19:58 +03:00
extra = " ignore " # Игнорируем лишние поля из .env
2025-10-24 12:02:17 +03:00
def get_settings ( ) - > Settings :
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
2025-10-24 12:02:17 +03:00
settings = get_settings ( )
2025-10-24 16:19:58 +03:00