""" Nakama Moderation Backend - Python FastAPI """ import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.trustedhost import TrustedHostMiddleware import uvicorn from config import settings from database import connect_db, close_db # Import from middleware.py file (not directory) import middleware as app_middleware from routes.mod_app import router as mod_app_router from routes.moderation_auth import router as moderation_auth_router from websocket_server import get_socketio_app from utils.minio_client import init_minio @asynccontextmanager async def lifespan(app: FastAPI): """Lifecycle management""" # Startup print("\n" + "=" * 60) print("🚀 Запуск сервера модерации (Python)...") await connect_db() # Initialize MinIO if settings.MINIO_ENABLED: init_minio() # Log Socket.IO status if socketio_app: print("✅ WebSocket (Socket.IO) доступен") print(" 📡 Namespace /mod-chat доступен для чата модераторов") print(" 🔌 WebSocket endpoint: /socket.io") else: print("⚠️ WebSocket (Socket.IO) недоступен - чат модераторов не будет работать") print("=" * 60 + "\n") yield # Shutdown print("\n" + "=" * 60) print("🛑 Остановка сервера модерации...") await close_db() print("=" * 60 + "\n") app = FastAPI( title="Nakama Moderation API", description="API для модерации Nakama", version="2.0.0", lifespan=lifespan ) # CORS app.add_middleware( CORSMiddleware, allow_origins=settings.MODERATION_CORS_ORIGIN.split(',') if settings.MODERATION_CORS_ORIGIN != '*' else ['*'], allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["Content-Type", "Authorization", "x-telegram-init-data"], max_age=86400, ) # Setup middleware (security, logging, etc.) app_middleware.setup_middleware(app) # Health check @app.get("/health") async def health_check(): return { "status": "ok", "service": "moderation", "version": "2.0.0-python" } # Root endpoint @app.get("/") async def root(): return {"message": "Nakama Moderation API - Python"} # Include routers app.include_router(mod_app_router, prefix="/api/mod-app", tags=["moderation"]) app.include_router(moderation_auth_router, prefix="/api/moderation-auth", tags=["auth"]) # Socket.IO будет обернут для правильной работы # Создаем обернутое приложение для Socket.IO # Socket.IO обрабатывает пути /socket.io, остальное идет в FastAPI socketio_app = None try: socketio_app = get_socketio_app() except Exception as e: # Ошибка инициализации Socket.IO - используем только FastAPI socketio_app = None # Создаем обернутое приложение для Socket.IO # Socket.IO обрабатывает пути /socket.io, остальное идет в FastAPI class SocketIOWrapper: """Wrapper to combine FastAPI and Socket.IO ASGI apps""" def __init__(self, fastapi_app, socketio_app): self.fastapi_app = fastapi_app self.socketio_app = socketio_app async def __call__(self, scope, receive, send): path = scope.get("path", "") scope_type = scope.get("type", "") # Логируем для отладки print(f"[SocketIOWrapper] Request: type={scope_type}, path={path}, method={scope.get('method', 'N/A')}") # Если это WebSocket или путь начинается с /socket.io, используем Socket.IO if scope_type == "websocket" or path.startswith("/socket.io"): print(f"[SocketIOWrapper] ✅ Routing to Socket.IO: type={scope_type}, path={path}") try: await self.socketio_app(scope, receive, send) except Exception as e: print(f"[SocketIOWrapper] ❌ Error in Socket.IO: {e}") import traceback traceback.print_exc() raise else: # Иначе используем FastAPI print(f"[SocketIOWrapper] → Routing to FastAPI: type={scope_type}, path={path}") await self.fastapi_app(scope, receive, send) # Создаем обернутое приложение (доступно для uvicorn) # Это должно быть на уровне модуля, чтобы uvicorn мог импортировать if socketio_app: wrapped_app = SocketIOWrapper(app, socketio_app) else: # Если Socket.IO не инициализирован, используем только FastAPI wrapped_app = app if __name__ == "__main__": print("\n" + "=" * 60) print("✅ Сервер модерации запущен (Python)") print(f" 🌐 API: http://0.0.0.0:{settings.MODERATION_PORT}/api") print(f" 🔌 WebSocket: http://0.0.0.0:{settings.MODERATION_PORT}/socket.io") print(f" 📦 MongoDB: {settings.MONGODB_URI.split('@')[-1] if '@' in settings.MONGODB_URI else settings.MONGODB_URI}") print("=" * 60 + "\n") uvicorn.run( wrapped_app, # Используем обернутое приложение host="0.0.0.0", port=settings.MODERATION_PORT, reload=settings.IS_DEVELOPMENT, log_level="info" )