Files

213 lines
6.9 KiB
Python

"""Django settings for the Cetana backend.
All secrets and environment-specific values are read from environment
variables (optionally loaded from a local .env file). Never commit secrets.
"""
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# Best-effort .env loading (dependency-free path; python-dotenv used when available).
try:
from dotenv import load_dotenv
load_dotenv(BASE_DIR / ".env")
except Exception: # pragma: no cover - dotenv is optional
pass
def env(name: str, default: str = "") -> str:
return os.environ.get(name, default)
def env_bool(name: str, default: bool = False) -> bool:
val = os.environ.get(name)
if val is None:
return default
return val.lower() in {"1", "true", "yes", "on"}
SECRET_KEY = env("SECRET_KEY", "insecure-dev-key-change-me")
DEBUG = env_bool("DEBUG", True)
ALLOWED_HOSTS = [h.strip() for h in env("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",") if h.strip()]
# ---------------------------------------------------------------------------
# Database
# ---------------------------------------------------------------------------
DATABASE_URL = env("DATABASE_URL", "")
if DATABASE_URL.startswith("postgres"):
import urllib.parse
_url = urllib.parse.urlparse(DATABASE_URL)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": _url.path.lstrip("/") or "cetana",
"USER": _url.username or "",
"PASSWORD": _url.password or "",
"HOST": _url.hostname or "localhost",
"PORT": _url.port or 5432,
}
}
else:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# ---------------------------------------------------------------------------
# Applications
# ---------------------------------------------------------------------------
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# third-party
"rest_framework",
"rest_framework_simplejwt",
"corsheaders",
"django_filters",
"drf_spectacular",
# local
"apps.core",
"apps.accounts",
"apps.rbac",
"apps.reservations",
"apps.orders",
"apps.workflows",
"apps.processing",
"apps.pricing",
"apps.payments",
"apps.invoicing",
"apps.notifications",
"apps.reports",
"apps.live_monitoring",
"apps.audit",
]
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",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "config.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"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 = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"
AUTH_USER_MODEL = "accounts.User"
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"},
]
LANGUAGE_CODE = "en-us"
# Iran Standard Time. Dates are rendered to users as Jalali, so the backend must
# agree with the frontend on what "today" and a timestamp's wall clock mean —
# otherwise a slot booked at 09:00 Tehran reads as 04:30 on the day it shifts.
TIME_ZONE = env("TIME_ZONE", "Asia/Tehran")
USE_I18N = True
USE_TZ = True
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# ---------------------------------------------------------------------------
# DRF
# ---------------------------------------------------------------------------
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
"DEFAULT_PAGINATION_CLASS": "apps.core.pagination.StandardPagination",
"PAGE_SIZE": 20,
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"EXCEPTION_HANDLER": "apps.core.errors.api_exception_handler",
}
SPECTACULAR_SETTINGS = {
"TITLE": "Cetana API",
"DESCRIPTION": "Configurable pistachio processing reservation & management platform",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
}
from datetime import timedelta # noqa: E402
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(hours=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"AUTH_HEADER_TYPES": ("Bearer",),
}
# ---------------------------------------------------------------------------
# CORS
# ---------------------------------------------------------------------------
CORS_ALLOW_ALL_ORIGINS = DEBUG
CORS_ALLOWED_ORIGINS = [
o.strip() for o in env("CORS_ALLOWED_ORIGINS", "http://localhost:5173").split(",") if o.strip()
]
# ---------------------------------------------------------------------------
# Celery
# ---------------------------------------------------------------------------
CELERY_BROKER_URL = env("CELERY_BROKER_URL", "redis://localhost:6379/0")
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
CELERY_TASK_ACKS_LATE = True
CELERY_TASK_DEFAULT_RETRY_DELAY = 60
CELERY_TASK_MAX_RETRIES = 5
# ---------------------------------------------------------------------------
# Cetana settings
# ---------------------------------------------------------------------------
BASE_CURRENCY = env("BASE_CURRENCY", "IRR")
ODOO_URL = env("ODOO_URL", "")
ODOO_DATABASE = env("ODOO_DATABASE", "")
ODOO_USERNAME = env("ODOO_USERNAME", "")
ODOO_PASSWORD = env("ODOO_PASSWORD", "")
ODOO_API_KEY = env("ODOO_API_KEY", "")
PAYMENT_GATEWAY_PROVIDER = env("PAYMENT_GATEWAY_PROVIDER", "mock")
PAYMENT_GATEWAY_MERCHANT_ID = env("PAYMENT_GATEWAY_MERCHANT_ID", "")
SMS_PROVIDER = env("SMS_PROVIDER", "mock")
SMS_PROVIDER_API_KEY = env("SMS_PROVIDER_API_KEY", "")