""" Django middleware to handle maintenance mode. """ import os from django.http import JsonResponse from django.urls import resolve, reverse from rest_framework.viewsets import ModelViewSet from peeringdb_server import settings def on(timeout=None): """ Turn maintenance mode on. Keyword Arguments: - timeout: if specified will automatically end maintenance mode after n seconds """ open(settings.MAINTENANCE_MODE_LOCKFILE, "ab", 0).close() def off(): """Turn maintenance mode off.""" if active(): os.remove(settings.MAINTENANCE_MODE_LOCKFILE) def active(): """Return True if maintenance mode is currently active.""" return os.path.isfile(settings.MAINTENANCE_MODE_LOCKFILE) def raise_if_active(): """Raise ActionBlocked exception if maintenance mode is active.""" if active(): raise ActionBlocked() class Middleware: """ Middleware will return 503 json responses for all write ops (POST PUT PATCH DELETE). """ def __init__(self, get_response=None): self.get_response = get_response def __call__(self, request): response = self.process_request(request) if response: return response return self.get_response(request) def process_request(self, request): if not active(): return None if request.method.lower() in ["post", "put", "patch", "delete"]: view, args, kwargs = resolve(request.path_info) if view.__name__ in ["request_login"]: # login should be allowed even in maint. mode return None elif hasattr(view, "cls") and issubclass(view.cls, ModelViewSet): # api response return JsonResponse( {"meta": {"error": str(ActionBlocked())}}, status=503 ) else: # other resolve(reverse("maintenance")) return JsonResponse( {"non_field_errors": [str(ActionBlocked())]}, status=503 ) else: return None class ActionBlocked(Exception): def __init__(self): super().__init__( "The site is currently in maintenance mode, during which this action is disabled, please try again in a few minutes" )