|
|
|
import time
|
|
|
|
from threading import Thread
|
|
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
from django.contrib.auth import logout
|
|
|
|
from django.contrib.auth.models import AnonymousUser
|
|
|
|
from rest_framework import generics
|
|
|
|
from rest_framework import status
|
|
|
|
from rest_framework.response import Response
|
|
|
|
|
|
|
|
from common.sessions.cache import user_session_manager
|
|
|
|
from common.utils import get_logger
|
|
|
|
|
|
|
|
__all__ = ['UserSessionApi']
|
|
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class UserSessionManager:
|
|
|
|
|
|
|
|
def __init__(self, request):
|
|
|
|
self.request = request
|
|
|
|
self.session = request.session
|
|
|
|
|
|
|
|
def connect(self):
|
|
|
|
user_session_manager.add_or_increment(self.session.session_key)
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
user_session_manager.decrement(self.session.session_key)
|
|
|
|
if self.should_delete_session():
|
|
|
|
thread = Thread(target=self.delay_delete_session)
|
|
|
|
thread.start()
|
|
|
|
|
|
|
|
def should_delete_session(self):
|
|
|
|
return (self.session.modified or settings.SESSION_SAVE_EVERY_REQUEST) and \
|
|
|
|
not self.session.is_empty() and \
|
|
|
|
self.session.get_expire_at_browser_close() and \
|
|
|
|
not user_session_manager.check_active(self.session.session_key)
|
|
|
|
|
|
|
|
def delay_delete_session(self):
|
|
|
|
timeout = 6
|
|
|
|
check_interval = 0.5
|
|
|
|
|
|
|
|
start_time = time.time()
|
|
|
|
while time.time() - start_time < timeout:
|
|
|
|
time.sleep(check_interval)
|
|
|
|
if user_session_manager.check_active(self.session.session_key):
|
|
|
|
return
|
|
|
|
|
|
|
|
logout(self.request)
|
|
|
|
|
|
|
|
|
|
|
|
class UserSessionApi(generics.RetrieveDestroyAPIView):
|
|
|
|
permission_classes = ()
|
|
|
|
|
|
|
|
def retrieve(self, request, *args, **kwargs):
|
|
|
|
if isinstance(request.user, AnonymousUser):
|
|
|
|
return Response(status=status.HTTP_403_FORBIDDEN)
|
|
|
|
|
|
|
|
UserSessionManager(request).connect()
|
|
|
|
return Response(status=status.HTTP_200_OK, data={'ok': True})
|
|
|
|
|
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
|
|
if isinstance(request.user, AnonymousUser):
|
|
|
|
return Response(status=status.HTTP_403_FORBIDDEN)
|
|
|
|
|
|
|
|
UserSessionManager(request).disconnect()
|
|
|
|
return Response(status=status.HTTP_200_OK, data={'ok': True})
|