skillbox/server/core/settings.py

450 lines
13 KiB
Python

import os
import sys
import dj_database_url
from django.utils.translation import gettext_lazy as _
from dotenv import find_dotenv, load_dotenv
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
from core.env_utils import bool_value
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
load_dotenv(find_dotenv())
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY")
SIGNING_SECRET = os.environ.get("SIGNING_SECRET")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool_value(os.environ.get("DEBUG", ""))
TEST = "test" in sys.argv
ENABLE_SILKY = bool_value(os.environ.get("ENABLE_SILKY", ""))
SERVE_VIA_WEBPACK = bool_value(os.environ.get("SERVE_VIA_WEBPACK", DEBUG))
ENABLE_SENTRY = not DEBUG or bool_value(os.environ.get("ENABLE_SENTRY_DEBUG", ""))
THEME = os.environ.get("THEME", "skillbox")
PLATFORM = os.environ.get("APP_FLAVOR", "myskillbox")
ALLOWED_HOSTS = ["*"]
if not DEBUG:
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Application definition
INSTALLED_APPS = [
"core",
"api",
"users",
"books",
"objectives",
"rooms",
"assignments",
"basicknowledge",
"portfolio",
"statistics",
"surveys",
"notes",
"news",
"oauth",
"wagtail.contrib.redirects",
"wagtail.contrib.modeladmin",
"wagtail.embeds",
"wagtail.sites",
"wagtail.users",
"wagtail.snippets",
"wagtail.documents",
"wagtail.images",
"wagtail.search",
"wagtail.locales",
"wagtail.contrib.simple_translation",
"wagtail.admin",
"wagtail",
"wagtail.api.v2",
"wagtailautocomplete",
"taggit",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"whitenoise.runserver_nostatic",
"django.contrib.staticfiles",
"django_filters",
"graphene_django",
"django_extensions",
"compressor",
]
if DEBUG:
INSTALLED_APPS += ["wagtail.contrib.styleguide"]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.middleware.gzip.GZipMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
]
# Enable CORS for local development
if DEBUG:
INSTALLED_APPS += ["corsheaders"]
MIDDLEWARE += ["corsheaders.middleware.CorsMiddleware"]
CORS_ORIGIN_WHITELIST = ("http://localhost:8080",)
CORS_ALLOW_CREDENTIALS = True
# enable silk for performance measuring
if ENABLE_SILKY:
INSTALLED_APPS += ["silk"]
MIDDLEWARE += [
"silk.middleware.SilkyMiddleware",
]
MIDDLEWARE += [
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
"core.middleware.ThreadLocalMiddleware",
"core.middleware.CommonRedirectMiddleware",
"core.middleware.UserLoggedInCookieMiddleWare",
"oauth.middleware.user_has_license_middleware",
]
ROOT_URLCONF = "core.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
os.path.join(BASE_DIR, "..", "client/dist"),
os.path.join(BASE_DIR, "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",
"core.context_processors.settings_context",
],
},
},
]
WSGI_APPLICATION = "core.wsgi.application"
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
# Database
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}
# Django custom user
AUTH_USER_MODEL = "users.User"
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
WEAK_PASSWORDS = DEBUG
if WEAK_PASSWORDS:
AUTH_PASSWORD_VALIDATORS = []
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.MD5PasswordHasher",
]
else:
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",
},
]
LOGOUT_REDIRECT_URL = "/login"
LOGIN_REDIRECT_URL = "/login"
LOGIN_URL = LOGIN_REDIRECT_URL
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = "de"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
GERMAN = ("de", _("German"))
ENGLISH = ("en", _("English"))
FRENCH = ("fr", _("French"))
if PLATFORM in ["my-kv"]:
WAGTAIL_I18N_ENABLED = True
WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
GERMAN,
ENGLISH,
FRENCH,
]
else:
WAGTAIL_I18N_ENABLED = False
LANGUAGES = [
GERMAN,
ENGLISH,
]
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = "/static/"
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "..", "client/dist/static"),
os.path.join(BASE_DIR, "..", "client/src/assets"),
)
COMPRESS_CSS_FILTERS = [
# 'django_compressor_autoprefixer.AutoprefixerFilter',
"compressor.filters.cssmin.CSSMinFilter",
]
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
"compressor.finders.CompressorFinder",
)
COMPRESS_PRECOMPILERS = (("text/x-scss", "django_libsass.SassCompiler"),)
COMPRESS_ENABLED = True
if not DEBUG:
COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
COMPRESS_OFFLINE = True
# AWS S3
# http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
USE_AWS = bool_value(os.environ.get("USE_AWS"))
AWS_QUERYSTRING_AUTH = False
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")
AWS_S3_FILE_OVERWRITE = False
# use with cloudfront
AWS_S3_CUSTOM_DOMAIN = "{}.s3-{}.amazonaws.com".format(
AWS_STORAGE_BUCKET_NAME, os.environ.get("AWS_REGION", "eu-west-1")
)
if USE_AWS:
STORAGE_BACKEND = "storages.backends.s3boto3.S3Boto3Storage"
# use with cloudfront
MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN
else:
STORAGE_BACKEND = "django.core.files.storage.FileSystemStorage"
MEDIA_URL = "/media/"
MEDIA_ROOT = os.environ.get("DJANGO_MEDIAFILES", os.path.join(BASE_DIR, "media"))
if not TEST:
STATICFILES_BACKEND = "whitenoise.storage.CompressedManifestStaticFilesStorage"
else:
STATICFILES_BACKEND = "django.contrib.staticfiles.storage.StaticFilesStorage"
STORAGES = {
"default": {"BACKEND": STORAGE_BACKEND},
"staticfiles": {"BACKEND": STATICFILES_BACKEND},
}
AWS_S3_OBJECT_PARAMETERS = {
"CacheControl": "max-age=86400",
}
# Media Files
USE_404_FALLBACK_IMAGE = bool_value(os.environ.get("USE_404_FALLBACK_IMAGE", "True"))
# Logging Conf
LOGGING = {
"version": 1,
"formatters": {
"verbose_format": {
"format": "%(levelname)s %(asctime)s %(module)s %(name)s (%(process)d): %(message)s"
},
"simple_format": {"format": "%(levelname)s %(name)s: %(message)s"},
},
"disable_existing_loggers": not DEBUG,
"handlers": {
"mail_admins": {
"level": "CRITICAL",
"class": "django.utils.log.AdminEmailHandler",
},
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "simple_format",
},
# for automatic papertrail logging
"SysLog": {
"level": "INFO",
"class": "logging.handlers.SysLogHandler",
"formatter": "simple_format",
},
},
"loggers": {
"": {"handlers": ["console"], "level": "WARNING"},
"skillbox": {
"handlers": ["console", "SysLog"],
"level": os.environ.get("DJANGO_LOG_LEVEL", "INFO"),
"propagate": False,
},
"graphql": {"handlers": ["console"], "level": "WARNING", "propagate": False},
"django": {
"handlers": ["console"],
"level": "INFO",
"propagate": False,
},
"django.server": {
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
},
},
}
if ENABLE_SENTRY and os.environ.get("SENTRY_DSN"):
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import ignore_logger
def before_send(event, hint):
user = event["user"]
id = user["id"]
event["user"] = {"id": id}
return event
environment = os.environ.get("SENTRY_ENV", "localhost")
sample_rate = os.environ.get("SENTRY_SAMPLE_RATE", 0.01)
sentry_sdk.init(
dsn=os.environ.get("SENTRY_DSN"),
integrations=[DjangoIntegration()],
send_default_pii=True,
before_send=before_send,
before_send_transaction=before_send,
environment=environment,
traces_sample_rate=sample_rate,
enable_tracing=True,
)
# ignore 'Traceback' error messages, they don't give enough information
# from https://jerrynsh.com/how-to-monitor-python-graphql-api-with-sentry/
ignore_logger("graphql.execution.utils")
GRAPHENE = {
"SCHEMA": "api.schema.schema",
"SCHEMA_OUTPUT": "schema.graphql",
"MIDDLEWARE": ["core.middleware.SentryMiddleware"],
}
# if DEBUG:
# GRAPHENE['MIDDLEWARE'] = [
# 'graphene_django.debug.DjangoDebugMiddleware',
# ]
# http://docs.wagtail.io/en/v2.1/advanced_topics/settings.html?highlight=urls
WAGTAIL_SITE_NAME = "skillbox"
WAGTAILSEARCH_BACKENDS = {
"default": {
"BACKEND": "wagtail.search.backends.database",
}
}
WAGTAILDOCS_DOCUMENT_MODEL = "books.CustomDocument"
GRAPHQL_QUERIES_DIR = os.path.join(
BASE_DIR, "..", "client", "src", "graphql", "gql", "queries"
)
GRAPHQL_MUTATIONS_DIR = os.path.join(GRAPHQL_QUERIES_DIR, "../mutations")
DEFAULT_FROM_EMAIL = "myskillbox <noreply@myskillbox.ch>"
# Metanet Config
if DEBUG:
EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
else:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
ALLOW_BETA_LOGIN = True
# HEP
HEP_URL = os.environ.get("HEP_URL")
# HEP Oauth
AUTHLIB_OAUTH_CLIENTS = {
"hep": {
"client_id": os.environ.get("OAUTH_CLIENT_ID"),
"client_secret": os.environ.get("OAUTH_CLIENT_SECRET"),
"request_token_url": None,
"request_token_params": None,
"access_token_url": os.environ.get("OAUTH_ACCESS_TOKEN_URL"),
"access_token_params": None,
"refresh_token_url": None,
"authorize_url": os.environ.get("OAUTH_AUTHORIZE_URL"),
"api_base_url": os.environ.get("OAUTH_API_BASE_URL"),
"client_kwargs": {
"scope": "orders",
"token_endpoint_auth_method": "client_secret_post",
"token_placement": "header",
},
}
}
OAUTH_LOCAL_REDIRECT_URI = os.environ.get("OAUTH_LOCAL_REDIRECT_URI")
TASKBASE_USER = os.environ.get("TASKBASE_USER")
TASKBASE_PASSWORD = os.environ.get("TASKBASE_PASSWORD")
TASKBASE_SUPERUSER = os.environ.get("TASKBASE_SUPERUSER")
TASKBASE_SUPERPASSWORD = os.environ.get("TASKBASE_SUPERPASSWORD")
TASKBASE_BASEURL = os.environ.get("TASKBASE_BASEURL")
ENABLE_SPELLCHECK = True if TASKBASE_BASEURL else False
TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner"
TEST_OUTPUT_DIR = "./test-reports/"
TEST_OUTPUT_VERBOSE = 1
# new default in Django 3.0, making it explicit to facilitate bug hunting
X_FRAME_OPTIONS = "DENY"
SECURE_CONTENT_TYPE_NOSNIFF = True
# Django 3.2 uses BitAutoField by default, we keep things the old way
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"