Browse Source

feat(course): sync live session status with PlugNMeet on validate

Poll PlugNMeet for room activity during online class token validation
and close inactive sessions, updating related LiveSessionUser entries.
Add PlugNMeetClient.is_room_active and improve error handling to raise
PlugNMeetError when response status is false. Includes logging and TODO
for future webhook-based sync.
master
mortezaei 7 months ago
parent
commit
839acf11f1
  1. 12
      apps/course/services/plugnmeet.py
  2. 63
      apps/course/views/course.py

12
apps/course/services/plugnmeet.py

@ -32,6 +32,9 @@ class PlugNMeetClient:
def get_join_token(self, payload: Dict[str, Any]) -> Dict[str, Any]:
return self._post("/auth/room/getJoinToken", payload)
def is_room_active(self, room_id: str) -> Dict[str, Any]:
return self._post("/auth/room/isRoomActive", {"roomId": room_id})
def _post(self, path: str, payload: Dict[str, Any]) -> Dict[str, Any]:
url = urljoin(f"{self.base_url}/", path.lstrip("/"))
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
@ -57,6 +60,15 @@ class PlugNMeetClient:
data = self._safe_json(response)
if data is None:
raise PlugNMeetError("PlugNMeet server returned an invalid response format.")
if isinstance(data, dict) and data.get('status') is False:
error_message = data.get('msg') or data.get('message') or "PlugNMeet operation failed."
raise PlugNMeetError(
error_message,
status_code=response.status_code,
response_data=data,
)
return data
def _build_signature(self, body: str) -> str:

63
apps/course/views/course.py

@ -30,9 +30,11 @@ from apps.course.models import (
CourseCategory,
CourseGlossary,
CourseLiveSession,
LiveSessionUser,
Participant,
)
from apps.course.doc import *
from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError
from apps.account.serializers import UserProfileSerializer
from utils.exceptions import AppAPIException
from utils.redis import OnlineClassTokenManager
@ -398,6 +400,10 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
course = get_object_or_404(queryset, slug=slug)
user = request.user
# TODO: This room activity check should be replaced by webhook integration in the future
# Webhook will automatically notify when room becomes inactive, eliminating polling
self._sync_room_status_with_plugnmeet(course)
course_data = CourseDetailSerializer(course, context={'request': request}).data
user_data = UserProfileSerializer(user, context={'request': request}).data
metadata = self._build_metadata(
@ -558,4 +564,59 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
return value
if timezone.is_naive(value):
value = timezone.make_aware(value, timezone.get_current_timezone())
return timezone.localtime(value).isoformat()
return timezone.localtime(value).isoformat()
def _sync_room_status_with_plugnmeet(self, course: Course):
"""
Check if active live session's room is still active in PlugNMeet.
If room is inactive, close the session and all related user entries.
TODO: This should be replaced by webhook integration in the future.
PlugNMeet should send webhooks when rooms end, eliminating the need for polling.
"""
active_session = CourseLiveSession.objects.filter(
course=course,
ended_at__isnull=True
).first()
if not active_session or not active_session.room_id:
return
try:
client = PlugNMeetClient()
response = client.is_room_active(active_session.room_id)
is_active = response.get('isActive', False)
if not is_active:
logger.info(f"[Room Sync] Room inactive in PlugNMeet - room_id={active_session.room_id} session_id={active_session.id}")
self._close_live_session(active_session)
else:
logger.debug(f"[Room Sync] Room still active - room_id={active_session.room_id} session_id={active_session.id}")
except (PlugNMeetError, Exception) as e:
logger.warning(f"[Room Sync] Failed to check room status - room_id={active_session.room_id} error={str(e)}")
@staticmethod
def _close_live_session(session: CourseLiveSession):
"""
Close a live session and all related user entries.
Sets ended_at for session and exited_at/is_online for users.
"""
now = timezone.now()
session.ended_at = now
session.save(update_fields=['ended_at', 'updated_at'])
logger.info(f"[Room Sync] Session closed - session_id={session.id} room_id={session.room_id} ended_at={now}")
updated_count = LiveSessionUser.objects.filter(
session=session,
is_online=True,
exited_at__isnull=True
).update(
is_online=False,
exited_at=now,
updated_at=now
)
if updated_count > 0:
logger.info(f"[Room Sync] User sessions closed - session_id={session.id} count={updated_count}")
Loading…
Cancel
Save