nakama/moderation/backend-py/main.py

157 lines
5.8 KiB
Python
Raw Normal View History

2025-12-14 23:45:41 +00:00
"""
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
2025-12-15 00:09:11 +00:00
# Import from middleware.py file (not directory)
import middleware as app_middleware
2025-12-14 23:45:41 +00:00
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()
2025-12-15 01:19:59 +00:00
# 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) недоступен - чат модераторов не будет работать")
2025-12-14 23:45:41 +00:00
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.)
2025-12-15 00:09:11 +00:00
app_middleware.setup_middleware(app)
2025-12-14 23:45:41 +00:00
# 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"])
2025-12-15 01:19:59 +00:00
# Socket.IO будет обернут для правильной работы
# Создаем обернутое приложение для Socket.IO
# Socket.IO обрабатывает пути /socket.io, остальное идет в FastAPI
socketio_app = None
2025-12-15 00:37:34 +00:00
try:
socketio_app = get_socketio_app()
except Exception as e:
2025-12-15 01:19:59 +00:00
# Ошибка инициализации 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", "")
2025-12-15 01:25:31 +00:00
scope_type = scope.get("type", "")
2025-12-15 03:40:42 +00:00
# Логируем для отладки (только для WebSocket и /socket.io)
if scope_type == "websocket" or path.startswith("/socket.io"):
print(f"[SocketIOWrapper] Request: type={scope_type}, path={path}, method={scope.get('method', 'N/A')}")
2025-12-15 01:25:31 +00:00
# Если это 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:
2025-12-15 03:40:42 +00:00
# Socket.IO ASGI app должен обработать WebSocket upgrade
2025-12-15 01:25:31 +00:00
await self.socketio_app(scope, receive, send)
except Exception as e:
2025-12-15 03:40:42 +00:00
print(f"[SocketIOWrapper] ❌ Error in Socket.IO: {type(e).__name__}: {e}")
2025-12-15 01:25:31 +00:00
import traceback
traceback.print_exc()
2025-12-15 03:40:42 +00:00
# Для WebSocket ошибок не поднимаем исключение, чтобы не ломать соединение
if scope_type != "websocket":
raise
2025-12-15 01:19:59 +00:00
else:
# Иначе используем FastAPI
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
2025-12-14 23:45:41 +00:00
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(
2025-12-15 01:16:04 +00:00
wrapped_app, # Используем обернутое приложение
2025-12-14 23:45:41 +00:00
host="0.0.0.0",
port=settings.MODERATION_PORT,
reload=settings.IS_DEVELOPMENT,
log_level="info"
)