240 lines
9.4 KiB
Python
240 lines
9.4 KiB
Python
"""
|
||
API views для работы с Jitsi Meet.
|
||
"""
|
||
from rest_framework import status
|
||
from rest_framework.decorators import api_view, permission_classes
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from rest_framework.response import Response
|
||
from django.conf import settings
|
||
|
||
from .jitsi_service import JitsiService
|
||
from .models import VideoRoom
|
||
from apps.schedule.models import Lesson
|
||
from apps.users.utils import format_datetime_for_user
|
||
|
||
|
||
@api_view(['POST'])
|
||
@permission_classes([IsAuthenticated])
|
||
def create_jitsi_room(request):
|
||
"""
|
||
Создание Jitsi комнаты для занятия.
|
||
|
||
POST /api/video/jitsi/create-room/
|
||
Body: {
|
||
"lesson_id": 123
|
||
}
|
||
|
||
Returns:
|
||
{
|
||
"room_name": "uuid",
|
||
"room_url": "http://jitsi.../room",
|
||
"jwt_token": "token",
|
||
"video_room_id": 1
|
||
}
|
||
"""
|
||
lesson_id = request.data.get('lesson_id')
|
||
|
||
if not lesson_id:
|
||
return Response(
|
||
{'error': 'lesson_id обязателен'},
|
||
status=status.HTTP_400_BAD_REQUEST
|
||
)
|
||
|
||
try:
|
||
lesson = Lesson.objects.get(id=lesson_id)
|
||
except Lesson.DoesNotExist:
|
||
return Response(
|
||
{'error': 'Занятие не найдено'},
|
||
status=status.HTTP_404_NOT_FOUND
|
||
)
|
||
|
||
# Проверка прав доступа
|
||
user = request.user
|
||
if user != lesson.mentor and (not hasattr(user, 'client_profile') or user.client_profile != lesson.client):
|
||
return Response(
|
||
{'error': 'Нет доступа к этому занятию'},
|
||
status=status.HTTP_403_FORBIDDEN
|
||
)
|
||
|
||
# Проверка времени - блокируем доступ через 10 минут после фактического окончания
|
||
from django.utils import timezone
|
||
from datetime import timedelta
|
||
if lesson.end_time:
|
||
now = timezone.now()
|
||
|
||
# Используем фактическое время завершения, если оно есть, иначе запланированное
|
||
actual_end_time = lesson.completed_at if (lesson.status == 'completed' and lesson.completed_at) else lesson.end_time
|
||
allowed_end_time = actual_end_time + timedelta(minutes=10)
|
||
|
||
if now > allowed_end_time:
|
||
return Response(
|
||
{'error': 'Доступ к видеокомнате закрыт. Время занятия истекло более 10 минут назад.'},
|
||
status=status.HTTP_403_FORBIDDEN
|
||
)
|
||
|
||
# Проверяем, есть ли уже VideoRoom для этого занятия
|
||
try:
|
||
video_room = VideoRoom.objects.get(lesson=lesson)
|
||
room_name = str(video_room.room_id) # Преобразуем UUID в строку
|
||
except VideoRoom.DoesNotExist:
|
||
# Создаем новую комнату
|
||
room_name = str(JitsiService.generate_room_name()) # Преобразуем UUID в строку
|
||
# Получаем User объект клиента
|
||
client_user = lesson.client.user if hasattr(lesson.client, 'user') else lesson.client
|
||
video_room = VideoRoom.objects.create(
|
||
lesson=lesson,
|
||
mentor=lesson.mentor,
|
||
client=client_user,
|
||
room_id=room_name,
|
||
is_recording=True,
|
||
max_participants=2
|
||
)
|
||
|
||
# Определяем роль пользователя
|
||
is_moderator = user == lesson.mentor
|
||
user_role = 'mentor' if is_moderator else 'client'
|
||
|
||
# Получаем данные пользователя
|
||
user_name = user.get_full_name() or user.email
|
||
user_email = user.email
|
||
avatar_url = request.build_absolute_uri(user.avatar.url) if user.avatar else None
|
||
|
||
# Генерируем JWT токен
|
||
jwt_token = JitsiService.generate_jwt_token(
|
||
room_name=room_name,
|
||
user_id=user.id,
|
||
user_name=user_name,
|
||
user_email=user_email,
|
||
is_moderator=is_moderator,
|
||
avatar_url=avatar_url,
|
||
expires_in_minutes=180 # 3 часа
|
||
)
|
||
|
||
# Получаем config overrides
|
||
config_overrides = JitsiService.get_default_config_overrides(user_role)
|
||
|
||
# Генерируем URL комнаты
|
||
room_url = JitsiService.get_room_url(
|
||
room_name=room_name,
|
||
jwt_token=jwt_token,
|
||
config_overrides=config_overrides
|
||
)
|
||
|
||
return Response({
|
||
'room_name': str(room_name), # Преобразуем UUID в строку
|
||
'room_url': room_url,
|
||
'jwt_token': jwt_token,
|
||
'video_room_id': video_room.id,
|
||
'is_moderator': is_moderator,
|
||
'lesson': {
|
||
'id': lesson.id,
|
||
'title': lesson.title,
|
||
'start_time': format_datetime_for_user(lesson.start_time, request.user.timezone) if lesson.start_time else None,
|
||
'end_time': format_datetime_for_user(lesson.end_time, request.user.timezone) if lesson.end_time else None,
|
||
}
|
||
})
|
||
|
||
|
||
@api_view(['GET'])
|
||
@permission_classes([IsAuthenticated])
|
||
def get_jitsi_config(request):
|
||
"""
|
||
Получение конфигурации Jitsi для фронтенда.
|
||
|
||
GET /api/video/jitsi/config/
|
||
|
||
Returns:
|
||
{
|
||
"jitsi_url": "http://127.0.0.1:8443",
|
||
"domain": "meet.jitsi",
|
||
"app_name": "Платформа"
|
||
}
|
||
"""
|
||
return Response({
|
||
'jitsi_url': getattr(settings, 'JITSI_PUBLIC_URL', 'http://127.0.0.1:8443'),
|
||
'domain': getattr(settings, 'JITSI_XMPP_DOMAIN', 'meet.jitsi'),
|
||
'app_name': getattr(settings, 'JITSI_APP_NAME', 'Платформа'),
|
||
})
|
||
|
||
|
||
@api_view(['DELETE'])
|
||
@permission_classes([IsAuthenticated])
|
||
def delete_jitsi_room_by_lesson(request, lesson_id):
|
||
"""
|
||
Удаление Jitsi видеокомнаты по ID занятия.
|
||
|
||
DELETE /api/video/jitsi/rooms/lesson/{lesson_id}/
|
||
|
||
Проверяет, что прошло минимум 10 минут после окончания занятия.
|
||
Только ментор может удалять видеокомнату.
|
||
"""
|
||
from django.utils import timezone
|
||
from datetime import timedelta
|
||
|
||
try:
|
||
lesson = Lesson.objects.get(id=lesson_id)
|
||
except Lesson.DoesNotExist:
|
||
return Response(
|
||
{'error': 'Занятие не найдено'},
|
||
status=status.HTTP_404_NOT_FOUND
|
||
)
|
||
|
||
# Проверка прав доступа - только ментор может удалять
|
||
user = request.user
|
||
if user != lesson.mentor:
|
||
return Response(
|
||
{'error': 'Только ментор может удалять видеокомнату'},
|
||
status=status.HTTP_403_FORBIDDEN
|
||
)
|
||
|
||
# Проверка времени - можно удалять только через 10 минут после фактического окончания
|
||
if lesson.end_time:
|
||
now = timezone.now()
|
||
|
||
# Используем фактическое время завершения, если оно есть, иначе запланированное
|
||
actual_end_time = lesson.completed_at if (lesson.status == 'completed' and lesson.completed_at) else lesson.end_time
|
||
allowed_delete_time = actual_end_time + timedelta(minutes=10)
|
||
|
||
if now < allowed_delete_time:
|
||
minutes_remaining = int((allowed_delete_time - now).total_seconds() / 60)
|
||
return Response(
|
||
{
|
||
'error': f'Видеокомнату можно удалить только через 10 минут после окончания занятия. Осталось {minutes_remaining} минут.'
|
||
},
|
||
status=status.HTTP_400_BAD_REQUEST
|
||
)
|
||
else:
|
||
return Response(
|
||
{'error': 'Время окончания занятия не указано'},
|
||
status=status.HTTP_400_BAD_REQUEST
|
||
)
|
||
|
||
# Проверяем, есть ли VideoRoom для этого занятия
|
||
try:
|
||
video_room = VideoRoom.objects.get(lesson=lesson)
|
||
|
||
# Удаляем комнату из SFU сервера (если используется)
|
||
from .services import get_sfu_client, SFUClientError
|
||
sfu_client = get_sfu_client()
|
||
try:
|
||
sfu_client.delete_room(str(video_room.room_id))
|
||
except SFUClientError as e:
|
||
# Логируем ошибку, но продолжаем удаление из Django
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.warning(f'Не удалось удалить комнату из SFU сервера: {e}')
|
||
|
||
# Удаляем комнату из базы данных
|
||
video_room.delete()
|
||
|
||
return Response(
|
||
{'message': 'Видеокомната успешно удалена'},
|
||
status=status.HTTP_200_OK
|
||
)
|
||
except VideoRoom.DoesNotExist:
|
||
return Response(
|
||
{'error': 'Видеокомната для этого занятия не найдена'},
|
||
status=status.HTTP_404_NOT_FOUND
|
||
)
|
||
|