-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added middleware and fixed weird static file url mapping, also refact…
…ored webp.py for readability middleware added bc whitenoise only scans the static folder on start up so the newly generated files are not added to its hashmap, the modded middleware does a doublecheck to ensure the file actually exists if its not on the hashmap
- Loading branch information
Showing
8 changed files
with
588 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
from __future__ import annotations | ||
|
||
import os | ||
from posixpath import basename | ||
from urllib.parse import urlparse | ||
|
||
from django.conf import settings | ||
from django.contrib.staticfiles import finders | ||
from django.contrib.staticfiles.storage import staticfiles_storage | ||
from django.http import FileResponse | ||
from django.urls import get_script_prefix | ||
|
||
from whitenoise.base import WhiteNoise | ||
from whitenoise.string_utils import ensure_leading_trailing_slash | ||
|
||
__all__ = ["WhiteNoiseMiddleware"] | ||
|
||
class WhiteNoiseFileResponse(FileResponse): | ||
""" | ||
Wrap Django's FileResponse to prevent setting any default headers. For the | ||
most part these just duplicate work already done by WhiteNoise but in some | ||
cases (e.g. the content-disposition header introduced in Django 3.0) they | ||
are actively harmful. | ||
""" | ||
|
||
def set_headers(self, *args, **kwargs): | ||
pass | ||
|
||
|
||
class ModdedWhiteNoiseMiddleware(WhiteNoise): | ||
""" | ||
Wrap WhiteNoise to allow it to function as Django middleware, rather | ||
than WSGI middleware. | ||
""" | ||
|
||
def __init__(self, get_response=None, settings=settings): | ||
self.get_response = get_response | ||
|
||
try: | ||
autorefresh: bool = settings.WHITENOISE_AUTOREFRESH | ||
except AttributeError: | ||
autorefresh = settings.DEBUG | ||
try: | ||
max_age = settings.WHITENOISE_MAX_AGE | ||
except AttributeError: | ||
if settings.DEBUG: | ||
max_age = 0 | ||
else: | ||
max_age = 60 | ||
try: | ||
allow_all_origins = settings.WHITENOISE_ALLOW_ALL_ORIGINS | ||
except AttributeError: | ||
allow_all_origins = True | ||
try: | ||
charset = settings.WHITENOISE_CHARSET | ||
except AttributeError: | ||
charset = "utf-8" | ||
try: | ||
mimetypes = settings.WHITENOISE_MIMETYPES | ||
except AttributeError: | ||
mimetypes = None | ||
try: | ||
add_headers_function = settings.WHITENOISE_ADD_HEADERS_FUNCTION | ||
except AttributeError: | ||
add_headers_function = None | ||
try: | ||
index_file = settings.WHITENOISE_INDEX_FILE | ||
except AttributeError: | ||
index_file = None | ||
try: | ||
immutable_file_test = settings.WHITENOISE_IMMUTABLE_FILE_TEST | ||
except AttributeError: | ||
immutable_file_test = None | ||
|
||
super().__init__( | ||
application=None, | ||
autorefresh=autorefresh, | ||
max_age=max_age, | ||
allow_all_origins=allow_all_origins, | ||
charset=charset, | ||
mimetypes=mimetypes, | ||
add_headers_function=add_headers_function, | ||
index_file=index_file, | ||
immutable_file_test=immutable_file_test, | ||
) | ||
|
||
try: | ||
self.use_finders = settings.WHITENOISE_USE_FINDERS | ||
except AttributeError: | ||
self.use_finders = settings.DEBUG | ||
|
||
try: | ||
self.static_prefix = settings.WHITENOISE_STATIC_PREFIX | ||
except AttributeError: | ||
self.static_prefix = urlparse(settings.STATIC_URL or "").path | ||
script_prefix = get_script_prefix().rstrip("/") | ||
if script_prefix: | ||
if self.static_prefix.startswith(script_prefix): | ||
self.static_prefix = self.static_prefix[len(script_prefix) :] | ||
self.static_prefix = ensure_leading_trailing_slash(self.static_prefix) | ||
|
||
self.static_root = settings.STATIC_ROOT | ||
if self.static_root: | ||
self.add_files(self.static_root, prefix=self.static_prefix) | ||
|
||
try: | ||
root = settings.WHITENOISE_ROOT | ||
except AttributeError: | ||
root = None | ||
if root: | ||
self.add_files(root) | ||
|
||
if self.use_finders and not self.autorefresh: | ||
self.add_files_from_finders() | ||
|
||
def __call__(self, request): | ||
if self.autorefresh: | ||
static_file = self.find_file(request.path_info) | ||
else: | ||
static_file = self.files.get(request.path_info) | ||
if static_file is not None: | ||
return self.serve(static_file, request) | ||
if static_file is None: | ||
if self.static_prefix in request.path_info: | ||
# using path info and static root to check the directory of the recently made webp | ||
raw_full_path = os.path.join(self.static_root, (request.path_info).lstrip("/")) | ||
clean_full_path = raw_full_path.replace("\\", "/") | ||
complete_full_path = clean_full_path.replace(self.static_prefix.rstrip("/"), "", 1) | ||
if os.path.exists(complete_full_path): | ||
self.add_files(self.static_root, prefix=self.static_prefix) | ||
static_file = self.files.get(complete_full_path) | ||
if static_file is None: | ||
request.not_serving = request.path_info | ||
return self.serve(static_file, request) | ||
return self.get_response(request) | ||
|
||
@staticmethod | ||
def serve(static_file, request): | ||
try: | ||
response = static_file.get_response(request.method, request.META) | ||
status = int(response.status) | ||
http_response = WhiteNoiseFileResponse(response.file or (), status=status) | ||
# Remove default content-type | ||
del http_response["content-type"] | ||
for key, value in response.headers: | ||
http_response[key] = value | ||
return http_response | ||
except: | ||
http_response = WhiteNoiseFileResponse((), status=500) | ||
http_response['not serving'] = request.not_serving | ||
return http_response | ||
|
||
|
||
def add_files_from_finders(self): | ||
files = {} | ||
for finder in finders.get_finders(): | ||
for path, storage in finder.list(None): | ||
prefix = (getattr(storage, "prefix", None) or "").strip("/") | ||
url = "".join( | ||
( | ||
self.static_prefix, | ||
prefix, | ||
"/" if prefix else "", | ||
path.replace("\\", "/"), | ||
) | ||
) | ||
# Use setdefault as only first matching file should be used | ||
files.setdefault(url, storage.path(path)) | ||
stat_cache = {path: os.stat(path) for path in files.values()} | ||
for url, path in files.items(): | ||
self.add_file_to_dictionary(url, path, stat_cache=stat_cache) | ||
|
||
def candidate_paths_for_url(self, url): | ||
if self.use_finders and url.startswith(self.static_prefix): | ||
path = finders.find(url[len(self.static_prefix) :]) | ||
if path: | ||
yield path | ||
paths = super().candidate_paths_for_url(url) | ||
for path in paths: | ||
yield path | ||
|
||
def immutable_file_test(self, path, url): | ||
""" | ||
Determine whether given URL represents an immutable file (i.e. a | ||
file with a hash of its contents as part of its name) which can | ||
therefore be cached forever | ||
""" | ||
if not url.startswith(self.static_prefix): | ||
return False | ||
name = url[len(self.static_prefix) :] | ||
name_without_hash = self.get_name_without_hash(name) | ||
if name == name_without_hash: | ||
return False | ||
static_url = self.get_static_url(name_without_hash) | ||
# If the static_url function maps the name without hash | ||
# back to the original name, then we know we've got a | ||
# versioned filename | ||
if static_url and basename(static_url) == basename(url): | ||
return True | ||
return False | ||
|
||
def get_name_without_hash(self, filename): | ||
""" | ||
Removes the version hash from a filename e.g, transforms | ||
'css/application.f3ea4bcc2.css' into 'css/application.css' | ||
Note: this is specific to the naming scheme used by Django's | ||
CachedStaticFilesStorage. You may have to override this if | ||
you are using a different static files versioning system | ||
""" | ||
name_with_hash, ext = os.path.splitext(filename) | ||
name = os.path.splitext(name_with_hash)[0] | ||
return name + ext | ||
|
||
def get_static_url(self, name): | ||
try: | ||
return staticfiles_storage.url(name) | ||
except ValueError: | ||
return None |
Oops, something went wrong.