This repository has been archived on 2023-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
python-blog/src/blog/settings.py

265 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Django settings for blog project.
Generated by 'django-admin startproject' using Django 3.1.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
from pathlib import Path
import environ
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
env = environ.Env(
DEBUG=(bool, False),
SECRET_KEY=(str, "s#!83!8e$3s89m)r$1ghsgxbndf8=#^qt(_*o%xbq0j2t8#db5"),
ADMINS=(list, []),
MAILGUN_API_KEY=(str, ""),
MAILGUN_SENDER_DOMAIN=(str, ""),
HOSTS=(list, []),
DATABASE_URL=(str, "sqlite:////app/db/db.sqlite3"),
BLOG_BASE_URL=(str, "https://gabnotes.org/"),
SERVICES_STATUS_URL=(str, None),
SHORTPIXEL_API_KEY=(str, None),
SHORTPIXEL_RESIZE_WIDTH=(int, 750),
SHORTPIXEL_RESIZE_HEIGHT=(int, 10000),
GOATCOUNTER_DOMAIN=(str, None),
)
env_file = os.getenv("ENV_FILE", None)
if env_file:
environ.Env.read_env(env_file)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY")
admins = env("ADMINS")
if admins:
ADMINS = [tuple(admin.split("|")) for admin in admins]
DEFAULT_FROM_EMAIL = "Gab's Notes <blog@mg.gabnotes.org>"
SERVER_EMAIL = "Gab's Notes <blog@mg.gabnotes.org>"
EMAIL_SUBJECT_PREFIX = "[Blog] "
EMAIL_TIMEOUT = 30
ANYMAIL = {
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_SENDER_DOMAIN"),
"MAILGUN_API_URL": "https://api.eu.mailgun.net/v3",
}
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env("DEBUG")
ALLOWED_HOSTS = ["localhost"] # Required for healthcheck
if DEBUG:
ALLOWED_HOSTS.append("127.0.0.1")
ALLOWED_HOSTS.extend(env("HOSTS"))
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = not DEBUG
CSRF_COOKIE_SECURE = not DEBUG
# Application definition
DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
EXTERNAL_APPS = [
"anymail",
"django_extensions",
"django_otp",
"django_otp.plugins.otp_static",
"django_otp.plugins.otp_totp",
"two_factor",
"django_cleanup.apps.CleanupConfig", # should be last: https://pypi.org/project/django-cleanup/
]
if DEBUG:
EXTERNAL_APPS.insert(-2, "debug_toolbar")
CUSTOM_APPS = [
"whitenoise.runserver_nostatic", # should be first
"articles",
"attachments",
]
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.middleware.gzip.GZipMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_otp.middleware.OTPMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"csp.middleware.CSPMiddleware",
]
if DEBUG:
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
ROOT_URLCONF = "blog.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": ["blog/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",
"articles.context_processors.drafts_count",
"articles.context_processors.date_format",
"articles.context_processors.git_version",
"articles.context_processors.analytics",
"articles.context_processors.open_graph_image_url",
"articles.context_processors.blog_metadata",
],
},
},
]
WSGI_APPLICATION = "blog.wsgi.application"
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "cache",
}
}
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {"default": env.db()}
INTERNAL_IPS = [
"127.0.0.1",
"localhost",
]
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "Europe/Paris"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
AUTH_USER_MODEL = "articles.User"
BLOG = {
"title": "Gabs Notes", # noqa: RUF001
"author": "Gabriel Augendre",
"email": "ga-notes@augendre.info",
"description": "My take on tech-related subjects (but not only).",
"base_url": env("BLOG_BASE_URL"),
"repo": {
"commit_url": "https://git.augendre.info/gaugendre/blog/commit/{commit_sha}",
"homepage": "https://git.augendre.info/gaugendre/blog",
"log": "https://git.augendre.info/gaugendre/blog/commits/branch/master",
},
"status_url": env("SERVICES_STATUS_URL"),
"licenses": {
"content": {
"url": "https://creativecommons.org/publicdomain/zero/1.0/?ref=chooser-v1",
"name": "CC0 1.0",
},
"code": {
"url": "https://git.augendre.info/gaugendre/blog/src/branch/master/LICENSE",
"name": "The Unlicense",
},
},
}
SHORTPIXEL_API_KEY = env("SHORTPIXEL_API_KEY")
SHORTPIXEL_RESIZE_WIDTH = env("SHORTPIXEL_RESIZE_WIDTH")
SHORTPIXEL_RESIZE_HEIGHT = env("SHORTPIXEL_RESIZE_HEIGHT")
GOATCOUNTER_DOMAIN = env("GOATCOUNTER_DOMAIN")
LOGIN_URL = "two_factor:login"
LOGIN_REDIRECT_URL = "two_factor:profile"
LOGOUT_REDIRECT_URL = "articles-list"
TWO_FACTOR_REMEMBER_COOKIE_AGE = 86400 * 30
TWO_FACTOR_REMEMBER_COOKIE_SECURE = True
TWO_FACTOR_REMEMBER_COOKIE_SAMESITE = "Strict"
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 63072000
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# CSP
CSP_DEFAULT_SRC = ("'none'",)
https_goatcounter_domain = "https://" + str(GOATCOUNTER_DOMAIN)
CSP_IMG_SRC = ("'self'", https_goatcounter_domain)
CSP_SCRIPT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'", https_goatcounter_domain)
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
CSP_MANIFEST_SRC = ("'self'",)
CSP_FONT_SRC = ("'self'",)
CSP_BASE_URI = ("'none'",)
CSP_FORM_ACTION = ("'self'",)
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"