Skip to content

Commit

Permalink
Add view for togging Django maintenance mode
Browse files Browse the repository at this point in the history
  • Loading branch information
epicserve committed Oct 9, 2023
1 parent 4231999 commit b105f84
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 1 deletion.
53 changes: 53 additions & 0 deletions apps/base/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import base64
import binascii
import json

from django.contrib.auth import authenticate
from django.http import JsonResponse
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.utils.encoding import smart_str
from django.views import generic
from django.views.decorators.csrf import csrf_exempt

from maintenance_mode.core import set_maintenance_mode


class IndexView(generic.TemplateView):
Expand All @@ -12,3 +23,45 @@ def http_500(request):

def http_404(request):
return render(request, "404.html")


class BasicAuthMixin:
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
auth_parts = request.META.get("HTTP_AUTHORIZATION", "").split()
auth_error = "Unauthorized"

if not auth_parts or len(auth_parts) != 2 or auth_parts[0].lower() != "basic":
return JsonResponse({"error": "Unauthorized"}, status=401)
else:
username_password = smart_str(auth_parts[1])
try:
username_password = base64.b64decode(username_password).decode("utf8")
username, password = username_password.split(":", 1)
user = authenticate(request, username=username, password=password)
except binascii.Error:
auth_error = (
"Invalid basic header: Username and password must be base64 encoded with a colon delimiter "
"(e.g. 'username:password')."
)
user = None

if user is not None:
request.user = user
else:
return JsonResponse({"error": auth_error}, status=401)

return super().dispatch(request, *args, **kwargs) # type: ignore


class ToggleMaintenanceModeView(BasicAuthMixin, generic.View):
# curl -X POST -H "Content-Type: application/json" -d '{"maintenance_mode": "off"}' -u "email:password" http://127.0.0.1:8000/maintenance-mode/
def post(self, request, *args, **kwargs):
data = json.loads(self.request.body.decode("utf-8"))
target_mode = data.get("maintenance_mode", None)
if target_mode is None or target_mode not in ("on", "off"):
return JsonResponse({"error": "Invalid request: maintenance_mode must be 'on' or 'off'"}, status=400)

target_mode = True if target_mode == "on" else False
set_maintenance_mode(target_mode)
return JsonResponse({"maintenance_mode": target_mode})
1 change: 1 addition & 0 deletions config/settings/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,4 @@
# MAINTENANCE MODE SETTINGS
MAINTENANCE_MODE_STATE_BACKEND = "maintenance_mode.backends.CacheBackend"
MAINTENANCE_MODE_STATE_BACKEND_FALLBACK_VALUE = True
MAINTENANCE_MODE_IGNORE_URLS = ("/maintenance-mode/",)
3 changes: 2 additions & 1 deletion config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.views.generic import TemplateView

from apps.accounts.views import NameChange
from apps.base.views import http_404, http_500
from apps.base.views import ToggleMaintenanceModeView, http_404, http_500

# Includes
urlpatterns: list[Union[URLResolver, URLPattern]] = [path(r"admin/", admin.site.urls)]
Expand All @@ -19,6 +19,7 @@
path("404/", http_404),
path("accounts/name/", NameChange.as_view(), name="account_change_name"),
path("accounts/", include("allauth.urls")),
path("maintenance-mode/", ToggleMaintenanceModeView.as_view(), name="maintenance_mode"),
]

# Debug/Development URLs
Expand Down

0 comments on commit b105f84

Please sign in to comment.