mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 09:51:09 -05:00
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
339 lines
14 KiB
Markdown
339 lines
14 KiB
Markdown
# Environment Variables Reference
|
|
|
|
Complete reference for all environment variables used in ThrillWiki.
|
|
|
|
## Quick Start
|
|
|
|
1. Copy the example file: `cp .env.example .env`
|
|
2. Edit `.env` with your values
|
|
3. Run validation: `python manage.py validate_settings`
|
|
|
|
## Required Variables
|
|
|
|
These must be set in all environments.
|
|
|
|
| Variable | Type | Description |
|
|
|----------|------|-------------|
|
|
| `SECRET_KEY` | string | Django secret key (minimum 50 characters) |
|
|
| `DATABASE_URL` | url | Database connection URL |
|
|
|
|
## Production-Required Variables
|
|
|
|
These variables **must** be explicitly set for production deployments. The application will not function correctly in production without them.
|
|
|
|
> **Important:** See [`.env.example`](../../.env.example) for example values and [`PRODUCTION_CHECKLIST.md`](../PRODUCTION_CHECKLIST.md) for deployment verification steps.
|
|
|
|
| Variable | Type | Format/Example | Description |
|
|
|----------|------|----------------|-------------|
|
|
| `DEBUG` | bool | `False` | **Must be `False` in production.** Enables detailed error pages and debug toolbar when `True`. |
|
|
| `DJANGO_SETTINGS_MODULE` | string | `config.django.production` | **Must use `config.django.production` for production.** Controls which settings module Django loads. |
|
|
| `ALLOWED_HOSTS` | list | `thrillwiki.com,www.thrillwiki.com` | **Required.** Comma-separated list of valid hostnames. No default in production - requests will fail without this. |
|
|
| `CSRF_TRUSTED_ORIGINS` | list | `https://thrillwiki.com,https://www.thrillwiki.com` | **Required.** Must include `https://` prefix. CSRF validation fails without proper configuration. |
|
|
| `REDIS_URL` | url | `redis://[:password@]host:6379/0` | **Required for caching and sessions.** Production uses Redis for caching, session storage, and Celery broker. |
|
|
|
|
### Production Settings Behavior
|
|
|
|
When `DJANGO_SETTINGS_MODULE=config.django.production`:
|
|
- `DEBUG` is always `False`
|
|
- `ALLOWED_HOSTS` has **no default** - must be explicitly set
|
|
- `CSRF_TRUSTED_ORIGINS` has **no default** - must be explicitly set
|
|
- SSL redirect is enforced (`SECURE_SSL_REDIRECT=True`)
|
|
- Secure cookies are enforced (`SESSION_COOKIE_SECURE=True`, `CSRF_COOKIE_SECURE=True`)
|
|
- HSTS is enabled with 1-year max-age
|
|
- Redis cache is required (application will fail if `REDIS_URL` is not set)
|
|
|
|
### Validation
|
|
|
|
Run the following to validate your production configuration:
|
|
|
|
```bash
|
|
DJANGO_SETTINGS_MODULE=config.django.production python manage.py check --deploy
|
|
```
|
|
|
|
## Core Django Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `DEBUG` | bool | `True` | Enable debug mode |
|
|
| `DJANGO_SETTINGS_MODULE` | string | `config.django.local` | Settings module to use |
|
|
| `ALLOWED_HOSTS` | list | `localhost,127.0.0.1` | Comma-separated allowed hosts |
|
|
| `CSRF_TRUSTED_ORIGINS` | list | `` | Comma-separated trusted origins |
|
|
|
|
## Database Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `DATABASE_URL` | url | (required) | Database connection URL |
|
|
| `DATABASE_CONN_MAX_AGE` | int | `600` | Connection pool timeout (seconds) |
|
|
| `DATABASE_CONNECT_TIMEOUT` | int | `10` | Connection timeout (seconds) |
|
|
| `DATABASE_STATEMENT_TIMEOUT` | int | `30000` | Query timeout (milliseconds) |
|
|
| `DATABASE_READ_REPLICA_URL` | url | `` | Optional read replica URL |
|
|
|
|
### Database URL Formats
|
|
|
|
```bash
|
|
# PostgreSQL with PostGIS
|
|
DATABASE_URL=postgis://user:password@host:5432/dbname
|
|
|
|
# PostgreSQL without PostGIS
|
|
DATABASE_URL=postgres://user:password@host:5432/dbname
|
|
|
|
# SQLite (development only)
|
|
DATABASE_URL=sqlite:///path/to/db.sqlite3
|
|
```
|
|
|
|
## GeoDjango Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `GDAL_LIBRARY_PATH` | path | `/opt/homebrew/lib/libgdal.dylib` | Path to GDAL library |
|
|
| `GEOS_LIBRARY_PATH` | path | `/opt/homebrew/lib/libgeos_c.dylib` | Path to GEOS library |
|
|
|
|
### Platform-Specific Paths
|
|
|
|
```bash
|
|
# macOS (Homebrew)
|
|
GDAL_LIBRARY_PATH=/opt/homebrew/lib/libgdal.dylib
|
|
GEOS_LIBRARY_PATH=/opt/homebrew/lib/libgeos_c.dylib
|
|
|
|
# Linux (Ubuntu/Debian)
|
|
GDAL_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/libgdal.so
|
|
GEOS_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/libgeos_c.so
|
|
```
|
|
|
|
## Cache Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `REDIS_URL` | url | `redis://127.0.0.1:6379/1` | Redis connection URL |
|
|
| `REDIS_SESSIONS_URL` | url | (from REDIS_URL) | Redis URL for sessions |
|
|
| `REDIS_API_URL` | url | (from REDIS_URL) | Redis URL for API cache |
|
|
| `REDIS_MAX_CONNECTIONS` | int | `100` | Maximum Redis connections |
|
|
| `REDIS_CONNECTION_TIMEOUT` | int | `20` | Redis connection timeout |
|
|
| `REDIS_IGNORE_EXCEPTIONS` | bool | `True` | Graceful degradation |
|
|
| `CACHE_MIDDLEWARE_SECONDS` | int | `300` | Cache middleware timeout |
|
|
| `CACHE_MIDDLEWARE_KEY_PREFIX` | string | `thrillwiki` | Cache key prefix |
|
|
| `CACHE_KEY_PREFIX` | string | `thrillwiki` | Default cache key prefix |
|
|
|
|
## Email Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `EMAIL_BACKEND` | string | `django_forwardemail.backends.ForwardEmailBackend` | Email backend class |
|
|
| `SERVER_EMAIL` | email | `django_webmaster@thrillwiki.com` | Server email address |
|
|
| `DEFAULT_FROM_EMAIL` | string | `ThrillWiki <noreply@thrillwiki.com>` | Default from address |
|
|
| `EMAIL_SUBJECT_PREFIX` | string | `[ThrillWiki]` | Subject prefix |
|
|
| `EMAIL_TIMEOUT` | int | `30` | Email operation timeout |
|
|
|
|
### ForwardEmail Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `FORWARD_EMAIL_BASE_URL` | url | `https://api.forwardemail.net` | API base URL |
|
|
| `FORWARD_EMAIL_API_KEY` | string | `` | API key |
|
|
| `FORWARD_EMAIL_DOMAIN` | string | `` | Sending domain |
|
|
|
|
### SMTP Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `EMAIL_HOST` | string | `localhost` | SMTP server |
|
|
| `EMAIL_PORT` | int | `587` | SMTP port |
|
|
| `EMAIL_USE_TLS` | bool | `True` | Use TLS |
|
|
| `EMAIL_USE_SSL` | bool | `False` | Use SSL |
|
|
| `EMAIL_HOST_USER` | string | `` | SMTP username |
|
|
| `EMAIL_HOST_PASSWORD` | string | `` | SMTP password |
|
|
|
|
## Security Settings
|
|
|
|
### SSL/HTTPS
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SECURE_SSL_REDIRECT` | bool | `False` | Redirect HTTP to HTTPS |
|
|
| `SECURE_PROXY_SSL_HEADER` | tuple | `` | Proxy SSL header |
|
|
|
|
### HSTS (HTTP Strict Transport Security)
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SECURE_HSTS_SECONDS` | int | `31536000` | HSTS max-age |
|
|
| `SECURE_HSTS_INCLUDE_SUBDOMAINS` | bool | `True` | Include subdomains |
|
|
| `SECURE_HSTS_PRELOAD` | bool | `False` | Allow preload |
|
|
| `SECURE_REDIRECT_EXEMPT` | list | `` | URLs exempt from redirect |
|
|
|
|
### Security Headers
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SECURE_BROWSER_XSS_FILTER` | bool | `True` | XSS filter header |
|
|
| `SECURE_CONTENT_TYPE_NOSNIFF` | bool | `True` | Nosniff header |
|
|
| `X_FRAME_OPTIONS` | string | `DENY` | Frame options |
|
|
| `SECURE_REFERRER_POLICY` | string | `strict-origin-when-cross-origin` | Referrer policy |
|
|
| `SECURE_CROSS_ORIGIN_OPENER_POLICY` | string | `same-origin` | COOP header |
|
|
|
|
### Cookie Security
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SESSION_COOKIE_SECURE` | bool | `False` | HTTPS-only session cookie |
|
|
| `SESSION_COOKIE_HTTPONLY` | bool | `True` | No JS access |
|
|
| `SESSION_COOKIE_SAMESITE` | string | `Lax` | SameSite attribute |
|
|
| `SESSION_COOKIE_AGE` | int | `3600` | Cookie age (seconds) |
|
|
| `CSRF_COOKIE_SECURE` | bool | `False` | HTTPS-only CSRF cookie |
|
|
| `CSRF_COOKIE_HTTPONLY` | bool | `True` | No JS access |
|
|
| `CSRF_COOKIE_SAMESITE` | string | `Lax` | SameSite attribute |
|
|
|
|
### Cloudflare Turnstile
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `TURNSTILE_SITE_KEY` | string | `` | Turnstile site key |
|
|
| `TURNSTILE_SECRET_KEY` | string | `` | Turnstile secret key |
|
|
| `TURNSTILE_VERIFY_URL` | url | `https://challenges.cloudflare.com/turnstile/v0/siteverify` | Verification URL |
|
|
|
|
## API Configuration
|
|
|
|
### CORS
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `CORS_ALLOWED_ORIGINS` | list | `` | Allowed origins |
|
|
| `CORS_ALLOW_ALL_ORIGINS` | bool | `False` | Allow all origins |
|
|
|
|
### Rate Limiting
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `API_RATE_LIMIT_PER_MINUTE` | int | `60` | Requests per minute |
|
|
| `API_RATE_LIMIT_PER_HOUR` | int | `1000` | Requests per hour |
|
|
| `API_RATE_LIMIT_ANON_PER_MINUTE` | int | `60` | Anonymous rate limit |
|
|
| `API_RATE_LIMIT_USER_PER_HOUR` | int | `1000` | Authenticated rate limit |
|
|
|
|
### Pagination
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `API_PAGE_SIZE` | int | `20` | Default page size |
|
|
| `API_MAX_PAGE_SIZE` | int | `100` | Maximum page size |
|
|
| `API_VERSION` | string | `1.0.0` | API version string |
|
|
|
|
## JWT Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `JWT_ACCESS_TOKEN_LIFETIME_MINUTES` | int | `15` | Access token lifetime |
|
|
| `JWT_REFRESH_TOKEN_LIFETIME_DAYS` | int | `7` | Refresh token lifetime |
|
|
| `JWT_ISSUER` | string | `thrillwiki` | Token issuer |
|
|
|
|
## Cloudflare Images
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `CLOUDFLARE_IMAGES_ACCOUNT_ID` | string | `` | Account ID |
|
|
| `CLOUDFLARE_IMAGES_API_TOKEN` | string | `` | API token |
|
|
| `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | string | `` | Account hash |
|
|
| `CLOUDFLARE_IMAGES_WEBHOOK_SECRET` | string | `` | Webhook secret |
|
|
| `CLOUDFLARE_IMAGES_DEFAULT_VARIANT` | string | `public` | Default variant |
|
|
| `CLOUDFLARE_IMAGES_UPLOAD_TIMEOUT` | int | `300` | Upload timeout |
|
|
| `CLOUDFLARE_IMAGES_CLEANUP_HOURS` | int | `24` | Cleanup interval |
|
|
| `CLOUDFLARE_IMAGES_MAX_FILE_SIZE` | int | `10485760` | Max file size (bytes) |
|
|
| `CLOUDFLARE_IMAGES_REQUIRE_SIGNED_URLS` | bool | `False` | Require signed URLs |
|
|
|
|
## Road Trip Service
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `ROADTRIP_USER_AGENT` | string | `ThrillWiki/1.0` | OSM API user agent |
|
|
| `ROADTRIP_CACHE_TIMEOUT` | int | `86400` | Geocoding cache timeout |
|
|
| `ROADTRIP_ROUTE_CACHE_TIMEOUT` | int | `21600` | Route cache timeout |
|
|
| `ROADTRIP_MAX_REQUESTS_PER_SECOND` | int | `1` | Rate limit |
|
|
| `ROADTRIP_REQUEST_TIMEOUT` | int | `10` | Request timeout |
|
|
| `ROADTRIP_MAX_RETRIES` | int | `3` | Max retries |
|
|
| `ROADTRIP_BACKOFF_FACTOR` | int | `2` | Retry backoff |
|
|
|
|
## Logging Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `LOG_DIR` | path | `logs` | Log directory |
|
|
| `ROOT_LOG_LEVEL` | string | `INFO` | Root log level |
|
|
| `DJANGO_LOG_LEVEL` | string | `WARNING` | Django log level |
|
|
| `DB_LOG_LEVEL` | string | `WARNING` | Database log level |
|
|
| `APP_LOG_LEVEL` | string | `INFO` | Application log level |
|
|
| `PERFORMANCE_LOG_LEVEL` | string | `INFO` | Performance log level |
|
|
| `QUERY_LOG_LEVEL` | string | `WARNING` | Query log level |
|
|
| `NPLUSONE_LOG_LEVEL` | string | `WARNING` | N+1 detection level |
|
|
| `REQUEST_LOG_LEVEL` | string | `INFO` | Request log level |
|
|
| `CELERY_LOG_LEVEL` | string | `INFO` | Celery log level |
|
|
| `CONSOLE_LOG_LEVEL` | string | `INFO` | Console output level |
|
|
| `FILE_LOG_LEVEL` | string | `INFO` | File output level |
|
|
| `FILE_LOG_FORMATTER` | string | `json` | File log format |
|
|
|
|
## Monitoring
|
|
|
|
### Sentry
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SENTRY_DSN` | url | `` | Sentry DSN |
|
|
| `SENTRY_ENVIRONMENT` | string | `production` | Sentry environment |
|
|
| `SENTRY_TRACES_SAMPLE_RATE` | float | `0.1` | Trace sampling rate |
|
|
|
|
### Health Checks
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `HEALTH_CHECK_DISK_USAGE_MAX` | int | `90` | Max disk usage % |
|
|
| `HEALTH_CHECK_MEMORY_MIN` | int | `100` | Min available memory MB |
|
|
|
|
## Feature Flags
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `ENABLE_DEBUG_TOOLBAR` | bool | `True` | Enable debug toolbar |
|
|
| `ENABLE_SILK_PROFILER` | bool | `False` | Enable Silk profiler |
|
|
| `TEMPLATES_ENABLED` | bool | `True` | Enable Django templates |
|
|
| `AUTOCOMPLETE_BLOCK_UNAUTHENTICATED` | bool | `False` | Require auth for autocomplete |
|
|
|
|
## File Upload Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `FILE_UPLOAD_MAX_MEMORY_SIZE` | int | `2621440` | Max in-memory upload (bytes) |
|
|
| `DATA_UPLOAD_MAX_MEMORY_SIZE` | int | `10485760` | Max request size (bytes) |
|
|
| `DATA_UPLOAD_MAX_NUMBER_FIELDS` | int | `1000` | Max form fields |
|
|
|
|
## WhiteNoise Settings
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `WHITENOISE_COMPRESSION_QUALITY` | int | `90` | Compression quality |
|
|
| `WHITENOISE_MAX_AGE` | int | `31536000` | Cache max-age |
|
|
| `WHITENOISE_MANIFEST_STRICT` | bool | `False` | Strict manifest mode |
|
|
|
|
## Secret Management
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `SECRET_ROTATION_ENABLED` | bool | `False` | Enable rotation checks |
|
|
| `SECRET_KEY_VERSION` | string | `1` | Current secret version |
|
|
| `SECRET_EXPIRY_WARNING_DAYS` | int | `30` | Warning threshold |
|
|
| `PASSWORD_MIN_LENGTH` | int | `8` | Minimum password length |
|
|
|
|
## Celery Configuration
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `CELERY_TASK_ALWAYS_EAGER` | bool | `False` | Run tasks synchronously |
|
|
| `CELERY_TASK_EAGER_PROPAGATES` | bool | `False` | Propagate task errors |
|
|
|
|
## Third-Party Integrations
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `FRONTEND_DOMAIN` | url | `https://thrillwiki.com` | Frontend URL |
|
|
| `LOGIN_REDIRECT_URL` | path | `/` | Post-login redirect |
|
|
| `ACCOUNT_LOGOUT_REDIRECT_URL` | path | `/` | Post-logout redirect |
|
|
| `ACCOUNT_EMAIL_VERIFICATION` | string | `mandatory` | Email verification mode |
|