Starting phase 2
This commit is contained in:
parent
65c1954db5
commit
b5d2605d99
182
server/main.py
182
server/main.py
@ -58,23 +58,27 @@ if not public_url.endswith("/"):
|
|||||||
|
|
||||||
ADMIN_TOKEN = os.getenv("ADMIN_TOKEN", None)
|
ADMIN_TOKEN = os.getenv("ADMIN_TOKEN", None)
|
||||||
|
|
||||||
|
# Create FastAPI app first
|
||||||
|
app = FastAPI(
|
||||||
|
title="AI Voice Bot Server (Refactored)",
|
||||||
|
description="WebRTC voice chat server with modular architecture",
|
||||||
|
version="2.0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Starting server with public URL: {public_url}")
|
||||||
|
|
||||||
|
|
||||||
# Global managers - these replace the global variables from original main.py
|
# Global managers - these replace the global variables from original main.py
|
||||||
session_manager: SessionManager = None
|
session_manager: SessionManager = None
|
||||||
lobby_manager: LobbyManager = None
|
lobby_manager: LobbyManager = None
|
||||||
auth_manager: AuthManager = None
|
auth_manager: AuthManager = None
|
||||||
websocket_manager: WebSocketConnectionManager = None
|
websocket_manager: WebSocketConnectionManager = None
|
||||||
|
|
||||||
# API routers
|
|
||||||
admin_api: AdminAPI = None
|
|
||||||
session_api: SessionAPI = None
|
|
||||||
lobby_api: LobbyAPI = None
|
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
"""Lifespan context manager for startup and shutdown events"""
|
"""Lifespan context manager for startup and shutdown events"""
|
||||||
global session_manager, lobby_manager, auth_manager, websocket_manager
|
global session_manager, lobby_manager, auth_manager, websocket_manager
|
||||||
global admin_api, session_api, lobby_api
|
|
||||||
|
|
||||||
# Startup
|
# Startup
|
||||||
logger.info("Starting AI Voice Bot server with modular architecture...")
|
logger.info("Starting AI Voice Bot server with modular architecture...")
|
||||||
@ -88,14 +92,11 @@ async def lifespan(app: FastAPI):
|
|||||||
session_manager.load()
|
session_manager.load()
|
||||||
|
|
||||||
# Restore lobbies for existing sessions
|
# Restore lobbies for existing sessions
|
||||||
# Note: This is a simplified version - full lobby restoration would be more complex
|
|
||||||
for session in session_manager.get_all_sessions():
|
for session in session_manager.get_all_sessions():
|
||||||
for lobby_info in session.lobbies:
|
for lobby_info in session.lobbies:
|
||||||
# Create lobby if it doesn't exist
|
|
||||||
lobby = lobby_manager.create_or_get_lobby(
|
lobby = lobby_manager.create_or_get_lobby(
|
||||||
name=lobby_info.name, private=lobby_info.private
|
name=lobby_info.name, private=lobby_info.private
|
||||||
)
|
)
|
||||||
# Add session to lobby (but don't trigger events during startup)
|
|
||||||
with lobby.lock:
|
with lobby.lock:
|
||||||
lobby.sessions[session.id] = session
|
lobby.sessions[session.id] = session
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ async def lifespan(app: FastAPI):
|
|||||||
auth_manager=auth_manager,
|
auth_manager=auth_manager,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Initialize API routers
|
# Create and register API routes
|
||||||
admin_api = AdminAPI(
|
admin_api = AdminAPI(
|
||||||
session_manager=session_manager,
|
session_manager=session_manager,
|
||||||
lobby_manager=lobby_manager,
|
lobby_manager=lobby_manager,
|
||||||
@ -126,92 +127,23 @@ async def lifespan(app: FastAPI):
|
|||||||
public_url=public_url,
|
public_url=public_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register API routes
|
# Register API routes during startup
|
||||||
app.include_router(admin_api.router)
|
app.include_router(admin_api.router)
|
||||||
app.include_router(session_api.router)
|
app.include_router(session_api.router)
|
||||||
app.include_router(lobby_api.router)
|
app.include_router(lobby_api.router)
|
||||||
|
|
||||||
# Start background tasks
|
# Register static file serving AFTER API routes to avoid conflicts
|
||||||
await session_manager.start_background_tasks()
|
PRODUCTION = os.getenv("PRODUCTION", "false").lower() == "true"
|
||||||
|
client_build_path = os.path.join(os.path.dirname(__file__), "/client/build")
|
||||||
|
|
||||||
logger.info("AI Voice Bot server started successfully!")
|
if PRODUCTION:
|
||||||
logger.info(f"Server URL: {public_url}")
|
|
||||||
logger.info(f"Sessions loaded: {session_manager.get_session_count()}")
|
|
||||||
logger.info(f"Lobbies available: {lobby_manager.get_lobby_count()}")
|
|
||||||
logger.info(f"Protected names: {auth_manager.get_protection_count()}")
|
|
||||||
|
|
||||||
if ADMIN_TOKEN:
|
|
||||||
logger.info("Admin endpoints protected with token")
|
|
||||||
else:
|
|
||||||
logger.warning("Admin endpoints are unprotected")
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
# Shutdown
|
|
||||||
logger.info("Shutting down AI Voice Bot server...")
|
|
||||||
|
|
||||||
# Stop background tasks
|
|
||||||
if session_manager:
|
|
||||||
await session_manager.stop_background_tasks()
|
|
||||||
|
|
||||||
logger.info("Server shutdown complete")
|
|
||||||
|
|
||||||
|
|
||||||
# Create FastAPI app
|
|
||||||
app = FastAPI(
|
|
||||||
title="AI Voice Bot Server (Refactored)",
|
|
||||||
description="WebRTC voice chat server with modular architecture",
|
|
||||||
version="2.0.0",
|
|
||||||
lifespan=lifespan,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(f"Starting server with public URL: {public_url}")
|
|
||||||
|
|
||||||
|
|
||||||
@app.websocket(f"{public_url}" + "ws/lobby/{lobby_id}/{session_id}")
|
|
||||||
async def lobby_websocket(
|
|
||||||
websocket: WebSocket,
|
|
||||||
lobby_id: str | None = Path(...),
|
|
||||||
session_id: str | None = Path(...),
|
|
||||||
):
|
|
||||||
"""WebSocket endpoint for lobby connections - now uses WebSocketConnectionManager"""
|
|
||||||
await websocket_manager.handle_connection(websocket, lobby_id, session_id)
|
|
||||||
|
|
||||||
|
|
||||||
# Health check for the new architecture
|
|
||||||
@app.get(f"{public_url}api/system/health")
|
|
||||||
def system_health():
|
|
||||||
"""System health check showing manager status"""
|
|
||||||
return {
|
|
||||||
"status": "ok",
|
|
||||||
"architecture": "modular",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"managers": {
|
|
||||||
"session_manager": "active" if session_manager else "inactive",
|
|
||||||
"lobby_manager": "active" if lobby_manager else "inactive",
|
|
||||||
"auth_manager": "active" if auth_manager else "inactive",
|
|
||||||
"websocket_manager": "active" if websocket_manager else "inactive",
|
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"sessions": session_manager.get_session_count() if session_manager else 0,
|
|
||||||
"lobbies": lobby_manager.get_lobby_count() if lobby_manager else 0,
|
|
||||||
"protected_names": auth_manager.get_protection_count()
|
|
||||||
if auth_manager
|
|
||||||
else 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Serve static files or proxy to frontend development server
|
|
||||||
PRODUCTION = os.getenv("PRODUCTION", "false").lower() == "true"
|
|
||||||
client_build_path = os.path.join(os.path.dirname(__file__), "/client/build")
|
|
||||||
|
|
||||||
if PRODUCTION:
|
|
||||||
logger.info(f"Serving static files from: {client_build_path} at {public_url}")
|
logger.info(f"Serving static files from: {client_build_path} at {public_url}")
|
||||||
app.mount(
|
app.mount(
|
||||||
public_url, StaticFiles(directory=client_build_path, html=True), name="static"
|
public_url,
|
||||||
|
StaticFiles(directory=client_build_path, html=True),
|
||||||
|
name="static",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.info(f"Proxying static files to http://client:3000 at {public_url}")
|
logger.info(f"Proxying static files to http://client:3000 at {public_url}")
|
||||||
|
|
||||||
import ssl
|
import ssl
|
||||||
@ -233,7 +165,10 @@ else:
|
|||||||
# Accept self-signed certs in dev
|
# Accept self-signed certs in dev
|
||||||
async with httpx.AsyncClient(verify=False) as client:
|
async with httpx.AsyncClient(verify=False) as client:
|
||||||
proxy_req = client.build_request(
|
proxy_req = client.build_request(
|
||||||
request.method, url, headers=headers, content=await request.body()
|
request.method,
|
||||||
|
url,
|
||||||
|
headers=headers,
|
||||||
|
content=await request.body(),
|
||||||
)
|
)
|
||||||
proxy_resp = await client.send(proxy_req, stream=True)
|
proxy_resp = await client.send(proxy_req, stream=True)
|
||||||
content = await proxy_resp.aread()
|
content = await proxy_resp.aread()
|
||||||
@ -243,7 +178,11 @@ else:
|
|||||||
k: v
|
k: v
|
||||||
for k, v in proxy_resp.headers.items()
|
for k, v in proxy_resp.headers.items()
|
||||||
if k.lower()
|
if k.lower()
|
||||||
not in ["content-encoding", "transfer-encoding", "content-length"]
|
not in [
|
||||||
|
"content-encoding",
|
||||||
|
"transfer-encoding",
|
||||||
|
"content-length",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return Response(
|
return Response(
|
||||||
content=content,
|
content=content,
|
||||||
@ -292,6 +231,69 @@ else:
|
|||||||
logger.error(f"REACT: WebSocket proxy error: {e}")
|
logger.error(f"REACT: WebSocket proxy error: {e}")
|
||||||
await websocket.close()
|
await websocket.close()
|
||||||
|
|
||||||
|
# Start background tasks
|
||||||
|
await session_manager.start_background_tasks()
|
||||||
|
|
||||||
|
logger.info("AI Voice Bot server started successfully!")
|
||||||
|
logger.info(f"Server URL: {public_url}")
|
||||||
|
logger.info(f"Sessions loaded: {session_manager.get_session_count()}")
|
||||||
|
logger.info(f"Lobbies available: {lobby_manager.get_lobby_count()}")
|
||||||
|
logger.info(f"Protected names: {auth_manager.get_protection_count()}")
|
||||||
|
|
||||||
|
if ADMIN_TOKEN:
|
||||||
|
logger.info("Admin endpoints protected with token")
|
||||||
|
else:
|
||||||
|
logger.warning("Admin endpoints are unprotected")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Shutdown
|
||||||
|
logger.info("Shutting down AI Voice Bot server...")
|
||||||
|
|
||||||
|
# Stop background tasks
|
||||||
|
if session_manager:
|
||||||
|
await session_manager.stop_background_tasks()
|
||||||
|
|
||||||
|
logger.info("Server shutdown complete")
|
||||||
|
|
||||||
|
|
||||||
|
# Set the lifespan
|
||||||
|
app.router.lifespan_context = lifespan
|
||||||
|
|
||||||
|
|
||||||
|
@app.websocket(f"{public_url}" + "ws/lobby/{lobby_id}/{session_id}")
|
||||||
|
async def lobby_websocket(
|
||||||
|
websocket: WebSocket,
|
||||||
|
lobby_id: str | None = Path(...),
|
||||||
|
session_id: str | None = Path(...),
|
||||||
|
):
|
||||||
|
"""WebSocket endpoint for lobby connections - now uses WebSocketConnectionManager"""
|
||||||
|
await websocket_manager.handle_connection(websocket, lobby_id, session_id)
|
||||||
|
|
||||||
|
|
||||||
|
# Health check for the new architecture
|
||||||
|
@app.get(f"{public_url}api/system/health")
|
||||||
|
def system_health():
|
||||||
|
"""System health check showing manager status"""
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"architecture": "modular",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"managers": {
|
||||||
|
"session_manager": "active" if session_manager else "inactive",
|
||||||
|
"lobby_manager": "active" if lobby_manager else "inactive",
|
||||||
|
"auth_manager": "active" if auth_manager else "inactive",
|
||||||
|
"websocket_manager": "active" if websocket_manager else "inactive",
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"sessions": session_manager.get_session_count() if session_manager else 0,
|
||||||
|
"lobbies": lobby_manager.get_lobby_count() if lobby_manager else 0,
|
||||||
|
"protected_names": auth_manager.get_protection_count()
|
||||||
|
if auth_manager
|
||||||
|
else 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user