- Removed 11 duplicate router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py) - Removed duplicate core files (logging_config.py, settings.py) - Removed unused services (card_mirror_service.py, mtgjson_loader.py, mtgjson_uploader.py) - Removed unused schemas (proto_messages.py, protocol_constants.py) - Created run.py startup script for simple execution - Created app/core/config.py for unified configuration management - Created app/core/logging.py for logging configuration - Consolidated routers into 6 focused files (auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py) - Updated all imports to use consolidated model paths - Added backward compatibility aliases in database.py - Created SIMPLIFICATION_SUMMARY.md documentation Architecture improvements: - Single unified database engine with auto-detection (SQLite/PostgreSQL) - Consolidated models into 2 files (models.py, mtg_models.py) - Clean router structure with one file per domain - Simplified configuration with sensible defaults - SQLite by default for development, PostgreSQL for production Testing: - All imports verified working - App loads successfully - Ready for development and testing
98 lines
2.6 KiB
Python
98 lines
2.6 KiB
Python
"""Redis client and caching layer (optional)."""
|
|
import json
|
|
import logging
|
|
from typing import Any, Optional
|
|
|
|
import redis.asyncio as aioredis
|
|
from app.core.config import get_config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
config = get_config()
|
|
|
|
# Redis client instance
|
|
redis_client: Optional[aioredis.Redis] = None
|
|
|
|
|
|
async def get_redis() -> Optional[aioredis.Redis]:
|
|
"""Get Redis client instance.
|
|
|
|
Returns None if Redis is not configured or connection fails.
|
|
"""
|
|
global redis_client
|
|
if redis_client is None:
|
|
redis_url = getattr(config, 'REDIS_URL', None)
|
|
if not redis_url:
|
|
logger.debug("Redis not configured")
|
|
return None
|
|
try:
|
|
redis_client = aioredis.from_url(
|
|
redis_url,
|
|
decode_responses=True,
|
|
socket_connect_timeout=10,
|
|
socket_timeout=10,
|
|
)
|
|
await redis_client.ping()
|
|
logger.info("Connected to Redis")
|
|
except Exception as e:
|
|
logger.warning(f"Failed to connect to Redis: {e}")
|
|
redis_client = None
|
|
return redis_client
|
|
|
|
|
|
async def close_redis():
|
|
"""Close Redis connection."""
|
|
global redis_client
|
|
if redis_client:
|
|
await redis_client.close()
|
|
redis_client = None
|
|
logger.info("Closed Redis connection")
|
|
|
|
|
|
async def cache_get(key: str) -> Optional[str]:
|
|
"""Get cached value."""
|
|
try:
|
|
client = await get_redis()
|
|
if not client:
|
|
return None
|
|
value = await client.get(key)
|
|
return value
|
|
except Exception as e:
|
|
logger.error(f"Cache get error for key {key}: {e}")
|
|
return None
|
|
|
|
|
|
async def cache_set(key: str, value: str, ttl: int = 3600):
|
|
"""Set cached value with TTL (default 1 hour)."""
|
|
try:
|
|
client = await get_redis()
|
|
if not client:
|
|
return
|
|
await client.set(key, value, ex=ttl)
|
|
except Exception as e:
|
|
logger.error(f"Cache set error for key {key}: {e}")
|
|
|
|
|
|
async def cache_delete(key: str):
|
|
"""Delete cached value."""
|
|
try:
|
|
client = await get_redis()
|
|
if not client:
|
|
return
|
|
await client.delete(key)
|
|
except Exception as e:
|
|
logger.error(f"Cache delete error for key {key}: {e}")
|
|
|
|
|
|
async def cache_invalidate_pattern(pattern: str):
|
|
"""Invalidate all cached keys matching pattern."""
|
|
try:
|
|
client = await get_redis()
|
|
if not client:
|
|
return
|
|
keys = await client.keys(pattern)
|
|
if keys:
|
|
await client.delete(*keys)
|
|
except Exception as e:
|
|
logger.error(f"Cache invalidate error for pattern {pattern}: {e}")
|