import os import logging import asyncio from dotenv import load_dotenv from aiohttp import web from providers import create_providers from cache import Cache from errors import ErrorLogger from router import Router from http_proxy import setup_routes from ws_proxy import setup_ws_routes @web.middleware async def cors_middleware(request, handler): """Add CORS headers to all responses""" if request.method == 'OPTIONS': # Handle preflight requests return web.Response(headers={ 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': '*', 'Access-Control-Max-Age': '86400' }) response = await handler(request) response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS' response.headers['Access-Control-Allow-Headers'] = '*' return response def load_config() -> dict: load_dotenv() return { "rpc_port": int(os.getenv("RPC_PORT", 8545)), "ws_port": int(os.getenv("WS_PORT", 8546)), "cache_size_gb": int(os.getenv("CACHE_SIZE_GB", 100)), "backoff_minutes": int(os.getenv("BACKOFF_MINUTES", 30)), "log_level": os.getenv("LOG_LEVEL", "INFO"), "error_db_path": os.getenv("ERROR_DB_PATH", "./errors.db"), } def setup_logging(log_level: str) -> None: logging.basicConfig( level=getattr(logging, log_level.upper()), format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) def create_rpc_app(config: dict, router: Router) -> web.Application: app = web.Application(middlewares=[cors_middleware]) app['router'] = router app['config'] = config setup_routes(app) return app def create_ws_app(config: dict, router: Router) -> web.Application: app = web.Application(middlewares=[cors_middleware]) app['router'] = router app['config'] = config setup_ws_routes(app) return app async def run_servers(config: dict) -> None: # Create shared components providers = create_providers() cache = Cache(size_limit_gb=config["cache_size_gb"]) error_logger = ErrorLogger(db_path=config["error_db_path"]) router = Router(providers, cache, error_logger) # Create separate apps rpc_app = create_rpc_app(config, router) ws_app = create_ws_app(config, router) # Create runners rpc_runner = web.AppRunner(rpc_app) ws_runner = web.AppRunner(ws_app) await rpc_runner.setup() await ws_runner.setup() # Create sites rpc_site = web.TCPSite(rpc_runner, '0.0.0.0', config["rpc_port"]) ws_site = web.TCPSite(ws_runner, '0.0.0.0', config["ws_port"]) # Start both servers await rpc_site.start() await ws_site.start() logger = logging.getLogger(__name__) logger.info(f"RPC server started on port {config['rpc_port']}") logger.info(f"WebSocket server started on port {config['ws_port']}") logger.info(f"Cache size limit: {config['cache_size_gb']}GB") logger.info(f"Provider backoff time: {config['backoff_minutes']} minutes") # Keep servers running try: await asyncio.Event().wait() except KeyboardInterrupt: pass finally: await rpc_runner.cleanup() await ws_runner.cleanup() def main() -> None: config = load_config() setup_logging(config["log_level"]) asyncio.run(run_servers(config)) if __name__ == "__main__": main()