import os from pathlib import Path from urllib.parse import parse_qsl, unquote, urlparse from config.env import load_project_environment BASE_DIR = Path(__file__).resolve().parents[2] load_project_environment(BASE_DIR) def env_bool(name, default=False): return os.getenv(name, str(default)).strip().lower() in {"1", "true", "yes", "on"} def env_list(name, default=""): return [item.strip() for item in os.getenv(name, default).split(",") if item.strip()] SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "dev-only-change-me") DEBUG = env_bool("DJANGO_DEBUG") ALLOWED_HOSTS = list(dict.fromkeys(env_list("DJANGO_ALLOWED_HOSTS") + ["localhost", "127.0.0.1"])) INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.humanize", "apps.core", "apps.accounts", "apps.customers", "apps.staff", "apps.tickets", "apps.messaging", "apps.attachments", "apps.notifications", "apps.phone_contacts_api", "apps.payments", ] 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", "apps.payments.middleware.FinancialGrantAuditMiddleware", "apps.core.middleware.NoStoreHTMLMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "config.urls" TEMPLATES = [{"BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, "OPTIONS": {"context_processors": ["django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", "apps.core.context_processors.static_asset_version", "apps.core.context_processors.site_branding", "apps.core.context_processors.staff_operations_access", "apps.payments.context_processors.payment_navigation"]}}] WSGI_APPLICATION = "config.wsgi.application" ASGI_APPLICATION = "config.asgi.application" database_url = os.getenv("DATABASE_URL", "").strip() if database_url: parsed_database_url = urlparse(database_url) database_options = dict(parse_qsl(parsed_database_url.query)) database_options.setdefault("connect_timeout", os.getenv("DB_CONNECT_TIMEOUT", "10")) DATABASES = {"default": { "ENGINE": "django.db.backends.postgresql", "NAME": unquote(parsed_database_url.path.lstrip("/")), "USER": unquote(parsed_database_url.username or ""), "PASSWORD": unquote(parsed_database_url.password or ""), "HOST": parsed_database_url.hostname or "", "PORT": str(parsed_database_url.port or 5432), "CONN_MAX_AGE": int(os.getenv("DB_CONN_MAX_AGE", "60")), "CONN_HEALTH_CHECKS": True, "OPTIONS": database_options, }} else: database_options = { "connect_timeout": os.getenv("DB_CONNECT_TIMEOUT", "10"), "sslmode": os.getenv("DB_SSLMODE", "prefer"), } DATABASES = {"default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.getenv("POSTGRES_DB", "customerTicket"), "USER": os.getenv("POSTGRES_USER", "postgres"), "PASSWORD": os.getenv("POSTGRES_PASSWORD", ""), "HOST": os.getenv("POSTGRES_HOST", ""), "PORT": os.getenv("POSTGRES_PORT", "5432"), "CONN_MAX_AGE": int(os.getenv("DB_CONN_MAX_AGE", "60")), "CONN_HEALTH_CHECKS": True, "OPTIONS": database_options, }} AUTH_PASSWORD_VALIDATORS = [{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}, {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}] AUTH_USER_MODEL = "accounts.User" LANGUAGE_CODE = "fa-ir" TIME_ZONE = os.getenv("TIME_ZONE", "Asia/Tehran") USE_I18N = True USE_TZ = True STATIC_URL = "/static/" STATIC_ROOT = BASE_DIR / "staticfiles" STATICFILES_DIRS = [BASE_DIR / "static"] MEDIA_ROOT = Path(os.getenv("MEDIA_ROOT", BASE_DIR / "media")) MEDIA_URL = "/media/" MEDIA_SIGNING_SECRET = os.getenv("MEDIA_SIGNING_SECRET", SECRET_KEY) USE_NGINX_MEDIA = env_bool("USE_NGINX_MEDIA", False) DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" LOGIN_URL = "/login/" OTP_LENGTH = int(os.getenv("OTP_LENGTH", "6")) OTP_EXPIRE_SECONDS = int(os.getenv("OTP_EXPIRE_SECONDS", "120")) OTP_RESEND_SECONDS = int(os.getenv("OTP_RESEND_SECONDS", "60")) OTP_MAX_ATTEMPTS = int(os.getenv("OTP_MAX_ATTEMPTS", "5")) OTP_RATE_LIMIT_WINDOW_SECONDS = int(os.getenv("OTP_RATE_LIMIT_WINDOW_SECONDS", "3600")) OTP_RATE_LIMIT_PER_MOBILE = int(os.getenv("OTP_RATE_LIMIT_PER_MOBILE", "10")) OTP_RATE_LIMIT_PER_IP = int(os.getenv("OTP_RATE_LIMIT_PER_IP", "30")) # Empty by default: fixed OTP must never be active in production accidentally. DEV_OTP_MOBILE = "" DEV_OTP_CODE = "" MAGIC_LINK_TTL_DAYS = int(os.getenv("MAGIC_LINK_TTL_DAYS", "7")) PAYMENT_LINK_TTL_DAYS = int(os.getenv("PAYMENT_LINK_TTL_DAYS", str(MAGIC_LINK_TTL_DAYS))) MAX_NOTIFICATION_ATTEMPTS = int(os.getenv("MAX_NOTIFICATION_ATTEMPTS", "5")) SMS_MAX_RETRIES = int(os.getenv("SMS_MAX_RETRIES", "3")) SMS_RETRY_DELAY_SECONDS = int(os.getenv("SMS_RETRY_DELAY_SECONDS", "180")) SMS_DELIVERY_CHECK_DELAY_SECONDS = int(os.getenv("SMS_DELIVERY_CHECK_DELAY_SECONDS", "180")) SMS_DELIVERY_MAX_CHECKS = int(os.getenv("SMS_DELIVERY_MAX_CHECKS", "15")) SMS_DEFINITIVE_DELIVERY_RETRY_AFTER_SECONDS = int(os.getenv("SMS_DEFINITIVE_DELIVERY_RETRY_AFTER_SECONDS", "2700")) SMS_HISTORICAL_DELIVERY_RETRY_STAGGER_SECONDS = int(os.getenv("SMS_HISTORICAL_DELIVERY_RETRY_STAGGER_SECONDS", "30")) PUBLIC_BASE_URL = os.getenv("PUBLIC_BASE_URL", "http://127.0.0.1:8000").rstrip("/") COMPANY_NAME = os.getenv("COMPANY_NAME", "لوله گستر گلپایگان") WEBOTP_HOST = os.getenv("WEBOTP_HOST", "") SMS_PROVIDER_CLASS = os.getenv("SMS_PROVIDER_CLASS", "apps.integrations.sms.sms0098.ZeroZeroNinetyEightSMSProvider") SMS_API_URL = os.getenv("SMS_API_URL", "") SMS_API_TOKEN = os.getenv("SMS_API_TOKEN", "") SMS_SENDER = os.getenv("SMS_SENDER", "") BEHIN_SMS_USERNAME = os.getenv("BEHIN_SMS_USERNAME", "") BEHIN_SMS_PASSWORD = os.getenv("BEHIN_SMS_PASSWORD", "") BEHIN_SMS_SPECIAL_NUMBER = os.getenv("BEHIN_SMS_SPECIAL_NUMBER", "") BEHIN_SMS_WSDL_URL = os.getenv("BEHIN_SMS_WSDL_URL", "http://panel.behinsms.com/SMSWS/SOAP.asmx?wsdl") SMS0098_USERNAME = os.getenv("SMS0098_USERNAME", "") SMS0098_PASSWORD = os.getenv("SMS0098_PASSWORD", "") SMS0098_FROM = os.getenv("SMS0098_FROM", "") SMS0098_WSDL_URL = os.getenv("SMS0098_WSDL_URL", "https://webservice.0098sms.com/service.asmx?WSDL") SMS_TEST_ALLOWED_MOBILES = ("+989202202129", "+989384999655") BALE_API_BASE = os.getenv("BALE_API_BASE", "https://tapi.bale.ai") BALE_BOT_TOKEN = os.getenv("BALE_BOT_TOKEN", "") INTEGRATION_HTTP_TIMEOUT_SECONDS = int(os.getenv("INTEGRATION_HTTP_TIMEOUT_SECONDS", "10")) STAFF_REPLY_SMS_THROTTLE_SECONDS = int(os.getenv("STAFF_REPLY_SMS_THROTTLE_SECONDS", "180")) X_FRAME_OPTIONS = "DENY" SECURE_REFERRER_POLICY = "same-origin" SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" CSRF_COOKIE_SAMESITE = "Lax" CSRF_TRUSTED_ORIGINS = env_list("CSRF_TRUSTED_ORIGINS")