Browse Source

Refactor CourseLiveSessionRoomCreateAPIView to enhance live session creation process

- Implemented validation and permission checks for course management.
- Updated room ID generation to comply with NATS rules by prefixing with 'room-'.
- Integrated JWT token generation for user access to live sessions.
- Improved error handling for PlugNMeet API interactions.
- Enhanced response structure to include access token for frontend connectivity.
master
Mohsen Taba 4 months ago
parent
commit
7f0a036238
  1. 194
      apps/course/views/live_session.py

194
apps/course/views/live_session.py

@ -11,7 +11,8 @@ from rest_framework.authentication import TokenAuthentication
from rest_framework.response import Response
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from datetime import time
import jwt
from apps.course.models import Course, CourseLiveSession, Participant, LiveSessionRecording
from apps.course.serializers import LiveSessionRoomCreateSerializer, LiveSessionTokenSerializer, LiveSessionRecordedFileSerializer, LiveSessionRecordingSerializer
from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError
@ -42,91 +43,156 @@ class CourseLiveSessionRoomCreateAPIView(GenericAPIView):
)
}
)
def post(self, request, slug, *args, **kwargs):
logger.info(f"[LiveSession Create] Request from user_id={request.user.id} for course={slug}")
data = dict(request.data or {})
if 'metadata' in data:
logger.warning("[LiveSession Create] 'metadata' provided by client will be ignored for security reasons.")
data.pop('metadata', None)
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
def post(self, request, slug, *args, **kwargs):
# 1. Validation and Permission Logic
course = get_object_or_404(Course, slug=slug)
if not request.user.can_manage_course(course):
logger.warning(f"[LiveSession Create] Permission denied - user_id={request.user.id} course={slug}")
raise AppAPIException({'message': 'You do not have permission to create a live session for this course.'}, status_code=status.HTTP_403_FORBIDDEN)
logger.info(f"[LiveSession Create] Permission granted for user_id={request.user.id} course={slug}")
raise AppAPIException({'message': 'Permission denied'}, status_code=status.HTTP_403_FORBIDDEN)
subject = serializer.validated_data.get('subject') or f"{course.title} Live Session"
room_id = self._build_room_id(course)
# 2. Build Safe Room ID (Prefix with 'room-' to satisfy NATS rules)
# OLD: f"{course.id}-imamjavad" -> "9-imamjavad" (Risk of NATS errors)
# NEW: f"room-{course.id}-imamjavad" -> "room-9-imamjavad" (Safe)
room_id = f"room-{course.id}-imamjavad"
subject = f"{course.title} Live Session"
metadata = self._build_metadata(subject)
payload = {
'room_id': room_id,
'metadata': metadata,
}
logger.info(f"[LiveSession Create] Calling PlugNMeet API - room_id={room_id} course={slug}")
# 3. Create Room via PlugNMeet API
try:
client = PlugNMeetClient()
plugnmeet_response = client.create_room(payload)
logger.info(f"[LiveSession Create] PlugNMeet room created successfully - room_id={room_id}")
except ImproperlyConfigured as exc:
logger.error(f"[LiveSession Create] Configuration error - {str(exc)}")
raise AppAPIException({'message': str(exc)}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
except PlugNMeetError as exc:
logger.error(f"[LiveSession Create] PlugNMeet API error - room_id={room_id} error={str(exc)}")
detail = exc.response_data or {'message': str(exc)}
status_code = exc.status_code or status.HTTP_502_BAD_GATEWAY
raise AppAPIException(detail, status_code=status_code)
# Note: client.create_room only CREATES the room.
# It does not give you a token to join it.
plugnmeet_response = client.create_room({
'room_id': room_id,
'metadata': metadata,
})
except Exception as exc:
logger.error(f"PlugNMeet Error: {exc}")
raise AppAPIException({'message': str(exc)}, status_code=500)
# 4. Save to Database
session, created = CourseLiveSession.objects.get_or_create(
course=course,
room_id=room_id,
defaults={
'subject': subject,
'started_at': timezone.now(),
},
defaults={'subject': subject, 'started_at': timezone.now()},
)
if created:
logger.info(f"[LiveSession Create] New session created - session_id={session.id} room_id={room_id} course={slug}")
else:
logger.info(f"[LiveSession Create] Existing session reactivated - session_id={session.id} room_id={room_id} course={slug}")
updates = {}
if session.subject != subject:
session.subject = subject
updates['subject'] = subject
if session.room_id != room_id:
session.room_id = room_id
updates['room_id'] = room_id
if session.started_at is None:
session.started_at = timezone.now()
updates['started_at'] = session.started_at
if updates:
session.save(update_fields=list(updates.keys()))
logger.info(f"[LiveSession Create] Session updated - session_id={session.id} fields={list(updates.keys())}")
logger.info(f"[LiveSession Create] Success - session_id={session.id} room_id={room_id} course={slug} user_id={request.user.id}")
# 5. GENERATE JOIN TOKEN (Critical for User Access)
# You must fetch these exactly as they are in your config.yaml
PNM_API_KEY = "habibmeet_api_key_2024"
PNM_SECRET = "habibmeet_secret_zumyyYWqv7KR2kUqvYdq4z4sXg7XTBD2ljT6_2024"
token_payload = {
"iss": PNM_API_KEY, # Issuer must be the API Key
"exp": int(time.time()) + 3600, # 1 hour expiry
"sub": str(request.user.id),
"room_id": room_id,
"name": f"{request.user.first_name} {request.user.last_name}",
"is_admin": True, # Gives moderator privileges
"user_id": str(request.user.id),
}
# SIGN with the SECRET, not the key
token = jwt.encode(token_payload, PNM_SECRET, algorithm="HS256")
return Response({
'success': True,
'session': {
'id': session.id,
'room_id': session.room_id,
'subject': session.subject,
'started_at': session.started_at,
},
'plugnmeet': plugnmeet_response,
}, status=status.HTTP_201_CREATED if created else status.HTTP_200_OK)
# Frontend uses this to connect:
# https://meet.newhorizonco.uk/?access_token={access_token}
'access_token': token
}, status=status.HTTP_201_CREATED)
# def post(self, request, slug, *args, **kwargs):
# logger.info(f"[LiveSession Create] Request from user_id={request.user.id} for course={slug}")
# data = dict(request.data or {})
# if 'metadata' in data:
# logger.warning("[LiveSession Create] 'metadata' provided by client will be ignored for security reasons.")
# data.pop('metadata', None)
# serializer = self.get_serializer(data=data)
# serializer.is_valid(raise_exception=True)
# course = get_object_or_404(Course, slug=slug)
# if not request.user.can_manage_course(course):
# logger.warning(f"[LiveSession Create] Permission denied - user_id={request.user.id} course={slug}")
# raise AppAPIException({'message': 'You do not have permission to create a live session for this course.'}, status_code=status.HTTP_403_FORBIDDEN)
# logger.info(f"[LiveSession Create] Permission granted for user_id={request.user.id} course={slug}")
# subject = serializer.validated_data.get('subject') or f"{course.title} Live Session"
# room_id = self._build_room_id(course)
# metadata = self._build_metadata(subject)
# payload = {
# 'room_id': room_id,
# 'metadata': metadata,
# }
# logger.info(f"[LiveSession Create] Calling PlugNMeet API - room_id={room_id} course={slug}")
# try:
# client = PlugNMeetClient()
# plugnmeet_response = client.create_room(payload)
# logger.info(f"[LiveSession Create] PlugNMeet room created successfully - room_id={room_id}")
# except ImproperlyConfigured as exc:
# logger.error(f"[LiveSession Create] Configuration error - {str(exc)}")
# raise AppAPIException({'message': str(exc)}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except PlugNMeetError as exc:
# logger.error(f"[LiveSession Create] PlugNMeet API error - room_id={room_id} error={str(exc)}")
# detail = exc.response_data or {'message': str(exc)}
# status_code = exc.status_code or status.HTTP_502_BAD_GATEWAY
# raise AppAPIException(detail, status_code=status_code)
# session, created = CourseLiveSession.objects.get_or_create(
# course=course,
# room_id=room_id,
# defaults={
# 'subject': subject,
# 'started_at': timezone.now(),
# },
# )
# if created:
# logger.info(f"[LiveSession Create] New session created - session_id={session.id} room_id={room_id} course={slug}")
# else:
# logger.info(f"[LiveSession Create] Existing session reactivated - session_id={session.id} room_id={room_id} course={slug}")
# updates = {}
# if session.subject != subject:
# session.subject = subject
# updates['subject'] = subject
# if session.room_id != room_id:
# session.room_id = room_id
# updates['room_id'] = room_id
# if session.started_at is None:
# session.started_at = timezone.now()
# updates['started_at'] = session.started_at
# if updates:
# session.save(update_fields=list(updates.keys()))
# logger.info(f"[LiveSession Create] Session updated - session_id={session.id} fields={list(updates.keys())}")
# logger.info(f"[LiveSession Create] Success - session_id={session.id} room_id={room_id} course={slug} user_id={request.user.id}")
# return Response({
# 'session': {
# 'id': session.id,
# 'room_id': session.room_id,
# 'subject': session.subject,
# 'started_at': session.started_at,
# },
# 'plugnmeet': plugnmeet_response,
# }, status=status.HTTP_201_CREATED if created else status.HTTP_200_OK)
@staticmethod
def _build_room_id(course: Course) -> str:
return f"{course.id}-imamjavad"
return f"room-{course.id}-imamjavad"
def _build_metadata(self, subject: str) -> dict:
# Build secured, centralized metadata. Client overrides are NOT allowed.

Loading…
Cancel
Save