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" 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" WAGTAILADMIN_BASE_URL = "/cms/" 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 " # 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"