refactor: Update environment variable handling in Django settings for consistency and security

This commit is contained in:
pacnpal
2025-08-26 15:21:00 -04:00
parent e4e36c7899
commit b627aed65d
3 changed files with 53 additions and 55 deletions

View File

@@ -3,32 +3,32 @@ Base Django settings for thrillwiki project.
Common settings shared across all environments. Common settings shared across all environments.
""" """
import environ # type: ignore[import] import environ
import sys import sys
from pathlib import Path from pathlib import Path
from decouple import config from decouple import config
# Initialize environment variables with better defaults # Initialize environment variables with better defaults
env = environ.Env(
DEBUG=(bool, False), DEBUG = config('DEBUG', default=True)
SECRET_KEY=(str, "django-insecure-change-this-in-production"), SECRET_KEY = config('SECRET_KEY')
ALLOWED_HOSTS=(list, ["localhost", "127.0.0.1"]), ALLOWED_HOSTS = config('ALLOWED_HOSTS')
DATABASE_URL=( DATABASE_URL = config('DATABASE_URL')
str, CACHE_URL = config('CACHE_URL', default="locmem://")
"postgis://thrillwiki_user:thrillwiki@localhost:5432/thrillwiki_test_db", EMAIL_URL = config('EMAIL_URL', default="console://")
), REDIS_URL = config('REDIS_URL', default="redis://127.0.0.1:6379/1")
CACHE_URL=(str, "locmem://"), CORS_ALLOW_ALL_ORIGINS = config('CORS_ALLOW_ALL_ORIGINS', default=False, cast=bool)
EMAIL_URL=(str, "console://"), CORS_ALLOWED_ORIGINS = config('CORS_ALLOWED_ORIGINS', default=[])
REDIS_URL=(str, "redis://127.0.0.1:6379/1"), API_RATE_LIMIT_PER_MINUTE = config('API_RATE_LIMIT_PER_MINUTE', default=60)
CORS_ALLOW_ALL_ORIGINS=(bool, False), API_RATE_LIMIT_PER_HOUR = config('API_RATE_LIMIT_PER_HOUR', default=1000)
CORS_ALLOWED_ORIGINS=(list, []), CACHE_MIDDLEWARE_SECONDS = config('CACHE_MIDDLEWARE_SECONDS', default=300)
API_RATE_LIMIT_PER_MINUTE=(int, 60), CACHE_MIDDLEWARE_KEY_PREFIX = config(
API_RATE_LIMIT_PER_HOUR=(int, 1000), 'CACHE_MIDDLEWARE_KEY_PREFIX', default="thrillwiki")
CACHE_MIDDLEWARE_SECONDS=(int, 300), GDAL_LIBRARY_PATH = config(
CACHE_MIDDLEWARE_KEY_PREFIX=(str, "thrillwiki"), 'GDAL_LIBRARY_PATH', default="/opt/homebrew/lib/libgdal.dylib")
GDAL_LIBRARY_PATH=(str, "/opt/homebrew/lib/libgdal.dylib"), GEOS_LIBRARY_PATH = config(
GEOS_LIBRARY_PATH=(str, "/opt/homebrew/lib/libgeos_c.dylib"), 'GEOS_LIBRARY_PATH', default="/opt/homebrew/lib/libgeos_c.dylib")
)
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent BASE_DIR = Path(__file__).resolve().parent.parent.parent
@@ -42,16 +42,14 @@ if apps_dir.exists() and str(apps_dir) not in sys.path:
environ.Env.read_env(BASE_DIR / ".env") environ.Env.read_env(BASE_DIR / ".env")
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY") SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env("DEBUG")
# Allowed hosts # Allowed hosts
ALLOWED_HOSTS = env("ALLOWED_HOSTS") ALLOWED_HOSTS = config('ALLOWED_HOSTS')
# CSRF trusted origins # CSRF trusted origins
CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS", default=[]) # type: ignore[arg-type] CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS',
default=[]) # type: ignore[arg-type]
# Application definition # Application definition
DJANGO_APPS = [ DJANGO_APPS = [
@@ -142,14 +140,23 @@ TEMPLATES = [
WSGI_APPLICATION = "thrillwiki.wsgi.application" WSGI_APPLICATION = "thrillwiki.wsgi.application"
# Cloudflare Images Settings # Cloudflare Images Settings
STORAGES = {"default": {"BACKEND": "cloudflare_images.storage.CloudflareImagesStorage"}} STORAGES = {
"default": {
"BACKEND": "cloudflare_images.storage.CloudflareImagesStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
"OPTIONS": {
"location": str(BASE_DIR / "staticfiles"),
},
},
}
CLOUDFLARE_IMAGES_ACCOUNT_ID = config('CLOUDFLARE_IMAGES_ACCOUNT_ID') CLOUDFLARE_IMAGES_ACCOUNT_ID = config('CLOUDFLARE_IMAGES_ACCOUNT_ID')
CLOUDFLARE_IMAGES_API_KEY = config('CLOUDFLARE_IMAGES_API_KEY') CLOUDFLARE_IMAGES_API_TOKEN = config('CLOUDFLARE_IMAGES_API_TOKEN')
CLOUDFLARE_IMAGES_ACCOUNT_HASH = config('CLOUDFLARE_IMAGES_ACCOUNT_HASH') CLOUDFLARE_IMAGES_ACCOUNT_HASH = config('CLOUDFLARE_IMAGES_ACCOUNT_HASH')
CLOUDFLARE_IMAGES_DOMAIN = config( CLOUDFLARE_IMAGES_DOMAIN = config(
'CLOUDFLARE_IMAGES_DOMAIN', default='imagedelivery.net') 'CLOUDFLARE_IMAGES_DOMAIN', default='imagedelivery.net')
CLOUDFLARE_EMAIL = config('CLOUDFLARE_EMAIL')
# Password validation # Password validation
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
@@ -242,7 +249,7 @@ TEST_RUNNER = "django.test.runner.DiscoverRunner"
ROADTRIP_CACHE_TIMEOUT = 3600 * 24 # 24 hours for geocoding ROADTRIP_CACHE_TIMEOUT = 3600 * 24 # 24 hours for geocoding
ROADTRIP_ROUTE_CACHE_TIMEOUT = 3600 * 6 # 6 hours for routes ROADTRIP_ROUTE_CACHE_TIMEOUT = 3600 * 6 # 6 hours for routes
ROADTRIP_MAX_REQUESTS_PER_SECOND = 1 # Respect OSM rate limits ROADTRIP_MAX_REQUESTS_PER_SECOND = 1 # Respect OSM rate limits
ROADTRIP_USER_AGENT = "ThrillWiki Road Trip Planner (https://thrillwiki.com)" ROADTRIP_USER_AGENT = config('ROADTRIP_USER_AGENT')
ROADTRIP_REQUEST_TIMEOUT = 10 # seconds ROADTRIP_REQUEST_TIMEOUT = 10 # seconds
ROADTRIP_MAX_RETRIES = 3 ROADTRIP_MAX_RETRIES = 3
ROADTRIP_BACKOFF_FACTOR = 2 ROADTRIP_BACKOFF_FACTOR = 2
@@ -282,18 +289,17 @@ REST_FRAMEWORK = {
} }
# CORS Settings for API # CORS Settings for API
CORS_ALLOWED_ORIGINS = env("CORS_ALLOWED_ORIGINS", default=[]) # type: ignore[arg-type] CORS_ALLOWED_ORIGINS = config('CORS_ALLOWED_ORIGINS',
default=[]) # type: ignore[arg-type]
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = env("CORS_ALLOW_ALL_ORIGINS", CORS_ALLOW_ALL_ORIGINS = config(
default=False) # type: ignore[arg-type] 'CORS_ALLOW_ALL_ORIGINS', default=False, cast=bool) # type: ignore[arg-type]
# API-specific settings
API_RATE_LIMIT_PER_MINUTE = env.int(
"API_RATE_LIMIT_PER_MINUTE", default=60) # type: ignore[arg-type]
API_RATE_LIMIT_PER_HOUR = env.int(
"API_RATE_LIMIT_PER_HOUR", default=1000) # type: ignore[arg-type]
# drf-spectacular settings 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 = { SPECTACULAR_SETTINGS = {
"TITLE": "ThrillWiki API", "TITLE": "ThrillWiki API",
"DESCRIPTION": "Comprehensive theme park and ride information API", "DESCRIPTION": "Comprehensive theme park and ride information API",
@@ -367,11 +373,9 @@ DJANGO_REDIS_CLIENT_CLASS = "django_redis.client.DefaultClient"
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": DJANGO_REDIS_CACHE_BACKEND, "BACKEND": DJANGO_REDIS_CACHE_BACKEND,
# type: ignore[arg-type]
# pyright: ignore[reportArgumentType]
# pyright: ignore[reportArgumentType] # pyright: ignore[reportArgumentType]
# type: ignore # type: ignore
"LOCATION": env("REDIS_URL", default="redis://127.0.0.1:6379/1"), "LOCATION": config('REDIS_URL', default="redis://127.0.0.1:6379/1"),
"OPTIONS": { "OPTIONS": {
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
"PARSER_CLASS": "redis.connection.HiredisParser", "PARSER_CLASS": "redis.connection.HiredisParser",
@@ -388,17 +392,14 @@ CACHES = {
}, },
"sessions": { "sessions": {
"BACKEND": DJANGO_REDIS_CACHE_BACKEND, "BACKEND": DJANGO_REDIS_CACHE_BACKEND,
# type: ignore[arg-type] "LOCATION": config('REDIS_URL', default="redis://127.0.0.1:6379/2"),
# type: ignore
"LOCATION": env("REDIS_URL", default="redis://127.0.0.1:6379/2"),
"OPTIONS": { "OPTIONS": {
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
}, },
}, },
"api": { "api": {
"BACKEND": DJANGO_REDIS_CACHE_BACKEND, "BACKEND": DJANGO_REDIS_CACHE_BACKEND,
# type: ignore[arg-type] "LOCATION": config('REDIS_URL', default="redis://127.0.0.1:6379/3"),
"LOCATION": env("REDIS_URL", default="redis://127.0.0.1:6379/3"),
"OPTIONS": { "OPTIONS": {
"CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS,
}, },

View File

@@ -2,13 +2,9 @@
Local development settings for thrillwiki project. Local development settings for thrillwiki project.
""" """
import logging
from .base import *
from ..settings import database from ..settings import database
import logging
# Import the module and use its members, e.g., email.EMAIL_HOST from .base import * # noqa: F403
# Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
# Import database configuration # Import database configuration
DATABASES = database.DATABASES DATABASES = database.DATABASES

View File

@@ -3,13 +3,14 @@
import os import os
import sys import sys
from decouple import config
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
# Auto-detect environment based on command line arguments and environment variables # Auto-detect environment based on command line arguments and environment variables
settings_module = detect_settings_module() settings_module = detect_settings_module()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module) config('DJANGO_SETTINGS_MODULE', settings_module)
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line