This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
The following commit(s) were added to refs/heads/main by this push:
new 142bf255c Consolidate portal settings around devstack defaults (#244)
142bf255c is described below
commit 142bf255cd59326eda4a08ccd5c860f2fe430293
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Fri Jun 19 22:14:54 2026 -0400
Consolidate portal settings around devstack defaults (#244)
settings.py now ships working Tilt devstack defaults (*.airavata.host, the
in-network airavata-server, and the dev Keycloak/pga client) so local dev
needs
no settings_local.py; boilerplate comments are trimmed and
settings_local.py.sample
is slimmed to the deployment-override essentials.
---
airavata-django-portal/django_airavata/settings.py | 201 ++++++---------------
.../django_airavata/settings_local.py.sample | 181 +++----------------
2 files changed, 83 insertions(+), 299 deletions(-)
diff --git a/airavata-django-portal/django_airavata/settings.py
b/airavata-django-portal/django_airavata/settings.py
index 325b5b6a3..2d3a18a03 100644
--- a/airavata-django-portal/django_airavata/settings.py
+++ b/airavata-django-portal/django_airavata/settings.py
@@ -1,14 +1,4 @@
-"""
-Django settings for django_airavata_gateway project.
-
-Generated by 'django-admin startproject' using Django 1.10.5.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.10/ref/settings/
-"""
+"""Django settings for the Airavata Django Portal."""
import contextlib
import os
@@ -20,35 +10,25 @@ from django_airavata.commons import dynamic_apps
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
+# SECURITY WARNING: override SECRET_KEY and set DEBUG=False in production.
SECRET_KEY = "bots0)m91u_i4gpw+103o%2jn#j57wjh7s@9$x*27_4^*jyku4"
-
-# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
-INTERNAL_IPS = ["127.0.0.1"]
-ALLOWED_HOSTS = [".airavata.localhost", "localhost", "127.0.0.1"]
+ALLOWED_HOSTS = [".airavata.host", "localhost", "127.0.0.1"]
-# The portal is served behind the airavata Traefik ingress (TLS terminates at
-# Traefik, which forwards plain HTTP + X-Forwarded-Proto=https). These let
Django
-# reconstruct the original https://gateway.airavata.localhost URL so OIDC
-# redirect_uris match Keycloak's whitelist and CSRF accepts the https origin.
+# Served behind the Traefik ingress (TLS terminates there, forwarding
+# X-Forwarded-Proto=https) so Django reconstructs the original https URL.
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
-CSRF_TRUSTED_ORIGINS = ["https://gateway.airavata.localhost"]
+CSRF_TRUSTED_ORIGINS = ["https://gateway.airavata.host"]
# Application definition
INSTALLED_APPS = [
"django_airavata.apps.admin.apps.AdminConfig",
- # No django.contrib.auth / contenttypes: the portal has no database and no
- # Django User model — identity comes from the verified Keycloak token
- # (apps/auth/middleware). Sessions are cache-backed and messages use
- # cookie/session storage, so neither needs a database.
+ # No django.contrib.auth/contenttypes: no database, no Django User model —
+ # identity comes from the Keycloak token (apps/auth/middleware).
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
@@ -71,28 +51,18 @@ MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
- # Derive request.user from the Keycloak access token stored in the session
- # (OIDC session flow; no DB User, replaces AuthenticationMiddleware). Must
- # run before authz_token_middleware so request.user is set when
- # get_authz_token runs, and before keycloak_bearer_middleware.
+ # request.user from the session's Keycloak token; before the authz/bearer
middleware.
"django_airavata.apps.auth.middleware.session_keycloak_user_middleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_airavata.apps.auth.middleware.authz_token_middleware",
- # Validate an Authorization: Bearer <jwt> against Keycloak for token
clients
- # (no-op when a session already authenticated the request). Must run AFTER
- # authz_token_middleware so a session login isn't clobbered, and BEFORE the
- # lazy gRPC client / gateway_groups so request.authz_token/user are set.
+ # Validate a Bearer JWT for token clients; after authz_token, before the
gRPC client.
"django_airavata.apps.auth.middleware.keycloak_bearer_middleware",
- # Augment every request with request.data (parsed body) and
- # request.query_params (=request.GET), which views/view_utils read (DRF
used
- # to add these on its Request wrapper).
+ # Adds request.data / request.query_params for views.
"django_airavata.apps.auth.middleware.request_data_middleware",
- # gRPC AiravataClient (request.airavata). Must come after
authz_token_middleware
- # (uses request.authz_token for the access token).
+ # gRPC AiravataClient (request.airavata); after authz_token_middleware.
"django_airavata.middleware.airavata_grpc_client",
- # Set is_gateway_admin / is_read_only_gateway_admin from the JWT realm
roles.
- # Must come after the auth middlewares so request.user is set.
+ # Sets is_gateway_admin / is_read_only_gateway_admin from JWT roles; after
auth.
"django_airavata.apps.auth.middleware.admin_flags_middleware",
]
@@ -105,7 +75,6 @@ TEMPLATES = [
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
- "django.template.context_processors.debug",
"django.template.context_processors.request",
"django.template.context_processors.csrf",
"django_airavata.context_processors.user",
@@ -124,19 +93,15 @@ TEMPLATES = [
WSGI_APPLICATION = "django_airavata.wsgi.application"
-# Database
-# The portal has no database. The dummy backend lets Django boot (and makes
-# runserver's migration check a no-op) while raising if any ORM query is ever
-# attempted — all persistence goes through the Airavata gRPC API / the cache.
+# No database — the dummy backend lets Django boot but raises on any ORM query;
+# all persistence goes through the Airavata gRPC API / the cache.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.dummy",
}
}
-# Sessions are stored in the cache, not the DB (no django_session rows). The
-# default cache is file-based so dev sessions survive the autoreloader and need
-# no DB or external store.
+# Cache-backed sessions (no DB); file-based cache survives the dev
autoreloader.
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
@@ -144,28 +109,12 @@ CACHES = {
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
-SESSION_CACHE_ALIAS = "default"
-
-# No password validation: the portal never sets or validates passwords —
-# authentication (and registration) is hosted entirely by Keycloak.
-
-
-# Internationalization
-# https://docs.djangoproject.com/en/1.10/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/1.10/howto/static-files/
+# Static files
STATIC_URL = "/static/"
STATICFILES_DIRS = [os.path.join(BASE_DIR, "django_airavata", "static")]
@@ -175,7 +124,6 @@ MEDIA_URL = "/media/"
# Data storage
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o777
-FILE_UPLOAD_PERMISSIONS = 0o644
FILE_UPLOAD_MAX_FILE_SIZE = 64 * 1024 * 1024 # 64 MB
FILE_UPLOAD_HANDLERS = [
"django.core.files.uploadhandler.MemoryFileUploadHandler",
@@ -186,30 +134,20 @@ FILE_UPLOAD_HANDLERS = [
DATA_UPLOAD_MAX_MEMORY_SIZE = 64 * 1024 * 1024 # 64 MB
FILE_UPLOAD_MAX_MEMORY_SIZE = 64 * 1024 * 1024 # 64 MB
-# Tus upload
-# Override and set to a valid tus endpoint, for example
-# "http://localhost:1080/files/"
+# Tus upload (override to enable): endpoint URL + storage dir.
TUS_ENDPOINT = None
-# Override and set to the directory where tus uploads will be stored
TUS_DATA_DIR = None
-# Max age in days after which archive_user_data will archive user data
+# Max archive age (days), echoed by ExperimentArchiveView; archive job runs
offline.
GATEWAY_USER_DATA_ARCHIVE_MAX_AGE_DAYS = None
-# Directory in which to copy archive text listing file and tarball
-GATEWAY_USER_DATA_ARCHIVE_DIRECTORY = "/tmp"
-# Minimum size of archive file. If archive is smaller than this, the archive
is aborted.
-GATEWAY_USER_DATA_ARCHIVE_MINIMUM_ARCHIVE_SIZE_GB = 1
-# Legacy (PGA) Portal link - provide a link to the legacy portal
+# Optional link to a legacy PGA portal.
PGA_URL = None
-# Portal title shown in the header and emails. Override in settings_local.py.
PORTAL_TITLE = "Airavata Django Portal"
-# Portal app-shell "chrome" (base.html): favicon, header logo, and user-menu
-# links. Previously sourced from Wagtail snippet models; now sourced from
-# settings so the app shell does not depend on Wagtail. Every key is optional;
-# override PORTAL_CHROME (and PORTAL_TITLE) in settings_local.py.
+# Portal app-shell "chrome" (base.html): favicon, header logo, user-menu links.
+# Every key is optional; override in settings_local.py.
PORTAL_CHROME = {
# Favicon URL (absolute or static). Falls back to the bundled Airavata
logo.
"favicon_url": None,
@@ -222,12 +160,8 @@ PORTAL_CHROME = {
"user_menu_links": [],
}
-# Portal email templates (formerly the django_airavata_auth EmailTemplate
table).
-# Keyed by template_type int (see apps/auth/models.py); rendered with the
Django
-# template engine in apps/auth/utils.send_email_to_user. Only the live
-# "user added to group" template (4) is retained — verify-email,
password-reset,
-# and email-change are now handled entirely by Keycloak. Override in
-# settings_local.py to customize.
+# Portal email templates, keyed by template_type int (see apps/auth/models.py)
and
+# rendered in apps/auth/utils.send_email_to_user. Override in
settings_local.py.
PORTAL_EMAIL_TEMPLATES = {
# USER_ADDED_TO_GROUP_TEMPLATE
4: {
@@ -264,50 +198,29 @@ Please let us know if you have any questions. Thanks.
},
}
-# Per-application custom template overrides (formerly the ApplicationTemplate /
-# ApplicationTemplateContextProcessor tables). Maps an application_module_id
to a
-# custom workspace template path and optional context-processor callables, read
-# in apps/workspace/views.get_custom_template. Empty by default (no overrides).
-# Override in settings_local.py, e.g.:
+# Per-application custom workspace template overrides, keyed by
application_module_id
+# and read in apps/workspace/views.get_custom_template. Override in
settings_local.py:
# PORTAL_APPLICATION_TEMPLATES = {
-# "<app_module_id>": {
-# "template_path": "custom/template.html",
-# "context_processors": ["pkg.module.callable"],
-# },
+# "<app_module_id>": {"template_path": "custom/template.html",
+# "context_processors": ["pkg.module.callable"]},
# }
PORTAL_APPLICATION_TEMPLATES = {}
-# No Django auth backends: identity comes from the verified Keycloak token
-# (session_keycloak_user_middleware / keycloak_bearer_middleware), not from
-# authenticate()/login(). An empty list is valid.
+# No Django auth backends: identity comes from the Keycloak token, not login().
AUTHENTICATION_BACKENDS = []
-# Default email backend (for local development)
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
LOGIN_URL = "django_airavata_auth:login"
LOGIN_REDIRECT_URL = "django_airavata_workspace:dashboard"
-LOGOUT_REDIRECT_URL = "/"
-# Login is hosted entirely by Keycloak (username/password, self-registration,
-# external IDPs). This is retained only for the desktop/CLI login templates and
-# the settings_local.py download helper, which still read it.
+# Login is hosted by Keycloak; read by the desktop/CLI login templates.
AUTHENTICATION_OPTIONS = {
- # Can have multiple external logins
- # 'external': [
- # {
- # 'idp_alias': 'cilogon',
- # 'name': 'CILogon',
- # # Static path to image
- # 'logo': 'path/to/image'
- # }
- # ]
+ # 'external': [{'idp_alias': 'cilogon', 'name': 'CILogon', 'logo':
'path/to/image'}]
}
-# Configure the URIs that can be redirected to with
/auth/access-token-redirect?redirect_uri=...
-# Takes a list of dicts, where the key 'URI' specifies the allowed redirect URI
-# and the optional key 'PARAM_NAME' allows specifying the query parameter name
-# for the access token parameter (defaults to 'access_token').
+# Allowed redirect targets for /auth/access-token-redirect. List of dicts with
+# 'URI' and optional 'PARAM_NAME' (token query param; default 'access_token').
ACCESS_TOKEN_REDIRECT_ALLOWED_URIS = []
# Webpack loader
@@ -449,12 +362,24 @@ LOGGING = {
}
-# New gRPC backend (Track D). The portal is migrating from the legacy Thrift
API
-# to the new Airavata gRPC/REST server (airavata-python-sdk AiravataClient).
These
-# defaults target the tilt-managed server on :9090 and may be overridden via
env
-# vars or settings_local.py. The gRPC client coexists with the Thrift client
while
-# apps/api views are repointed resource-family by resource-family.
-GRPC_API_HOST = os.environ.get("GRPC_API_HOST", "localhost")
+# Devstack defaults: the portal runs on the shared `airavata-devstack` network
and
+# reaches Keycloak/Airavata at their *.airavata.host / in-network names, so it
works
+# with no settings_local.py. Override via env vars or settings_local.py.
+GATEWAY_ID = os.environ.get("GATEWAY_ID", "default")
+
+# Keycloak OIDC (realm: default, client: pga). The secret is the committed dev
secret.
+KEYCLOAK_CLIENT_ID = "pga"
+KEYCLOAK_CLIENT_SECRET = "m36BXQIxX3j3VILadeHMK5IvbOeRlCCc"
+KEYCLOAK_AUTHORIZE_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/auth"
+KEYCLOAK_TOKEN_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/token"
+KEYCLOAK_USERINFO_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/userinfo"
+KEYCLOAK_LOGOUT_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/logout"
+# mkcert dev cert; the python OIDC client doesn't import the CA, so skip
verify.
+KEYCLOAK_VERIFY_SSL = False
+
+# Airavata gRPC/REST server (airavata-python-sdk AiravataClient), in-network as
+# airavata-server:9090 (not published to the host).
+GRPC_API_HOST = os.environ.get("GRPC_API_HOST", "airavata-server")
GRPC_API_PORT = int(os.environ.get("GRPC_API_PORT", 9090))
GRPC_API_SECURE = os.environ.get("GRPC_API_SECURE", "false").lower() == "true"
@@ -462,31 +387,15 @@ GRPC_API_SECURE = os.environ.get("GRPC_API_SECURE",
"false").lower() == "true"
with contextlib.suppress(ImportError):
from django_airavata.settings_local import * # noqa
-# Keycloak account console URL (self-service profile/password/email management
-# the portal no longer hosts). Defaults to the realm account console derived
-# from KEYCLOAK_AUTHORIZE_URL; override in settings_local.py if needed.
+# Keycloak self-service account console, derived from KEYCLOAK_AUTHORIZE_URL.
if "KEYCLOAK_ACCOUNT_CONSOLE_URL" not in dir() and "KEYCLOAK_AUTHORIZE_URL" in
dir():
KEYCLOAK_ACCOUNT_CONSOLE_URL = (
- KEYCLOAK_AUTHORIZE_URL.split("/protocol/openid-connect/")[0] # noqa:
F405 # from settings_local star import, guarded by `in dir()` above
+ KEYCLOAK_AUTHORIZE_URL.split("/protocol/openid-connect/")[0] # noqa:
F405
+ "/account/"
)
-# NOTE: custom code must be loaded last so that the above settings take effect
-# for any views, etc. defined or imported by custom code
-
-# Add any custom apps installed in the virtual environment
-
-# Essentially this looks for the entry_points metadata in all installed Python
packages. The format of the metadata in setup.py is the following:
-#
-# setuptools.setup(
-# ...
-# entry_points="""
-# [airavata.djangoapp]
-# dynamic_djangoapp = dynamic_djangoapp.apps:DynamicDjangoAppConfig
-# """,
-# ...
-# )
-#
+# Load custom Django apps registered via the "airavata.djangoapp" entry point;
+# must run after the settings above so custom code sees them.
dynamic_apps.load(INSTALLED_APPS, "airavata.djangoapp")
# Merge WEBPACK_LOADER settings from custom Django apps
diff --git a/airavata-django-portal/django_airavata/settings_local.py.sample
b/airavata-django-portal/django_airavata/settings_local.py.sample
index c9d92ee07..2c417ea90 100644
--- a/airavata-django-portal/django_airavata/settings_local.py.sample
+++ b/airavata-django-portal/django_airavata/settings_local.py.sample
@@ -1,164 +1,39 @@
"""
-Override default Django settings for a particular instance.
+Optional local settings overrides, imported last by settings.py.
-Copy this file to settings_local.py and modify as appropriate. This file will
-be imported into settings.py last of all so settings in this file override any
-defaults specified in settings.py.
+settings.py already ships working defaults for the Tilt devstack
(*.airavata.host
++ the in-network airavata-server), so local Tilt dev needs no
settings_local.py.
+Copy this file to settings_local.py only to override those defaults, e.g. for a
+real deployment.
"""
import os
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-# Django - general settings
-# Uncomment and specify for production deployments
-# DEBUG = False
-# STATIC_ROOT = "/var/www/path/to/sitename/static/"
-# ALLOWED_HOSTS = ['production.hostname']
-
-# Django - database settings
-# MySQL - to use MySQL, uncomment and specify the settings
-# DATABASES = {
-# 'default': {
-# 'ENGINE': 'django.db.backends.mysql',
-# 'NAME': '...',
-# 'HOST': '...',
-# 'USER': '...',
-# 'PASSWORD': '...',
-# 'OPTIONS': {
-# 'init_command': 'SET
default_storage_engine=INNODB,collation_connection=utf8_bin',
-# }
-# }
-
-# Django - Email settings
-# Uncomment and specify the following for sending emails (default email backend
-# just prints to the console)
-# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
-# EMAIL_HOST = '...'
-# EMAIL_PORT = '...'
-# EMAIL_HOST_USER = '...'
-# EMAIL_HOST_PASSWORD = '...'
-# EMAIL_USE_TLS = True
-# ADMINS receive error emails
-# ADMINS = [('Admin Name', '[email protected]')]
-# Optional: PORTAL_ADMINS receive administrative emails, like when a new user
is created
-# This can be set to a different value than ADMINS so that the PORTAL_ADMINS
-# don't receive error emails. Defaults to the same value as ADMINS.
-# PORTAL_ADMINS = ADMINS
-# SERVER_EMAIL = '[email protected]'
-
-# Keycloak Configuration
-KEYCLOAK_CLIENT_ID = '...'
-KEYCLOAK_CLIENT_SECRET = '...'
-KEYCLOAK_AUTHORIZE_URL = '...'
-KEYCLOAK_TOKEN_URL = '...'
-KEYCLOAK_USERINFO_URL = '...'
-KEYCLOAK_LOGOUT_URL = '...'
-# Optional: specify if using self-signed certificate or certificate from
unrecognized CA
-#KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata", "resources",
"incommon_rsa_server_ca.pem")
-KEYCLOAK_VERIFY_SSL = True
-
-AUTHENTICATION_OPTIONS = {
- # Control whether username/password authentication is allowed
- 'password': {
- 'name': 'your account',
- # Uncomment following to hide username/password login except from
directly going to /auth/login-password URL
- # 'hidden': True,
- },
- # Can have multiple external logins
- # 'external': [
- # {
- # 'idp_alias': 'cilogon',
- # 'name': 'CILogon',
- # }
- # ]
-}
-
-# Path to the CA certificates bundle for secure connections
-CA_CERTS_PATH = '/etc/ssl/certs/ca-certificates.crt'
-
-# Airavata API Configuration
-GATEWAY_ID = 'default'
-AIRAVATA_API_HOST = 'localhost'
-AIRAVATA_API_PORT = 8930
-AIRAVATA_API_SECURE = False
-FILE_UPLOAD_TEMP_DIR = '/tmp'
+# Gateway + Airavata gRPC server (in-network on the devstack network).
+GATEWAY_ID = "default"
+GRPC_API_HOST = "airavata-server"
+GRPC_API_PORT = 9090
+GRPC_API_SECURE = False
-# Define shared directories. Each entry is symlinked into the user's storage.
-# GATEWAY_DATA_SHARED_DIRECTORIES = {
-# 'Display Name': {
-# 'path': '/path/to/shared/dir'
-# }
-# }
+# Keycloak OIDC (devstack realm: default, client: pga; secret is the dev
secret).
+KEYCLOAK_CLIENT_ID = "pga"
+KEYCLOAK_CLIENT_SECRET = "m36BXQIxX3j3VILadeHMK5IvbOeRlCCc"
+KEYCLOAK_AUTHORIZE_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/auth"
+KEYCLOAK_TOKEN_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/token"
+KEYCLOAK_USERINFO_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/userinfo"
+KEYCLOAK_LOGOUT_URL =
"https://auth.airavata.host/realms/default/protocol/openid-connect/logout"
+KEYCLOAK_VERIFY_SSL = False
-# Profile Service Configuration
-PROFILE_SERVICE_HOST = AIRAVATA_API_HOST
-PROFILE_SERVICE_PORT = 8962
-PROFILE_SERVICE_SECURE = False
+PORTAL_TITLE = "Airavata Django Portal (devstack)"
-# Gateway user data archive configuration. User data can be periodically
-# archived and deleted to free up storage space.
-# Max age in days after which archive_user_data will archive user data
-# GATEWAY_USER_DATA_ARCHIVE_MAX_AGE_DAYS = 90
-# Directory in which to copy archive text listing file and tarball
-# GATEWAY_USER_DATA_ARCHIVE_DIRECTORY = "/path/dir/where/to/copy/archives"
-# Minimum size of archive file. If archive is smaller than this, the archive
is aborted.
-# GATEWAY_USER_DATA_ARCHIVE_MINIMUM_ARCHIVE_SIZE_GB = 1
-
-# Portal settings
-PORTAL_TITLE = 'Django Airavata Gateway'
-
-# Tus upload - uncomment the following to enable tus uploads
-# Override and set to a valid tus endpoint
-# TUS_ENDPOINT = "https://tus.domainname.org/files/"
-# Override and set to the directory where tus uploads will be stored.
-# TUS_DATA_DIR = "/path/to/tus-temp-dir"
-
-# Legacy (PGA) Portal link - uncomment to provide a link to the legacy portal
-# PGA_URL = '...'
-
-# Google Analytics Tracking ID ("UA-XXXXXXXX-X"). If this setting is set, then
-# Google Analytics tracking will be added to all pages.
-# GOOGLE_ANALYTICS_TRACKING_ID = '...'
-
-# Logging configuration. Uncomment following to override default log
configuration
-# LOGGING = {
-# 'version': 1,
-# 'disable_existing_loggers': False,
-# 'filters': {
-# 'require_debug_false': {
-# '()': 'django.utils.log.RequireDebugFalse',
-# },
-# 'require_debug_true': {
-# '()': 'django.utils.log.RequireDebugTrue',
-# },
-# },
-# 'formatters': {
-# 'verbose': {
-# 'format': '[%(asctime)s %(name)s:%(lineno)d %(levelname)s]
%(message)s'
-# },
-# },
-# 'handlers': {
-# 'console': {
-# 'class': 'logging.StreamHandler',
-# 'formatter': 'verbose'
-# },
-# 'mail_admins': {
-# 'filters': ['require_debug_false'],
-# 'level': 'ERROR',
-# 'class': 'django.utils.log.AdminEmailHandler',
-# 'include_html': True,
-# }
-# },
-# 'loggers': {
-# 'django_airavata': {
-# 'handlers': ['console', 'mail_admins'],
-# 'level': 'DEBUG' if DEBUG else 'INFO'
-# },
-# 'root': {
-# 'handlers': ['console', 'mail_admins'],
-# 'level': 'WARNING'
-# }
-# },
-# }
+# --- Production overrides (uncomment + edit) ---
+# DEBUG = False
+# ALLOWED_HOSTS = ["gateway.example.org"]
+# CSRF_TRUSTED_ORIGINS = ["https://gateway.example.org"]
+# KEYCLOAK_VERIFY_SSL = True
+# KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata",
"resources", "incommon_rsa_server_ca.pem")
+# EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
+# EMAIL_HOST = "..."
+# ADMINS = [("Admin Name", "[email protected]")]