157 lines
5.8 KiB
Python
157 lines
5.8 KiB
Python
"""
|
||
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", "")
|
||
|
||
# Логируем для отладки (только для 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')}")
|
||
|
||
# Если это 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:
|
||
# Socket.IO ASGI app должен обработать WebSocket upgrade
|
||
await self.socketio_app(scope, receive, send)
|
||
except Exception as e:
|
||
print(f"[SocketIOWrapper] ❌ Error in Socket.IO: {type(e).__name__}: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
# Для WebSocket ошибок не поднимаем исключение, чтобы не ломать соединение
|
||
if scope_type != "websocket":
|
||
raise
|
||
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
|
||
|
||
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"
|
||
)
|
||
|