Files
videocall-app/backend/apps/rooms/views.py
september 54a81cbdcf init
2025-09-03 06:51:24 -07:00

294 lines
9.6 KiB
Python

# rooms/views.py - Room management API views
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from django_ratelimit.decorators import ratelimit
from apps.rooms.models import RoomManager
from apps.core.views import get_client_ip
import qrcode
import io
import base64
import logging
logger = logging.getLogger(__name__)
def require_auth(view_func):
"""Decorator to require authentication for views"""
def wrapper(request, *args, **kwargs):
if not request.session.get('authenticated'):
logger.warning(f"Unauthenticated access attempt to {request.path}")
return Response(
{'error': 'Authentication required'},
status=status.HTTP_401_UNAUTHORIZED
)
return view_func(request, *args, **kwargs)
return wrapper
@api_view(['POST'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
@ratelimit(key='ip', rate='30/min', method='POST', block=True)
def create_room(request):
"""Create a new video call room"""
try:
client_ip = get_client_ip(request)
logger.info(f"Creating room for IP: {client_ip}")
room_data = RoomManager.create_room(creator_ip=client_ip)
# Generate QR code for the room
room_url = f"{request.build_absolute_uri('/')}join/{room_data['short_code']}"
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(room_url)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
buffer = io.BytesIO()
qr_image.save(buffer, format='PNG')
qr_code_data = base64.b64encode(buffer.getvalue()).decode()
response_data = {
'room_id': room_data['room_id'],
'short_code': room_data['short_code'],
'room_url': room_url,
'qr_code': f"data:image/png;base64,{qr_code_data}",
'expires_at': room_data['expires_at'],
'max_participants': room_data['max_participants']
}
logger.info(f"Room created successfully: {room_data['room_id']}")
return Response(response_data, status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(f"Room creation failed: {e}")
return Response(
{'error': 'Failed to create room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['GET'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
def get_room(request, room_id):
"""Get room information by room ID"""
try:
logger.info(f"Getting room info for: {room_id}")
room_data = RoomManager.get_room_by_id(room_id)
if not room_data:
logger.warning(f"Room not found: {room_id}")
return Response(
{'error': 'Room not found'},
status=status.HTTP_404_NOT_FOUND
)
# Check if room has expired
expires_at = timezone.datetime.fromisoformat(
room_data['expires_at'].replace('Z', '+00:00')
)
if timezone.now() > expires_at:
logger.info(f"Room expired, deleting: {room_id}")
RoomManager.delete_room(room_id)
return Response(
{'error': 'Room has expired'},
status=status.HTTP_404_NOT_FOUND
)
response_data = {
'room_id': room_data['room_id'],
'short_code': room_data['short_code'],
'is_active': room_data['is_active'],
'participant_count': len(room_data.get('participants', [])),
'max_participants': room_data.get('max_participants', 2),
'expires_at': room_data['expires_at']
}
logger.info(f"Room info retrieved: {room_id}, participants: {len(room_data.get('participants', []))}")
return Response(response_data)
except Exception as e:
logger.error(f"Failed to retrieve room {room_id}: {e}")
return Response(
{'error': 'Failed to retrieve room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['POST'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
@ratelimit(key='ip', rate='60/min', method='POST', block=True)
def join_room(request):
"""Join a room by room ID or short code"""
try:
room_identifier = request.data.get('room_identifier')
# Ensure we have a session
if not request.session.session_key:
request.session.create()
participant_id = request.session.session_key
if not room_identifier:
return Response(
{'error': 'Room identifier is required'},
status=status.HTTP_400_BAD_REQUEST
)
logger.info(f"Joining room: {room_identifier} with participant: {participant_id}")
client_ip = get_client_ip(request)
room_data, message = RoomManager.join_room(
room_identifier,
participant_id,
client_ip
)
if not room_data:
logger.warning(f"Failed to join room: {room_identifier}, reason: {message}")
return Response(
{'error': message},
status=status.HTTP_400_BAD_REQUEST
)
response_data = {
'success': True,
'message': message,
'room_id': room_data['room_id'],
'short_code': room_data['short_code'],
'participant_count': len(room_data.get('participants', [])),
'participant_id': participant_id
}
logger.info(f"User joined room: {room_data['room_id']}, participant: {participant_id}")
return Response(response_data)
except Exception as e:
logger.error(f"Failed to join room: {e}")
return Response(
{'error': 'Failed to join room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['POST'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
def leave_room(request, room_id):
"""Leave a room"""
try:
# Ensure we have a session
if not request.session.session_key:
logger.warning(f"No session key when trying to leave room: {room_id}")
return Response(
{'success': True, # Return success even if no session, as user wasn't in room anyway
'message': 'No active session found'})
participant_id = request.session.session_key
logger.info(f"Leaving room: {room_id} with participant: {participant_id}")
# Check if room exists first
room_data = RoomManager.get_room_by_id(room_id)
if not room_data:
logger.info(f"Room {room_id} not found when trying to leave")
return Response({
'success': True, # Return success as room doesn't exist anyway
'message': 'Room not found'
})
# Check if participant is actually in the room
participants = room_data.get('participants', [])
if participant_id not in participants:
logger.info(f"Participant {participant_id} not in room {room_id}")
return Response({
'success': True, # Return success as user wasn't in room anyway
'message': 'Not in room'
})
success = RoomManager.leave_room(room_id, participant_id)
if success:
logger.info(f"User left room: {room_id}, participant: {participant_id}")
return Response({
'success': True,
'message': 'Left room successfully'
})
else:
logger.warning(f"Failed to leave room: {room_id}, participant: {participant_id}")
return Response({
'success': True, # Still return success to avoid client errors
'message': 'Room leave processed'
})
except Exception as e:
logger.error(f"Failed to leave room {room_id}: {e}")
return Response(
{'error': 'Failed to leave room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['DELETE'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
def delete_room(request, room_id):
"""Delete a room (only creator or admin can delete)"""
try:
logger.info(f"Deleting room: {room_id}")
# Check if room exists
room_data = RoomManager.get_room_by_id(room_id)
if not room_data:
return Response(
{'error': 'Room not found'},
status=status.HTTP_404_NOT_FOUND
)
# Additional permission check could be added here
success = RoomManager.delete_room(room_id)
if success:
logger.info(f"Room deleted: {room_id}")
return Response({
'success': True,
'message': 'Room deleted successfully'
})
else:
return Response(
{'error': 'Failed to delete room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
except Exception as e:
logger.error(f"Failed to delete room {room_id}: {e}")
return Response(
{'error': 'Failed to delete room'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['GET'])
@permission_classes([AllowAny])
@csrf_exempt
@require_auth
def health_check(request):
"""API health check endpoint"""
return Response({
'status': 'healthy',
'timestamp': timezone.now().isoformat(),
'authenticated': True,
'session_key': request.session.session_key
})