Make sentry errors more useful
This commit is contained in:
parent
d846e76245
commit
8bdf17685a
|
|
@ -2,24 +2,31 @@ from django.conf import settings
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.urls import include
|
from django.urls import include
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from graphene_django.views import GraphQLView
|
|
||||||
|
|
||||||
from api.schema_public import schema
|
from api.schema_public import schema
|
||||||
|
|
||||||
from core.views import PrivateGraphQLView
|
from core.views import SentryGraphQLView, PrivateGraphQLView
|
||||||
|
|
||||||
app_name = 'api'
|
app_name = "api"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^graphql-public', csrf_exempt(GraphQLView.as_view(schema=schema))),
|
url(r"^graphql-public", csrf_exempt(SentryGraphQLView.as_view(schema=schema))),
|
||||||
url(r'^graphql', csrf_exempt(PrivateGraphQLView.as_view())),
|
url(r"^graphql", csrf_exempt(PrivateGraphQLView.as_view())),
|
||||||
|
|
||||||
# oauth
|
# oauth
|
||||||
url(r'^oauth/', include('oauth.urls', namespace="oauth")),
|
url(r"^oauth/", include("oauth.urls", namespace="oauth")),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += [url(r'^graphiql-public', csrf_exempt(GraphQLView.as_view(schema=schema, graphiql=True,
|
urlpatterns += [
|
||||||
pretty=True)))]
|
url(
|
||||||
urlpatterns += [url(r'^graphiql', csrf_exempt(PrivateGraphQLView.as_view(graphiql=True, pretty=True)))]
|
r"^graphiql-public",
|
||||||
|
csrf_exempt(
|
||||||
|
SentryGraphQLView.as_view(schema=schema, graphiql=True, pretty=True)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
urlpatterns += [
|
||||||
|
url(
|
||||||
|
r"^graphiql",
|
||||||
|
csrf_exempt(PrivateGraphQLView.as_view(graphiql=True, pretty=True)),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ from django.conf import settings
|
||||||
from django.http import Http404, HttpResponsePermanentRedirect
|
from django.http import Http404, HttpResponsePermanentRedirect
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
from graphene import ResolveInfo
|
||||||
|
from sentry_sdk import capture_exception
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -15,25 +17,25 @@ _thread_locals = local()
|
||||||
|
|
||||||
|
|
||||||
def get_current_request():
|
def get_current_request():
|
||||||
""" returns the request object for this thread """
|
"""returns the request object for this thread"""
|
||||||
return getattr(_thread_locals, "request", None)
|
return getattr(_thread_locals, "request", None)
|
||||||
|
|
||||||
|
|
||||||
def get_current_user():
|
def get_current_user():
|
||||||
""" returns the current user, if exist, otherwise returns None """
|
"""returns the current user, if exist, otherwise returns None"""
|
||||||
request = get_current_request()
|
request = get_current_request()
|
||||||
if request:
|
if request:
|
||||||
return getattr(request, "user", None)
|
return getattr(request, "user", None)
|
||||||
|
|
||||||
|
|
||||||
class ThreadLocalMiddleware(MiddlewareMixin):
|
class ThreadLocalMiddleware(MiddlewareMixin):
|
||||||
""" Simple middleware that adds the request object in thread local storage."""
|
"""Simple middleware that adds the request object in thread local storage."""
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
_thread_locals.request = request
|
_thread_locals.request = request
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
if hasattr(_thread_locals, 'request'):
|
if hasattr(_thread_locals, "request"):
|
||||||
del _thread_locals.request
|
del _thread_locals.request
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
@ -42,9 +44,14 @@ class CommonRedirectMiddleware(MiddlewareMixin):
|
||||||
"""
|
"""
|
||||||
redirects common bad requests or missing images
|
redirects common bad requests or missing images
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_REDIRECTS = [
|
DEFAULT_REDIRECTS = [
|
||||||
(re.compile("(?i)(.+\.php|.+wp-admin.+|.+\.htm|\/wordpress\/|\/wp\/|\/mm5\/|\/wp-content\/)"),
|
(
|
||||||
'http://example.org/'),
|
re.compile(
|
||||||
|
"(?i)(.+\.php|.+wp-admin.+|.+\.htm|\/wordpress\/|\/wp\/|\/mm5\/|\/wp-content\/)"
|
||||||
|
),
|
||||||
|
"http://example.org/",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def process_exception(self, request, exception):
|
def process_exception(self, request, exception):
|
||||||
|
|
@ -72,10 +79,17 @@ class CommonRedirectMiddleware(MiddlewareMixin):
|
||||||
# some static image is missing show a placeholder (use full for local dev)
|
# some static image is missing show a placeholder (use full for local dev)
|
||||||
m = re.match(r".*(max|fill)-(?P<dimensions>\d+x\d+)\.(jpg|png|svg)", path)
|
m = re.match(r".*(max|fill)-(?P<dimensions>\d+x\d+)\.(jpg|png|svg)", path)
|
||||||
if m:
|
if m:
|
||||||
return 'https://picsum.photos/{}'.format(m.group('dimensions').replace('x', '/'))
|
return "https://picsum.photos/{}".format(
|
||||||
|
m.group("dimensions").replace("x", "/")
|
||||||
|
)
|
||||||
# or dummy image: return 'http://via.placeholder.com/{}'.format(m.group('dimensions'))
|
# or dummy image: return 'http://via.placeholder.com/{}'.format(m.group('dimensions'))
|
||||||
if '.png' in path or '.jpg' in path or '.svg' in path or 'not-found' in path:
|
if (
|
||||||
return 'https://picsum.photos/400/400'
|
".png" in path
|
||||||
|
or ".jpg" in path
|
||||||
|
or ".svg" in path
|
||||||
|
or "not-found" in path
|
||||||
|
):
|
||||||
|
return "https://picsum.photos/400/400"
|
||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/questions/4898408/how-to-set-a-login-cookie-in-django
|
# https://stackoverflow.com/questions/4898408/how-to-set-a-login-cookie-in-django
|
||||||
|
|
@ -86,14 +100,24 @@ class UserLoggedInCookieMiddleWare(MiddlewareMixin):
|
||||||
If the user is not authenticated and the cookie remains, delete it
|
If the user is not authenticated and the cookie remains, delete it
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cookie_name = 'loginStatus'
|
cookie_name = "loginStatus"
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
#if user and no cookie, set cookie
|
# if user and no cookie, set cookie
|
||||||
if request.user.is_authenticated and not request.COOKIES.get(self.cookie_name):
|
if request.user.is_authenticated and not request.COOKIES.get(self.cookie_name):
|
||||||
response.set_cookie(self.cookie_name, 'true')
|
response.set_cookie(self.cookie_name, "true")
|
||||||
elif not request.user.is_authenticated and request.COOKIES.get(self.cookie_name):
|
elif not request.user.is_authenticated and request.COOKIES.get(
|
||||||
#else if if no user and cookie remove user cookie, logout
|
self.cookie_name
|
||||||
|
):
|
||||||
|
# else if if no user and cookie remove user cookie, logout
|
||||||
response.delete_cookie(self.cookie_name)
|
response.delete_cookie(self.cookie_name)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class SentryMiddleware(object):
|
||||||
|
def on_error(self, error):
|
||||||
|
capture_exception(error)
|
||||||
|
raise error
|
||||||
|
|
||||||
|
def resolve(self, next, root, info: ResolveInfo, **args):
|
||||||
|
return next(root, info, **args).catch(self.on_error)
|
||||||
|
|
|
||||||
|
|
@ -16,142 +16,138 @@ load_dotenv(find_dotenv())
|
||||||
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||||
SIGNING_SECRET = os.environ.get('SIGNING_SECRET')
|
SIGNING_SECRET = os.environ.get("SIGNING_SECRET")
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = bool_value(os.environ.get('DEBUG', ''))
|
DEBUG = bool_value(os.environ.get("DEBUG", ""))
|
||||||
TEST = 'test' in sys.argv
|
TEST = "test" in sys.argv
|
||||||
ENABLE_SILKY = bool_value(os.environ.get('ENABLE_SILKY', ''))
|
ENABLE_SILKY = bool_value(os.environ.get("ENABLE_SILKY", ""))
|
||||||
SERVE_VIA_WEBPACK = bool_value(os.environ.get('SERVE_VIA_WEBPACK', DEBUG))
|
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', ''))
|
ENABLE_SENTRY = not DEBUG or bool_value(os.environ.get("ENABLE_SENTRY_DEBUG", ""))
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
|
||||||
if not DEBUG:
|
if not DEBUG:
|
||||||
SECURE_SSL_REDIRECT = True
|
SECURE_SSL_REDIRECT = True
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'core',
|
"core",
|
||||||
'api',
|
"api",
|
||||||
'users',
|
"users",
|
||||||
'books',
|
"books",
|
||||||
'objectives',
|
"objectives",
|
||||||
'rooms',
|
"rooms",
|
||||||
'assignments',
|
"assignments",
|
||||||
'basicknowledge',
|
"basicknowledge",
|
||||||
'portfolio',
|
"portfolio",
|
||||||
'statistics',
|
"statistics",
|
||||||
'surveys',
|
"surveys",
|
||||||
'notes',
|
"notes",
|
||||||
'news',
|
"news",
|
||||||
'oauth',
|
"oauth",
|
||||||
|
"wagtail.contrib.redirects",
|
||||||
'wagtail.contrib.redirects',
|
"wagtail.contrib.modeladmin",
|
||||||
'wagtail.contrib.modeladmin',
|
"wagtail.embeds",
|
||||||
'wagtail.embeds',
|
"wagtail.sites",
|
||||||
'wagtail.sites',
|
"wagtail.users",
|
||||||
'wagtail.users',
|
"wagtail.snippets",
|
||||||
'wagtail.snippets',
|
"wagtail.documents",
|
||||||
'wagtail.documents',
|
"wagtail.images",
|
||||||
'wagtail.images',
|
"wagtail.search",
|
||||||
'wagtail.search',
|
"wagtail.admin",
|
||||||
'wagtail.admin',
|
"wagtail",
|
||||||
'wagtail',
|
"wagtail.api.v2",
|
||||||
'wagtail.api.v2',
|
"wagtailautocomplete",
|
||||||
'wagtailautocomplete',
|
"taggit",
|
||||||
|
"django.contrib.admin",
|
||||||
'taggit',
|
"django.contrib.auth",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.admin',
|
"django.contrib.sessions",
|
||||||
'django.contrib.auth',
|
"django.contrib.messages",
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
# 'raven.contrib.django.raven_compat',
|
# 'raven.contrib.django.raven_compat',
|
||||||
'whitenoise.runserver_nostatic',
|
"whitenoise.runserver_nostatic",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'django_filters',
|
"django_filters",
|
||||||
'graphene_django',
|
"graphene_django",
|
||||||
'django_extensions',
|
"django_extensions",
|
||||||
'compressor',
|
"compressor",
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
INSTALLED_APPS += ['wagtail.contrib.styleguide']
|
INSTALLED_APPS += ["wagtail.contrib.styleguide"]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||||
'django.middleware.gzip.GZipMiddleware',
|
"django.middleware.gzip.GZipMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.locale.LocaleMiddleware'
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Enable CORS for local development
|
# Enable CORS for local development
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
INSTALLED_APPS += ['corsheaders']
|
INSTALLED_APPS += ["corsheaders"]
|
||||||
MIDDLEWARE += ['corsheaders.middleware.CorsMiddleware']
|
MIDDLEWARE += ["corsheaders.middleware.CorsMiddleware"]
|
||||||
CORS_ORIGIN_WHITELIST = (
|
CORS_ORIGIN_WHITELIST = ("http://localhost:8080",)
|
||||||
'http://localhost:8080',
|
|
||||||
)
|
|
||||||
CORS_ALLOW_CREDENTIALS = True
|
CORS_ALLOW_CREDENTIALS = True
|
||||||
|
|
||||||
# enable silk for performance measuring
|
# enable silk for performance measuring
|
||||||
if ENABLE_SILKY:
|
if ENABLE_SILKY:
|
||||||
INSTALLED_APPS += ['silk']
|
INSTALLED_APPS += ["silk"]
|
||||||
MIDDLEWARE += ['silk.middleware.SilkyMiddleware', ]
|
MIDDLEWARE += [
|
||||||
|
"silk.middleware.SilkyMiddleware",
|
||||||
|
]
|
||||||
|
|
||||||
MIDDLEWARE += [
|
MIDDLEWARE += [
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||||
'wagtail.contrib.redirects.middleware.RedirectMiddleware',
|
"core.middleware.ThreadLocalMiddleware",
|
||||||
|
"core.middleware.CommonRedirectMiddleware",
|
||||||
'core.middleware.ThreadLocalMiddleware',
|
"core.middleware.UserLoggedInCookieMiddleWare",
|
||||||
'core.middleware.CommonRedirectMiddleware',
|
"oauth.middleware.user_has_license_middleware",
|
||||||
'core.middleware.UserLoggedInCookieMiddleWare',
|
|
||||||
'oauth.middleware.user_has_license_middleware',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'core.urls'
|
ROOT_URLCONF = "core.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [os.path.join(BASE_DIR, '..', 'client/dist'), os.path.join(BASE_DIR, 'templates')],
|
"DIRS": [
|
||||||
'APP_DIRS': True,
|
os.path.join(BASE_DIR, "..", "client/dist"),
|
||||||
'OPTIONS': {
|
os.path.join(BASE_DIR, "templates"),
|
||||||
'context_processors': [
|
],
|
||||||
'django.template.context_processors.debug',
|
"APP_DIRS": True,
|
||||||
'django.template.context_processors.request',
|
"OPTIONS": {
|
||||||
'django.contrib.auth.context_processors.auth',
|
"context_processors": [
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.template.context_processors.debug",
|
||||||
'core.context_processors.settings_context',
|
"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'
|
WSGI_APPLICATION = "core.wsgi.application"
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DATABASES = {
|
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}
|
||||||
'default': dj_database_url.config(conn_max_age=600)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Django custom user
|
# Django custom user
|
||||||
AUTH_USER_MODEL = 'users.User'
|
AUTH_USER_MODEL = "users.User"
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||||
|
|
@ -162,30 +158,30 @@ if WEAK_PASSWORDS:
|
||||||
else:
|
else:
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGOUT_REDIRECT_URL = '/login'
|
LOGOUT_REDIRECT_URL = "/login"
|
||||||
LOGIN_REDIRECT_URL = '/login'
|
LOGIN_REDIRECT_URL = "/login"
|
||||||
|
|
||||||
LOGIN_URL = LOGIN_REDIRECT_URL
|
LOGIN_URL = LOGIN_REDIRECT_URL
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'de'
|
LANGUAGE_CODE = "de"
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = "UTC"
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
|
@ -194,167 +190,161 @@ USE_L10N = True
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
LANGUAGES = [
|
LANGUAGES = [
|
||||||
('de', _('German')),
|
("de", _("German")),
|
||||||
('en', _('English')),
|
("en", _("English")),
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCALE_PATHS = [
|
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
||||||
os.path.join(BASE_DIR, 'locale')
|
|
||||||
]
|
|
||||||
|
|
||||||
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||||
|
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = "/static/"
|
||||||
|
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
os.path.join(BASE_DIR, '..', 'client/dist/static'),
|
os.path.join(BASE_DIR, "..", "client/dist/static"),
|
||||||
os.path.join(BASE_DIR, '..', 'client/src/assets'),
|
os.path.join(BASE_DIR, "..", "client/src/assets"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not TEST:
|
if not TEST:
|
||||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
|
|
||||||
COMPRESS_CSS_FILTERS = [
|
COMPRESS_CSS_FILTERS = [
|
||||||
# 'django_compressor_autoprefixer.AutoprefixerFilter',
|
# 'django_compressor_autoprefixer.AutoprefixerFilter',
|
||||||
'compressor.filters.cssmin.CSSMinFilter',
|
"compressor.filters.cssmin.CSSMinFilter",
|
||||||
]
|
]
|
||||||
|
|
||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
'compressor.finders.CompressorFinder',
|
"compressor.finders.CompressorFinder",
|
||||||
)
|
)
|
||||||
|
|
||||||
COMPRESS_PRECOMPILERS = (
|
COMPRESS_PRECOMPILERS = (("text/x-scss", "django_libsass.SassCompiler"),)
|
||||||
('text/x-scss', 'django_libsass.SassCompiler'),
|
|
||||||
)
|
|
||||||
|
|
||||||
COMPRESS_ENABLED = True
|
COMPRESS_ENABLED = True
|
||||||
|
|
||||||
if not DEBUG:
|
if not DEBUG:
|
||||||
COMPRESS_STORAGE = 'compressor.storage.GzipCompressorFileStorage'
|
COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
|
||||||
COMPRESS_OFFLINE = True
|
COMPRESS_OFFLINE = True
|
||||||
|
|
||||||
# AWS S3
|
# AWS S3
|
||||||
# http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
|
# http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
|
||||||
|
|
||||||
USE_AWS = bool_value(os.environ.get('USE_AWS'))
|
USE_AWS = bool_value(os.environ.get("USE_AWS"))
|
||||||
AWS_QUERYSTRING_AUTH = False
|
AWS_QUERYSTRING_AUTH = False
|
||||||
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
|
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
|
||||||
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
|
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")
|
||||||
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
|
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")
|
||||||
AWS_S3_FILE_OVERWRITE = False
|
AWS_S3_FILE_OVERWRITE = False
|
||||||
|
|
||||||
# use with cloudfront
|
# use with cloudfront
|
||||||
AWS_S3_CUSTOM_DOMAIN = '{}.s3-{}.amazonaws.com'.format(AWS_STORAGE_BUCKET_NAME,
|
AWS_S3_CUSTOM_DOMAIN = "{}.s3-{}.amazonaws.com".format(
|
||||||
os.environ.get('AWS_REGION', 'eu-west-1'))
|
AWS_STORAGE_BUCKET_NAME, os.environ.get("AWS_REGION", "eu-west-1")
|
||||||
|
)
|
||||||
if USE_AWS:
|
if USE_AWS:
|
||||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||||
# use with cloudfront
|
# use with cloudfront
|
||||||
MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN
|
MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN
|
||||||
else:
|
else:
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = "/media/"
|
||||||
MEDIA_ROOT = os.environ.get('DJANGO_MEDIAFILES', os.path.join(BASE_DIR, 'media'))
|
MEDIA_ROOT = os.environ.get("DJANGO_MEDIAFILES", os.path.join(BASE_DIR, "media"))
|
||||||
|
|
||||||
AWS_S3_OBJECT_PARAMETERS = {
|
AWS_S3_OBJECT_PARAMETERS = {
|
||||||
'CacheControl': 'max-age=86400',
|
"CacheControl": "max-age=86400",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Media Files
|
# Media Files
|
||||||
USE_404_FALLBACK_IMAGE = bool_value(os.environ.get('USE_404_FALLBACK_IMAGE', 'True'))
|
USE_404_FALLBACK_IMAGE = bool_value(os.environ.get("USE_404_FALLBACK_IMAGE", "True"))
|
||||||
|
|
||||||
# Logging Conf
|
# Logging Conf
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
"version": 1,
|
||||||
'formatters': {
|
"formatters": {
|
||||||
'verbose_format': {
|
"verbose_format": {
|
||||||
'format': '%(levelname)s %(asctime)s %(module)s %(name)s (%(process)d): %(message)s'
|
"format": "%(levelname)s %(asctime)s %(module)s %(name)s (%(process)d): %(message)s"
|
||||||
},
|
|
||||||
'simple_format': {
|
|
||||||
'format': '%(levelname)s %(name)s: %(message)s'
|
|
||||||
},
|
},
|
||||||
|
"simple_format": {"format": "%(levelname)s %(name)s: %(message)s"},
|
||||||
},
|
},
|
||||||
'disable_existing_loggers': not DEBUG,
|
"disable_existing_loggers": not DEBUG,
|
||||||
'handlers': {
|
"handlers": {
|
||||||
'mail_admins': {
|
"mail_admins": {
|
||||||
'level': 'CRITICAL',
|
"level": "CRITICAL",
|
||||||
'class': 'django.utils.log.AdminEmailHandler'
|
"class": "django.utils.log.AdminEmailHandler",
|
||||||
},
|
},
|
||||||
'console': {
|
"console": {
|
||||||
'level': 'DEBUG',
|
"level": "DEBUG",
|
||||||
'class': 'logging.StreamHandler',
|
"class": "logging.StreamHandler",
|
||||||
'stream': sys.stdout,
|
"stream": sys.stdout,
|
||||||
'formatter': 'simple_format'
|
"formatter": "simple_format",
|
||||||
},
|
},
|
||||||
# for automatic papertrail logging
|
# for automatic papertrail logging
|
||||||
'SysLog': {
|
"SysLog": {
|
||||||
'level': 'INFO',
|
"level": "INFO",
|
||||||
'class': 'logging.handlers.SysLogHandler',
|
"class": "logging.handlers.SysLogHandler",
|
||||||
'formatter': 'simple_format',
|
"formatter": "simple_format",
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'loggers': {
|
"loggers": {
|
||||||
'': {
|
"": {"handlers": ["console"], "level": "WARNING"},
|
||||||
'handlers': ['console'],
|
"skillbox": {
|
||||||
'level': 'WARNING'
|
"handlers": ["console", "SysLog"],
|
||||||
|
"level": os.environ.get("DJANGO_LOG_LEVEL", "INFO"),
|
||||||
|
"propagate": False,
|
||||||
},
|
},
|
||||||
'skillbox': {
|
"graphql": {"handlers": ["console"], "level": "WARNING", "propagate": False},
|
||||||
'handlers': ['console', 'SysLog'],
|
"django": {
|
||||||
'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'),
|
"handlers": ["console"],
|
||||||
'propagate': False
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
},
|
},
|
||||||
'graphql': {
|
"django.server": {
|
||||||
'handlers': ['console'],
|
"handlers": ["console"],
|
||||||
'level': 'WARNING',
|
"level": "WARNING",
|
||||||
'propagate': False
|
"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'):
|
if ENABLE_SENTRY and os.environ.get("SENTRY_DSN"):
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
from sentry_sdk.integrations.django import DjangoIntegration
|
from sentry_sdk.integrations.django import DjangoIntegration
|
||||||
|
from sentry_sdk.integrations.logging import ignore_logger
|
||||||
|
|
||||||
def before_send(event, hint):
|
def before_send(event, hint):
|
||||||
user = event['user']
|
user = event["user"]
|
||||||
id = user['id']
|
id = user["id"]
|
||||||
event['user'] = {'id': id}
|
event["user"] = {"id": id}
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
environment = os.environ.get("SENTRY_ENV", "localhost")
|
||||||
environment = os.environ.get('SENTRY_ENV', 'localhost')
|
sample_rate = os.environ.get("SENTRY_SAMPLE_RATE", 0.01)
|
||||||
|
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn=os.environ.get('SENTRY_DSN'),
|
dsn=os.environ.get("SENTRY_DSN"),
|
||||||
integrations=[DjangoIntegration()],
|
integrations=[DjangoIntegration()],
|
||||||
send_default_pii=True,
|
send_default_pii=True,
|
||||||
before_send=before_send,
|
before_send=before_send,
|
||||||
environment=environment
|
before_send_transaction=before_send,
|
||||||
|
environment=environment,
|
||||||
|
traces_sample_rate=sample_rate,
|
||||||
)
|
)
|
||||||
|
|
||||||
RAVEN_DSN_JS = os.environ.get('RAVEN_DSN_JS', '')
|
# 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")
|
||||||
|
|
||||||
|
RAVEN_DSN_JS = os.environ.get("RAVEN_DSN_JS", "")
|
||||||
|
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SCHEMA': 'api.schema.schema',
|
"SCHEMA": "api.schema.schema",
|
||||||
'SCHEMA_OUTPUT': 'schema.graphql'
|
"SCHEMA_OUTPUT": "schema.graphql",
|
||||||
|
"MIDDLEWARE": ["core.middleware.SentryMiddleware"],
|
||||||
}
|
}
|
||||||
|
|
||||||
# if DEBUG:
|
# if DEBUG:
|
||||||
|
|
@ -363,25 +353,27 @@ GRAPHENE = {
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
# http://docs.wagtail.io/en/v2.1/advanced_topics/settings.html?highlight=urls
|
# http://docs.wagtail.io/en/v2.1/advanced_topics/settings.html?highlight=urls
|
||||||
WAGTAIL_SITE_NAME = 'skillbox'
|
WAGTAIL_SITE_NAME = "skillbox"
|
||||||
WAGTAILSEARCH_BACKENDS = {
|
WAGTAILSEARCH_BACKENDS = {
|
||||||
'default': {
|
"default": {
|
||||||
'BACKEND': 'wagtail.search.backends.database',
|
"BACKEND": "wagtail.search.backends.database",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WAGTAILDOCS_DOCUMENT_MODEL = 'books.CustomDocument'
|
WAGTAILDOCS_DOCUMENT_MODEL = "books.CustomDocument"
|
||||||
|
|
||||||
|
|
||||||
GRAPHQL_QUERIES_DIR = os.path.join(BASE_DIR, '..', 'client', 'src', 'graphql', 'gql', 'queries')
|
GRAPHQL_QUERIES_DIR = os.path.join(
|
||||||
GRAPHQL_MUTATIONS_DIR = os.path.join(GRAPHQL_QUERIES_DIR, '../mutations')
|
BASE_DIR, "..", "client", "src", "graphql", "gql", "queries"
|
||||||
|
)
|
||||||
|
GRAPHQL_MUTATIONS_DIR = os.path.join(GRAPHQL_QUERIES_DIR, "../mutations")
|
||||||
|
|
||||||
DEFAULT_FROM_EMAIL = 'myskillbox <noreply@myskillbox.ch>'
|
DEFAULT_FROM_EMAIL = "myskillbox <noreply@myskillbox.ch>"
|
||||||
|
|
||||||
# Metanet Config
|
# Metanet Config
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
|
EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
||||||
else:
|
else:
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||||
|
|
||||||
ALLOW_BETA_LOGIN = True
|
ALLOW_BETA_LOGIN = True
|
||||||
|
|
||||||
|
|
@ -390,25 +382,25 @@ HEP_URL = os.environ.get("HEP_URL")
|
||||||
|
|
||||||
# HEP Oauth
|
# HEP Oauth
|
||||||
AUTHLIB_OAUTH_CLIENTS = {
|
AUTHLIB_OAUTH_CLIENTS = {
|
||||||
'hep': {
|
"hep": {
|
||||||
'client_id': os.environ.get("OAUTH_CLIENT_ID"),
|
"client_id": os.environ.get("OAUTH_CLIENT_ID"),
|
||||||
'client_secret': os.environ.get("OAUTH_CLIENT_SECRET"),
|
"client_secret": os.environ.get("OAUTH_CLIENT_SECRET"),
|
||||||
'request_token_url': None,
|
"request_token_url": None,
|
||||||
'request_token_params': None,
|
"request_token_params": None,
|
||||||
'access_token_url': os.environ.get("OAUTH_ACCESS_TOKEN_URL"),
|
"access_token_url": os.environ.get("OAUTH_ACCESS_TOKEN_URL"),
|
||||||
'access_token_params': None,
|
"access_token_params": None,
|
||||||
'refresh_token_url': None,
|
"refresh_token_url": None,
|
||||||
'authorize_url': os.environ.get("OAUTH_AUTHORIZE_URL"),
|
"authorize_url": os.environ.get("OAUTH_AUTHORIZE_URL"),
|
||||||
'api_base_url': os.environ.get("OAUTH_API_BASE_URL"),
|
"api_base_url": os.environ.get("OAUTH_API_BASE_URL"),
|
||||||
'client_kwargs': {
|
"client_kwargs": {
|
||||||
'scope': 'orders',
|
"scope": "orders",
|
||||||
'token_endpoint_auth_method': 'client_secret_post',
|
"token_endpoint_auth_method": "client_secret_post",
|
||||||
'token_placement': 'header',
|
"token_placement": "header",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PLATFORM = os.environ.get('APP_FLAVOR', 'myskillbox')
|
PLATFORM = os.environ.get("APP_FLAVOR", "myskillbox")
|
||||||
|
|
||||||
OAUTH_LOCAL_REDIRECT_URI = os.environ.get("OAUTH_LOCAL_REDIRECT_URI")
|
OAUTH_LOCAL_REDIRECT_URI = os.environ.get("OAUTH_LOCAL_REDIRECT_URI")
|
||||||
|
|
||||||
|
|
@ -419,12 +411,12 @@ TASKBASE_SUPERPASSWORD = os.environ.get("TASKBASE_SUPERPASSWORD")
|
||||||
TASKBASE_BASEURL = os.environ.get("TASKBASE_BASEURL")
|
TASKBASE_BASEURL = os.environ.get("TASKBASE_BASEURL")
|
||||||
ENABLE_SPELLCHECK = True if TASKBASE_BASEURL else False
|
ENABLE_SPELLCHECK = True if TASKBASE_BASEURL else False
|
||||||
|
|
||||||
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
|
TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner"
|
||||||
TEST_OUTPUT_DIR = './test-reports/'
|
TEST_OUTPUT_DIR = "./test-reports/"
|
||||||
TEST_OUTPUT_VERBOSE = 1
|
TEST_OUTPUT_VERBOSE = 1
|
||||||
|
|
||||||
# new default in Django 3.0, making it explicit to facilitate bug hunting
|
# new default in Django 3.0, making it explicit to facilitate bug hunting
|
||||||
X_FRAME_OPTIONS = 'DENY'
|
X_FRAME_OPTIONS = "DENY"
|
||||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||||
# Django 3.2 uses BitAutoField by default, we keep things the old way
|
# Django 3.2 uses BitAutoField by default, we keep things the old way
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.http.request import HttpRequest
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
@ -5,10 +6,34 @@ from django.http.response import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from graphene_django.views import GraphQLView
|
from graphene_django.views import GraphQLView
|
||||||
|
from sentry_sdk.api import start_transaction
|
||||||
from wagtail.admin.views.pages.listing import index as wagtailadmin_explore
|
from wagtail.admin.views.pages.listing import index as wagtailadmin_explore
|
||||||
|
|
||||||
|
|
||||||
class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
|
# For sentry perfomance monitoring
|
||||||
|
# taken from https://jerrynsh.com/how-to-monitor-python-graphql-api-with-sentry/
|
||||||
|
class SentryGraphQLView(GraphQLView):
|
||||||
|
def execute_graphql_request(
|
||||||
|
self,
|
||||||
|
request: HttpRequest,
|
||||||
|
data,
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
operation_name,
|
||||||
|
show_graphiql,
|
||||||
|
):
|
||||||
|
operation_type = (
|
||||||
|
self.get_backend(request)
|
||||||
|
.document_from_string(self.schema, query)
|
||||||
|
.get_operation_type(operation_name)
|
||||||
|
)
|
||||||
|
with start_transaction(op=operation_type, name=operation_name):
|
||||||
|
return super().execute_graphql_request(
|
||||||
|
request, data, query, variables, operation_name, show_graphiql
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateGraphQLView(LoginRequiredMixin, SentryGraphQLView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -16,22 +41,24 @@ class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
|
||||||
def home(request):
|
def home(request):
|
||||||
if settings.SERVE_VIA_WEBPACK:
|
if settings.SERVE_VIA_WEBPACK:
|
||||||
try:
|
try:
|
||||||
res = requests.get('http://localhost:8080{}'.format(request.get_full_path()))
|
res = requests.get(
|
||||||
|
"http://localhost:8080{}".format(request.get_full_path())
|
||||||
|
)
|
||||||
headers = res.headers
|
headers = res.headers
|
||||||
content_type = headers.get('content-type', 'text/html')
|
content_type = headers.get("content-type", "text/html")
|
||||||
return HttpResponse(res.text, content_type=content_type)
|
return HttpResponse(res.text, content_type=content_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Can not connect to dev server at http://localhost:8080:', e)
|
print("Can not connect to dev server at http://localhost:8080:", e)
|
||||||
|
|
||||||
return render(request, 'index.html', {})
|
return render(request, "index.html", {})
|
||||||
|
|
||||||
|
|
||||||
def override_wagtailadmin_explore_default_ordering(request, parent_page_id):
|
def override_wagtailadmin_explore_default_ordering(request, parent_page_id):
|
||||||
"""
|
"""
|
||||||
Wrap Wagtail's explore view to change the default ordering
|
Wrap Wagtail's explore view to change the default ordering
|
||||||
"""
|
"""
|
||||||
if request.method == 'GET' and 'ordering' not in request.GET:
|
if request.method == "GET" and "ordering" not in request.GET:
|
||||||
# Display reordering handles by default for children of all Page types.
|
# Display reordering handles by default for children of all Page types.
|
||||||
return HttpResponseRedirect(request.path_info + '?ordering=ord')
|
return HttpResponseRedirect(request.path_info + "?ordering=ord")
|
||||||
|
|
||||||
return wagtailadmin_explore(request, parent_page_id=parent_page_id)
|
return wagtailadmin_explore(request, parent_page_id=parent_page_id)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue