""" Base settings to build other settings files upon. """ import logging from pathlib import Path import structlog from environs import Env from vbv_lernwelt.core.utils import structlog_add_app_info SERVER_ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent APPS_DIR = SERVER_ROOT_DIR / "vbv_lernwelt" env = Env() env.read_env() # set to "development" for local development APP_ENVIRONMENT = env("IT_APP_ENVIRONMENT") # GENERAL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#debug DEBUG = env.bool("IT_DJANGO_DEBUG", True if APP_ENVIRONMENT == "development" else False) # Local time zone. Choices are # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # though not all of them may be available with every OS. # In Windows, this must be set to your system time zone. TIME_ZONE = "Europe/Zurich" # https://docs.djangoproject.com/en/dev/ref/settings/#language-code LANGUAGE_CODE = "de-CH" # https://docs.djangoproject.com/en/dev/ref/settings/#site-id SITE_ID = 1 # https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n USE_I18N = True # https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n USE_L10N = True # https://docs.djangoproject.com/en/dev/ref/settings/#use-tz USE_TZ = True # https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths LOCALE_PATHS = [str(SERVER_ROOT_DIR / "locale")] # DATABASES # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = { "default": env.dj_db_url( "DATABASE_URL", default="postgres://postgres@localhost:5432/vbv_lernwelt", ) } DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405 DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405 # https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # URLS # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf ROOT_URLCONF = "config.urls" # https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application WSGI_APPLICATION = "config.wsgi.application" # APPS # ------------------------------------------------------------------------------ DJANGO_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", # "django.contrib.humanize", # Handy template tags "django.contrib.admin", "django.forms", ] THIRD_PARTY_APPS = [ "rest_framework", "rest_framework.authtoken", "corsheaders", "drf_spectacular", "wagtail.contrib.forms", "wagtail.contrib.redirects", "wagtail.contrib.styleguide", "wagtail.embeds", "wagtail.sites", "wagtail.users", "wagtail.snippets", "wagtail.documents", "wagtail.images", "wagtail.search", "wagtail.admin", "wagtail", # 'wagtail.locales', "wagtail_localize", "wagtail_localize.locales", "wagtail.api.v2", "modelcluster", "taggit", ] LOCAL_APPS = [ "vbv_lernwelt.core", "vbv_lernwelt.sso", "vbv_lernwelt.course", "vbv_lernwelt.learnpath", "vbv_lernwelt.competence", "vbv_lernwelt.media_library", ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS # MIGRATIONS # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules MIGRATION_MODULES = {"sites": "vbv_lernwelt.contrib.sites.migrations"} # AUTHENTICATION # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", ] # https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model AUTH_USER_MODEL = "core.User" # https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url # https://docs.djangoproject.com/en/dev/ref/settings/#login-url # FIXME make configurable!? # LOGIN_URL = "/sso/login/" LOGIN_URL = "/login" LOGIN_REDIRECT_URL = "/" ALLOW_LOCAL_LOGIN = env.bool("IT_ALLOW_LOCAL_LOGIN", default=DEBUG) # PASSWORDS # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers PASSWORD_HASHERS = [ # https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] # https://docs.djangoproject.com/en/dev/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"}, ] # MIDDLEWARE # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#middleware MIDDLEWARE = [ "vbv_lernwelt.core.middleware.security.GetIpBehindReverseProxyMiddleWare", "django.middleware.security.SecurityMiddleware", "corsheaders.middleware.CorsMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.locale.LocaleMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.common.BrokenLinkEmailsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "csp.middleware.CSPMiddleware", "vbv_lernwelt.core.middleware.auth.AuthenticationRequiredMiddleware", "vbv_lernwelt.core.middleware.security.SecurityRequestResponseLoggingMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", "vbv_lernwelt.core.middleware.auth.UserLoggedInCookieMiddleWare", ] # STATIC # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#static-root STATIC_ROOT = str(SERVER_ROOT_DIR / "staticfiles") # https://docs.djangoproject.com/en/dev/ref/settings/#static-url STATIC_URL = "/static/" # https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS STATICFILES_DIRS = [str(APPS_DIR / "static")] # https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", ] # MEDIA # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#media-root MEDIA_ROOT = str(APPS_DIR / "media") # https://docs.djangoproject.com/en/dev/ref/settings/#media-url MEDIA_URL = "/server/media/" IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG) IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", "http://localhost:5173") # WAGTAIL # ------------------------------------------------------------------------------ WAGTAIL_SITE_NAME = "VBV Lernwelt" WAGTAIL_I18N_ENABLED = True LANGUAGES = [ ("en-US", "English (American)"), ("fr-CH", "Swiss French"), ("de-CH", "Swiss German"), ("it-CH", "Swiss Italian"), ] WAGTAILDOCS_DOCUMENT_MODEL = "media_library.LibraryDocument" WAGTAIL_CONTENT_LANGUAGES = [ ("fr-CH", "Swiss French"), ("de-CH", "Swiss German"), ("it-CH", "Swiss Italian"), ] WAGTAILSEARCH_BACKENDS = { "default": { "BACKEND": "wagtail.search.backends.database", } } # TEMPLATES # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#templates TEMPLATES = [ { # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND "BACKEND": "django.template.backends.django.DjangoTemplates", # https://docs.djangoproject.com/en/dev/ref/settings/#dirs "DIRS": [str(APPS_DIR / "templates")], # https://docs.djangoproject.com/en/dev/ref/settings/#app-dirs "APP_DIRS": True, "OPTIONS": { # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.template.context_processors.i18n", "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", ], }, } ] # https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer FORM_RENDERER = "django.forms.renderers.TemplatesSetting" # FIXTURES # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),) # SECURITY # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly SESSION_COOKIE_HTTPONLY = True # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly CSRF_COOKIE_HTTPONLY = False # https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter SECURE_BROWSER_XSS_FILTER = True # https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options X_FRAME_OPTIONS = "DENY" # EMAIL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend # FIXME how to send emails? EMAIL_BACKEND = env( "DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" ) # https://docs.djangoproject.com/en/dev/ref/settings/#email-timeout EMAIL_TIMEOUT = 5 # ADMIN # ------------------------------------------------------------------------------ # Django Admin URL. ADMIN_URL = "server/admin/" # https://docs.djangoproject.com/en/dev/ref/settings/#admins ADMINS = [("""Daniel Egger""", "info@iterativ.ch")] # https://docs.djangoproject.com/en/dev/ref/settings/#managers MANAGERS = ADMINS # LOGGING # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#logging # See https://docs.djangoproject.com/en/dev/topics/logging for # more details on how to customize your logging configuration. logging_conf_default = "IT_DJANGO_LOGGING_CONF_JSON_FILE" if DEBUG: logging_conf_default = "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR" IT_DJANGO_LOGGING_CONF = env("IT_DJANGO_LOGGING_CONF", default=logging_conf_default) if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR": timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "colored": { "()": structlog.stdlib.ProcessorFormatter, "processor": structlog.dev.ConsoleRenderer(colors=True), "foreign_pre_chain": [ structlog.stdlib.add_log_level, timestamper, ], }, }, "handlers": { "default": { "class": "logging.StreamHandler", "formatter": "colored", }, }, "loggers": { "": { "handlers": ["default"], "level": "INFO", "propagate": True, }, "vbv_lernwelt": { "handlers": ["default"], "level": "DEBUG", "propagate": False, }, }, } structlog.configure( processors=[ structlog.threadlocal.merge_threadlocal, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), timestamper, structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) else: shared_processors = [ structlog.threadlocal.merge_threadlocal, structlog.stdlib.add_log_level, structlog.stdlib.add_logger_name, structlog_add_app_info, structlog.processors.TimeStamper(fmt="iso"), structlog.processors.CallsiteParameterAdder(), ] LOGGING = { "version": 1, "disable_existing_loggers": True, "formatters": { "json": { "()": structlog.stdlib.ProcessorFormatter, "processors": [ structlog.stdlib.ProcessorFormatter.remove_processors_meta, structlog.processors.JSONRenderer(), ], "foreign_pre_chain": shared_processors, }, }, "handlers": { "file": { "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", "filename": f"{SERVER_ROOT_DIR}/log/myservice.log", "maxBytes": 1024 * 1024 * 100, "backupCount": 50, "formatter": "json", }, "console": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "json", }, }, "loggers": { "": { "handlers": ["console", "file"], "level": "INFO", }, "django": { "handlers": ["console", "file"], "level": "WARNING", "propagate": False, }, "vbv_lernwelt": { "handlers": ["console", "file"], "level": "DEBUG", "propagate": False, }, "sentry_sdk": { "level": "ERROR", "handlers": ["console", "file"], "propagate": False, }, }, } structlog.configure( processors=shared_processors + [ structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) # django-rest-framework # ------------------------------------------------------------------------------- # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework.authentication.SessionAuthentication", "rest_framework.authentication.TokenAuthentication", ), "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), "TEST_REQUEST_DEFAULT_FORMAT": "json", "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "vbv_lernwelt.core.utils.HourUserRateThrottle", "vbv_lernwelt.core.utils.DayUserRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "anon": "200/day", "hour-throttle": "400/hour", "day-throttle": "2000/day", }, } # django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup CORS_URLS_REGEX = r"^/api/.*$" # django-csp CSP_DEFAULT_SRC = [ "'self'", "'unsafe-inline'", "'unsafe-eval'", "ws://localhost:5173", "ws://127.0.0.1:5173", "localhost:8000", "localhost:8001", "blob:", "data:", "http://*", ] CSP_FRAME_ANCESTORS = ("'self'",) SPECTACULAR_SETTINGS = { "TITLE": "VBV Lernwelt API", "DESCRIPTION": "Documentation of API endpoints of VBV Lernwelt", "VERSION": "1.0.0", "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"], "SERVERS": [ {"url": "https://127.0.0.1:8000", "description": "Local Development server"}, {"url": "https://vbv-lernwelt.iterativ.ch", "description": "Production server"}, ], } # Your stuff... # ------------------------------------------------------------------------------ if DEBUG: SECRET_KEY = env( "IT_DJANGO_SECRET_KEY", default="J9FiYN31FuY7lHrmx9Mpai3GGpTVCxakEclOfCLretDe7bTf2DtTsgazJ0aIMtbq", ) else: SECRET_KEY = env("IT_DJANGO_SECRET_KEY") # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = env.list( "IT_DJANGO_ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"] ) # CACHES CACHES = { "default": { "BACKEND": env( "IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache", ), "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"), }, } if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default=""): CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": env("IT_DJANGO_CACHE_LOCATION"), "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "IGNORE_EXCEPTIONS": True, }, }, } CACHES["api_page_cache"] = { "BACKEND": "django.core.cache.backends.db.DatabaseCache", "LOCATION": "django_cache_table_api_page", } # OAuth/OpenId Connect IT_OAUTH_TENANT_ID = env.str("IT_OAUTH_TENANT_ID", default=None) if IT_OAUTH_TENANT_ID: IT_OAUTH_AUTHORIZE_PARAMS = {"tenant_id": IT_OAUTH_TENANT_ID} else: IT_OAUTH_AUTHORIZE_PARAMS = {} OAUTH = { "client_name": env("IT_OAUTH_CLIENT_NAME", default="lernetz"), "client_id": env("IT_OAUTH_CLIENT_ID", default="iterativ"), "client_secret": env("IT_OAUTH_CLIENT_SECRET", default=""), "authorize_params": IT_OAUTH_AUTHORIZE_PARAMS, "access_token_params": IT_OAUTH_AUTHORIZE_PARAMS, "api_base_url": env( "IT_OAUTH_API_BASE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/", ), "local_redirect_uri": env( "IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/sso/callback/" ), "server_metadata_url": env( "IT_OAUTH_SERVER_METADATA_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/.well-known/openid-configuration", ), "client_kwargs": { "scope": env("IT_OAUTH_SCOPE", default=""), "token_endpoint_auth_method": "client_secret_post", "token_placement": "body", }, } if APP_ENVIRONMENT == "development": # http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405 # django-debug-toolbar # ------------------------------------------------------------------------------ # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites # INSTALLED_APPS += ["debug_toolbar"] # noqa F405 # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware # MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405 # https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config # DEBUG_TOOLBAR_CONFIG = { # "DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"], # "SHOW_TEMPLATE_CONTEXT": True, # } # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips INTERNAL_IPS = ["127.0.0.1", "10.0.2.2"] if env.bool("IT_DJANGO_LOCAL_DOCKER", False): import socket hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips] try: _, _, ips = socket.gethostbyname_ex("node") INTERNAL_IPS.extend(ips) except socket.gaierror: # The node container isn't started (yet?) pass # django-extensions # ------------------------------------------------------------------------------ # https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration INSTALLED_APPS += ["django_extensions", "django_watchfiles"] # noqa F405 if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith( "caprover" ): # SECURITY # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect SECURE_SSL_REDIRECT = env.bool("IT_DJANGO_SECURE_SSL_REDIRECT", default=True) # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure SESSION_COOKIE_SECURE = True # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure CSRF_COOKIE_SECURE = True # https://docs.djangoproject.com/en/dev/topics/security/#ssl-https # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds # TODO: set this to 60 seconds first and then to 518400 once you prove the former works SECURE_HSTS_SECONDS = 60 # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( "DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True ) # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True) # https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff SECURE_CONTENT_TYPE_NOSNIFF = env.bool( "DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True ) # STATIC # ------------------------ STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" # MEDIA # ------------------------------------------------------------------------------ # EMAIL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email DEFAULT_FROM_EMAIL = env( "DJANGO_DEFAULT_FROM_EMAIL", default="VBV Lernwelt ", ) # https://docs.djangoproject.com/en/dev/ref/settings/#server-email SERVER_EMAIL = env("IT_DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) # https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix EMAIL_SUBJECT_PREFIX = env( "DJANGO_EMAIL_SUBJECT_PREFIX", default="[VBV Lernwelt]", ) # ADMIN # ------------------------------------------------------------------------------ # Django Admin URL regex. ADMIN_URL = env("IT_DJANGO_ADMIN_URL", "admin/") # Anymail # ------------------------------------------------------------------------------ # https://anymail.readthedocs.io/en/stable/installation/#installing-anymail # INSTALLED_APPS += ["anymail"] # noqa F405 # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend # https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference # https://anymail.readthedocs.io/en/stable/esps EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" ANYMAIL = {} # Sentry # ------------------------------------------------------------------------------ import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.logging import LoggingIntegration from sentry_sdk.integrations.redis import RedisIntegration SENTRY_DSN = env("IT_SENTRY_DSN") SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO) sentry_logging = LoggingIntegration( level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs event_level=logging.ERROR, # Send errors as events ) integrations = [sentry_logging, DjangoIntegration(), RedisIntegration()] # pylint: disable=abstract-class-instantiated sentry_sdk.init( dsn=SENTRY_DSN, integrations=integrations, environment=env("SENTRY_ENVIRONMENT", default="production"), traces_sample_rate=env.float("SENTRY_TRACES_SAMPLE_RATE", default=0.0), ) # Your stuff... # ------------------------------------------------------------------------------