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. 46
      utils/redis.py

22
apps/course/views/course.py

@ -452,11 +452,15 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
} }
) )
def get(self, request, slug, *args, **kwargs): 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() detail_view = CourseDetailAPIView()
queryset = detail_view.get_queryset() queryset = detail_view.get_queryset()
course = get_object_or_404(queryset, slug=slug) course = get_object_or_404(queryset, slug=slug)
user = request.user 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 # DEPRECATED: Polling approach replaced by webhook integration
# Room status is now updated automatically via PlugNMeet webhooks # Room status is now updated automatically via PlugNMeet webhooks
# self._sync_room_status_with_plugnmeet(course) # self._sync_room_status_with_plugnmeet(course)
@ -469,6 +473,8 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
user=user, 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({ return Response({
'course': course_data, 'course': course_data,
'user': user_data, 'user': user_data,
@ -498,41 +504,43 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
} }
) )
def post(self, request, *args, **kwargs): 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 = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
token_value = serializer.validated_data['token'] token_value = serializer.validated_data['token']
logger.info(f"[Online Validate POST] Token extracted - token={token_value[:16]}...")
manager = OnlineClassTokenManager() manager = OnlineClassTokenManager()
try: try:
payload = manager.get_payload(token_value) 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: 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 raise
course_id = payload.get('course_id') course_id = payload.get('course_id')
user_id = payload.get('user_id') user_id = payload.get('user_id')
if not course_id or not 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) 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() detail_view = CourseDetailAPIView()
queryset = detail_view.get_queryset() queryset = detail_view.get_queryset()
course = get_object_or_404(queryset, pk=course_id) course = get_object_or_404(queryset, pk=course_id)
user = get_object_or_404(UserModel.objects.all(), pk=user_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 course_data = CourseDetailSerializer(course, context={'request': request}).data
user_data = UserProfileSerializer(user, context={'request': request}).data user_data = UserProfileSerializer(user, context={'request': request}).data
metadata = self._build_metadata(course, payload, user=user) 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({ return Response({
'course': course_data, 'course': course_data,

46
utils/redis.py

@ -2,6 +2,7 @@ import json
import hashlib import hashlib
import random import random
import secrets import secrets
import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional from typing import Optional
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse 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 config.redis_config import RedisConfig
from utils.exceptions import ServiceUnavailableException, NotFoundException from utils.exceptions import ServiceUnavailableException, NotFoundException
logger = logging.getLogger(__name__)
class RedisManager(RedisConfig): class RedisManager(RedisConfig):
def __serialize(self, code, fullname, password): def __serialize(self, code, fullname, password):
@ -91,23 +94,52 @@ class OnlineClassTokenManager(RedisConfig):
def generate_token(self, course_id: int, user_identifier: str) -> str: def generate_token(self, course_id: int, user_identifier: str) -> str:
seed = f"{course_id}:{user_identifier}:{secrets.token_urlsafe(16)}" 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: def store_token(self, token: str, payload: dict, ttl: Optional[int] = None) -> None:
data = { data = {
**payload, **payload,
"generated_at": datetime.utcnow().isoformat() + "Z", "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: 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: 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 @staticmethod
def build_entry_url(token: str, base_url: Optional[str] = None) -> str: def build_entry_url(token: str, base_url: Optional[str] = None) -> str:

Loading…
Cancel
Save