mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 12:31:22 -05:00
Implement robust security headers, including CSP with nonces, and integrate comprehensive SEO meta tags into the base template and homepage. Add inline styles for CSP compliance and improve theme management script for immediate theme application. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 48ecdb60-d0f0-4b75-95c9-34e409ef35fb Replit-Commit-Checkpoint-Type: intermediate_checkpoint
528 lines
18 KiB
Python
528 lines
18 KiB
Python
"""
|
|
Base Django settings for thrillwiki project.
|
|
Common settings shared across all environments.
|
|
"""
|
|
|
|
from datetime import timedelta
|
|
import sys
|
|
import warnings
|
|
from pathlib import Path
|
|
from decouple import config
|
|
|
|
# Suppress django-allauth deprecation warnings for dj_rest_auth compatibility
|
|
# TODO: Remove this once dj_rest_auth is updated to work with the new ACCOUNT_SIGNUP_FIELDS format
|
|
warnings.filterwarnings(
|
|
"ignore",
|
|
message=r"app_settings\.(USERNAME|EMAIL)_REQUIRED is deprecated",
|
|
module="dj_rest_auth.registration.serializers"
|
|
)
|
|
|
|
# Initialize environment variables with better defaults
|
|
|
|
DEBUG = config("DEBUG", default=True)
|
|
SECRET_KEY = config("SECRET_KEY")
|
|
ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost,127.0.0.1", cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
|
DATABASE_URL = config("DATABASE_URL")
|
|
CACHE_URL = config("CACHE_URL", default="locmem://")
|
|
EMAIL_URL = config("EMAIL_URL", default="console://")
|
|
REDIS_URL = config("REDIS_URL", default="redis://127.0.0.1:6379/1")
|
|
CORS_ALLOWED_ORIGINS = config("CORS_ALLOWED_ORIGINS", default="", cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
|
API_RATE_LIMIT_PER_MINUTE = config("API_RATE_LIMIT_PER_MINUTE", default=60)
|
|
API_RATE_LIMIT_PER_HOUR = config("API_RATE_LIMIT_PER_HOUR", default=1000)
|
|
CACHE_MIDDLEWARE_SECONDS = config("CACHE_MIDDLEWARE_SECONDS", default=300)
|
|
CACHE_MIDDLEWARE_KEY_PREFIX = config(
|
|
"CACHE_MIDDLEWARE_KEY_PREFIX", default="thrillwiki"
|
|
)
|
|
GDAL_LIBRARY_PATH = config(
|
|
"GDAL_LIBRARY_PATH", default="/nix/store/c5y314zvvrbr9lx4wh06ibl1b5c07x92-gdal-3.11.0/lib/libgdal.so"
|
|
)
|
|
GEOS_LIBRARY_PATH = config(
|
|
"GEOS_LIBRARY_PATH", default="/nix/store/r5sgxqxrwfvms97v4v239qbivwsmdfjf-geos-3.13.1/lib/libgeos_c.so"
|
|
)
|
|
|
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
|
|
# Add apps directory to sys.path so Django can find the apps
|
|
apps_dir = BASE_DIR / "apps"
|
|
if apps_dir.exists() and str(apps_dir) not in sys.path:
|
|
sys.path.insert(0, str(apps_dir))
|
|
|
|
# SECURITY WARNING: keep the secret key used in production secret!
|
|
SECRET_KEY = config("SECRET_KEY")
|
|
|
|
# Allowed hosts (already configured above)
|
|
|
|
# CSRF trusted origins
|
|
CSRF_TRUSTED_ORIGINS = config(
|
|
"CSRF_TRUSTED_ORIGINS", default="", cast=lambda v: [s.strip() for s in v.split(',') if s.strip()]
|
|
)
|
|
|
|
# Application definition
|
|
DJANGO_APPS = [
|
|
"django.contrib.admin",
|
|
"django.contrib.auth",
|
|
"django.contrib.contenttypes",
|
|
"django.contrib.sessions",
|
|
"django.contrib.messages",
|
|
"django.contrib.staticfiles",
|
|
"django.contrib.sites",
|
|
# "django.contrib.gis", # GeoDjango - Disabled temporarily for setup
|
|
]
|
|
|
|
THIRD_PARTY_APPS = [
|
|
# Django Cloudflare Images Toolkit - moved to top to avoid circular imports
|
|
"django_cloudflareimages_toolkit",
|
|
"rest_framework", # Django REST Framework
|
|
# Token authentication (kept for backward compatibility)
|
|
"rest_framework.authtoken",
|
|
"rest_framework_simplejwt", # JWT authentication
|
|
"rest_framework_simplejwt.token_blacklist", # JWT token blacklist
|
|
"dj_rest_auth", # REST authentication with JWT support
|
|
"dj_rest_auth.registration", # REST registration support
|
|
"drf_spectacular", # OpenAPI 3.0 documentation
|
|
"corsheaders", # CORS headers for API
|
|
"pghistory", # django-pghistory
|
|
"pgtrigger", # Required by django-pghistory
|
|
"allauth",
|
|
"allauth.account",
|
|
"allauth.socialaccount",
|
|
"allauth.socialaccount.providers.google",
|
|
"allauth.socialaccount.providers.discord",
|
|
"django_cleanup",
|
|
"django_filters",
|
|
"django_htmx",
|
|
"whitenoise",
|
|
"django_tailwind_cli",
|
|
"autocomplete", # Django HTMX Autocomplete
|
|
"django_cotton", # Django Cotton for component templates
|
|
"health_check", # Health checks
|
|
"health_check.db",
|
|
"health_check.cache",
|
|
"health_check.storage",
|
|
"health_check.contrib.migrations",
|
|
"health_check.contrib.redis",
|
|
"django_celery_beat", # Celery beat scheduler
|
|
"django_celery_results", # Celery result backend
|
|
"django_extensions", # Django Extensions for enhanced development tools
|
|
]
|
|
|
|
LOCAL_APPS = [
|
|
"apps.core",
|
|
"apps.accounts",
|
|
"apps.parks",
|
|
"apps.rides",
|
|
"django_forwardemail", # New PyPI package for email service
|
|
"apps.moderation",
|
|
]
|
|
|
|
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
|
|
|
MIDDLEWARE = [
|
|
"django.middleware.cache.UpdateCacheMiddleware",
|
|
"corsheaders.middleware.CorsMiddleware", # CORS middleware for API
|
|
"django.middleware.security.SecurityMiddleware",
|
|
"apps.core.middleware.security_headers.SecurityHeadersMiddleware", # Modern security headers
|
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
"django.middleware.common.CommonMiddleware",
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
"apps.core.middleware.analytics.PgHistoryContextMiddleware", # Add history context tracking
|
|
"allauth.account.middleware.AccountMiddleware",
|
|
"django.middleware.cache.FetchFromCacheMiddleware",
|
|
"django_htmx.middleware.HtmxMiddleware",
|
|
]
|
|
|
|
ROOT_URLCONF = "thrillwiki.urls"
|
|
|
|
# Add a toggle to enable/disable Django template support via env var
|
|
# Use a distinct environment variable name so it doesn't collide with Django's TEMPLATES setting
|
|
TEMPLATES_ENABLED = config("TEMPLATES_ENABLED", default=True, cast=bool)
|
|
|
|
# Conditional TEMPLATES configuration
|
|
if TEMPLATES_ENABLED:
|
|
TEMPLATES = [
|
|
{
|
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
"DIRS": [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",
|
|
"apps.moderation.context_processors.moderation_access",
|
|
]
|
|
},
|
|
}
|
|
]
|
|
else:
|
|
# When templates are disabled, we still need APP_DIRS=True for DRF Spectacular to work
|
|
TEMPLATES = [
|
|
{
|
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
"APP_DIRS": True, # Changed from False to True to support DRF Spectacular templates
|
|
"DIRS": [BASE_DIR / "templates/" / "404"],
|
|
"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",
|
|
"apps.moderation.context_processors.moderation_access",
|
|
]
|
|
},
|
|
}
|
|
]
|
|
|
|
WSGI_APPLICATION = "thrillwiki.wsgi.application"
|
|
|
|
# Cloudflare Images Settings - Updated for django-cloudflareimages-toolkit
|
|
CLOUDFLARE_IMAGES = {
|
|
'ACCOUNT_ID': config("CLOUDFLARE_IMAGES_ACCOUNT_ID"),
|
|
'API_TOKEN': config("CLOUDFLARE_IMAGES_API_TOKEN"),
|
|
'ACCOUNT_HASH': config("CLOUDFLARE_IMAGES_ACCOUNT_HASH"),
|
|
|
|
# Optional settings
|
|
'DEFAULT_VARIANT': 'public',
|
|
'UPLOAD_TIMEOUT': 300,
|
|
'WEBHOOK_SECRET': config("CLOUDFLARE_IMAGES_WEBHOOK_SECRET", default=""),
|
|
'CLEANUP_EXPIRED_HOURS': 24,
|
|
'MAX_FILE_SIZE': 10 * 1024 * 1024, # 10MB
|
|
'ALLOWED_FORMATS': ['jpeg', 'png', 'gif', 'webp'],
|
|
'REQUIRE_SIGNED_URLS': False,
|
|
'DEFAULT_METADATA': {},
|
|
}
|
|
|
|
# Storage configuration
|
|
STORAGES = {
|
|
"default": {
|
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
|
"OPTIONS": {
|
|
"location": str(BASE_DIR.parent / "shared" / "media"),
|
|
},
|
|
},
|
|
"staticfiles": {
|
|
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
|
"OPTIONS": {
|
|
"location": str(BASE_DIR / "staticfiles"),
|
|
},
|
|
},
|
|
}
|
|
|
|
# Password validation
|
|
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",
|
|
},
|
|
]
|
|
|
|
# Internationalization
|
|
LANGUAGE_CODE = "en-us"
|
|
TIME_ZONE = "America/New_York"
|
|
USE_I18N = True
|
|
USE_TZ = True
|
|
|
|
# Static files (CSS, JavaScript, Images)
|
|
STATIC_URL = "static/"
|
|
STATICFILES_DIRS = [BASE_DIR / "static"]
|
|
STATIC_ROOT = BASE_DIR / "staticfiles"
|
|
|
|
# Media files - point to shared/media directory
|
|
MEDIA_URL = "/media/"
|
|
MEDIA_ROOT = BASE_DIR.parent / "shared" / "media"
|
|
|
|
# Default primary key field type
|
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
|
|
# Authentication settings
|
|
AUTHENTICATION_BACKENDS = [
|
|
"django.contrib.auth.backends.ModelBackend",
|
|
"allauth.account.auth_backends.AuthenticationBackend",
|
|
]
|
|
|
|
# django-allauth settings
|
|
SITE_ID = 1
|
|
|
|
# CORRECTED: Django allauth still expects the old format with asterisks for required fields
|
|
# The deprecation warnings are from dj_rest_auth, not our configuration
|
|
ACCOUNT_SIGNUP_FIELDS = ["email*", "username*", "password1*", "password2*"]
|
|
|
|
ACCOUNT_LOGIN_METHODS = {"email", "username"}
|
|
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
|
ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE = True
|
|
ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True
|
|
ACCOUNT_REAUTHENTICATION_REQUIRED = True
|
|
ACCOUNT_EMAIL_NOTIFICATIONS = True
|
|
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
|
LOGIN_REDIRECT_URL = "/"
|
|
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
|
|
|
|
# Custom adapters
|
|
ACCOUNT_ADAPTER = "apps.accounts.adapters.CustomAccountAdapter"
|
|
SOCIALACCOUNT_ADAPTER = "apps.accounts.adapters.CustomSocialAccountAdapter"
|
|
|
|
# Social account settings
|
|
SOCIALACCOUNT_PROVIDERS = {
|
|
"google": {
|
|
"SCOPE": [
|
|
"profile",
|
|
"email",
|
|
],
|
|
"AUTH_PARAMS": {"access_type": "online"},
|
|
},
|
|
"discord": {
|
|
"SCOPE": ["identify", "email"],
|
|
"OAUTH_PKCE_ENABLED": True,
|
|
},
|
|
}
|
|
|
|
# Additional social account settings
|
|
SOCIALACCOUNT_LOGIN_ON_GET = True
|
|
SOCIALACCOUNT_AUTO_SIGNUP = False
|
|
SOCIALACCOUNT_STORE_TOKENS = True
|
|
|
|
# Custom User Model
|
|
AUTH_USER_MODEL = "accounts.User"
|
|
|
|
# Autocomplete configuration
|
|
AUTOCOMPLETE_BLOCK_UNAUTHENTICATED = False
|
|
|
|
# Tailwind configuration
|
|
TAILWIND_CLI_CONFIG_FILE = "tailwind.config.js"
|
|
TAILWIND_CLI_SRC_CSS = "static/css/src/input.css"
|
|
TAILWIND_CLI_DIST_CSS = "css/tailwind.css"
|
|
|
|
# Test runner
|
|
TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
|
|
|
# Road Trip Service Settings
|
|
ROADTRIP_CACHE_TIMEOUT = 3600 * 24 # 24 hours for geocoding
|
|
ROADTRIP_ROUTE_CACHE_TIMEOUT = 3600 * 6 # 6 hours for routes
|
|
ROADTRIP_MAX_REQUESTS_PER_SECOND = 1 # Respect OSM rate limits
|
|
ROADTRIP_USER_AGENT = config("ROADTRIP_USER_AGENT")
|
|
ROADTRIP_REQUEST_TIMEOUT = 10 # seconds
|
|
ROADTRIP_MAX_RETRIES = 3
|
|
ROADTRIP_BACKOFF_FACTOR = 2
|
|
|
|
# Frontend URL Configuration
|
|
FRONTEND_DOMAIN = config("FRONTEND_DOMAIN", default="https://thrillwiki.com")
|
|
|
|
# ForwardEmail Configuration
|
|
FORWARD_EMAIL_BASE_URL = config(
|
|
"FORWARD_EMAIL_BASE_URL", default="https://api.forwardemail.net")
|
|
FORWARD_EMAIL_API_KEY = config("FORWARD_EMAIL_API_KEY", default="")
|
|
FORWARD_EMAIL_DOMAIN = config("FORWARD_EMAIL_DOMAIN", default="")
|
|
|
|
# Django REST Framework Settings
|
|
REST_FRAMEWORK = {
|
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
"rest_framework.authentication.SessionAuthentication",
|
|
"rest_framework.authentication.TokenAuthentication", # Kept for backward compatibility
|
|
],
|
|
"DEFAULT_PERMISSION_CLASSES": [
|
|
"rest_framework.permissions.IsAuthenticated",
|
|
],
|
|
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
|
"PAGE_SIZE": 20,
|
|
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.AcceptHeaderVersioning",
|
|
"DEFAULT_VERSION": "v1",
|
|
"ALLOWED_VERSIONS": ["v1"],
|
|
"DEFAULT_RENDERER_CLASSES": [
|
|
"rest_framework.renderers.JSONRenderer",
|
|
"rest_framework.renderers.BrowsableAPIRenderer",
|
|
],
|
|
"DEFAULT_PARSER_CLASSES": [
|
|
"rest_framework.parsers.JSONParser",
|
|
"rest_framework.parsers.FormParser",
|
|
"rest_framework.parsers.MultiPartParser",
|
|
],
|
|
"EXCEPTION_HANDLER": "apps.core.api.exceptions.custom_exception_handler",
|
|
"DEFAULT_FILTER_BACKENDS": [
|
|
"django_filters.rest_framework.DjangoFilterBackend",
|
|
"rest_framework.filters.SearchFilter",
|
|
"rest_framework.filters.OrderingFilter",
|
|
],
|
|
"TEST_REQUEST_DEFAULT_FORMAT": "json",
|
|
"NON_FIELD_ERRORS_KEY": "non_field_errors",
|
|
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
|
}
|
|
|
|
# CORS Settings for API
|
|
|
|
CORS_ALLOW_CREDENTIALS = True
|
|
CORS_ALLOW_ALL_ORIGINS = config(
|
|
"CORS_ALLOW_ALL_ORIGINS", default=False, cast=bool
|
|
) # type: ignore[arg-type]
|
|
|
|
|
|
API_RATE_LIMIT_PER_MINUTE = config(
|
|
"API_RATE_LIMIT_PER_MINUTE", default=60, cast=int
|
|
) # type: ignore[arg-type]
|
|
API_RATE_LIMIT_PER_HOUR = config(
|
|
"API_RATE_LIMIT_PER_HOUR", default=1000, cast=int
|
|
) # type: ignore[arg-type]
|
|
SPECTACULAR_SETTINGS = {
|
|
"TITLE": "ThrillWiki API",
|
|
"DESCRIPTION": "Comprehensive theme park and ride information API",
|
|
"VERSION": "1.0.0",
|
|
"SERVE_INCLUDE_SCHEMA": False,
|
|
"COMPONENT_SPLIT_REQUEST": True,
|
|
"TAGS": [
|
|
{"name": "Parks", "description": "Theme park operations"},
|
|
{"name": "Rides", "description": "Ride information and management"},
|
|
{
|
|
"name": "Statistics",
|
|
"description": "Statistical endpoints providing aggregated data and insights",
|
|
},
|
|
],
|
|
"SCHEMA_PATH_PREFIX": "/api/",
|
|
"DEFAULT_GENERATOR_CLASS": "drf_spectacular.generators.SchemaGenerator",
|
|
"DEFAULT_AUTO_SCHEMA": "drf_spectacular.openapi.AutoSchema",
|
|
"PREPROCESSING_HOOKS": [
|
|
"api.v1.schema.custom_preprocessing_hook",
|
|
],
|
|
# "POSTPROCESSING_HOOKS": [
|
|
# "api.v1.schema.custom_postprocessing_hook",
|
|
# ],
|
|
"SERVE_PERMISSIONS": ["rest_framework.permissions.AllowAny"],
|
|
"SWAGGER_UI_SETTINGS": {
|
|
"deepLinking": True,
|
|
"persistAuthorization": True,
|
|
"displayOperationId": False,
|
|
"displayRequestDuration": True,
|
|
},
|
|
"REDOC_UI_SETTINGS": {
|
|
"hideDownloadButton": False,
|
|
"hideHostname": False,
|
|
"hideLoading": False,
|
|
"hideSchemaPattern": True,
|
|
"scrollYOffset": 0,
|
|
"theme": {"colors": {"primary": {"main": "#1976d2"}}},
|
|
},
|
|
}
|
|
|
|
# Health Check Configuration
|
|
HEALTH_CHECK = {
|
|
"DISK_USAGE_MAX": 90, # Fail if disk usage is over 90%
|
|
"MEMORY_MIN": 100, # Fail if less than 100MB available memory
|
|
}
|
|
|
|
# Custom health check backends
|
|
HEALTH_CHECK_BACKENDS = [
|
|
"health_check.db",
|
|
"health_check.cache",
|
|
"health_check.storage",
|
|
"core.health_checks.custom_checks.CacheHealthCheck",
|
|
"core.health_checks.custom_checks.DatabasePerformanceCheck",
|
|
"core.health_checks.custom_checks.ApplicationHealthCheck",
|
|
"core.health_checks.custom_checks.ExternalServiceHealthCheck",
|
|
"core.health_checks.custom_checks.DiskSpaceHealthCheck",
|
|
]
|
|
|
|
# Enhanced Cache Configuration
|
|
DJANGO_REDIS_CACHE_BACKEND = "django_redis.cache.RedisCache"
|
|
DJANGO_REDIS_CLIENT_CLASS = "django_redis.client.DefaultClient"
|
|
|
|
CACHES = {
|
|
"default": {
|
|
"BACKEND": DJANGO_REDIS_CACHE_BACKEND,
|
|
# pyright: ignore[reportArgumentType]
|
|
# type: ignore
|
|
"LOCATION": config("REDIS_URL", default="redis://127.0.0.1:6379/1"),
|
|
"OPTIONS": {
|
|
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
|
|
"PARSER_CLASS": "redis.connection.HiredisParser",
|
|
"CONNECTION_POOL_CLASS": "redis.BlockingConnectionPool",
|
|
"CONNECTION_POOL_CLASS_KWARGS": {
|
|
"max_connections": 50,
|
|
"timeout": 20,
|
|
},
|
|
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
|
|
"IGNORE_EXCEPTIONS": True,
|
|
},
|
|
"KEY_PREFIX": "thrillwiki",
|
|
"VERSION": 1,
|
|
},
|
|
"sessions": {
|
|
"BACKEND": DJANGO_REDIS_CACHE_BACKEND,
|
|
"LOCATION": config("REDIS_URL", default="redis://127.0.0.1:6379/2"),
|
|
"OPTIONS": {
|
|
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
|
|
},
|
|
},
|
|
"api": {
|
|
"BACKEND": DJANGO_REDIS_CACHE_BACKEND,
|
|
"LOCATION": config("REDIS_URL", default="redis://127.0.0.1:6379/3"),
|
|
"OPTIONS": {
|
|
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
|
|
},
|
|
},
|
|
}
|
|
|
|
# Use Redis for sessions
|
|
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
|
SESSION_CACHE_ALIAS = "sessions"
|
|
SESSION_COOKIE_AGE = 86400 # 24 hours
|
|
|
|
# Cache middleware settings
|
|
CACHE_MIDDLEWARE_SECONDS = 300 # 5 minutes
|
|
CACHE_MIDDLEWARE_KEY_PREFIX = "thrillwiki"
|
|
|
|
# JWT Settings
|
|
|
|
SIMPLE_JWT = {
|
|
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60), # 1 hour
|
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=7), # 7 days
|
|
"ROTATE_REFRESH_TOKENS": True,
|
|
"BLACKLIST_AFTER_ROTATION": True,
|
|
"UPDATE_LAST_LOGIN": True,
|
|
"ALGORITHM": "HS256",
|
|
"SIGNING_KEY": SECRET_KEY,
|
|
"VERIFYING_KEY": None,
|
|
"AUDIENCE": None,
|
|
"ISSUER": None,
|
|
"JWK_URL": None,
|
|
"LEEWAY": 0,
|
|
"AUTH_HEADER_TYPES": ("Bearer",),
|
|
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
|
|
"USER_ID_FIELD": "id",
|
|
"USER_ID_CLAIM": "user_id",
|
|
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
|
|
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
|
|
"TOKEN_TYPE_CLAIM": "token_type",
|
|
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
|
|
"JTI_CLAIM": "jti",
|
|
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
|
|
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=60),
|
|
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
|
|
}
|
|
|
|
# dj-rest-auth settings
|
|
REST_AUTH = {
|
|
"USE_JWT": True,
|
|
"JWT_AUTH_COOKIE": "thrillwiki-auth",
|
|
"JWT_AUTH_REFRESH_COOKIE": "thrillwiki-refresh",
|
|
"JWT_AUTH_SECURE": not DEBUG, # Use secure cookies in production
|
|
"JWT_AUTH_HTTPONLY": True,
|
|
"JWT_AUTH_SAMESITE": "Lax",
|
|
"JWT_AUTH_RETURN_EXPIRATION": True,
|
|
"JWT_TOKEN_CLAIMS_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
|
|
}
|