Browse Source

Enhance logging in CourseOnlineClassTokenValidateAPIView and OnlineClassTokenManager for better traceability

- Added detailed logging for GET and POST requests in CourseOnlineClassTokenValidateAPIView to track user actions and course validation.
- Improved logging in OnlineClassTokenManager for token generation, storage, retrieval, and deletion processes, including error handling for Redis interactions.
master
mortezaei 3 months ago
parent
commit
49283428c5
  1. 22
      apps/course/views/course.py
  2. 42
      utils/redis.py

22
apps/course/views/course.py

@ -452,11 +452,15 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
}
)
def get(self, request, slug, *args, **kwargs):
logger.info(f"[Online Validate GET] Request received - slug={slug} user_id={request.user.id if request.user.is_authenticated else 'anonymous'}")
detail_view = CourseDetailAPIView()
queryset = detail_view.get_queryset()
course = get_object_or_404(queryset, slug=slug)
user = request.user
logger.info(f"[Online Validate GET] Course found - course_id={course.id} slug={slug} is_online={course.is_online}")
# DEPRECATED: Polling approach replaced by webhook integration
# Room status is now updated automatically via PlugNMeet webhooks
# self._sync_room_status_with_plugnmeet(course)
@ -469,6 +473,8 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
user=user,
)
logger.info(f"[Online Validate GET] Success - user_id={user.id} course={slug} can_create={metadata.get('can_create_live_session')} can_join={metadata.get('can_join_live_session')}")
return Response({
'course': course_data,
'user': user_data,
@ -498,41 +504,43 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
}
)
def post(self, request, *args, **kwargs):
logger.info(f"[Online Validate] Request received")
logger.info(f"[Online Validate POST] Request received - has_token={'token' in request.data}")
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
token_value = serializer.validated_data['token']
logger.info(f"[Online Validate POST] Token extracted - token={token_value[:16]}...")
manager = OnlineClassTokenManager()
try:
payload = manager.get_payload(token_value)
logger.info(f"[Online Validate] Token decoded successfully")
logger.info(f"[Online Validate POST] Token decoded successfully - payload={payload}")
except Exception as e:
logger.error(f"[Online Validate] Token decode failed - error={str(e)}")
logger.error(f"[Online Validate POST] Token decode failed - error={str(e)} type={type(e).__name__}")
raise
course_id = payload.get('course_id')
user_id = payload.get('user_id')
if not course_id or not user_id:
logger.warning(f"[Online Validate] Invalid token payload - course_id={course_id} user_id={user_id}")
logger.warning(f"[Online Validate POST] Invalid token payload - course_id={course_id} user_id={user_id}")
raise AppAPIException({'message': 'Token payload is invalid.'}, status_code=status.HTTP_400_BAD_REQUEST)
logger.info(f"[Online Validate] Processing for user_id={user_id} course_id={course_id}")
logger.info(f"[Online Validate POST] Processing for user_id={user_id} course_id={course_id}")
detail_view = CourseDetailAPIView()
queryset = detail_view.get_queryset()
course = get_object_or_404(queryset, pk=course_id)
user = get_object_or_404(UserModel.objects.all(), pk=user_id)
logger.info(f"[Online Validate] Course found - slug={course.slug} is_online={course.is_online}")
logger.info(f"[Online Validate POST] Course found - slug={course.slug} is_online={course.is_online}")
course_data = CourseDetailSerializer(course, context={'request': request}).data
user_data = UserProfileSerializer(user, context={'request': request}).data
metadata = self._build_metadata(course, payload, user=user)
logger.info(f"[Online Validate] Success - user_id={user_id} course={course.slug} can_create={metadata.get('can_create_live_session')} can_join={metadata.get('can_join_live_session')}")
logger.info(f"[Online Validate POST] Success - user_id={user_id} course={course.slug} can_create={metadata.get('can_create_live_session')} can_join={metadata.get('can_join_live_session')}")
return Response({
'course': course_data,

42
utils/redis.py

@ -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))
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.")
return json.loads(stored)
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:

Loading…
Cancel
Save