mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-26 22:31:44 +00:00
- Add pyproject.toml with uv and ruff configuration - Pin Python version to 3.13 via .python-version - Add Makefile commands: lint, format, fix - Apply ruff formatting to entire codebase - Remove unused imports (base64 in yookassa/simple_subscription) - Update .gitignore for new config files
187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
from __future__ import annotations
|
||
|
||
import logging
|
||
from pathlib import Path
|
||
|
||
from aiogram import Bot, Dispatcher
|
||
from fastapi import FastAPI, status
|
||
from fastapi.responses import JSONResponse, RedirectResponse
|
||
from fastapi.staticfiles import StaticFiles
|
||
|
||
from app.cabinet.routes import router as cabinet_router
|
||
from app.config import settings
|
||
from app.services.payment_service import PaymentService
|
||
from app.webapi.app import create_web_api_app
|
||
from app.webapi.docs import add_redoc_endpoint
|
||
|
||
from . import payments, telegram
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def _attach_docs_alias(app: FastAPI, docs_url: str | None) -> None:
|
||
if not docs_url:
|
||
return
|
||
|
||
alias_path = '/doc'
|
||
if alias_path == docs_url:
|
||
return
|
||
|
||
for route in app.router.routes:
|
||
if getattr(route, 'path', None) == alias_path:
|
||
return
|
||
|
||
target_url = docs_url
|
||
|
||
@app.get(alias_path, include_in_schema=False)
|
||
async def redirect_doc() -> RedirectResponse: # pragma: no cover - simple redirect
|
||
return RedirectResponse(url=target_url, status_code=status.HTTP_307_TEMPORARY_REDIRECT)
|
||
|
||
|
||
def _create_base_app() -> FastAPI:
|
||
docs_config = settings.get_web_api_docs_config()
|
||
|
||
if settings.is_web_api_enabled():
|
||
app = create_web_api_app()
|
||
else:
|
||
app = FastAPI(
|
||
title='Bedolaga Unified Server',
|
||
version=settings.WEB_API_VERSION,
|
||
docs_url=docs_config.get('docs_url'),
|
||
redoc_url=None,
|
||
openapi_url=docs_config.get('openapi_url'),
|
||
)
|
||
|
||
add_redoc_endpoint(
|
||
app,
|
||
redoc_url=docs_config.get('redoc_url'),
|
||
openapi_url=docs_config.get('openapi_url'),
|
||
title='Bedolaga Unified Server',
|
||
)
|
||
|
||
# Add cabinet routes even when web API is disabled
|
||
if settings.is_cabinet_enabled():
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
|
||
cabinet_origins = settings.get_cabinet_allowed_origins()
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=['*'] if '*' in cabinet_origins else cabinet_origins,
|
||
allow_credentials=True,
|
||
allow_methods=['*'],
|
||
allow_headers=['*'],
|
||
)
|
||
app.include_router(cabinet_router)
|
||
|
||
_attach_docs_alias(app, app.docs_url)
|
||
return app
|
||
|
||
|
||
def _mount_miniapp_static(app: FastAPI) -> tuple[bool, Path]:
|
||
static_path: Path = settings.get_miniapp_static_path()
|
||
if not static_path.exists():
|
||
logger.debug('Miniapp static path %s does not exist, skipping mount', static_path)
|
||
return False, static_path
|
||
|
||
try:
|
||
app.mount('/miniapp/static', StaticFiles(directory=static_path), name='miniapp-static')
|
||
logger.info('📦 Miniapp static files mounted at /miniapp/static from %s', static_path)
|
||
except RuntimeError as error: # pragma: no cover - defensive guard
|
||
logger.warning('Не удалось смонтировать статические файлы миниаппа: %s', error)
|
||
return False, static_path
|
||
|
||
return True, static_path
|
||
|
||
|
||
def create_unified_app(
|
||
bot: Bot,
|
||
dispatcher: Dispatcher,
|
||
payment_service: PaymentService,
|
||
*,
|
||
enable_telegram_webhook: bool,
|
||
) -> FastAPI:
|
||
app = _create_base_app()
|
||
|
||
app.state.bot = bot
|
||
app.state.dispatcher = dispatcher
|
||
app.state.payment_service = payment_service
|
||
|
||
payments_router = payments.create_payment_router(bot, payment_service)
|
||
if payments_router:
|
||
app.include_router(payments_router)
|
||
payment_providers_state = {
|
||
'tribute': settings.TRIBUTE_ENABLED,
|
||
'mulenpay': settings.is_mulenpay_enabled(),
|
||
'cryptobot': settings.is_cryptobot_enabled(),
|
||
'yookassa': settings.is_yookassa_enabled(),
|
||
'pal24': settings.is_pal24_enabled(),
|
||
'wata': settings.is_wata_enabled(),
|
||
'heleket': settings.is_heleket_enabled(),
|
||
'freekassa': settings.is_freekassa_enabled(),
|
||
}
|
||
|
||
if enable_telegram_webhook:
|
||
telegram_processor = telegram.TelegramWebhookProcessor(
|
||
bot=bot,
|
||
dispatcher=dispatcher,
|
||
queue_maxsize=settings.get_webhook_queue_maxsize(),
|
||
worker_count=settings.get_webhook_worker_count(),
|
||
enqueue_timeout=settings.get_webhook_enqueue_timeout(),
|
||
shutdown_timeout=settings.get_webhook_shutdown_timeout(),
|
||
)
|
||
app.state.telegram_webhook_processor = telegram_processor
|
||
|
||
@app.on_event('startup')
|
||
async def start_telegram_webhook_processor() -> None: # pragma: no cover - event hook
|
||
await telegram_processor.start()
|
||
|
||
@app.on_event('shutdown')
|
||
async def stop_telegram_webhook_processor() -> None: # pragma: no cover - event hook
|
||
await telegram_processor.stop()
|
||
|
||
app.include_router(telegram.create_telegram_router(bot, dispatcher, processor=telegram_processor))
|
||
else:
|
||
telegram_processor = None
|
||
|
||
miniapp_mounted, miniapp_path = _mount_miniapp_static(app)
|
||
|
||
unified_health_path = '/health/unified' if settings.is_web_api_enabled() else '/health'
|
||
|
||
@app.get(unified_health_path)
|
||
async def unified_health() -> JSONResponse:
|
||
webhook_path = settings.get_telegram_webhook_path() if enable_telegram_webhook else None
|
||
|
||
telegram_state = {
|
||
'enabled': enable_telegram_webhook,
|
||
'running': bool(telegram_processor and telegram_processor.is_running),
|
||
'url': settings.get_telegram_webhook_url(),
|
||
'path': webhook_path,
|
||
'secret_configured': bool(settings.WEBHOOK_SECRET_TOKEN),
|
||
'queue_maxsize': settings.get_webhook_queue_maxsize(),
|
||
'workers': settings.get_webhook_worker_count(),
|
||
}
|
||
|
||
payment_state = {
|
||
'enabled': bool(payments_router),
|
||
'providers': payment_providers_state,
|
||
}
|
||
|
||
miniapp_state = {
|
||
'mounted': miniapp_mounted,
|
||
'path': str(miniapp_path),
|
||
}
|
||
|
||
return JSONResponse(
|
||
{
|
||
'status': 'ok',
|
||
'bot_run_mode': settings.get_bot_run_mode(),
|
||
'web_api_enabled': settings.is_web_api_enabled(),
|
||
'payment_webhooks': payment_state,
|
||
'telegram_webhook': telegram_state,
|
||
'miniapp_static': miniapp_state,
|
||
}
|
||
)
|
||
|
||
return app
|