Browse Source

Enhance Live Session Token Handling and Room Verification

- Improved room verification logic in CourseLiveSessionTokenAPIView to handle both boolean and string responses for room activity status.
- Added functionality to automatically recreate inactive rooms for professors while denying token issuance for students if the room is inactive.
- Enhanced logging for better traceability of room status and actions taken during token requests.
- Introduced a new method to handle room recreation in PlugNMeet, ensuring sessions can be reactivated when necessary.
master
mortezaei 3 months ago
parent
commit
c11bc44c95
  1. 12
      apps/course/views/course.py
  2. 88
      apps/course/views/live_session.py

12
apps/course/views/course.py

@ -679,9 +679,19 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
client = PlugNMeetClient() client = PlugNMeetClient()
response = client.is_room_active(session.room_id) response = client.is_room_active(session.room_id)
# Debug: Log full response to understand structure
logger.debug(f"[Room Sync] PlugNMeet response - room_id={session.room_id} response={response}")
# PlugNMeet returns: {"status": true, "msg": "...", "isActive": true/false} # PlugNMeet returns: {"status": true, "msg": "...", "isActive": true/false}
is_active = response.get('isActive', False)
# Note: isActive might be boolean or string, handle both
is_active_raw = response.get('isActive', False)
is_active = is_active_raw if isinstance(is_active_raw, bool) else str(is_active_raw).lower() == 'true'
response_msg = response.get('msg', 'unknown') response_msg = response.get('msg', 'unknown')
response_status = response.get('status', False)
# Additional check: if status is true and msg says "active", trust that
if response_status and 'active' in response_msg.lower() and 'not' not in response_msg.lower():
is_active = True
if is_active: if is_active:
logger.debug(f"[Room Sync] ✓ Room verified active - room_id={session.room_id} session_id={session.id} msg={response_msg}") logger.debug(f"[Room Sync] ✓ Room verified active - room_id={session.room_id} session_id={session.id} msg={response_msg}")

88
apps/course/views/live_session.py

@ -300,21 +300,40 @@ class CourseLiveSessionTokenAPIView(GenericAPIView):
logger.warning(f"[LiveSession Token] No active session found - course={course_slug} user_id={user.id}") logger.warning(f"[LiveSession Token] No active session found - course={course_slug} user_id={user.id}")
raise AppAPIException({'message': 'No active live session found for this course.'}, status_code=status.HTTP_404_NOT_FOUND) raise AppAPIException({'message': 'No active live session found for this course.'}, status_code=status.HTTP_404_NOT_FOUND)
# Check user role first to determine permissions
is_admin = user.can_manage_course(course)
user_role = "professor" if is_admin else "student"
logger.info(f"[LiveSession Token] User role determined - user_id={user.id} role={user_role} course={course_slug}")
# CRITICAL: Verify the room is actually active in PlugNMeet before issuing token # CRITICAL: Verify the room is actually active in PlugNMeet before issuing token
# This prevents issuing tokens for rooms that have crashed or ended without webhook notification # This prevents issuing tokens for rooms that have crashed or ended without webhook notification
room_id = session.room_id room_id = session.room_id
if not self._verify_room_is_active(session):
logger.error(f"[LiveSession Token] Room not active in PlugNMeet - refusing token - room_id={room_id} session_id={session.id}")
room_is_active = self._verify_room_is_active(session)
if not room_is_active:
# Room is not active in PlugNMeet but we have a session record
if is_admin:
# For professors: Auto-recreate the room in PlugNMeet
logger.info(f"[LiveSession Token] Room inactive but professor requesting - recreating room - room_id={room_id} session_id={session.id}")
try:
self._recreate_room_in_plugnmeet(course, session)
logger.info(f"[LiveSession Token] Room recreated successfully - room_id={room_id}")
except Exception as e:
logger.error(f"[LiveSession Token] Failed to recreate room - room_id={room_id} error={str(e)}")
raise AppAPIException({
'status': 'False',
'message': f'Failed to recreate room: {str(e)}',
'msg': f'Failed to recreate room: {str(e)}'
}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
# For students: Refuse token - they cannot create rooms
logger.error(f"[LiveSession Token] Room not active and user is student - refusing token - room_id={room_id} user_id={user.id}")
raise AppAPIException({ raise AppAPIException({
'status': 'False', 'status': 'False',
'message': 'room is not active. create room first',
'msg': 'room is not active. create room first'
'message': 'room is not active. Please wait for the professor to start the class.',
'msg': 'room is not active. Please wait for the professor to start the class.'
}, status_code=status.HTTP_400_BAD_REQUEST) }, status_code=status.HTTP_400_BAD_REQUEST)
is_admin = user.can_manage_course(course)
user_role = "professor" if is_admin else "student"
logger.info(f"[LiveSession Token] User role determined - user_id={user.id} role={user_role} course={course_slug}")
if not is_admin and not Participant.objects.filter(course=course, student_id=user.id, is_active=True).exists(): if not is_admin and not Participant.objects.filter(course=course, student_id=user.id, is_active=True).exists():
logger.warning(f"[LiveSession Token] Access denied - user_id={user.id} not enrolled in course={course_slug}") logger.warning(f"[LiveSession Token] Access denied - user_id={user.id} not enrolled in course={course_slug}")
raise AppAPIException({'message': 'You do not have access to this live session.'}, status_code=status.HTTP_403_FORBIDDEN) raise AppAPIException({'message': 'You do not have access to this live session.'}, status_code=status.HTTP_403_FORBIDDEN)
@ -399,8 +418,18 @@ class CourseLiveSessionTokenAPIView(GenericAPIView):
client = PlugNMeetClient() client = PlugNMeetClient()
response = client.is_room_active(session.room_id) response = client.is_room_active(session.room_id)
is_active = response.get('isActive', False)
# Debug: Log full response
logger.debug(f"[Room Verify] PlugNMeet response - room_id={session.room_id} response={response}")
# Handle isActive as boolean or string
is_active_raw = response.get('isActive', False)
is_active = is_active_raw if isinstance(is_active_raw, bool) else str(is_active_raw).lower() == 'true'
response_msg = response.get('msg', 'unknown') response_msg = response.get('msg', 'unknown')
response_status = response.get('status', False)
# Trust status and msg if they indicate active room
if response_status and 'active' in response_msg.lower() and 'not' not in response_msg.lower():
is_active = True
if is_active: if is_active:
logger.debug(f"[Room Verify] ✓ Room is active - room_id={session.room_id} session_id={session.id}") logger.debug(f"[Room Verify] ✓ Room is active - room_id={session.room_id} session_id={session.id}")
@ -431,6 +460,47 @@ class CourseLiveSessionTokenAPIView(GenericAPIView):
logger.error(f"[Room Verify] Unexpected error - room_id={session.room_id} error={type(e).__name__}: {str(e)}") logger.error(f"[Room Verify] Unexpected error - room_id={session.room_id} error={type(e).__name__}: {str(e)}")
return False return False
def _recreate_room_in_plugnmeet(self, course: Course, session: CourseLiveSession) -> None:
"""
Recreate a room in PlugNMeet when session exists but room is inactive.
This happens when:
- Webhook failed to notify us of room closure
- PlugNMeet server restarted
- Room was manually ended
Args:
course: The course for which to recreate the room
session: The existing session to reactivate
Raises:
PlugNMeetError: If room creation fails
"""
subject = session.subject or f"{course.title} Live Session"
room_id = session.room_id
metadata = self._build_metadata(subject)
payload = {
'room_id': room_id,
'metadata': metadata,
}
logger.info(f"[Room Recreate] Recreating room in PlugNMeet - room_id={room_id} session_id={session.id}")
try:
client = PlugNMeetClient()
plugnmeet_response = client.create_room(payload)
logger.info(f"[Room Recreate] Room recreated successfully - room_id={room_id} response={plugnmeet_response}")
# Reset session ended_at to mark it as active again
session.ended_at = None
session.save(update_fields=['ended_at', 'updated_at'])
logger.info(f"[Room Recreate] Session reactivated - session_id={session.id}")
except PlugNMeetError as exc:
logger.error(f"[Room Recreate] Failed to recreate room - room_id={room_id} error={str(exc)}")
raise
@staticmethod @staticmethod
def _build_profile_url(request, user): def _build_profile_url(request, user):
avatar = getattr(user, 'avatar', None) avatar = getattr(user, 'avatar', None)

Loading…
Cancel
Save