import os from pathlib import Path import environ env = environ.Env() BASE_DIR = Path(__file__).resolve().parent.parent dsp_env = os.environ.get("DSP_ENV", "production") env_file = BASE_DIR / f"config/secrets/env.{dsp_env}" if env_file.exists(): environ.Env.read_env(str(env_file)) else: environ.Env.read_env(str(BASE_DIR / "config/secrets/env.production")) SECRET_KEY = env("DJANGO_SECRET_KEY", default="insecure-dev-key-change-in-production") DEBUG = env.bool("DJANGO_DEBUG", default=False) ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["194.164.95.50", "localhost", "127.0.0.1"]) # Trust nginx TLS termination headers SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") USE_X_FORWARDED_HOST = True SESSION_COOKIE_SECURE = env.bool("SESSION_COOKIE_SECURE", default=True) CSRF_COOKIE_SECURE = env.bool("CSRF_COOKIE_SECURE", default=True) CSRF_TRUSTED_ORIGINS = env.list( "DJANGO_CSRF_TRUSTED_ORIGINS", default=[ "https://ixgltd.com", "https://www.ixgltd.com", "https://app.ixgltd.com", "https://portal.ixgltd.com", "https://auth.ixgltd.com", ], ) INSTALLED_APPS = [ "grappelli", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.humanize", "django.contrib.staticfiles", "django_extensions", "rest_framework", "rest_framework.authtoken", "mozilla_django_oidc", "corsheaders", "django_filters", "apps.core", "apps.users", "apps.subscriptions", "apps.batches", "apps.qc", "apps.shipments", "apps.documents", "apps.capa", "apps.esg", "apps.compliance", "apps.ai", "apps.notifications", "apps.ops", "apps.buyer", "apps.guides", "encrypted_model_fields", ] MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "apps.core.middleware.RBACMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "dsp_platform.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 = "dsp_platform.wsgi.application" DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": env("DB_NAME", default="ixg_platform"), "USER": env("DB_USER", default="postgres"), "PASSWORD": env("DB_PASSWORD", default=""), "HOST": env("DB_HOST", default="127.0.0.1"), "PORT": env.int("DB_PORT", default=5432), }, "wikijs": { "ENGINE": "django.db.backends.postgresql", "NAME": env("WIKI_DB_NAME", default="wikijs"), "USER": env("WIKI_DB_USER", default=""), "PASSWORD": env("WIKI_DB_PASSWORD", default=""), "HOST": env("WIKI_DB_HOST", default="127.0.0.1"), "PORT": env.int("WIKI_DB_PORT", default=5432), }, } AUTH_PASSWORD_VALIDATORS = [ {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, ] LANGUAGE_CODE = "en-gb" TIME_ZONE = "Europe/London" USE_I18N = True USE_TZ = True STATIC_URL = "/static/" STATIC_ROOT = BASE_DIR / "staticfiles" MEDIA_URL = "/media/" MEDIA_ROOT = BASE_DIR / "media" WIKI_PUBLIC_BASE = env("WIKI_PUBLIC_BASE", default="http://194.164.95.50") # Tile source for the Leaflet map showing origin zones (apps/compliance/admin.py). # Self-host this, or use a licensed provider, before it carries production # traffic: OSM's public tile servers are governed by their usage policy and are # only suitable for internal, low-volume use. Note also that any hosted tile # source sees the areas being viewed — which, for this platform, are the places # IXG sources shea. LEAFLET_TILE_URL = env( "LEAFLET_TILE_URL", default="https://tile.openstreetmap.org/{z}/{x}/{y}.png" ) # Serve tiles from our own origin instead of letting the browser fetch them from # a third party (apps/core/tiles.py). The map is the only part of the admin page # that depends on an external domain, and when a viewer's network, proxy or # browser filter refuses that domain the map silently never draws — the page, # the Leaflet bundle and the polygon are all same-origin and fine. With this on, # the browser asks us and we fetch and cache. Set LEAFLET_TILE_PROXY_ENABLED=False # to go back to direct browser fetches. LEAFLET_TILE_PROXY_ENABLED = env.bool("LEAFLET_TILE_PROXY_ENABLED", default=True) LEAFLET_TILE_CACHE_DIR = env( "LEAFLET_TILE_CACHE_DIR", default=str(MEDIA_ROOT / "tile-cache") ) DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" AUTH_USER_MODEL = "users.User" AUTHENTICATION_BACKENDS = [ "apps.users.auth.KeycloakOIDCBackend", "django.contrib.auth.backends.ModelBackend", ] OIDC_OP_AUTHORIZATION_ENDPOINT = env("OIDC_OP_AUTHORIZATION_ENDPOINT", default="http://194.164.95.50:8443/realms/ixg/protocol/openid-connect/auth") OIDC_OP_TOKEN_ENDPOINT = env("OIDC_OP_TOKEN_ENDPOINT", default="http://194.164.95.50:8443/realms/ixg/protocol/openid-connect/token") OIDC_OP_USER_ENDPOINT = env("OIDC_OP_USER_ENDPOINT", default="http://194.164.95.50:8443/realms/ixg/protocol/openid-connect/userinfo") OIDC_OP_JWKS_ENDPOINT = env("OIDC_OP_JWKS_ENDPOINT", default="http://194.164.95.50:8443/realms/ixg/protocol/openid-connect/certs") OIDC_OP_LOGOUT_ENDPOINT = env("OIDC_OP_LOGOUT_ENDPOINT", default="http://194.164.95.50:8443/realms/ixg/protocol/openid-connect/logout") OIDC_RP_SIGN_ALGO = "RS256" OIDC_RP_CLIENT_ID = env("OIDC_RP_CLIENT_ID", default="ixg-platform") OIDC_RP_CLIENT_SECRET = env("OIDC_RP_CLIENT_SECRET", default="") OIDC_RP_SCOPES = "openid email profile" OIDC_USE_NONCE = True OIDC_CREATE_USER = True OIDC_STORE_ACCESS_TOKEN = True OIDC_STORE_ID_TOKEN = True LOGIN_URL = "oidc_authentication_init" LOGOUT_REDIRECT_URL = "/" # RP-initiated logout: end the Keycloak session as well as the Django one, or # the next request silently re-authenticates (apps/users/oidc.py). OIDC_OP_LOGOUT_URL_METHOD = "apps.users.oidc.provider_logout" # The root view dispatches by role (apps/core/views.RoleRedirectView), so a # buyer who signs in is not aimed at the admin area, and the RBAC middleware # can redirect a wrong-role request here without looping. LOGIN_REDIRECT_URL = "/" KEYCLOAK_SERVER_URL = env("KEYCLOAK_SERVER_URL", default="http://194.164.95.50:8443") KEYCLOAK_REALM = env("KEYCLOAK_REALM", default="ixg") # Service account used by the public buyer sign-up screen to create Keycloak # users through the Admin API (apps/users/keycloak.py). It holds only # realm-management's manage-users and view-users. Secrets come from the # environment and are never written to code, logs or the wiki. KEYCLOAK_PROVISIONING_CLIENT_ID = env("KEYCLOAK_PROVISIONING_CLIENT_ID", default="") KEYCLOAK_PROVISIONING_CLIENT_SECRET = env("KEYCLOAK_PROVISIONING_CLIENT_SECRET", default="") REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework.authentication.SessionAuthentication", "rest_framework.authentication.TokenAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "rest_framework.permissions.IsAuthenticated", ], "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", "PAGE_SIZE": 20, } CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://127.0.0.1:6379/0") CELERY_RESULT_BACKEND = env("CELERY_RESULT_BACKEND", default="redis://127.0.0.1:6379/0") CELERY_ACCEPT_CONTENT = ["json"] CELERY_TASK_SERIALIZER = "json" CELERY_RESULT_SERIALIZER = "json" CELERY_TIMEZONE = "Europe/London" CORS_ALLOWED_ORIGINS = env.list("CORS_ALLOWED_ORIGINS", default=["http://194.164.95.50"]) CORS_ALLOW_CREDENTIALS = True STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY", default="") STRIPE_WEBHOOK_SECRET = env("STRIPE_WEBHOOK_SECRET", default="") FIELD_ENCRYPTION_KEY = env("ENCRYPTION_KEY", default="")