@ -2,6 +2,7 @@ import json
import hashlib
import random
import secrets
import logging
from datetime import datetime , timedelta
from typing import Optional
from urllib.parse import parse_qsl , urlencode , urlparse , urlunparse
@ -13,6 +14,8 @@ from django.conf import settings
from config.redis_config import RedisConfig
from utils.exceptions import ServiceUnavailableException , NotFoundException
logger = logging . getLogger ( __name__ )
class RedisManager ( RedisConfig ) :
def __serialize ( self , code , fullname , password ) :
@ -91,23 +94,52 @@ class OnlineClassTokenManager(RedisConfig):
def generate_token ( self , course_id : int , user_identifier : str ) - > str :
seed = f " {course_id}:{user_identifier}:{secrets.token_urlsafe(16)} "
return hashlib . sha256 ( seed . encode ( ) ) . hexdigest ( )
token = hashlib . sha256 ( seed . encode ( ) ) . hexdigest ( )
logger . info ( f " [OnlineClassToken] Token generated - course_id={course_id} user={user_identifier} token={token[:16]}... " )
return token
def store_token ( self , token : str , payload : dict , ttl : Optional [ int ] = None ) - > None :
data = {
* * payload ,
" generated_at " : datetime . utcnow ( ) . isoformat ( ) + " Z " ,
}
self . redis . set ( self . _build_key ( token ) , json . dumps ( data ) , ex = ttl or self . ttl )
key = self . _build_key ( token )
ttl_value = ttl or self . ttl
logger . info ( f " [OnlineClassToken] Storing token - key={key} ttl={ttl_value}s payload={payload} " )
try :
self . redis . set ( key , json . dumps ( data ) , ex = ttl_value )
logger . info ( f " [OnlineClassToken] Token stored successfully - key={key} " )
except RedisError as e :
logger . error ( f " [OnlineClassToken] Failed to store token - key={key} error={str(e)} " )
raise
def get_payload ( self , token : str ) - > dict :
stored = self . redis . get ( self . _build_key ( token ) )
if not stored :
raise NotFoundException ( " Token not found or has expired. " )
return json . loads ( stored )
key = self . _build_key ( token )
logger . info ( f " [OnlineClassToken] Retrieving token - key={key} token={token[:16]}... " )
try :
stored = self . redis . get ( key )
if not stored :
logger . warning ( f " [OnlineClassToken] Token not found or expired - key={key} " )
raise NotFoundException ( " Token not found or has expired. " )
payload = json . loads ( stored )
logger . info ( f " [OnlineClassToken] Token retrieved successfully - key={key} payload={payload} " )
return payload
except RedisError as e :
logger . error ( f " [OnlineClassToken] Redis error retrieving token - key={key} error={str(e)} " )
raise
except json . JSONDecodeError as e :
logger . error ( f " [OnlineClassToken] Invalid JSON in stored token - key={key} error={str(e)} " )
raise NotFoundException ( " Invalid token data. " )
def delete_token ( self , token : str ) - > None :
self . redis . delete ( self . _build_key ( token ) )
key = self . _build_key ( token )
logger . info ( f " [OnlineClassToken] Deleting token - key={key} " )
try :
result = self . redis . delete ( key )
logger . info ( f " [OnlineClassToken] Token deleted - key={key} deleted={result} " )
except RedisError as e :
logger . error ( f " [OnlineClassToken] Failed to delete token - key={key} error={str(e)} " )
raise
@staticmethod
def build_entry_url ( token : str , base_url : Optional [ str ] = None ) - > str :