Remove Nginx-specific static and media serving, and serve media through Django using WhiteNoise and FileResponse.
This commit is contained in:
parent
aa348d2b04
commit
92e1336f80
6 changed files with 46 additions and 23 deletions
15
README.md
15
README.md
|
|
@ -17,9 +17,6 @@ run and to maintain.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install git gettext python3-django python3-django-allauth python3-django-crispy-forms python3-docutils python3-exifread python3-pil
|
sudo apt install git gettext python3-django python3-django-allauth python3-django-crispy-forms python3-docutils python3-exifread python3-pil
|
||||||
|
|
||||||
# Only for production
|
|
||||||
sudo apt install nginx uwsgi uwsgi-plugin-python3 python3-certbot-nginx
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Cloning.**
|
2. **Cloning.**
|
||||||
|
|
@ -33,19 +30,8 @@ run and to maintain.
|
||||||
3. **Configuration (production only).**
|
3. **Configuration (production only).**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Only for production
|
|
||||||
sudo mkdir static media
|
sudo mkdir static media
|
||||||
sudo cp docs/maintenance.html static/maintenance.html
|
|
||||||
sudo chown www-data:www-data -R static media
|
|
||||||
sudo chmod g+rwx -R static media
|
|
||||||
sudo chmod +x maintenance_tool.sh
|
sudo chmod +x maintenance_tool.sh
|
||||||
sudo cp docs/uwsgi_photos.ini /etc/uwsgi/apps-available/uwsgi_photos.ini
|
|
||||||
sudo ln -s /etc/uwsgi/apps-available/uwsgi_photos.ini /etc/uwsgi/apps-enabled/
|
|
||||||
sudo cp docs/nginx_photos_maintenance /etc/nginx/sites-available/photos.crans.org
|
|
||||||
sudo ln -s /etc/nginx/sites-available/photos.crans.org /etc/nginx/sites-enabled/
|
|
||||||
sudo cp docs/letsencrypt_photos.crans.org /etc/letsencrypt/conf.d/photos.crans.org
|
|
||||||
sudo cp docs/renewal-hooks_post_nginx /etc/letsencrypt/renewal-hooks/post/nginx
|
|
||||||
sudo certbot --config /etc/letsencrypt/conf.d/photos.crans.org.ini certonly
|
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Database (production only).**
|
4. **Database (production only).**
|
||||||
|
|
@ -82,7 +68,6 @@ run and to maintain.
|
||||||
|
|
||||||
6. *Enjoy \o/*
|
6. *Enjoy \o/*
|
||||||
|
|
||||||
In production, the NGINX site should now work.
|
|
||||||
In development, you can launch the development server using:
|
In development, you can launch the development server using:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ ADMINS = [tuple(a.split(":")) for a in config("ADMINS", default="", cast=Csv())
|
||||||
SESSION_COOKIE_SECURE = not DEBUG
|
SESSION_COOKIE_SECURE = not DEBUG
|
||||||
CSRF_COOKIE_SECURE = not DEBUG
|
CSRF_COOKIE_SECURE = not DEBUG
|
||||||
|
|
||||||
|
# Trust Caddy's forwarded proto header
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
|
||||||
# Remember HTTPS for 1 year
|
# Remember HTTPS for 1 year
|
||||||
SECURE_HSTS_SECONDS = 31536000
|
SECURE_HSTS_SECONDS = 31536000
|
||||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||||
|
|
@ -77,6 +80,7 @@ if DEBUG:
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
"django.middleware.common.CommonMiddleware",
|
"django.middleware.common.CommonMiddleware",
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
|
|
@ -189,6 +193,18 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||||
MEDIA_URL = "/media/"
|
MEDIA_URL = "/media/"
|
||||||
|
|
||||||
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||||
|
},
|
||||||
|
"staticfiles": {
|
||||||
|
"BACKEND": "photo21.storage.CompressedManifestStorage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WHITENOISE_MANIFEST_STRICT = False
|
||||||
|
|
||||||
LOCALE_PATHS = [os.path.join(BASE_DIR, "photo21/locale")]
|
LOCALE_PATHS = [os.path.join(BASE_DIR, "photo21/locale")]
|
||||||
|
|
||||||
FIXTURE_DIRS = [os.path.join(BASE_DIR, "photo21/fixtures")]
|
FIXTURE_DIRS = [os.path.join(BASE_DIR, "photo21/fixtures")]
|
||||||
|
|
|
||||||
12
photo21/storage.py
Normal file
12
photo21/storage.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
from whitenoise.storage import CompressedManifestStaticFilesStorage
|
||||||
|
|
||||||
|
|
||||||
|
class CompressedManifestStorage(CompressedManifestStaticFilesStorage):
|
||||||
|
"""Like CompressedManifestStaticFilesStorage but silently skips missing
|
||||||
|
referenced files (e.g. source maps not included in the package)."""
|
||||||
|
|
||||||
|
def hashed_name(self, name, content=None, filename=None):
|
||||||
|
try:
|
||||||
|
return super().hashed_name(name, content, filename)
|
||||||
|
except ValueError:
|
||||||
|
return name
|
||||||
|
|
@ -2,21 +2,29 @@
|
||||||
# Copyright (C) 2021-2022 Amicale des élèves de l'ENS Paris-Saclay
|
# Copyright (C) 2021-2022 Amicale des élèves de l'ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponse
|
from django.http import FileResponse, Http404
|
||||||
from django.views.generic import ListView, View
|
from django.views.generic import ListView, View
|
||||||
from photologue.models import Gallery
|
from photologue.models import Gallery
|
||||||
|
|
||||||
|
|
||||||
|
class MediaAccess(View):
|
||||||
class MediaAccess(LoginRequiredMixin, View):
|
|
||||||
def get(self, request, path):
|
def get(self, request, path):
|
||||||
response = HttpResponse()
|
if not request.user.is_authenticated and not request.session.get('public_gallery_access'):
|
||||||
# Content-type will be detected by nginx
|
from django.contrib.auth.views import redirect_to_login
|
||||||
del response["Content-Type"]
|
return redirect_to_login(request.get_full_path())
|
||||||
response["X-Accel-Redirect"] = "/protected/media/" + path
|
media_root = os.path.realpath(settings.MEDIA_ROOT)
|
||||||
response["Cache-Control"] = 'max-age=2678400'
|
file_path = os.path.realpath(os.path.join(media_root, path))
|
||||||
|
if not file_path.startswith(media_root + os.sep):
|
||||||
|
raise Http404
|
||||||
|
if not os.path.isfile(file_path):
|
||||||
|
raise Http404
|
||||||
|
response = FileResponse(open(file_path, 'rb'))
|
||||||
|
response['Cache-Control'] = 'max-age=2678400'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ class GalleryPublicView(DetailView):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
gallery = self.get_object()
|
gallery = self.get_object()
|
||||||
return redirect("photologue:pl-gallery", slug=gallery.slug)
|
return redirect("photologue:pl-gallery", slug=gallery.slug)
|
||||||
|
request.session['public_gallery_access'] = True
|
||||||
request.guest_mode = True
|
request.guest_mode = True
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ ExifRead>=2.1.2
|
||||||
Pillow>=6.0.0
|
Pillow>=6.0.0
|
||||||
django-debug-toolbar>=3.2.0
|
django-debug-toolbar>=3.2.0
|
||||||
python-decouple>=3.6
|
python-decouple>=3.6
|
||||||
|
whitenoise>=6.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue