Add source

This commit is contained in:
2025-12-04 00:12:56 +03:00
parent b75875df5e
commit 0cb7045e7a
75 changed files with 9055 additions and 0 deletions

138
web/utils/redis_session.py Normal file
View 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