django-maintenancemode-0.11.7+git221001/0000755000175100017510000000000014322157470017645 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/CHANGES.rst0000644000175100017510000000416714322157470021457 0ustar kittermakittermaChanges ------- 0.11.7 ~~~~~~ - Fixed broken tests 0.11.6 ~~~~~~ - Nothing 0.11.5 ~~~~~~ - Made sure the app runs on Django 3.2, dropped support for Django < 2.x. It may still work with Django 1.11, but this is no longer tested. 0.11.4 ~~~~~~ - Changed the middleware to not fetch the user instance if both ``MAINTENANCE_ALLOW_STAFF`` and ``MAINTENANCE_ALLOW_SUPERUSER`` are ``False``. - Added support for django 3.1. 0.11.3 ~~~~~~ - Added support for django 2.x, dropped support for django < 1.11. It may still work with django 1.8, but this is no longer tested. 0.11.2 ~~~~~~ - Getting ready for Django 1.10 release. - Dropped support for Django 1.3 and older. 0.11.1 ~~~~~~ - Enable network specify in INTERNAL_IPS 0.11.0 ~~~~~~ - Added management command to set maintenance mode on/off 0.10.1 ~~~~~~ - Made sure the app runs on Django 1.8. 0.10.0 ~~~~~~ - Got rid of dependency on setuptools - Added ability to exclude specific paths from maintenance mode with the ``MAINTENANCE_IGNORE_URLS`` setting. - Use RequestContext when rending the ``503.html`` template. - Use tox for running the tests instead of buildout. - Made sure the app runs on Django 1.4. 0.9.3 ~~~~~~ - Minor documentation updates for the switch to github, expect more changes to follow soon. 0.9.2 ~~~~~~ - Fixed an issue with setuptools, thanks for reporting this ksato9700 0.9.1 ~~~~~~ - Tested django-maintenancemode with django-1.0 release (following the 1.0.X release branch) - Bundled buildout.cfg and bootstrap with the source version of the project, allowing repeatable buildout - The middleware now uses its own default config file, thanks to a patch by semente - Use INTERNAL_IPS to check for users that need access. user.is_staff will stay in place for backwards incompatibility. Thanks for the idea Joshua Works - Have setup.py sdist only distribute maintenancemode itself, no longer distribute tests and buildout stuff - Use README and CHANGES in setup.py's long_description, stolen from Jeroen's djangorecipe :) - Updated the documentation and now use pypi as the documentation source (link there from google code) 0.9 ~~~~~~ First release django-maintenancemode-0.11.7+git221001/setup.cfg0000644000175100017510000000354714322157470021477 0ustar kittermakitterma[metadata] name = django-maintenancemode version = 0.11.7+git221001 description = django-maintenancemode allows you to temporary shutdown your site for maintenance work long_description = file: README.rst long_description_content_type = text/x-rst author = Remco Wendt author_email = remco@maykinmedia.nl maintainer = Basil Shubin maintainer_email = basil.shubin@gmail.com url = https://github.com/shanx/django-maintenancemode download_url = https://github.com/shanx/django-maintenancemode/zipball/master license = BSD License classifiers = Development Status :: 5 - Production/Stable Environment :: Web Environment Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Framework :: Django Framework :: Django :: 2.2 Framework :: Django :: 3.0 Framework :: Django :: 3.1 Framework :: Django :: 3.2 [options] zip_safe = False include_package_data = True packages = find: install_requires = django-appconf six>=1.9.0 ipy [options.packages.find] exclude = example* [options.extras_require] develop = tox django pytest-django pytest test = pytest-django pytest-cov pytest [bdist_wheel] # No longer universal (Python 3 only) but leaving this section in here will # trigger zest to build a wheel. universal = 0 [flake8] # Some sane defaults for the code style checker flake8 # black compatibility max-line-length = 88 # E203 and W503 have edge cases handled by black extend-ignore = E203, W503 exclude = .tox build dist .eggs [tool:pytest] DJANGO_SETTINGS_MODULE = maintenancemode.tests.settings django-maintenancemode-0.11.7+git221001/tox.ini0000644000175100017510000000071114322157470021157 0ustar kittermakitterma[tox] distribute = False envlist = py{36,37,38,39}-dj{22,30,31,32} skip_missing_interpreters = True [travis] python = 3.6: py36 3.7: py37 3.8: py38 3.9: py39 [testenv] usedevelop = True extras = test setenv = DJANGO_SETTINGS_MODULE = maintenancemode.tests.settings deps = dj22: Django>=2.2,<3.0 dj30: Django>=3.0,<3.1 dj31: Django>=3.1,<3.2 dj32: Django>=3.2,<4.0 commands = pytest --cov --cov-append --cov-report= django-maintenancemode-0.11.7+git221001/LICENSE0000644000175100017510000000306014322157470020651 0ustar kittermakittermaCopyright (c) 2008, Maykin Media Copyright (c) 2012, enn.io Copyright (c) 2015-2019, Basil Shubin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-maintenancemode-0.11.7+git221001/setup.py0000644000175100017510000000007414322157470021360 0ustar kittermakitterma#!/usr/bin/env python from setuptools import setup setup() django-maintenancemode-0.11.7+git221001/example/0000755000175100017510000000000014322157470021300 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/example/requirements.txt0000644000175100017510000000003214322157470024557 0ustar kittermakittermadjango django-appconf ipy django-maintenancemode-0.11.7+git221001/example/manage.py0000755000175100017510000000061414322157470023106 0ustar kittermakitterma#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") from django.core.management import execute_from_command_line # Allow starting the app without installing the module. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) execute_from_command_line(sys.argv) django-maintenancemode-0.11.7+git221001/example/urls.py0000644000175100017510000000017414322157470022641 0ustar kittermakittermafrom django.urls import path from .views import index urlpatterns = [ path('', index), path('ignored/', index), ] django-maintenancemode-0.11.7+git221001/example/templates/0000755000175100017510000000000014322157470023276 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/example/templates/503.html0000644000175100017510000000011114322157470024464 0ustar kittermakitterma

Temporary unavailable

You requested: {{ request_path }}

django-maintenancemode-0.11.7+git221001/example/views.py0000644000175100017510000000013114322157470023002 0ustar kittermakittermafrom django.http import HttpResponse def index(request): return HttpResponse("OK") django-maintenancemode-0.11.7+git221001/example/settings.py0000755000175100017510000000620414322157470023517 0ustar kittermakitterma""" Django settings for app project. For more information on this file, see https://docs.djangoproject.com/en/1.7/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.7/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os import re BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = "YOUR_SECRET_KEY" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition PROJECT_APPS = [ "maintenancemode", ] INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.admin", "django.contrib.sessions", "django.contrib.contenttypes", "django.contrib.messages", "django.contrib.sites", ] + PROJECT_APPS MIDDLEWARE_CLASSES = [ "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", #'django.contrib.auth.middleware.SessionAuthenticationMiddleware', "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", #'django.middleware.security.SecurityMiddleware', "maintenancemode.middleware.MaintenanceModeMiddleware", ] MIDDLEWARE = MIDDLEWARE_CLASSES ROOT_URLCONF = "example.urls" SITE_ID = 1 TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates"), ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } # Internationalization # https://docs.djangoproject.com/en/1.7/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ # Absolute filesystem path to the directory that will hold user-uploaded files. MEDIA_ROOT = os.path.join(BASE_DIR, "media") # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. MEDIA_URL = "/media/" STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") STATIC_URL = "/static/" ## Maintenance Mode settings MAINTENANCE_MODE = True # or ``False`` and use ``maintenance`` command MAINTENANCE_IGNORE_URLS = (re.compile(r"^/ignored.*"),) django-maintenancemode-0.11.7+git221001/example/README.rst0000644000175100017510000000103514322157470022766 0ustar kittermakittermaExample ======= To run the example application, make sure you have the required packages installed. You can do this using following commands : .. code-block:: bash mkvirtualenv example pip install -r example/requirements.txt This assumes you already have ``virtualenv`` and ``virtualenvwrapper`` installed and configured. Next, you can setup the django instance using : .. code-block:: bash python example/manage.py syncdb --noinput And run it : .. code-block:: bash python example/manage.py runserver Good luck! django-maintenancemode-0.11.7+git221001/example/__init__.py0000644000175100017510000000000014322157470023377 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/MANIFEST.in0000644000175100017510000000022614322157470021403 0ustar kittermakittermainclude AUTHORS include CHANGES.rst include LICENSE include README.rst recursive-exclude example * recursive-include maintenancemode templates *.html django-maintenancemode-0.11.7+git221001/AUTHORS0000644000175100017510000000023514322157470020715 0ustar kittermakittermadjango-maintenancemode is written by Remco Wendt Feel free to contact me by mail if you have any questions, or just want to say hi. django-maintenancemode-0.11.7+git221001/maintenancemode/0000755000175100017510000000000014322157470022774 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/tests/0000755000175100017510000000000014322157470024136 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/tests/urls.py0000644000175100017510000000040514322157470025474 0ustar kittermakittermafrom django.urls import re_path from django.http import HttpResponse urlpatterns = [ re_path("^$", lambda r: HttpResponse("Rendered response page"), name="test"), re_path("^ignored/$", lambda r: HttpResponse("Rendered response page"), name="test"), ] django-maintenancemode-0.11.7+git221001/maintenancemode/tests/templates/0000755000175100017510000000000014322157470026134 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/tests/templates/503.html0000644000175100017510000000007014322157470027326 0ustar kittermakittermaTemporary unavailable You requested: {{ request_path }}django-maintenancemode-0.11.7+git221001/maintenancemode/tests/conftest.py0000644000175100017510000000032114322157470026331 0ustar kittermakitterma""" Dummy conftest.py for maintenancemode. If you don't know what this is for, just leave it empty. Read more about conftest.py under: https://pytest.org/latest/plugins.html """ import pytest django-maintenancemode-0.11.7+git221001/maintenancemode/tests/settings.py0000644000175100017510000000306414322157470026353 0ustar kittermakitterma# Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os import re BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = "DUMMY_SECRET_KEY" INTERNAL_IPS = [] # Application definition PROJECT_APPS = ["maintenancemode.tests", "maintenancemode"] INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.sites", ] + PROJECT_APPS MIDDLEWARE = [ "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "maintenancemode.middleware.MaintenanceModeMiddleware", ] TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [os.path.join(BASE_DIR, "tests", "templates")], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ] }, }, ] ROOT_URLCONF = "maintenancemode.tests.urls" SITE_ID = 1 # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} MAINTENANCE_IGNORE_URLS = (re.compile(r"^/ignored.*"),) django-maintenancemode-0.11.7+git221001/maintenancemode/tests/__init__.py0000644000175100017510000000000014322157470026235 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/tests/test_middleware.py0000644000175100017510000001455014322157470027671 0ustar kittermakittermaimport re from django.core import management try: from django.utils.six import StringIO except ImportError: from six import StringIO from django.contrib.auth.models import User from django.template import TemplateDoesNotExist from django.test import TestCase from django.test.client import Client from django.test.utils import override_settings from maintenancemode import utils @override_settings(ROOT_URLCONF="maintenancemode.tests.urls", MAINTENANCE_MODE=False) class MaintenanceModeMiddlewareTestCase(TestCase): def setUp(self): utils.deactivate() # make sure maintenance mode is off self.user = User.objects.create_user( username="maintenance", email="maintenance@example.org", password="password" ) def tearDown(self): self.user.delete() def test_default_middleware(self): # Middleware should default to being disabled response = self.client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_disabled_middleware(self): # Explicitly disabling the ``MAINTENANCE_MODE`` should work with self.settings(MAINTENANCE_MODE=False): response = self.client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_enabled_middleware_without_template(self): # Enabling the middleware without a proper 503 template should # raise a template error with self.settings(MAINTENANCE_MODE=True, TEMPLATES=[]): self.assertRaises(TemplateDoesNotExist, self.client.get, "/") def test_enabled_middleware_with_template(self): # Enabling the middleware having a ``503.html`` in any of the # template locations should return the rendered template" with self.settings(MAINTENANCE_MODE=True): response = self.client.get("/") self.assertContains(response, text="Temporary unavailable", count=1, status_code=503) self.assertContains(response, text="You requested: /", count=1, status_code=503) def test_middleware_with_non_staff_user(self): # A logged in user that is not a staff user should see the 503 message self.client.login(username="maintenance", password="password") with self.settings(MAINTENANCE_MODE=True): response = self.client.get("/") self.assertContains(response, text="Temporary unavailable", count=1, status_code=503) def test_middleware_with_staff_user(self): # A logged in user that _is_ a staff user should be able to # use the site normally User.objects.filter(pk=self.user.pk).update(is_staff=True) self.client.login(username="maintenance", password="password") with self.settings(MAINTENANCE_MODE=True): response = self.client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_middleware_with_staff_user_denied(self): # A logged in user that _is_ a staff user should be able to # use the site normally User.objects.filter(pk=self.user.pk).update(is_staff=True) self.client.login(username="maintenance", password="password") with self.settings(MAINTENANCE_MODE=True, MAINTENANCE_ALLOW_STAFF=False): response = self.client.get("/") self.assertContains(response, text="Temporary unavailable", count=1, status_code=503) def test_middleware_with_superuser_user_denied(self): # A logged in user that _is_ a staff user should be able to # use the site normally User.objects.filter(pk=self.user.pk).update(is_superuser=True) self.client.login(username="maintenance", password="password") with self.settings(MAINTENANCE_MODE=True, MAINTENANCE_ALLOW_SUPERUSER=False): response = self.client.get("/") self.assertContains(response, text="Temporary unavailable", count=1, status_code=503) def test_middleware_with_superuser_user_allowed(self): # A logged in user that _is_ a staff user should be able to # use the site normally User.objects.filter(pk=self.user.pk).update(is_superuser=True) self.client.login(username="maintenance", password="password") with self.settings(MAINTENANCE_MODE=True, MAINTENANCE_ALLOW_STAFF=False): response = self.client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_middleware_with_internal_ips(self): # A user that visits the site from an IP in ``INTERNAL_IPS`` # should be able to use the site normally # Use a new Client instance to be able to set the REMOTE_ADDR used by INTERNAL_IPS client = Client(REMOTE_ADDR="127.0.0.1") with self.settings(MAINTENANCE_MODE=True, INTERNAL_IPS=("127.0.0.1",)): response = client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_middleware_with_internal_ips_range(self): client = Client(REMOTE_ADDR="10.10.10.1") with self.settings(MAINTENANCE_MODE=True, INTERNAL_IPS=("10.10.10.0/24",)): response = client.get("/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_ignored_path(self): # A path is ignored when applying the maintanance mode and # should be reachable normally with self.settings(MAINTENANCE_MODE=True): # Note that we cannot override the settings here, since they are # ONLY used when the middleware starts up. # For this reason, MAINTENANCE_IGNORE_URLS is set in the base # settings file. response = self.client.get("/ignored/") self.assertContains(response, text="Rendered response page", count=1, status_code=200) def test_management_command(self): out = StringIO() # Explicitly disabling the ``MAINTENANCE_MODE`` with self.settings(MAINTENANCE_MODE=False): management.call_command("maintenance", "on", stdout=out) self.assertContains(self.client.get("/"), text="Temporary unavailable", count=1, status_code=503) management.call_command("maintenance", "off", stdout=out) self.assertContains(self.client.get("/"), text="Rendered response page", count=1, status_code=200) django-maintenancemode-0.11.7+git221001/maintenancemode/tests/models.py0000644000175100017510000000000014322157470025761 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/management/0000755000175100017510000000000014322157470025110 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/management/commands/0000755000175100017510000000000014322157470026711 5ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/management/commands/__init__.py0000644000175100017510000000000014322157470031010 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/management/commands/maintenance.py0000644000175100017510000000204614322157470031547 0ustar kittermakittermafrom django.core.management.base import BaseCommand, CommandError from maintenancemode import utils as maintenance class Command(BaseCommand): opts = ("on", "off", "activate", "deactivate") def add_arguments(self, parser): parser.add_argument("command", nargs="?", help="|".join(self.opts)) def handle(self, *args, **options): command = options.get("command", args[0] if len(args) > 0 else None) verbosity = int(options.get("verbosity")) if command is not None: if command.lower() in ("on", "activate"): maintenance.activate() if verbosity > 0: self.stdout.write("Maintenance mode was activated succesfully") elif command.lower() in ("off", "deactivate"): maintenance.deactivate() if verbosity > 0: self.stdout.write("Maintenance mode was deactivated succesfully") if command not in self.opts: raise CommandError("Allowed commands are: %s" % "|".join(self.opts)) django-maintenancemode-0.11.7+git221001/maintenancemode/management/__init__.py0000644000175100017510000000000014322157470027207 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/views.py0000644000175100017510000000164314322157470024507 0ustar kittermakittermaimport django if django.get_version() >= "1.8": from django.template.loader import render_to_string else: from django.template import RequestContext, loader def render_to_string(template_name, context=None, request=None): context_instance = RequestContext(request) if request else None return loader.render_to_string(template_name, context, context_instance) from . import http def temporary_unavailable(request, template_name="503.html"): """ Default 503 handler, which looks for the requested URL in the redirects table, redirects if found, and displays 404 page if not redirected. Templates: ``503.html`` Context: request_path The path of the requested URL (e.g., '/app/pages/bad_page/') """ context = { "request_path": request.path, } return http.HttpResponseTemporaryUnavailable(render_to_string(template_name, context)) django-maintenancemode-0.11.7+git221001/maintenancemode/http.py0000644000175100017510000000016314322157470024325 0ustar kittermakittermafrom django.http import HttpResponse class HttpResponseTemporaryUnavailable(HttpResponse): status_code = 503 django-maintenancemode-0.11.7+git221001/maintenancemode/middleware.py0000644000175100017510000000472014322157470025466 0ustar kittermakittermaimport re from django import VERSION as django_version from django.conf import urls from django.urls import get_resolver from django.urls import resolvers from django.utils.deprecation import MiddlewareMixin from . import utils as maintenance from .conf import settings urls.handler503 = "maintenancemode.views.temporary_unavailable" urls.__all__.append("handler503") IGNORE_URLS = tuple(re.compile(u) for u in settings.MAINTENANCE_IGNORE_URLS) DJANGO_VERSION_MAJOR = django_version[0] DJANGO_VERSION_MINOR = django_version[1] class MaintenanceModeMiddleware(MiddlewareMixin): def process_request(self, request): # Allow access if middleware is not activated allow_staff = getattr(settings, "MAINTENANCE_ALLOW_STAFF", True) allow_superuser = getattr(settings, "MAINTENANCE_ALLOW_SUPERUSER", True) if not (settings.MAINTENANCE_MODE or maintenance.status()): return None INTERNAL_IPS = maintenance.IPList(settings.INTERNAL_IPS) # Preferentially check HTTP_X_FORWARDED_FOR b/c a proxy # server might have obscured REMOTE_ADDR for ip in request.headers.get('X-Forwarded-For', "").split(","): if ip.strip() in INTERNAL_IPS: return None # Allow access if remote ip is in INTERNAL_IPS if request.META.get("REMOTE_ADDR") in INTERNAL_IPS: return None # Allow access if the user doing the request is logged in and a # staff member. if hasattr(request, "user"): if allow_staff and request.user.is_staff: return None if allow_superuser and request.user.is_superuser: return None # Check if a path is explicitly excluded from maintenance mode for url in IGNORE_URLS: if url.match(request.path_info): return None # Otherwise show the user the 503 page if (DJANGO_VERSION_MAJOR == 3 and DJANGO_VERSION_MINOR < 2) or DJANGO_VERSION_MAJOR < 3: # Checks if DJANGO version is less than 3.2.0 for breaking change resolver = get_resolver() callback, param_dict = resolver.resolve_error_handler("503") return callback(request, **param_dict) else: # Default behaviour for django 3.2 and higher resolver = resolvers.get_resolver(None) resolve = resolver.resolve_error_handler callback = resolve('503') return callback(request) django-maintenancemode-0.11.7+git221001/maintenancemode/conf.py0000644000175100017510000000053714322157470024300 0ustar kittermakittermaimport os from django.conf import settings # noqa from appconf import AppConf class MaintenanceSettings(AppConf): IGNORE_URLS = () LOCKFILE_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "maintenance.lock") MODE = False class Meta: prefix = "maintenance" holder = "maintenancemode.conf.settings" django-maintenancemode-0.11.7+git221001/maintenancemode/__init__.py0000644000175100017510000000000014322157470025073 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/maintenancemode/utils.py0000644000175100017510000000157014322157470024511 0ustar kittermakittermaimport os from .conf import settings class IPList(list): """Stolen from https://djangosnippets.org/snippets/1362/""" def __init__(self, ips): try: from IPy import IP for ip in ips: self.append(IP(ip)) except ImportError: pass def __contains__(self, ip): try: for net in self: if ip in net: return True except: # noqa pass return False def activate(): try: open(settings.MAINTENANCE_LOCKFILE_PATH, "ab", 0).close() except OSError: pass # shit happens def deactivate(): if os.path.isfile(settings.MAINTENANCE_LOCKFILE_PATH): os.remove(settings.MAINTENANCE_LOCKFILE_PATH) def status(): return settings.MAINTENANCE_MODE or os.path.isfile(settings.MAINTENANCE_LOCKFILE_PATH) django-maintenancemode-0.11.7+git221001/maintenancemode/models.py0000644000175100017510000000000014322157470024617 0ustar kittermakittermadjango-maintenancemode-0.11.7+git221001/README.rst0000644000175100017510000000761014322157470021340 0ustar kittermakittermadjango-maintenancemode ====================== .. image:: https://img.shields.io/pypi/v/django-maintenancemode.svg :target: https://pypi.python.org/pypi/django-maintenancemode/ .. image:: https://img.shields.io/pypi/dm/django-maintenancemode.svg :target: https://pypi.python.org/pypi/django-maintenancemode/ .. image:: https://img.shields.io/github/license/shanx/django-maintenancemode.svg :target: https://pypi.python.org/pypi/django-maintenancemode/ .. image:: https://app.travis-ci.com/bashu/django-maintenancemode.svg?branch=develop :target: https://app.travis-ci.com/github/bashu/django-maintenancemode .. image:: https://coveralls.io/repos/github/shanx/django-maintenancemode/badge.svg?branch=develop :target: https://coveralls.io/github/shanx/django-maintenancemode?branch=develop django-maintenancemode is a middleware that allows you to temporary shutdown your site for maintenance work. Logged in users having staff credentials can still fully use the site as can users visiting the site from an IP address defined in Django's ``INTERNAL_IPS``. Authored by `Remco Wendt `_, and some great `contributors `_. How it works ------------ ``maintenancemode`` works the same way as handling 404 or 500 error in Django work. It adds a ``handler503`` which you can override in your main ``urls.py`` or you can add a ``503.html`` to your templates directory. * If user is logged in and staff member, the maintenance page is not displayed. * If user's IP is in ``INTERNAL_IPS``, the maintenance page is not displayed. * To override the default view which is used if the maintenance mode is enabled you can simply define a ``handler503`` variable in your ROOT_URLCONF_, similar to how you would customize other `error handlers`_, e.g. : .. code-block:: python handler503 = 'example.views.maintenance_mode' Installation ------------ 1. Either checkout ``maintenancemode`` from GitHub, or install using pip : .. code-block:: bash pip install django-maintenancemode 2. Add ``maintenancemode`` to your ``INSTALLED_APPS`` : .. code-block:: python INSTALLED_APPS = ( ... 'maintenancemode', ) 3. Add ``MaintenanceModeMiddleware`` to ``MIDDLEWARE_CLASSES``, make sure it comes after ``AuthenticationMiddleware`` : .. code-block:: python MIDDLEWARE_CLASSES = ( ... 'django.contrib.auth.middleware.AuthenticationMiddleware', 'maintenancemode.middleware.MaintenanceModeMiddleware', ) 4. Add variable called ``MAINTENANCE_MODE`` in your project's ``settings.py`` file : .. code-block:: python MAINTENANCE_MODE = True # Setting this variable to ``True`` activates the middleware. or set ``MAINTENANCE_MODE`` to ``False`` and use ``maintenance`` command : .. code-block:: shell python ./manage.py maintenance Please see ``example`` application. This application is used to manually test the functionalities of this package. This also serves as a good example... You need only Django 1.4 or above to run that. It might run on older versions but that is not tested. Configuration ------------- There are various optional configuration options you can set in your ``settings.py`` .. code-block:: python # Enable / disable maintenance mode. # Default: False MAINTENANCE_MODE = True # or ``False`` and use ``maintenance`` command # Sequence of URL path regexes to exclude from the maintenance mode. # Default: () MAINTENANCE_IGNORE_URLS = ( r'^/docs/.*', r'^/contact' ) License ------- ``django-maintenancemode`` is released under the BSD license. .. _ROOT_URLCONF: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf .. _`error handlers`: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views