Add source
This commit is contained in:
138
web/utils/redis_session.py
Normal file
138
web/utils/redis_session.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Redis session utilities
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
from shared.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_redis_client = None
|
||||
|
||||
|
||||
def get_redis_client():
|
||||
"""Get Redis client"""
|
||||
global _redis_client
|
||||
if _redis_client is None:
|
||||
try:
|
||||
import redis.asyncio as redis
|
||||
_redis_client = redis.Redis(
|
||||
host=settings.REDIS_HOST,
|
||||
port=settings.REDIS_PORT,
|
||||
db=settings.REDIS_DB,
|
||||
decode_responses=True
|
||||
)
|
||||
logger.info(f"Connected to Redis: {settings.REDIS_HOST}:{settings.REDIS_PORT}")
|
||||
except ImportError:
|
||||
logger.error("Redis library not installed. Install: pip install redis")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error connecting to Redis: {e}")
|
||||
return None
|
||||
return _redis_client
|
||||
|
||||
|
||||
async def create_redis_session(user_id: int) -> str:
|
||||
"""Create session in Redis"""
|
||||
import secrets
|
||||
from web.utils.csrf import generate_csrf_token
|
||||
|
||||
session_id = secrets.token_urlsafe(32)
|
||||
csrf_token = generate_csrf_token()
|
||||
|
||||
session_data = {
|
||||
"user_id": user_id,
|
||||
"is_owner": user_id == settings.OWNER_ID,
|
||||
"created_at": datetime.utcnow().isoformat(),
|
||||
"expires_at": (datetime.utcnow() + timedelta(days=7)).isoformat(),
|
||||
"csrf_token": csrf_token
|
||||
}
|
||||
|
||||
redis_client = get_redis_client()
|
||||
if not redis_client:
|
||||
logger.warning("Redis client unavailable, session not created")
|
||||
return None
|
||||
|
||||
try:
|
||||
# Save session with TTL 7 days
|
||||
await redis_client.setex(
|
||||
f"session:{session_id}",
|
||||
7 * 24 * 3600, # 7 days in seconds
|
||||
json.dumps(session_data)
|
||||
)
|
||||
logger.debug(f"Session created in Redis: {session_id}")
|
||||
return session_id
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating session in Redis: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def get_redis_session(session_id: str) -> Optional[dict]:
|
||||
"""Get session from Redis"""
|
||||
redis_client = get_redis_client()
|
||||
if not redis_client:
|
||||
return None
|
||||
|
||||
try:
|
||||
data = await redis_client.get(f"session:{session_id}")
|
||||
if data:
|
||||
session_data = json.loads(data)
|
||||
# Check expires_at
|
||||
expires_at_str = session_data.get('expires_at')
|
||||
if expires_at_str:
|
||||
try:
|
||||
expires_at = datetime.fromisoformat(expires_at_str)
|
||||
if datetime.utcnow() < expires_at:
|
||||
return session_data
|
||||
else:
|
||||
# Session expired, delete
|
||||
logger.debug(f"Session {session_id} expired in Redis (expires_at: {expires_at})")
|
||||
await delete_redis_session(session_id)
|
||||
return None
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.warning(f"Failed to parse expires_at for session {session_id}: {e}")
|
||||
# If parsing failed, consider session valid (Redis TTL will still work)
|
||||
return session_data
|
||||
else:
|
||||
# If expires_at missing, return session (Redis TTL will still work)
|
||||
return session_data
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting session from Redis: {e}")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def delete_redis_session(session_id: str) -> None:
|
||||
"""Delete session from Redis"""
|
||||
redis_client = get_redis_client()
|
||||
if redis_client:
|
||||
try:
|
||||
await redis_client.delete(f"session:{session_id}")
|
||||
logger.debug(f"Session deleted from Redis: {session_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting session from Redis: {e}")
|
||||
|
||||
|
||||
async def update_redis_session(session_id: str, session_data: dict) -> bool:
|
||||
"""Update session in Redis"""
|
||||
redis_client = get_redis_client()
|
||||
if not redis_client:
|
||||
return False
|
||||
|
||||
try:
|
||||
expires_at = datetime.fromisoformat(session_data['expires_at'])
|
||||
ttl = int((expires_at - datetime.utcnow()).total_seconds())
|
||||
if ttl > 0:
|
||||
await redis_client.setex(
|
||||
f"session:{session_id}",
|
||||
ttl,
|
||||
json.dumps(session_data)
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating session in Redis: {e}")
|
||||
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user