mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
189 lines
6.3 KiB
Python
189 lines
6.3 KiB
Python
from __future__ import annotations
|
||
|
||
import logging
|
||
from pathlib import Path
|
||
|
||
from fastapi import FastAPI, status
|
||
from fastapi.responses import JSONResponse, RedirectResponse
|
||
from fastapi.staticfiles import StaticFiles
|
||
|
||
from aiogram import Bot
|
||
from aiogram import Dispatcher
|
||
|
||
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 app.cabinet.routes import router as cabinet_router
|
||
|
||
from . import payments
|
||
from . import 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
|