updates
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.so
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
.venv
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
.env
|
||||
.venv
|
||||
*.log
|
||||
logs/
|
||||
*.db
|
||||
*.sqlite3
|
||||
db.sqlite3
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
*.md
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.pytest_cache
|
||||
.coverage
|
||||
htmlcov/
|
||||
.tox/
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
10
backEnd/.env
10
backEnd/.env
@@ -1,10 +1,10 @@
|
||||
# Development Environment Configuration
|
||||
# Django Settings
|
||||
SECRET_KEY=ks68*5@of1l&4rn1imsqdk9$khcya!&a#jtd89f!v^qg1w0&hc
|
||||
SECRET_KEY=2Yq6sylwG3rLGvD6AQHCsk2nmcwy2EOj5iFhOOR8ZkEeGsnDz_BNvu7J_fGudIkIyug
|
||||
DEBUG=True
|
||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||
ALLOWED_HOSTS=gnxsoft.com,www.gnxsoft.com,YOUR_SERVER_IP,localhost,127.0.0.1
|
||||
|
||||
INTERNAL_API_KEY=your-generated-key-here
|
||||
INTERNAL_API_KEY=9hZtPwyScigoBAl59Uvcz_9VztSRC6Zt_6L1B2xTM2M
|
||||
PRODUCTION_ORIGINS=https://gnxsoft.com,https://www.gnxsoft.com
|
||||
CSRF_TRUSTED_ORIGINS=https://gnxsoft.com,https://www.gnxsoft.com
|
||||
|
||||
@@ -15,11 +15,11 @@ COMPANY_EMAIL=support@gnxsoft.com
|
||||
SUPPORT_EMAIL=support@gnxsoft.com
|
||||
|
||||
# Site URL
|
||||
SITE_URL=http://localhost:3000
|
||||
SITE_URL=https://gnxsoft.com
|
||||
|
||||
# SMTP Configuration (for production or when USE_SMTP_IN_DEV=True)
|
||||
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||||
EMAIL_HOST=mail.gnxsoft.com
|
||||
EMAIL_HOST=localhost
|
||||
EMAIL_PORT=587
|
||||
EMAIL_USE_TLS=True
|
||||
EMAIL_USE_SSL=False
|
||||
|
||||
68
backEnd/.gitignore
vendored
Normal file
68
backEnd/.gitignore
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Virtual Environment
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
.venv
|
||||
|
||||
# Django
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
/media
|
||||
/staticfiles
|
||||
/static
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Coverage
|
||||
htmlcov/
|
||||
.coverage
|
||||
.coverage.*
|
||||
coverage.xml
|
||||
*.cover
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.tox/
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# Django Backend Dockerfile
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Set work directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt /app/
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy project
|
||||
COPY . /app/
|
||||
|
||||
# Create directories for media and static files
|
||||
RUN mkdir -p /app/media /app/staticfiles /app/logs
|
||||
|
||||
# Collect static files (will be done at runtime if needed)
|
||||
# RUN python manage.py collectstatic --noinput
|
||||
|
||||
# Expose port
|
||||
EXPOSE 1086
|
||||
|
||||
# Run gunicorn
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:1086", "--workers", "3", "--timeout", "120", "--access-logfile", "-", "--error-logfile", "-", "gnx.wsgi:application"]
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
from django.utils.html import strip_tags, escape
|
||||
from django.core.exceptions import ValidationError
|
||||
import re
|
||||
from .models import ContactSubmission
|
||||
|
||||
|
||||
@@ -126,6 +129,72 @@ class ContactSubmissionCreateSerializer(serializers.ModelSerializer):
|
||||
'privacy_consent',
|
||||
]
|
||||
|
||||
def _sanitize_text_field(self, value):
|
||||
"""
|
||||
Sanitize text fields by detecting and rejecting HTML/script tags.
|
||||
Returns cleaned text or raises ValidationError if dangerous content is detected.
|
||||
"""
|
||||
if not value:
|
||||
return value
|
||||
|
||||
# Check for script tags and other dangerous HTML patterns
|
||||
dangerous_patterns = [
|
||||
(r'<script[^>]*>.*?</script>', 'Script tags are not allowed'),
|
||||
(r'<iframe[^>]*>.*?</iframe>', 'Iframe tags are not allowed'),
|
||||
(r'javascript:', 'JavaScript protocol is not allowed'),
|
||||
(r'on\w+\s*=', 'Event handlers are not allowed'),
|
||||
(r'<svg[^>]*onload', 'SVG onload handlers are not allowed'),
|
||||
(r'<img[^>]*onerror', 'Image onerror handlers are not allowed'),
|
||||
(r'<[^>]+>', 'HTML tags are not allowed'), # Catch any remaining HTML tags
|
||||
]
|
||||
|
||||
value_lower = value.lower()
|
||||
for pattern, message in dangerous_patterns:
|
||||
if re.search(pattern, value_lower, re.IGNORECASE | re.DOTALL):
|
||||
raise serializers.ValidationError(
|
||||
f"Invalid input detected: {message}. Please remove HTML tags and scripts."
|
||||
)
|
||||
|
||||
# Strip any remaining HTML tags (defense in depth)
|
||||
cleaned = strip_tags(value)
|
||||
# Remove any remaining script-like content
|
||||
cleaned = re.sub(r'javascript:', '', cleaned, flags=re.IGNORECASE)
|
||||
|
||||
return cleaned.strip()
|
||||
|
||||
def validate_first_name(self, value):
|
||||
"""Sanitize first name field."""
|
||||
return self._sanitize_text_field(value)
|
||||
|
||||
def validate_last_name(self, value):
|
||||
"""Sanitize last name field."""
|
||||
return self._sanitize_text_field(value)
|
||||
|
||||
def validate_company(self, value):
|
||||
"""Sanitize company field."""
|
||||
return self._sanitize_text_field(value)
|
||||
|
||||
def validate_job_title(self, value):
|
||||
"""Sanitize job title field."""
|
||||
return self._sanitize_text_field(value)
|
||||
|
||||
def validate_message(self, value):
|
||||
"""Sanitize message field."""
|
||||
return self._sanitize_text_field(value)
|
||||
|
||||
def validate_phone(self, value):
|
||||
"""Sanitize phone field - only allow alphanumeric, spaces, dashes, parentheses, and plus."""
|
||||
if not value:
|
||||
return value
|
||||
|
||||
# Remove HTML tags
|
||||
cleaned = strip_tags(value)
|
||||
# Only allow phone number characters
|
||||
if not re.match(r'^[\d\s\-\+\(\)]+$', cleaned):
|
||||
raise serializers.ValidationError("Phone number contains invalid characters.")
|
||||
|
||||
return cleaned.strip()
|
||||
|
||||
def validate_privacy_consent(self, value):
|
||||
"""
|
||||
Ensure privacy consent is given.
|
||||
|
||||
@@ -62,6 +62,15 @@ class ContactSubmissionViewSet(viewsets.ModelViewSet):
|
||||
permission_classes = [IsAuthenticated]
|
||||
return [permission() for permission in permission_classes]
|
||||
|
||||
def get_authenticators(self):
|
||||
"""
|
||||
Override authentication for create action to bypass CSRF.
|
||||
By returning an empty list, DRF won't enforce CSRF for this action.
|
||||
"""
|
||||
if hasattr(self, 'action') and self.action == 'create':
|
||||
return []
|
||||
return super().get_authenticators()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
Create a new contact submission.
|
||||
@@ -259,4 +268,4 @@ class ContactSubmissionViewSet(viewsets.ModelViewSet):
|
||||
return Response({
|
||||
'error': 'Failed to send test email',
|
||||
'status': 'error'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
Binary file not shown.
101
backEnd/gnx/email_backend.py
Normal file
101
backEnd/gnx/email_backend.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Custom email backend that handles localhost SSL certificate issues.
|
||||
Disables SSL certificate verification for localhost connections.
|
||||
"""
|
||||
import ssl
|
||||
from django.core.mail.backends.smtp import EmailBackend
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocalhostSMTPBackend(EmailBackend):
|
||||
"""
|
||||
Custom SMTP backend that disables SSL certificate verification
|
||||
for localhost connections. This is safe for localhost mail servers.
|
||||
"""
|
||||
|
||||
def open(self):
|
||||
"""
|
||||
Override to create SSL context without certificate verification
|
||||
when connecting to localhost.
|
||||
"""
|
||||
if self.use_ssl or self.use_tls:
|
||||
# Check if connecting to localhost
|
||||
if self.host in ['localhost', '127.0.0.1', '::1']:
|
||||
# Create SSL context without certificate verification for localhost
|
||||
self.connection = None
|
||||
try:
|
||||
import smtplib
|
||||
|
||||
if self.use_ssl:
|
||||
# For SSL connections
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
# SMTP_SSL uses 'context' parameter (Python 3.3+)
|
||||
import sys
|
||||
if sys.version_info >= (3, 3):
|
||||
self.connection = smtplib.SMTP_SSL(
|
||||
self.host,
|
||||
self.port,
|
||||
timeout=self.timeout,
|
||||
context=context
|
||||
)
|
||||
else:
|
||||
# For older Python, use unverified context
|
||||
self.connection = smtplib.SMTP_SSL(
|
||||
self.host,
|
||||
self.port,
|
||||
timeout=self.timeout
|
||||
)
|
||||
else:
|
||||
# For TLS connections
|
||||
self.connection = smtplib.SMTP(
|
||||
self.host,
|
||||
self.port,
|
||||
timeout=self.timeout
|
||||
)
|
||||
# Create SSL context without certificate verification
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
# Use context parameter (Python 3.4+ uses 'context', not 'ssl_context')
|
||||
# For older versions, we'll need to patch the socket after starttls
|
||||
import sys
|
||||
if sys.version_info >= (3, 4):
|
||||
# Python 3.4+ supports context parameter
|
||||
self.connection.starttls(context=context)
|
||||
else:
|
||||
# For older Python, disable verification globally for this connection
|
||||
# by monkey-patching ssl._create_default_https_context temporarily
|
||||
original_context = ssl._create_default_https_context
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
try:
|
||||
self.connection.starttls()
|
||||
finally:
|
||||
ssl._create_default_https_context = original_context
|
||||
|
||||
if self.username and self.password:
|
||||
self.connection.login(self.username, self.password)
|
||||
|
||||
logger.info(f"Successfully connected to localhost mail server at {self.host}:{self.port}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to localhost mail server: {str(e)}")
|
||||
if self.connection:
|
||||
try:
|
||||
self.connection.quit()
|
||||
except:
|
||||
pass
|
||||
self.connection = None
|
||||
raise
|
||||
else:
|
||||
# For non-localhost, use standard SSL/TLS with certificate verification
|
||||
return super().open()
|
||||
else:
|
||||
# No SSL/TLS, use standard connection
|
||||
return super().open()
|
||||
|
||||
37
backEnd/gnx/middleware/csrf_exempt.py
Normal file
37
backEnd/gnx/middleware/csrf_exempt.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
CSRF Exemption Middleware
|
||||
Exempts CSRF checks for specific public API endpoints that don't require authentication.
|
||||
"""
|
||||
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
import re
|
||||
|
||||
|
||||
class CSRFExemptMiddleware(MiddlewareMixin):
|
||||
"""
|
||||
Middleware to exempt CSRF for public API endpoints.
|
||||
Runs before CSRF middleware to set the exemption flag.
|
||||
"""
|
||||
|
||||
# Paths that should be exempt from CSRF (public endpoints)
|
||||
# Patterns match both with and without trailing slashes
|
||||
EXEMPT_PATHS = [
|
||||
r'^/api/contact/submissions/?$', # Contact form submission
|
||||
r'^/api/career/applications/?$', # Job application submission (if needed)
|
||||
r'^/api/support/tickets/?$', # Support ticket creation (if needed)
|
||||
]
|
||||
|
||||
def process_request(self, request):
|
||||
"""
|
||||
Set CSRF exemption flag for matching paths.
|
||||
"""
|
||||
if request.method == 'POST':
|
||||
path = request.path
|
||||
for pattern in self.EXEMPT_PATHS:
|
||||
if re.match(pattern, path):
|
||||
# Set flag to bypass CSRF check
|
||||
setattr(request, '_dont_enforce_csrf_checks', True)
|
||||
break
|
||||
|
||||
return None
|
||||
|
||||
@@ -68,6 +68,7 @@ MIDDLEWARE = [
|
||||
'gnx.middleware.api_security.FrontendAPIProxyMiddleware', # Validate requests from frontend/nginx
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'gnx.middleware.csrf_exempt.CSRFExemptMiddleware', # Exempt CSRF for public API endpoints
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
@@ -98,22 +99,34 @@ WSGI_APPLICATION = 'gnx.wsgi.application'
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
# Support both PostgreSQL (production) and SQLite (development)
|
||||
DATABASE_URL = config('DATABASE_URL', default='')
|
||||
if DATABASE_URL and DATABASE_URL.startswith('postgresql://'):
|
||||
# PostgreSQL configuration
|
||||
import dj_database_url
|
||||
DATABASES = {
|
||||
'default': dj_database_url.parse(DATABASE_URL, conn_max_age=600)
|
||||
}
|
||||
# Force SQLite - change this to False and set USE_POSTGRESQL=True to use PostgreSQL
|
||||
FORCE_SQLITE = True # Set to False to allow PostgreSQL
|
||||
|
||||
if not FORCE_SQLITE:
|
||||
# PostgreSQL configuration (only if FORCE_SQLITE is False)
|
||||
USE_POSTGRESQL = config('USE_POSTGRESQL', default='False', cast=bool)
|
||||
DATABASE_URL = config('DATABASE_URL', default='')
|
||||
if USE_POSTGRESQL and DATABASE_URL and DATABASE_URL.startswith('postgresql://'):
|
||||
import dj_database_url
|
||||
DATABASES = {
|
||||
'default': dj_database_url.parse(DATABASE_URL, conn_max_age=600)
|
||||
}
|
||||
else:
|
||||
# Fallback to SQLite
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
else:
|
||||
# SQLite configuration (development/fallback)
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
# SQLite configuration (forced)
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
@@ -355,8 +368,12 @@ if DEBUG and not USE_SMTP_IN_DEV:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
else:
|
||||
# Production or Dev with SMTP enabled - use SMTP backend
|
||||
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
|
||||
EMAIL_HOST = config('EMAIL_HOST', default='mail.gnxsoft.com')
|
||||
# Use custom backend for localhost to handle SSL certificate issues
|
||||
if EMAIL_HOST in ['localhost', '127.0.0.1', '::1']:
|
||||
EMAIL_BACKEND = 'gnx.email_backend.LocalhostSMTPBackend'
|
||||
else:
|
||||
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
|
||||
EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
|
||||
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
|
||||
EMAIL_USE_SSL = config('EMAIL_USE_SSL', default=False, cast=bool)
|
||||
@@ -367,7 +384,8 @@ else:
|
||||
EMAIL_TIMEOUT = config('EMAIL_TIMEOUT', default=30, cast=int)
|
||||
|
||||
# Site URL for email links
|
||||
SITE_URL = config('SITE_URL', default='http://localhost:3000')
|
||||
# Use production URL by default if not in DEBUG mode
|
||||
SITE_URL = config('SITE_URL', default='https://gnxsoft.com' if not DEBUG else 'http://localhost:3000')
|
||||
|
||||
# Email connection settings for production reliability
|
||||
EMAIL_CONNECTION_TIMEOUT = config('EMAIL_CONNECTION_TIMEOUT', default=10, cast=int)
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
# Production Environment Configuration for GNX Contact Form
|
||||
# Copy this file to .env and update with your actual values
|
||||
# Production Environment Configuration for GNX-WEB
|
||||
# Copy this file to .env in the backEnd directory and update with your actual values
|
||||
# Backend runs on port 1086 (internal only, proxied through nginx)
|
||||
|
||||
# Django Settings
|
||||
SECRET_KEY=your-super-secret-production-key-here
|
||||
SECRET_KEY=your-super-secret-production-key-here-change-this-immediately
|
||||
DEBUG=False
|
||||
ALLOWED_HOSTS=gnxsoft.com,www.gnxsoft.com,your-server-ip
|
||||
ALLOWED_HOSTS=gnxsoft.com,www.gnxsoft.com,your-server-ip,localhost,127.0.0.1
|
||||
|
||||
# Database - Using SQLite (default)
|
||||
# SQLite is configured in settings.py - no DATABASE_URL needed
|
||||
# Database - PostgreSQL on host (port 5433 to avoid conflict with Docker instance on 5432)
|
||||
# Format: postgresql://USER:PASSWORD@HOST:PORT/DBNAME
|
||||
# Create database: sudo -u postgres psql
|
||||
# CREATE DATABASE gnx_db;
|
||||
# CREATE USER gnx_user WITH PASSWORD 'your_secure_password';
|
||||
# GRANT ALL PRIVILEGES ON DATABASE gnx_db TO gnx_user;
|
||||
DATABASE_URL=postgresql://gnx_user:your_password_here@localhost:5433/gnx_db
|
||||
|
||||
# Email Configuration (Production)
|
||||
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||||
EMAIL_HOST=smtp.gmail.com
|
||||
EMAIL_HOST=mail.gnxsoft.com
|
||||
EMAIL_PORT=587
|
||||
EMAIL_USE_TLS=True
|
||||
EMAIL_USE_SSL=False
|
||||
EMAIL_HOST_USER=your-email@gmail.com
|
||||
EMAIL_HOST_PASSWORD=your-app-password
|
||||
EMAIL_HOST_USER=your-email@gnxsoft.com
|
||||
EMAIL_HOST_PASSWORD=your-email-password
|
||||
DEFAULT_FROM_EMAIL=noreply@gnxsoft.com
|
||||
|
||||
# Company email for contact form notifications
|
||||
COMPANY_EMAIL=contact@gnxsoft.com
|
||||
SUPPORT_EMAIL=support@gnxsoft.com
|
||||
|
||||
# Email timeout settings for production reliability
|
||||
EMAIL_TIMEOUT=30
|
||||
@@ -35,6 +42,8 @@ SECURE_HSTS_PRELOAD=True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF=True
|
||||
SECURE_BROWSER_XSS_FILTER=True
|
||||
X_FRAME_OPTIONS=DENY
|
||||
SESSION_COOKIE_SECURE=True
|
||||
CSRF_COOKIE_SECURE=True
|
||||
|
||||
# CORS Settings (Production)
|
||||
PRODUCTION_ORIGINS=https://gnxsoft.com,https://www.gnxsoft.com
|
||||
@@ -47,15 +56,27 @@ CSRF_TRUSTED_ORIGINS=https://gnxsoft.com,https://www.gnxsoft.com
|
||||
# REQUIRED in production! Auto-generated only in DEBUG mode.
|
||||
# Generate a secure key: python -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
# Or get current key: python manage.py show_api_key
|
||||
# This key must match the one in nginx configuration
|
||||
INTERNAL_API_KEY=your-secure-api-key-here-change-this-in-production
|
||||
|
||||
# Admin IP Restriction - Only these IPs can access Django admin
|
||||
# Comma-separated list of IP addresses or CIDR networks (e.g., 193.194.155.249 or 192.168.1.0/24)
|
||||
ADMIN_ALLOWED_IPS=193.194.155.249
|
||||
|
||||
# Static Files
|
||||
STATIC_ROOT=/var/www/gnx/staticfiles/
|
||||
MEDIA_ROOT=/var/www/gnx/media/
|
||||
# Custom allowed IPs for IP whitelist middleware (optional, comma-separated)
|
||||
CUSTOM_ALLOWED_IPS=
|
||||
|
||||
# Site URL for email links and absolute URLs
|
||||
SITE_URL=https://gnxsoft.com
|
||||
|
||||
# Static and Media Files (relative to backEnd directory)
|
||||
# These will be collected/served from these locations
|
||||
STATIC_ROOT=/home/gnx/Desktop/GNX-WEB/backEnd/staticfiles
|
||||
MEDIA_ROOT=/home/gnx/Desktop/GNX-WEB/backEnd/media
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# Backend Port (internal only, nginx proxies to this)
|
||||
# Backend runs on 127.0.0.1:1086
|
||||
BACKEND_PORT=1086
|
||||
|
||||
Reference in New Issue
Block a user