diff --git a/memelord-kkurval.yaml b/memelord-kkurval.yaml index 877c1b5..a73fe25 100644 --- a/memelord-kkurval.yaml +++ b/memelord-kkurval.yaml @@ -16,12 +16,12 @@ spec: labels: app: memelord-kkurval-app spec: - volumes: - - name: settings - projected: - sources: - - configMap: - name: settings + # volumes: + # - name: settings + # projected: + # sources: + # - configMap: + # name: settings containers: - name: memelord image: ghcr.io/l4rm4nd/memelord:latest @@ -129,709 +129,709 @@ spec: name: oidc-client-memelord-kkurval-owner-secrets key: OIDC_IDP_USERINFO_URI ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: settings -data: - settings.py: | - """ - Django settings for myproject project. - - Generated by 'django-admin startproject' using Django 3.2.16. - - For more information on this file, see - https://docs.djangoproject.com/en/3.2/topics/settings/ - - For the full list of settings and their values, see - https://docs.djangoproject.com/en/3.2/ref/settings/ - """ - - from pathlib import Path - from dotenv import load_dotenv - import os - import pytz - import secrets - from django.utils.html import escape - from django.utils.translation import gettext_lazy as _ - from csp.constants import NONE, SELF, UNSAFE_INLINE, UNSAFE_EVAL - - # Load environment variables from .env file - load_dotenv() - - # Build paths inside the project like this: BASE_DIR / 'subdir'. - BASE_DIR = Path(__file__).resolve().parent.parent - - # get debug modus from env - DEBUG = os.environ.get('DEBUG', 'False').lower() in ['true'] - - # get container version from env - VERSION = escape(os.environ.get("VERSION", '')) - - # Enable/disable public meme feed feature (must be after import os) - ENABLE_PUBLIC_FEED = os.environ.get('ENABLE_PUBLIC_FEED', 'False').lower() in ['true', '1', 'yes'] - - # auto-generate a secure secret key or use from env variable - SECRET_KEY = os.environ.get("SECRET_KEY", secrets.token_urlsafe(32)) - - # define allowed hosts and trusted domains via env variables - DOMAIN = "" - ALLOWED_HOSTS = ["127.0.0.1"] - CSRF_TRUSTED_ORIGINS = ["http://127.0.0.1:8000"] - - DOMAIN = str(os.environ.get("DOMAIN", "localhost")) - TRUSTED_PORT = str(os.environ.get("PORT", "8000")) - - if DOMAIN: - domains = DOMAIN.split(',') - for domain in domains: - domain = domain.strip().rstrip('/').replace('http://', '').replace('https://', '') - if domain: - ALLOWED_HOSTS.append(domain) - TRUSTED_USER_DOMAIN_HTTP = f"http://{domain}:{TRUSTED_PORT}" - TRUSTED_USER_DOMAIN_HTTP_80_DEFAULT = f"http://{domain}" - TRUSTED_USER_DOMAIN_HTTPS = f"https://{domain}:{TRUSTED_PORT}" - TRUSTED_USER_DOMAIN_HTTPS_443_DEFAULT = f"https://{domain}" - CSRF_TRUSTED_ORIGINS.extend([TRUSTED_USER_DOMAIN_HTTP, TRUSTED_USER_DOMAIN_HTTPS, TRUSTED_USER_DOMAIN_HTTP_80_DEFAULT, TRUSTED_USER_DOMAIN_HTTPS_443_DEFAULT]) - - #Session Management - CSRF_COOKIE_HTTPONLY = True - SESSION_EXPIRE_AT_BROWSER_CLOSE = os.environ.get('SESSION_EXPIRE_AT_BROWSER_CLOSE', 'True').lower() in ['true'] - SESSION_COOKIE_AGE = int(os.environ.get('SESSION_COOKIE_AGE', '30')) * 60 - SESSION_COOKIE_NAME = 'Session' - SESSION_COOKIE_SAMESITE = 'Lax' - - # ============================================================================= - # REDIS CACHE CONFIGURATION (for sessions and general caching) - # ============================================================================= - # If REDIS_HOST is not set, sessions will use database (backward compatible) - REDIS_HOST = os.environ.get("REDIS_HOST", "") - REDIS_PORT = os.environ.get("REDIS_PORT", "6379") - REDIS_DB = os.environ.get("REDIS_DB", "0") - REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD", "") - - if REDIS_HOST: - # Redis is available - use it for caching and sessions - CACHES = { - "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}", - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - "PASSWORD": REDIS_PASSWORD if REDIS_PASSWORD else None, - "SOCKET_CONNECT_TIMEOUT": 5, # seconds - "SOCKET_TIMEOUT": 5, # seconds - "RETRY_ON_TIMEOUT": True, - "CONNECTION_POOL_KWARGS": { - "max_connections": 50, - "retry_on_timeout": True, - }, - "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", - "IGNORE_EXCEPTIONS": True, # Don't crash if Redis is down - }, - "KEY_PREFIX": "memelord", - "TIMEOUT": 300, # 5 minutes default - } - } - - # Use Redis for session storage (cloud-native) - SESSION_ENGINE = "django.contrib.sessions.backends.cache" - SESSION_CACHE_ALIAS = "default" - else: - # Redis not configured - use database sessions (backward compatible) - SESSION_ENGINE = "django.contrib.sessions.backends.db" - # No CACHES configuration - Django will use local memory cache - - SECURE_COOKIES = os.environ.get('SECURE_COOKIES', 'False').lower() in ['true'] - - if SECURE_COOKIES: - # transmit cookies over encrypted https only - SESSION_COOKIE_SECURE = True - CSRF_COOKIE_SECURE = True - # also set hsts response header - SECURE_HSTS_SECONDS = "31536000" - SECURE_HSTS_PRELOAD = True - SECURE_HSTS_INCLUDE_SUBDOMAINS = True - else: - # transmit cookies over unencrypted http - SESSION_COOKIE_SECURE = False - CSRF_COOKIE_SECURE = False - - # http security response headers - SECURE_BROWSER_XSS_FILTER = True - SECURE_CONTENT_TYPE_NOSNIFF = True - X_FRAME_OPTIONS = 'DENY' - REFERRER_POLICY = 'same-origin' - - # Load from environment, default to "'self'" - raw_frame_ancestors = os.environ.get("CSP_FRAME_ANCESTORS", "'none'") - # Split by comma, strip spaces, and keep properly quoted entries - FRAME_ANCESTORS = [item.strip() for item in raw_frame_ancestors.split(',') if item.strip()] - - # Build CSP img-src list dynamically based on storage backend - STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() - IMG_SRC_LIST = ["'self'", "data:", "blob:", "https://img.logo.dev"] - - # Add S3 domains to CSP if using S3 storage - if STORAGE_BACKEND == 's3': - AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') - AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') - AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') - AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') - AWS_S3_USE_ACCELERATE_ENDPOINT = os.environ.get('AWS_S3_USE_ACCELERATE_ENDPOINT', 'False').lower() in ['true'] - - # Always add custom domain if specified (CDN like CloudFront) - if AWS_S3_CUSTOM_DOMAIN: - IMG_SRC_LIST.append(f"https://{AWS_S3_CUSTOM_DOMAIN}") - - # Detect S3 provider based on endpoint URL - if AWS_S3_ENDPOINT_URL: - # S3-compatible service detected - from urllib.parse import urlparse - parsed_url = urlparse(AWS_S3_ENDPOINT_URL) - endpoint_domain = parsed_url.netloc - endpoint_scheme = parsed_url.scheme or 'https' - - # Add the endpoint domain - IMG_SRC_LIST.append(f"{endpoint_scheme}://{endpoint_domain}") - - # Add bucket-based subdomain format if applicable - if endpoint_domain and AWS_STORAGE_BUCKET_NAME: - IMG_SRC_LIST.append(f"{endpoint_scheme}://{AWS_STORAGE_BUCKET_NAME}.{endpoint_domain}") - - # Provider-specific URL patterns - endpoint_lower = endpoint_domain.lower() if endpoint_domain else "" - - # DigitalOcean Spaces: also add CDN domain - if 'digitaloceanspaces.com' in endpoint_lower: - # DigitalOcean Spaces CDN format: bucket-name.region.cdn.digitaloceanspaces.com - if AWS_STORAGE_BUCKET_NAME and AWS_S3_REGION_NAME: - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.cdn.digitaloceanspaces.com") - - # Cloudflare R2: also add public bucket URL format - elif 'r2.cloudflarestorage.com' in endpoint_lower: - # Cloudflare R2 can use custom domains via public.r2.dev - # Format: https://bucket-name.account-id.r2.dev (if public) - # This is typically set via AWS_S3_CUSTOM_DOMAIN, but we note it here - pass - - # Wasabi: supports path-style and virtual-hosted-style - elif 'wasabisys.com' in endpoint_lower: - # Already covered by endpoint_domain and bucket subdomain - pass - - # Linode Object Storage: supports path-style and virtual-hosted-style - elif 'linodeobjects.com' in endpoint_lower: - # Already covered by endpoint_domain and bucket subdomain - pass - - # Backblaze B2: supports path-style and virtual-hosted-style - elif 'backblazeb2.com' in endpoint_lower: - # Already covered by endpoint_domain and bucket subdomain - pass - - # MinIO: custom deployment, already covered - # Other S3-compatible services: already covered - - else: - # No endpoint URL = Standard AWS S3 - if AWS_STORAGE_BUCKET_NAME: - # Add AWS S3 virtual-hosted-style URLs - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com") - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com") - - # Add path-style URL format (legacy but still supported) - IMG_SRC_LIST.append("https://s3.amazonaws.com") - IMG_SRC_LIST.append(f"https://s3.{AWS_S3_REGION_NAME}.amazonaws.com") - - # Add dual-stack endpoints (IPv6 support) - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.dualstack.{AWS_S3_REGION_NAME}.amazonaws.com") - - # Add S3 Transfer Acceleration endpoint if enabled - if AWS_S3_USE_ACCELERATE_ENDPOINT: - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3-accelerate.amazonaws.com") - IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3-accelerate.dualstack.amazonaws.com") - - # Add Azure domains to CSP if using Azure storage - elif STORAGE_BACKEND == 'azure': - AZURE_ACCOUNT_NAME = os.environ.get('AZURE_ACCOUNT_NAME') - AZURE_CUSTOM_DOMAIN = os.environ.get('AZURE_CUSTOM_DOMAIN') - - if AZURE_CUSTOM_DOMAIN: - IMG_SRC_LIST.append(f"https://{AZURE_CUSTOM_DOMAIN}") - - if AZURE_ACCOUNT_NAME: - IMG_SRC_LIST.append(f"https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net") - - # Add GCS domains to CSP if using GCS storage - elif STORAGE_BACKEND == 'gcs': - GS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME') - GS_CUSTOM_ENDPOINT = os.environ.get('GS_CUSTOM_ENDPOINT') - - if GS_CUSTOM_ENDPOINT: - IMG_SRC_LIST.append(GS_CUSTOM_ENDPOINT) - - if GS_BUCKET_NAME: - # GCS can use multiple URL formats - IMG_SRC_LIST.append("https://storage.googleapis.com") - IMG_SRC_LIST.append("https://storage.cloud.google.com") - IMG_SRC_LIST.append(f"https://{GS_BUCKET_NAME}.storage.googleapis.com") - - # SFTP/Dropbox typically serve through Django (using 'self' origin) - elif STORAGE_BACKEND == 'sftp': - # SFTP files are retrieved by Django and served through Django views - # Optional: If you have nginx serving the same SFTP directory via HTTP - SFTP_CUSTOM_DOMAIN = os.environ.get('SFTP_CUSTOM_DOMAIN') - if SFTP_CUSTOM_DOMAIN: - IMG_SRC_LIST.append(f"https://{SFTP_CUSTOM_DOMAIN}") - - elif STORAGE_BACKEND == 'dropbox': - # Dropbox files are retrieved by Django and served through Django views - # No additional CSP configuration needed - pass - - CSP_IMG_SRC_EXTRA = os.environ.get('CSP_IMG_SRC_EXTRA', '') - if CSP_IMG_SRC_EXTRA: - extra_domains = [domain.strip() for domain in CSP_IMG_SRC_EXTRA.split(',') if domain.strip()] - IMG_SRC_LIST.extend(extra_domains) - - CONTENT_SECURITY_POLICY = { - "DIRECTIVES": { - "default-src": ["'self'"], - "style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdn.jsdelivr.net"], - "script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://cdn.jsdelivr.net"], - "font-src": ["'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"], - "img-src": IMG_SRC_LIST, - "object-src": ["'none'"], - "connect-src": ["'self'"], - "frame-ancestors": FRAME_ANCESTORS, - }, - } - - # Application definition - INSTALLED_APPS = [ - 'myapp', - 'django_celery_beat', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'csp', - 'storages', - ] - - MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - #'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django_http_referrer_policy.middleware.ReferrerPolicyMiddleware', - 'csp.middleware.CSPMiddleware', - ] - - ROOT_URLCONF = 'myproject.urls' - - TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'myapp/templates/registration')], - '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', - ], - }, - }, - ] - - # Database - # https://docs.djangoproject.com/en/3.2/ref/settings/#databases - - DB_ENGINE = os.environ.get("DB_ENGINE", "sqlite3") - - if DB_ENGINE == "sqlite3": - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'database', 'db.sqlite3'), - } - } - - elif DB_ENGINE == "postgres": - - DB_HOST = os.environ.get("POSTGRES_HOST", "db") - DB_PORT = os.environ.get("POSTGRES_PORT", "5432") - DB_USER = os.environ.get("POSTGRES_USER", "memelord") - DB_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "memelord") - DB_NAME = os.environ.get("POSTGRES_DB", "memelord") - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': DB_NAME, - 'HOST': DB_HOST, - 'PORT': DB_PORT, - 'USER': DB_USER, - 'PASSWORD': DB_PASSWORD, - } - } - - # Password validation - # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators - - 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 - # https://docs.djangoproject.com/en/3.2/topics/i18n/ - LANGUAGE_CODE = 'en-us' - TIME_ZONE = os.environ.get('TZ', 'Europe/Berlin') - USE_I18N = True - USE_L10N = True - USE_TZ = True - - LANGUAGES = [ - ('en', _('English')), - ('de', _('German')), - ('fr', _('French')), - ('it', _('Italian')), - ] - - LOCALE_PATHS = [ - os.path.join(BASE_DIR, 'locale') - ] - - # Celery configuration - # http://docs.celeryproject.org/en/latest/configuration.html - - LOGS_DIR = os.path.join(BASE_DIR, 'logs') - - # ============================================================================= - # LOGGING CONFIGURATION - # ============================================================================= - LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'verbose': { - 'format': '{levelname} {asctime} {module} {message}', - 'style': '{', - }, - 'simple': { - 'format': '{levelname} {message}', - 'style': '{', - }, - }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'verbose', - }, - 'file': { - 'class': 'logging.handlers.RotatingFileHandler', - 'filename': os.path.join(LOGS_DIR, 'django.log'), - 'maxBytes': 1024 * 1024 * 15, # 15MB - 'backupCount': 10, - 'formatter': 'verbose', - }, - }, - 'loggers': { - 'django': { - 'handlers': ['console', 'file'], - 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), - 'propagate': False, - }, - 'myapp': { - 'handlers': ['console', 'file'], - 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), - 'propagate': False, - }, - 'storages': { - 'handlers': ['console', 'file'], - 'level': 'DEBUG' if DEBUG else 'INFO', - 'propagate': False, - }, - 'boto3': { - 'handlers': ['console', 'file'], - 'level': 'DEBUG' if DEBUG else 'INFO', - 'propagate': False, - }, - 'botocore': { - 'handlers': ['console', 'file'], - 'level': 'DEBUG' if DEBUG else 'INFO', - 'propagate': False, - }, - }, - 'root': { - 'handlers': ['console', 'file'], - 'level': 'INFO', - }, - } - - # Create logs directory if it doesn't exist - os.makedirs(LOGS_DIR, exist_ok=True) - - STATIC_URL = '/static/' - STATIC_ROOT = os.path.join(BASE_DIR, 'myapp', 'static') - - DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - - LOGIN_URL = '/accounts/login/' - LOGIN_REDIRECT_URL = '/' - LOGOUT_REDIRECT_URL = '/post-logout/' - ALLOW_LOGOUT_GET_METHOD = True - - WSGI_APPLICATION = 'myproject.wsgi.application' - - # check if oidc is enabled - OIDC_ENABLED = os.environ.get('OIDC_ENABLED', 'False').lower() in ['true'] - OIDC_AUTOLOGIN = os.environ.get('OIDC_AUTOLOGIN', 'False').lower() in ['true'] - - # Max file upload size in MB (default: 10MB) - # Can be configured via MAX_UPLOAD_SIZE_MB environment variable - MAX_UPLOAD_SIZE_MB = int(os.environ.get('MAX_UPLOAD_SIZE_MB', '10')) * 1024 * 1024 - - # ============================================================================= - # STORAGE BACKEND CONFIGURATION - # ============================================================================= - # IMPORTANT: This must be set BEFORE MEDIA_URL and MEDIA_ROOT - # Configure storage backend via STORAGE_BACKEND environment variable - # Supported backends: local, s3, azure, gcs, sftp, dropbox - # Default: local (filesystem storage) - - STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() - - # Django 5.0+ uses STORAGES instead of DEFAULT_FILE_STORAGE - # Set both for compatibility - if STORAGE_BACKEND == 's3': - # Amazon S3 / Compatible S3 Storage (MinIO, DigitalOcean Spaces, etc.) - AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') - AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') - AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') - AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') - AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') - AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') # Only set for S3-compatible services (MinIO, etc.) - AWS_DEFAULT_ACL = os.environ.get('AWS_DEFAULT_ACL', 'private') - AWS_S3_OBJECT_PARAMETERS = { - 'CacheControl': 'max-age=86400', - } - AWS_QUERYSTRING_AUTH = os.environ.get('AWS_QUERYSTRING_AUTH', 'True').lower() in ['true'] - AWS_S3_FILE_OVERWRITE = os.environ.get('AWS_S3_FILE_OVERWRITE', 'False').lower() in ['true'] - AWS_LOCATION = os.environ.get('AWS_LOCATION', 'media') - - # Use signature version 4 (required for all regions) - AWS_S3_SIGNATURE_VERSION = 's3v4' - - # Use virtual-hosted-style URLs (bucket-name.s3.region.amazonaws.com) - # This is the default and recommended format - AWS_S3_ADDRESSING_STYLE = 'virtual' - - # Django 5.0+ STORAGES configuration - STORAGES = { - "default": { - "BACKEND": "storages.backends.s3boto3.S3Boto3Storage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - # Legacy setting for older Django versions - DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' - - # Update MEDIA_URL - if AWS_S3_CUSTOM_DOMAIN: - MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_LOCATION}/' - elif AWS_STORAGE_BUCKET_NAME: - # Use region-specific virtual-hosted-style URL - if AWS_S3_REGION_NAME == 'us-east-1': - MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/{AWS_LOCATION}/' - else: - MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com/{AWS_LOCATION}/' - else: - MEDIA_URL = "/media/" - - MEDIA_ROOT = BASE_DIR / "media" - - elif STORAGE_BACKEND == 'azure': - # Microsoft Azure Blob Storage - AZURE_ACCOUNT_NAME = os.environ.get('AZURE_ACCOUNT_NAME') - AZURE_ACCOUNT_KEY = os.environ.get('AZURE_ACCOUNT_KEY') - AZURE_CONTAINER = os.environ.get('AZURE_CONTAINER', 'media') - AZURE_SSL = os.environ.get('AZURE_SSL', 'True').lower() in ['true'] - AZURE_UPLOAD_MAX_CONN = int(os.environ.get('AZURE_UPLOAD_MAX_CONN', '2')) - AZURE_CONNECTION_TIMEOUT_SECS = int(os.environ.get('AZURE_CONNECTION_TIMEOUT_SECS', '20')) - AZURE_BLOB_MAX_MEMORY_SIZE = os.environ.get('AZURE_BLOB_MAX_MEMORY_SIZE', '2MB') - AZURE_URL_EXPIRATION_SECS = int(os.environ.get('AZURE_URL_EXPIRATION_SECS', '3600')) - AZURE_OVERWRITE_FILES = os.environ.get('AZURE_OVERWRITE_FILES', 'False').lower() in ['true'] - AZURE_LOCATION = os.environ.get('AZURE_LOCATION', '') - AZURE_CUSTOM_DOMAIN = os.environ.get('AZURE_CUSTOM_DOMAIN') - - STORAGES = { - "default": { - "BACKEND": "storages.backends.azure_storage.AzureStorage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - DEFAULT_FILE_STORAGE = 'storages.backends.azure_storage.AzureStorage' - - if AZURE_CUSTOM_DOMAIN: - MEDIA_URL = f'https://{AZURE_CUSTOM_DOMAIN}/{AZURE_LOCATION}' - elif AZURE_ACCOUNT_NAME: - MEDIA_URL = f'https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net/{AZURE_CONTAINER}/{AZURE_LOCATION}' - else: - MEDIA_URL = "/media/" - - MEDIA_ROOT = BASE_DIR / "media" - - elif STORAGE_BACKEND == 'gcs': - # Google Cloud Storage - GS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME') - GS_PROJECT_ID = os.environ.get('GS_PROJECT_ID') - GS_CREDENTIALS = os.environ.get('GS_CREDENTIALS') - GS_DEFAULT_ACL = os.environ.get('GS_DEFAULT_ACL', 'private') - GS_FILE_OVERWRITE = os.environ.get('GS_FILE_OVERWRITE', 'False').lower() in ['true'] - GS_LOCATION = os.environ.get('GS_LOCATION', 'media') - GS_CUSTOM_ENDPOINT = os.environ.get('GS_CUSTOM_ENDPOINT') - GS_QUERYSTRING_AUTH = os.environ.get('GS_QUERYSTRING_AUTH', 'True').lower() in ['true'] - - STORAGES = { - "default": { - "BACKEND": "storages.backends.gcloud.GoogleCloudStorage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage' - - if GS_CUSTOM_ENDPOINT: - MEDIA_URL = f'{GS_CUSTOM_ENDPOINT}/{GS_LOCATION}/' - elif GS_BUCKET_NAME: - MEDIA_URL = f'https://storage.googleapis.com/{GS_BUCKET_NAME}/{GS_LOCATION}/' - else: - MEDIA_URL = "/media/" - - MEDIA_ROOT = BASE_DIR / "media" - - elif STORAGE_BACKEND == 'sftp': - # SFTP Storage - SFTP_STORAGE_HOST = os.environ.get('SFTP_STORAGE_HOST') - SFTP_STORAGE_ROOT = os.environ.get('SFTP_STORAGE_ROOT', '/media/') - SFTP_STORAGE_PARAMS = { - 'port': int(os.environ.get('SFTP_STORAGE_PORT', '22')), - 'username': os.environ.get('SFTP_STORAGE_USERNAME'), - 'password': os.environ.get('SFTP_STORAGE_PASSWORD'), - 'pkey': os.environ.get('SFTP_STORAGE_PRIVATE_KEY'), - } - SFTP_STORAGE_INTERACTIVE = os.environ.get('SFTP_STORAGE_INTERACTIVE', 'False').lower() in ['true'] - SFTP_STORAGE_FILE_MODE = os.environ.get('SFTP_STORAGE_FILE_MODE') - SFTP_STORAGE_DIR_MODE = os.environ.get('SFTP_STORAGE_DIR_MODE') - SFTP_STORAGE_UID = os.environ.get('SFTP_STORAGE_UID') - SFTP_STORAGE_GID = os.environ.get('SFTP_STORAGE_GID') - SFTP_KNOWN_HOST_FILE = os.environ.get('SFTP_KNOWN_HOST_FILE') - - STORAGES = { - "default": { - "BACKEND": "storages.backends.sftpstorage.SFTPStorage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage' - MEDIA_URL = "/media/" - MEDIA_ROOT = BASE_DIR / "media" - - elif STORAGE_BACKEND == 'dropbox': - # Dropbox Storage - DROPBOX_OAUTH2_TOKEN = os.environ.get('DROPBOX_OAUTH2_TOKEN') - DROPBOX_ROOT_PATH = os.environ.get('DROPBOX_ROOT_PATH', '/media') - DROPBOX_TIMEOUT = int(os.environ.get('DROPBOX_TIMEOUT', '100')) - DROPBOX_WRITE_MODE = os.environ.get('DROPBOX_WRITE_MODE', 'add') - - STORAGES = { - "default": { - "BACKEND": "storages.backends.dropbox.DropBoxStorage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - DEFAULT_FILE_STORAGE = 'storages.backends.dropbox.DropBoxStorage' - MEDIA_URL = "/media/" - MEDIA_ROOT = BASE_DIR / "media" - - else: - # Local filesystem storage (default) - STORAGES = { - "default": { - "BACKEND": "django.core.files.storage.FileSystemStorage", - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", - }, - } - - DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' - MEDIA_URL = "/media/" - MEDIA_ROOT = BASE_DIR / "media" - - if OIDC_ENABLED: - # get oidc config from env - OIDC_CREATE_USER = os.environ.get('OIDC_CREATE_USER', 'True').lower() in ['true'] - OIDC_RP_SIGN_ALGO = os.environ.get('OIDC_RP_SIGN_ALGO', 'HS256') - OIDC_OP_JWKS_ENDPOINT = os.environ.get('OIDC_OP_JWKS_ENDPOINT') - OIDC_RP_IDP_SIGN_KEY = os.environ.get('OIDC_RP_IDP_SIGN_KEY') - OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID') - OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET') - OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT') - OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT') - OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT') - OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = float(os.environ.get('OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS', 900)) - OIDC_USERNAME_ALGO = 'myapp.utils.generate_username' - - # Add 'mozilla_django_oidc.middleware.SessionRefresh' to INSTALLED_APPS - INSTALLED_APPS.append('mozilla_django_oidc') - - # Add 'mozilla_django_oidc' authentication backend - AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'mozilla_django_oidc.auth.OIDCAuthenticationBackend', - ) - - # Add 'mozilla_django_oidc.middleware.SessionRefresh' to MIDDLEWARE - # https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html#validate-id-tokens-by-renewing-them - MIDDLEWARE.append('mozilla_django_oidc.middleware.SessionRefresh') - - # Fix http callback issue in mozilla-django-oidc by forcing https; https://github.com/mozilla/mozilla-django-oidc/issues/417 - # OIDC should only be setup behind a TLS reverse proxy anyways - SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") \ No newline at end of file +# --- +# apiVersion: v1 +# kind: ConfigMap +# metadata: +# name: settings +# data: +# settings.py: | +# """ +# Django settings for myproject project. + +# Generated by 'django-admin startproject' using Django 3.2.16. + +# For more information on this file, see +# https://docs.djangoproject.com/en/3.2/topics/settings/ + +# For the full list of settings and their values, see +# https://docs.djangoproject.com/en/3.2/ref/settings/ +# """ + +# from pathlib import Path +# from dotenv import load_dotenv +# import os +# import pytz +# import secrets +# from django.utils.html import escape +# from django.utils.translation import gettext_lazy as _ +# from csp.constants import NONE, SELF, UNSAFE_INLINE, UNSAFE_EVAL + +# # Load environment variables from .env file +# load_dotenv() + +# # Build paths inside the project like this: BASE_DIR / 'subdir'. +# BASE_DIR = Path(__file__).resolve().parent.parent + +# # get debug modus from env +# DEBUG = os.environ.get('DEBUG', 'False').lower() in ['true'] + +# # get container version from env +# VERSION = escape(os.environ.get("VERSION", '')) + +# # Enable/disable public meme feed feature (must be after import os) +# ENABLE_PUBLIC_FEED = os.environ.get('ENABLE_PUBLIC_FEED', 'False').lower() in ['true', '1', 'yes'] + +# # auto-generate a secure secret key or use from env variable +# SECRET_KEY = os.environ.get("SECRET_KEY", secrets.token_urlsafe(32)) + +# # define allowed hosts and trusted domains via env variables +# DOMAIN = "" +# ALLOWED_HOSTS = ["127.0.0.1"] +# CSRF_TRUSTED_ORIGINS = ["http://127.0.0.1:8000"] + +# DOMAIN = str(os.environ.get("DOMAIN", "localhost")) +# TRUSTED_PORT = str(os.environ.get("PORT", "8000")) + +# if DOMAIN: +# domains = DOMAIN.split(',') +# for domain in domains: +# domain = domain.strip().rstrip('/').replace('http://', '').replace('https://', '') +# if domain: +# ALLOWED_HOSTS.append(domain) +# TRUSTED_USER_DOMAIN_HTTP = f"http://{domain}:{TRUSTED_PORT}" +# TRUSTED_USER_DOMAIN_HTTP_80_DEFAULT = f"http://{domain}" +# TRUSTED_USER_DOMAIN_HTTPS = f"https://{domain}:{TRUSTED_PORT}" +# TRUSTED_USER_DOMAIN_HTTPS_443_DEFAULT = f"https://{domain}" +# CSRF_TRUSTED_ORIGINS.extend([TRUSTED_USER_DOMAIN_HTTP, TRUSTED_USER_DOMAIN_HTTPS, TRUSTED_USER_DOMAIN_HTTP_80_DEFAULT, TRUSTED_USER_DOMAIN_HTTPS_443_DEFAULT]) + +# #Session Management +# CSRF_COOKIE_HTTPONLY = True +# SESSION_EXPIRE_AT_BROWSER_CLOSE = os.environ.get('SESSION_EXPIRE_AT_BROWSER_CLOSE', 'True').lower() in ['true'] +# SESSION_COOKIE_AGE = int(os.environ.get('SESSION_COOKIE_AGE', '30')) * 60 +# SESSION_COOKIE_NAME = 'Session' +# SESSION_COOKIE_SAMESITE = 'Lax' + +# # ============================================================================= +# # REDIS CACHE CONFIGURATION (for sessions and general caching) +# # ============================================================================= +# # If REDIS_HOST is not set, sessions will use database (backward compatible) +# REDIS_HOST = os.environ.get("REDIS_HOST", "") +# REDIS_PORT = os.environ.get("REDIS_PORT", "6379") +# REDIS_DB = os.environ.get("REDIS_DB", "0") +# REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD", "") + +# if REDIS_HOST: +# # Redis is available - use it for caching and sessions +# CACHES = { +# "default": { +# "BACKEND": "django_redis.cache.RedisCache", +# "LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}", +# "OPTIONS": { +# "CLIENT_CLASS": "django_redis.client.DefaultClient", +# "PASSWORD": REDIS_PASSWORD if REDIS_PASSWORD else None, +# "SOCKET_CONNECT_TIMEOUT": 5, # seconds +# "SOCKET_TIMEOUT": 5, # seconds +# "RETRY_ON_TIMEOUT": True, +# "CONNECTION_POOL_KWARGS": { +# "max_connections": 50, +# "retry_on_timeout": True, +# }, +# "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", +# "IGNORE_EXCEPTIONS": True, # Don't crash if Redis is down +# }, +# "KEY_PREFIX": "memelord", +# "TIMEOUT": 300, # 5 minutes default +# } +# } + +# # Use Redis for session storage (cloud-native) +# SESSION_ENGINE = "django.contrib.sessions.backends.cache" +# SESSION_CACHE_ALIAS = "default" +# else: +# # Redis not configured - use database sessions (backward compatible) +# SESSION_ENGINE = "django.contrib.sessions.backends.db" +# # No CACHES configuration - Django will use local memory cache + +# SECURE_COOKIES = os.environ.get('SECURE_COOKIES', 'False').lower() in ['true'] + +# if SECURE_COOKIES: +# # transmit cookies over encrypted https only +# SESSION_COOKIE_SECURE = True +# CSRF_COOKIE_SECURE = True +# # also set hsts response header +# SECURE_HSTS_SECONDS = "31536000" +# SECURE_HSTS_PRELOAD = True +# SECURE_HSTS_INCLUDE_SUBDOMAINS = True +# else: +# # transmit cookies over unencrypted http +# SESSION_COOKIE_SECURE = False +# CSRF_COOKIE_SECURE = False + +# # http security response headers +# SECURE_BROWSER_XSS_FILTER = True +# SECURE_CONTENT_TYPE_NOSNIFF = True +# X_FRAME_OPTIONS = 'DENY' +# REFERRER_POLICY = 'same-origin' + +# # Load from environment, default to "'self'" +# raw_frame_ancestors = os.environ.get("CSP_FRAME_ANCESTORS", "'none'") +# # Split by comma, strip spaces, and keep properly quoted entries +# FRAME_ANCESTORS = [item.strip() for item in raw_frame_ancestors.split(',') if item.strip()] + +# # Build CSP img-src list dynamically based on storage backend +# STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() +# IMG_SRC_LIST = ["'self'", "data:", "blob:", "https://img.logo.dev"] + +# # Add S3 domains to CSP if using S3 storage +# if STORAGE_BACKEND == 's3': +# AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') +# AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') +# AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') +# AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') +# AWS_S3_USE_ACCELERATE_ENDPOINT = os.environ.get('AWS_S3_USE_ACCELERATE_ENDPOINT', 'False').lower() in ['true'] + +# # Always add custom domain if specified (CDN like CloudFront) +# if AWS_S3_CUSTOM_DOMAIN: +# IMG_SRC_LIST.append(f"https://{AWS_S3_CUSTOM_DOMAIN}") + +# # Detect S3 provider based on endpoint URL +# if AWS_S3_ENDPOINT_URL: +# # S3-compatible service detected +# from urllib.parse import urlparse +# parsed_url = urlparse(AWS_S3_ENDPOINT_URL) +# endpoint_domain = parsed_url.netloc +# endpoint_scheme = parsed_url.scheme or 'https' + +# # Add the endpoint domain +# IMG_SRC_LIST.append(f"{endpoint_scheme}://{endpoint_domain}") + +# # Add bucket-based subdomain format if applicable +# if endpoint_domain and AWS_STORAGE_BUCKET_NAME: +# IMG_SRC_LIST.append(f"{endpoint_scheme}://{AWS_STORAGE_BUCKET_NAME}.{endpoint_domain}") + +# # Provider-specific URL patterns +# endpoint_lower = endpoint_domain.lower() if endpoint_domain else "" + +# # DigitalOcean Spaces: also add CDN domain +# if 'digitaloceanspaces.com' in endpoint_lower: +# # DigitalOcean Spaces CDN format: bucket-name.region.cdn.digitaloceanspaces.com +# if AWS_STORAGE_BUCKET_NAME and AWS_S3_REGION_NAME: +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.cdn.digitaloceanspaces.com") + +# # Cloudflare R2: also add public bucket URL format +# elif 'r2.cloudflarestorage.com' in endpoint_lower: +# # Cloudflare R2 can use custom domains via public.r2.dev +# # Format: https://bucket-name.account-id.r2.dev (if public) +# # This is typically set via AWS_S3_CUSTOM_DOMAIN, but we note it here +# pass + +# # Wasabi: supports path-style and virtual-hosted-style +# elif 'wasabisys.com' in endpoint_lower: +# # Already covered by endpoint_domain and bucket subdomain +# pass + +# # Linode Object Storage: supports path-style and virtual-hosted-style +# elif 'linodeobjects.com' in endpoint_lower: +# # Already covered by endpoint_domain and bucket subdomain +# pass + +# # Backblaze B2: supports path-style and virtual-hosted-style +# elif 'backblazeb2.com' in endpoint_lower: +# # Already covered by endpoint_domain and bucket subdomain +# pass + +# # MinIO: custom deployment, already covered +# # Other S3-compatible services: already covered + +# else: +# # No endpoint URL = Standard AWS S3 +# if AWS_STORAGE_BUCKET_NAME: +# # Add AWS S3 virtual-hosted-style URLs +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com") +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com") + +# # Add path-style URL format (legacy but still supported) +# IMG_SRC_LIST.append("https://s3.amazonaws.com") +# IMG_SRC_LIST.append(f"https://s3.{AWS_S3_REGION_NAME}.amazonaws.com") + +# # Add dual-stack endpoints (IPv6 support) +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.dualstack.{AWS_S3_REGION_NAME}.amazonaws.com") + +# # Add S3 Transfer Acceleration endpoint if enabled +# if AWS_S3_USE_ACCELERATE_ENDPOINT: +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3-accelerate.amazonaws.com") +# IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3-accelerate.dualstack.amazonaws.com") + +# # Add Azure domains to CSP if using Azure storage +# elif STORAGE_BACKEND == 'azure': +# AZURE_ACCOUNT_NAME = os.environ.get('AZURE_ACCOUNT_NAME') +# AZURE_CUSTOM_DOMAIN = os.environ.get('AZURE_CUSTOM_DOMAIN') + +# if AZURE_CUSTOM_DOMAIN: +# IMG_SRC_LIST.append(f"https://{AZURE_CUSTOM_DOMAIN}") + +# if AZURE_ACCOUNT_NAME: +# IMG_SRC_LIST.append(f"https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net") + +# # Add GCS domains to CSP if using GCS storage +# elif STORAGE_BACKEND == 'gcs': +# GS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME') +# GS_CUSTOM_ENDPOINT = os.environ.get('GS_CUSTOM_ENDPOINT') + +# if GS_CUSTOM_ENDPOINT: +# IMG_SRC_LIST.append(GS_CUSTOM_ENDPOINT) + +# if GS_BUCKET_NAME: +# # GCS can use multiple URL formats +# IMG_SRC_LIST.append("https://storage.googleapis.com") +# IMG_SRC_LIST.append("https://storage.cloud.google.com") +# IMG_SRC_LIST.append(f"https://{GS_BUCKET_NAME}.storage.googleapis.com") + +# # SFTP/Dropbox typically serve through Django (using 'self' origin) +# elif STORAGE_BACKEND == 'sftp': +# # SFTP files are retrieved by Django and served through Django views +# # Optional: If you have nginx serving the same SFTP directory via HTTP +# SFTP_CUSTOM_DOMAIN = os.environ.get('SFTP_CUSTOM_DOMAIN') +# if SFTP_CUSTOM_DOMAIN: +# IMG_SRC_LIST.append(f"https://{SFTP_CUSTOM_DOMAIN}") + +# elif STORAGE_BACKEND == 'dropbox': +# # Dropbox files are retrieved by Django and served through Django views +# # No additional CSP configuration needed +# pass + +# CSP_IMG_SRC_EXTRA = os.environ.get('CSP_IMG_SRC_EXTRA', '') +# if CSP_IMG_SRC_EXTRA: +# extra_domains = [domain.strip() for domain in CSP_IMG_SRC_EXTRA.split(',') if domain.strip()] +# IMG_SRC_LIST.extend(extra_domains) + +# CONTENT_SECURITY_POLICY = { +# "DIRECTIVES": { +# "default-src": ["'self'"], +# "style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdn.jsdelivr.net"], +# "script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://cdn.jsdelivr.net"], +# "font-src": ["'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"], +# "img-src": IMG_SRC_LIST, +# "object-src": ["'none'"], +# "connect-src": ["'self'"], +# "frame-ancestors": FRAME_ANCESTORS, +# }, +# } + +# # Application definition +# INSTALLED_APPS = [ +# 'myapp', +# 'django_celery_beat', +# 'django.contrib.admin', +# 'django.contrib.auth', +# 'django.contrib.contenttypes', +# 'django.contrib.sessions', +# 'django.contrib.messages', +# 'django.contrib.staticfiles', +# 'csp', +# 'storages', +# ] + +# MIDDLEWARE = [ +# 'django.middleware.security.SecurityMiddleware', +# 'django.contrib.sessions.middleware.SessionMiddleware', +# #'django.middleware.locale.LocaleMiddleware', +# 'django.middleware.common.CommonMiddleware', +# 'django.middleware.csrf.CsrfViewMiddleware', +# 'django.contrib.auth.middleware.AuthenticationMiddleware', +# 'django.contrib.messages.middleware.MessageMiddleware', +# 'django_http_referrer_policy.middleware.ReferrerPolicyMiddleware', +# 'csp.middleware.CSPMiddleware', +# ] + +# ROOT_URLCONF = 'myproject.urls' + +# TEMPLATES = [ +# { +# 'BACKEND': 'django.template.backends.django.DjangoTemplates', +# 'DIRS': [os.path.join(BASE_DIR, 'myapp/templates/registration')], +# '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', +# ], +# }, +# }, +# ] + +# # Database +# # https://docs.djangoproject.com/en/3.2/ref/settings/#databases + +# DB_ENGINE = os.environ.get("DB_ENGINE", "sqlite3") + +# if DB_ENGINE == "sqlite3": + +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': os.path.join(BASE_DIR, 'database', 'db.sqlite3'), +# } +# } + +# elif DB_ENGINE == "postgres": + +# DB_HOST = os.environ.get("POSTGRES_HOST", "db") +# DB_PORT = os.environ.get("POSTGRES_PORT", "5432") +# DB_USER = os.environ.get("POSTGRES_USER", "memelord") +# DB_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "memelord") +# DB_NAME = os.environ.get("POSTGRES_DB", "memelord") + +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql', +# 'NAME': DB_NAME, +# 'HOST': DB_HOST, +# 'PORT': DB_PORT, +# 'USER': DB_USER, +# 'PASSWORD': DB_PASSWORD, +# } +# } + +# # Password validation +# # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +# 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 +# # https://docs.djangoproject.com/en/3.2/topics/i18n/ +# LANGUAGE_CODE = 'en-us' +# TIME_ZONE = os.environ.get('TZ', 'Europe/Berlin') +# USE_I18N = True +# USE_L10N = True +# USE_TZ = True + +# LANGUAGES = [ +# ('en', _('English')), +# ('de', _('German')), +# ('fr', _('French')), +# ('it', _('Italian')), +# ] + +# LOCALE_PATHS = [ +# os.path.join(BASE_DIR, 'locale') +# ] + +# # Celery configuration +# # http://docs.celeryproject.org/en/latest/configuration.html + +# LOGS_DIR = os.path.join(BASE_DIR, 'logs') + +# # ============================================================================= +# # LOGGING CONFIGURATION +# # ============================================================================= +# LOGGING = { +# 'version': 1, +# 'disable_existing_loggers': False, +# 'formatters': { +# 'verbose': { +# 'format': '{levelname} {asctime} {module} {message}', +# 'style': '{', +# }, +# 'simple': { +# 'format': '{levelname} {message}', +# 'style': '{', +# }, +# }, +# 'handlers': { +# 'console': { +# 'class': 'logging.StreamHandler', +# 'formatter': 'verbose', +# }, +# 'file': { +# 'class': 'logging.handlers.RotatingFileHandler', +# 'filename': os.path.join(LOGS_DIR, 'django.log'), +# 'maxBytes': 1024 * 1024 * 15, # 15MB +# 'backupCount': 10, +# 'formatter': 'verbose', +# }, +# }, +# 'loggers': { +# 'django': { +# 'handlers': ['console', 'file'], +# 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), +# 'propagate': False, +# }, +# 'myapp': { +# 'handlers': ['console', 'file'], +# 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), +# 'propagate': False, +# }, +# 'storages': { +# 'handlers': ['console', 'file'], +# 'level': 'DEBUG' if DEBUG else 'INFO', +# 'propagate': False, +# }, +# 'boto3': { +# 'handlers': ['console', 'file'], +# 'level': 'DEBUG' if DEBUG else 'INFO', +# 'propagate': False, +# }, +# 'botocore': { +# 'handlers': ['console', 'file'], +# 'level': 'DEBUG' if DEBUG else 'INFO', +# 'propagate': False, +# }, +# }, +# 'root': { +# 'handlers': ['console', 'file'], +# 'level': 'INFO', +# }, +# } + +# # Create logs directory if it doesn't exist +# os.makedirs(LOGS_DIR, exist_ok=True) + +# STATIC_URL = '/static/' +# STATIC_ROOT = os.path.join(BASE_DIR, 'myapp', 'static') + +# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# LOGIN_URL = '/accounts/login/' +# LOGIN_REDIRECT_URL = '/' +# LOGOUT_REDIRECT_URL = '/post-logout/' +# ALLOW_LOGOUT_GET_METHOD = True + +# WSGI_APPLICATION = 'myproject.wsgi.application' + +# # check if oidc is enabled +# OIDC_ENABLED = os.environ.get('OIDC_ENABLED', 'False').lower() in ['true'] +# OIDC_AUTOLOGIN = os.environ.get('OIDC_AUTOLOGIN', 'False').lower() in ['true'] + +# # Max file upload size in MB (default: 10MB) +# # Can be configured via MAX_UPLOAD_SIZE_MB environment variable +# MAX_UPLOAD_SIZE_MB = int(os.environ.get('MAX_UPLOAD_SIZE_MB', '10')) * 1024 * 1024 + +# # ============================================================================= +# # STORAGE BACKEND CONFIGURATION +# # ============================================================================= +# # IMPORTANT: This must be set BEFORE MEDIA_URL and MEDIA_ROOT +# # Configure storage backend via STORAGE_BACKEND environment variable +# # Supported backends: local, s3, azure, gcs, sftp, dropbox +# # Default: local (filesystem storage) + +# STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() + +# # Django 5.0+ uses STORAGES instead of DEFAULT_FILE_STORAGE +# # Set both for compatibility +# if STORAGE_BACKEND == 's3': +# # Amazon S3 / Compatible S3 Storage (MinIO, DigitalOcean Spaces, etc.) +# AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') +# AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') +# AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') +# AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') +# AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') +# AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') # Only set for S3-compatible services (MinIO, etc.) +# AWS_DEFAULT_ACL = os.environ.get('AWS_DEFAULT_ACL', 'private') +# AWS_S3_OBJECT_PARAMETERS = { +# 'CacheControl': 'max-age=86400', +# } +# AWS_QUERYSTRING_AUTH = os.environ.get('AWS_QUERYSTRING_AUTH', 'True').lower() in ['true'] +# AWS_S3_FILE_OVERWRITE = os.environ.get('AWS_S3_FILE_OVERWRITE', 'False').lower() in ['true'] +# AWS_LOCATION = os.environ.get('AWS_LOCATION', 'media') + +# # Use signature version 4 (required for all regions) +# AWS_S3_SIGNATURE_VERSION = 's3v4' + +# # Use virtual-hosted-style URLs (bucket-name.s3.region.amazonaws.com) +# # This is the default and recommended format +# AWS_S3_ADDRESSING_STYLE = 'virtual' + +# # Django 5.0+ STORAGES configuration +# STORAGES = { +# "default": { +# "BACKEND": "storages.backends.s3boto3.S3Boto3Storage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# # Legacy setting for older Django versions +# DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' + +# # Update MEDIA_URL +# if AWS_S3_CUSTOM_DOMAIN: +# MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_LOCATION}/' +# elif AWS_STORAGE_BUCKET_NAME: +# # Use region-specific virtual-hosted-style URL +# if AWS_S3_REGION_NAME == 'us-east-1': +# MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/{AWS_LOCATION}/' +# else: +# MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com/{AWS_LOCATION}/' +# else: +# MEDIA_URL = "/media/" + +# MEDIA_ROOT = BASE_DIR / "media" + +# elif STORAGE_BACKEND == 'azure': +# # Microsoft Azure Blob Storage +# AZURE_ACCOUNT_NAME = os.environ.get('AZURE_ACCOUNT_NAME') +# AZURE_ACCOUNT_KEY = os.environ.get('AZURE_ACCOUNT_KEY') +# AZURE_CONTAINER = os.environ.get('AZURE_CONTAINER', 'media') +# AZURE_SSL = os.environ.get('AZURE_SSL', 'True').lower() in ['true'] +# AZURE_UPLOAD_MAX_CONN = int(os.environ.get('AZURE_UPLOAD_MAX_CONN', '2')) +# AZURE_CONNECTION_TIMEOUT_SECS = int(os.environ.get('AZURE_CONNECTION_TIMEOUT_SECS', '20')) +# AZURE_BLOB_MAX_MEMORY_SIZE = os.environ.get('AZURE_BLOB_MAX_MEMORY_SIZE', '2MB') +# AZURE_URL_EXPIRATION_SECS = int(os.environ.get('AZURE_URL_EXPIRATION_SECS', '3600')) +# AZURE_OVERWRITE_FILES = os.environ.get('AZURE_OVERWRITE_FILES', 'False').lower() in ['true'] +# AZURE_LOCATION = os.environ.get('AZURE_LOCATION', '') +# AZURE_CUSTOM_DOMAIN = os.environ.get('AZURE_CUSTOM_DOMAIN') + +# STORAGES = { +# "default": { +# "BACKEND": "storages.backends.azure_storage.AzureStorage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# DEFAULT_FILE_STORAGE = 'storages.backends.azure_storage.AzureStorage' + +# if AZURE_CUSTOM_DOMAIN: +# MEDIA_URL = f'https://{AZURE_CUSTOM_DOMAIN}/{AZURE_LOCATION}' +# elif AZURE_ACCOUNT_NAME: +# MEDIA_URL = f'https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net/{AZURE_CONTAINER}/{AZURE_LOCATION}' +# else: +# MEDIA_URL = "/media/" + +# MEDIA_ROOT = BASE_DIR / "media" + +# elif STORAGE_BACKEND == 'gcs': +# # Google Cloud Storage +# GS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME') +# GS_PROJECT_ID = os.environ.get('GS_PROJECT_ID') +# GS_CREDENTIALS = os.environ.get('GS_CREDENTIALS') +# GS_DEFAULT_ACL = os.environ.get('GS_DEFAULT_ACL', 'private') +# GS_FILE_OVERWRITE = os.environ.get('GS_FILE_OVERWRITE', 'False').lower() in ['true'] +# GS_LOCATION = os.environ.get('GS_LOCATION', 'media') +# GS_CUSTOM_ENDPOINT = os.environ.get('GS_CUSTOM_ENDPOINT') +# GS_QUERYSTRING_AUTH = os.environ.get('GS_QUERYSTRING_AUTH', 'True').lower() in ['true'] + +# STORAGES = { +# "default": { +# "BACKEND": "storages.backends.gcloud.GoogleCloudStorage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage' + +# if GS_CUSTOM_ENDPOINT: +# MEDIA_URL = f'{GS_CUSTOM_ENDPOINT}/{GS_LOCATION}/' +# elif GS_BUCKET_NAME: +# MEDIA_URL = f'https://storage.googleapis.com/{GS_BUCKET_NAME}/{GS_LOCATION}/' +# else: +# MEDIA_URL = "/media/" + +# MEDIA_ROOT = BASE_DIR / "media" + +# elif STORAGE_BACKEND == 'sftp': +# # SFTP Storage +# SFTP_STORAGE_HOST = os.environ.get('SFTP_STORAGE_HOST') +# SFTP_STORAGE_ROOT = os.environ.get('SFTP_STORAGE_ROOT', '/media/') +# SFTP_STORAGE_PARAMS = { +# 'port': int(os.environ.get('SFTP_STORAGE_PORT', '22')), +# 'username': os.environ.get('SFTP_STORAGE_USERNAME'), +# 'password': os.environ.get('SFTP_STORAGE_PASSWORD'), +# 'pkey': os.environ.get('SFTP_STORAGE_PRIVATE_KEY'), +# } +# SFTP_STORAGE_INTERACTIVE = os.environ.get('SFTP_STORAGE_INTERACTIVE', 'False').lower() in ['true'] +# SFTP_STORAGE_FILE_MODE = os.environ.get('SFTP_STORAGE_FILE_MODE') +# SFTP_STORAGE_DIR_MODE = os.environ.get('SFTP_STORAGE_DIR_MODE') +# SFTP_STORAGE_UID = os.environ.get('SFTP_STORAGE_UID') +# SFTP_STORAGE_GID = os.environ.get('SFTP_STORAGE_GID') +# SFTP_KNOWN_HOST_FILE = os.environ.get('SFTP_KNOWN_HOST_FILE') + +# STORAGES = { +# "default": { +# "BACKEND": "storages.backends.sftpstorage.SFTPStorage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage' +# MEDIA_URL = "/media/" +# MEDIA_ROOT = BASE_DIR / "media" + +# elif STORAGE_BACKEND == 'dropbox': +# # Dropbox Storage +# DROPBOX_OAUTH2_TOKEN = os.environ.get('DROPBOX_OAUTH2_TOKEN') +# DROPBOX_ROOT_PATH = os.environ.get('DROPBOX_ROOT_PATH', '/media') +# DROPBOX_TIMEOUT = int(os.environ.get('DROPBOX_TIMEOUT', '100')) +# DROPBOX_WRITE_MODE = os.environ.get('DROPBOX_WRITE_MODE', 'add') + +# STORAGES = { +# "default": { +# "BACKEND": "storages.backends.dropbox.DropBoxStorage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# DEFAULT_FILE_STORAGE = 'storages.backends.dropbox.DropBoxStorage' +# MEDIA_URL = "/media/" +# MEDIA_ROOT = BASE_DIR / "media" + +# else: +# # Local filesystem storage (default) +# STORAGES = { +# "default": { +# "BACKEND": "django.core.files.storage.FileSystemStorage", +# }, +# "staticfiles": { +# "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", +# }, +# } + +# DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' +# MEDIA_URL = "/media/" +# MEDIA_ROOT = BASE_DIR / "media" + +# if OIDC_ENABLED: +# # get oidc config from env +# OIDC_CREATE_USER = os.environ.get('OIDC_CREATE_USER', 'True').lower() in ['true'] +# OIDC_RP_SIGN_ALGO = os.environ.get('OIDC_RP_SIGN_ALGO', 'HS256') +# OIDC_OP_JWKS_ENDPOINT = os.environ.get('OIDC_OP_JWKS_ENDPOINT') +# OIDC_RP_IDP_SIGN_KEY = os.environ.get('OIDC_RP_IDP_SIGN_KEY') +# OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID') +# OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET') +# OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT') +# OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT') +# OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT') +# OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = float(os.environ.get('OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS', 900)) +# OIDC_USERNAME_ALGO = 'myapp.utils.generate_username' + +# # Add 'mozilla_django_oidc.middleware.SessionRefresh' to INSTALLED_APPS +# INSTALLED_APPS.append('mozilla_django_oidc') + +# # Add 'mozilla_django_oidc' authentication backend +# AUTHENTICATION_BACKENDS = ( +# 'django.contrib.auth.backends.ModelBackend', +# 'mozilla_django_oidc.auth.OIDCAuthenticationBackend', +# ) + +# # Add 'mozilla_django_oidc.middleware.SessionRefresh' to MIDDLEWARE +# # https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html#validate-id-tokens-by-renewing-them +# MIDDLEWARE.append('mozilla_django_oidc.middleware.SessionRefresh') + +# # Fix http callback issue in mozilla-django-oidc by forcing https; https://github.com/mozilla/mozilla-django-oidc/issues/417 +# # OIDC should only be setup behind a TLS reverse proxy anyways +# SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") \ No newline at end of file