""" Django settings for apibase project. Generated by 'django-admin startproject' using Django 5.0. For more information on this file, see https://docs.djangoproject.com/en/5.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.0/ref/settings/ """ import os from pathlib import Path import datetime from rest_framework.settings import api_settings from decouple import config from django.core.management.utils import get_random_secret_key from django.utils.log import DEFAULT_LOGGING import logging.config # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = config("SECRET_KEY", default=get_random_secret_key()) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = config("DEBUG", cast=bool) ALLOWED_HOSTS = [] # Add explicit hosts from environment env_hosts = config("ALLOWED_HOSTS", default="").split() ALLOWED_HOSTS.extend(env_hosts) if DEBUG: INTERNAL_IPS = [ "127.0.0.1", ] # CORS # CORS_ALLOWED_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:3000"] # CORS_ALLOWED_ORIGIN_REGEXES CORS_ALLOW_ALL_ORIGINS = True # Application definition INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "rest_framework", "django_rest_passwordreset", "djangopasswordlessknox", "django_extensions", "django_seed", "storages", "whitenoise.runserver_nostatic", "django.contrib.staticfiles", # applications "api", "billing", "devices", # Authentication "knox", # third party "django_filters", "corsheaders", ] if DEBUG: INSTALLED_APPS.append("debug_toolbar") INSTALLED_APPS.append("drf_spectacular") MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "corsheaders.middleware.CorsMiddleware", "easyaudit.middleware.easyaudit.EasyAuditMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] if DEBUG: MIDDLEWARE.append( "debug_toolbar.middleware.DebugToolbarMiddleware", ) ROOT_URLCONF = "apibase.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ BASE_DIR, "templates/", ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] WSGI_APPLICATION = "apibase.wsgi.application" # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases if not DEBUG: DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": config("POSTGRES_DATABASE"), "USER": config("POSTGRES_USER"), "PASSWORD": config("POSTGRES_PASSWORD"), "HOST": config("POSTGRES_HOST"), "PORT": config("POSTGRES_PORT"), "OPTIONS": { "pool": True, }, }, } else: DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3", } } # More robust caching configuration CACHES = { "default": { "BACKEND": ( "django_redis.cache.RedisCache" if not DEBUG else "django.core.cache.backends.locmem.LocMemCache" ), "LOCATION": ( config("REDIS_URL", default="redis://redis:6379/") if not DEBUG else "" ), "OPTIONS": ( { "CLIENT_CLASS": ( "django_redis.client.DefaultClient" if not DEBUG else None ), } if not DEBUG else {} ), } } # Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators # Uses email as username for login AUTH_USER_MODEL = "api.User" AUTHENTICATION_BACKENDS = ["api.backends.EmailBackend"] 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/5.0/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ STATIC_URL = "/static/" STATIC_ROOT = BASE_DIR / "staticfiles" STATICFILES_DIRS = [ # os.path.join(BASE_DIR, 'static'), ] MEDIA_URL = "/media/" MEDIA_ROOT = os.path.join(BASE_DIR, "media") STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" REST_FRAMEWORK = { "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], "DEFAULT_AUTHENTICATION_CLASSES": [ "knox.auth.TokenAuthentication", # "api.authentication.TokenAuthentication", # "api.authentication.TokenAuthSupportCookie", # "rest_framework.authentication.SessionAuthentication", # "rest_framework_simplejwt.authentication.JWTAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "rest_framework.permissions.IsAuthenticatedOrReadOnly" ], "DEFAULT_PAGINATION_CLASS": "api.pagination.CustomPagination", "PAGE_SIZE": 10, "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "rest_framework.throttling.UserRateThrottle", "rest_framework.throttling.ScopedRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "anon": "100/day", "user": "1000/min", "login": "1000/min", }, "EXCEPTION_HANDLER": "api.exceptions.custom_exception_handler", "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), # "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema" } REST_KNOX = { "SECURE_HASH_ALGORITHM": "cryptography.hazmat.primitives.hashes.SHA512", "AUTH_TOKEN_CHARACTER_LENGTH": 64, "TOKEN_TTL": datetime.timedelta(minutes=30), "USER_SERIALIZER": "api.serializers.CustomUserSerializer", "TOKEN_LIMIT_PER_USER": 10, "AUTO_REFRESH": True, "EXPIRY_DATETIME_FORMAT": api_settings.DATETIME_FORMAT, } DJANGORESIZED_DEFAULT_SIZE = [500, 500] DJANGORESIZED_DEFAULT_QUALITY = 75 DJANGORESIZED_DEFAULT_KEEP_META = True DJANGORESIZED_DEFAULT_FORCE_FORMAT = "JPEG" DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {"JPEG": ".jpg"} DJANGORESIZED_DEFAULT_NORMALIZE_ROTATION = True DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", }, } LOGGING_CONFIG = None LOGLEVEL = os.environ.get("LOGLEVEL", "WARNING").upper() logging.config.dictConfig( { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s", }, "request": { "format": "%(asctime)s %(levelname)s %(message)s", }, "django.server": DEFAULT_LOGGING["formatters"]["django.server"], }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "default", }, "request": { "class": "logging.StreamHandler", "formatter": "request", }, "django.server": DEFAULT_LOGGING["handlers"]["django.server"], }, "loggers": { "": { "level": "WARNING", "handlers": ["console"], }, "app": { "level": LOGLEVEL, "handlers": ["console"], "propagate": False, }, "django.request": { "level": "WARNING", "handlers": ["request"], "propagate": False, }, "django.server": DEFAULT_LOGGING["loggers"]["django.server"], "django": { "level": LOGLEVEL, "handlers": ["console"], "propagate": False, }, }, } ) if not DEBUG: SECURE_SSL_REDIRECT = config("DJANGO_SECURE_SSL_REDIRECT", cast=bool) SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS = config("SECURE_HSTS_SECONDS", default=3600, cast=int) SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True CSRF_TRUSTED_ORIGINS = [config("CSRF_TRUSTED_ORIGINS")] CSRF_COOKIE_DOMAIN = config("CSRF_COOKIE_DOMAIN") SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # Additional security headers SECURE_BROWSER_XSS_FILTER = True X_FRAME_OPTIONS = "DENY" SECURE_CONTENT_TYPE_NOSNIFF = True EMAIL_BACKEND = ( "django.core.mail.backends.smtp.EmailBackend" # Replace with your preferred backend ) EMAIL_HOST = config("EMAIL_HOSTNAME") EMAIL_PORT = config("EMAIL_PORT", cast=int) EMAIL_HOST_USER = config("EMAIL_USERNAME") EMAIL_HOST_PASSWORD = config("EMAIL_PASSWORD") # DEFAULT_FROM_EMAIL = "noreply@sarlink.net" EMAIL_USE_TLS = True PASSWORDLESS_AUTH = { # 'PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE_NAME': "password_reset_email.html", "PASSWORDLESS_AUTH_TYPES": ["EMAIL", "MOBILE"], "PASSWORDLESS_USER_MOBILE_FIELD_NAME": "mobile", "PASSWORDLESS_TEST_SUPPRESSION": False, "PASSWORDLESS_REGISTER_NEW_USERS": True, "PASSWORDLESS_EMAIL_NOREPLY_ADDRESS": "noreply@sarlink.net", }