Add production config: WhiteNoise, CORS, security headers, custom Swagger UI
This commit is contained in:
parent
d449b14d6a
commit
a61c7f3572
6 changed files with 101 additions and 4 deletions
|
|
@ -2,3 +2,4 @@ DEBUG=1
|
|||
SECRET_KEY=change-me-in-production
|
||||
DATABASE_URL=postgres://locflow:locflow@db:5432/locflow
|
||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||
UMAMI_WEBSITE_ID=
|
||||
|
|
|
|||
52
locflow/settings.py
Normal file → Executable file
52
locflow/settings.py
Normal file → Executable file
|
|
@ -36,6 +36,7 @@ INSTALLED_APPS = [
|
|||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'rest_framework_simplejwt.token_blacklist',
|
||||
'corsheaders',
|
||||
'django_filters',
|
||||
'drf_spectacular',
|
||||
# Local apps
|
||||
|
|
@ -46,7 +47,9 @@ INSTALLED_APPS = [
|
|||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
|
|
@ -60,7 +63,7 @@ ROOT_URLCONF = 'locflow.urls'
|
|||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
|
|
@ -76,8 +79,9 @@ TEMPLATES = [
|
|||
WSGI_APPLICATION = 'locflow.wsgi.application'
|
||||
|
||||
# Database
|
||||
# Parse DATABASE_URL: postgres://user:pass@host:port/dbname
|
||||
# Supports DATABASE_URL format or individual POSTGRES_* env vars
|
||||
DATABASE_URL = os.getenv('DATABASE_URL', '')
|
||||
POSTGRES_HOST = os.getenv('POSTGRES_HOST', '')
|
||||
|
||||
if DATABASE_URL:
|
||||
_db_url = urlparse(DATABASE_URL)
|
||||
|
|
@ -91,6 +95,17 @@ if DATABASE_URL:
|
|||
'PORT': str(_db_url.port or 5432),
|
||||
}
|
||||
}
|
||||
elif POSTGRES_HOST:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': os.getenv('POSTGRES_DB', 'locflow'),
|
||||
'USER': os.getenv('POSTGRES_USER', 'locflow'),
|
||||
'PASSWORD': os.getenv('POSTGRES_PASSWORD', ''),
|
||||
'HOST': POSTGRES_HOST,
|
||||
'PORT': os.getenv('POSTGRES_PORT', '5432'),
|
||||
}
|
||||
}
|
||||
else:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
|
|
@ -162,10 +177,43 @@ SPECTACULAR_SETTINGS = {
|
|||
},
|
||||
},
|
||||
},
|
||||
'SWAGGER_UI_SETTINGS': {
|
||||
'deepLinking': True,
|
||||
'persistAuthorization': True,
|
||||
},
|
||||
}
|
||||
|
||||
# CORS
|
||||
CORS_ALLOWED_ORIGINS = os.getenv(
|
||||
'CORS_ALLOWED_ORIGINS', 'http://localhost:3000'
|
||||
).split(',')
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
# Translation Memory
|
||||
TRANSLATION_MEMORY = {
|
||||
'MIN_SIMILARITY': 0.7,
|
||||
'MAX_RESULTS': 10,
|
||||
}
|
||||
|
||||
# WhiteNoise static files compression
|
||||
STORAGES = {
|
||||
'staticfiles': {
|
||||
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
|
||||
},
|
||||
}
|
||||
|
||||
# Production security settings (when behind Traefik reverse proxy)
|
||||
if not DEBUG:
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
'https://locflow.richardnixon.dev',
|
||||
]
|
||||
|
||||
# Umami Analytics
|
||||
UMAMI_WEBSITE_ID = os.getenv('UMAMI_WEBSITE_ID', '')
|
||||
|
|
|
|||
6
locflow/urls.py
Normal file → Executable file
6
locflow/urls.py
Normal file → Executable file
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
from drf_spectacular.views import SpectacularAPIView
|
||||
|
||||
from locflow.views import swagger_ui
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
|
@ -11,5 +13,5 @@ urlpatterns = [
|
|||
path('api/v1/', include('apps.resources.urls')),
|
||||
path('api/v1/', include('apps.translations.urls')),
|
||||
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
|
||||
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
||||
path('api/docs/', swagger_ui, name='swagger-ui'),
|
||||
]
|
||||
|
|
|
|||
10
locflow/views.py
Executable file
10
locflow/views.py
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
from django.conf import settings
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def swagger_ui(request):
|
||||
return render(request, 'swagger_ui.html', {
|
||||
'schema_url': reverse('schema'),
|
||||
'umami_website_id': getattr(settings, 'UMAMI_WEBSITE_ID', ''),
|
||||
})
|
||||
|
|
@ -2,11 +2,14 @@ django>=5.1,<5.2
|
|||
djangorestframework>=3.15,<4.0
|
||||
psycopg2-binary>=2.9
|
||||
gunicorn>=22.0
|
||||
whitenoise>=6.5
|
||||
polib>=1.2
|
||||
django-cors-headers>=4.3
|
||||
django-filter>=24.0
|
||||
python-dotenv>=1.0
|
||||
drf-spectacular>=0.27
|
||||
djangorestframework-simplejwt>=5.3,<6.0
|
||||
requests>=2.31
|
||||
pytest>=8.0
|
||||
pytest-django>=4.8
|
||||
factory-boy>=3.3
|
||||
|
|
|
|||
33
templates/swagger_ui.html
Normal file
33
templates/swagger_ui.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LocFlow API</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" >
|
||||
{% if umami_website_id %}
|
||||
<script defer src="https://analytics.richardnixon.dev/script.js" data-website-id="{{ umami_website_id }}"></script>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"> </script>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "{{ schema_url }}",
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
layout: "StandaloneLayout",
|
||||
deepLinking: true,
|
||||
persistAuthorization: true,
|
||||
})
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue