2026-08-24 15:54:08 +03:30
|
|
|
# config/settings.py
|
|
|
|
|
"""
|
2026-08-31 15:11:52 +03:30
|
|
|
تنظیمات جنگو برای پروژه «شاپشیپ».
|
|
|
|
|
همهی مقادیر حساس/محیطی از .env خوانده میشوند.
|
2026-08-24 15:54:08 +03:30
|
|
|
"""
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
import os
|
2026-08-24 15:54:08 +03:30
|
|
|
from datetime import timedelta
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
from decouple import Config, Csv, RepositoryEnv
|
|
|
|
|
|
|
|
|
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# بارگذاری فایل .env
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
_env_path = BASE_DIR / ".env"
|
|
|
|
|
if not _env_path.exists():
|
|
|
|
|
raise RuntimeError(
|
2026-08-31 15:11:52 +03:30
|
|
|
f"❌ فایل .env پیدا نشد ({_env_path}) — لطفاً یک فایل .env ایجاد کنید."
|
2026-08-24 15:54:08 +03:30
|
|
|
)
|
|
|
|
|
config = Config(RepositoryEnv(str(_env_path)))
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# امنیت و محیط اجرا
|
|
|
|
|
# ============================================
|
|
|
|
|
SECRET_KEY = config("SECRET_KEY")
|
|
|
|
|
DEBUG = False
|
2026-08-24 15:54:08 +03:30
|
|
|
ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost,127.0.0.1", cast=Csv())
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# اپلیکیشنها
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
DJANGO_APPS = [
|
|
|
|
|
"django.contrib.admin",
|
|
|
|
|
"django.contrib.auth",
|
|
|
|
|
"django.contrib.contenttypes",
|
|
|
|
|
"django.contrib.sessions",
|
|
|
|
|
"django.contrib.messages",
|
|
|
|
|
"django.contrib.staticfiles",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
THIRD_PARTY_APPS = [
|
|
|
|
|
"corsheaders",
|
|
|
|
|
"rest_framework",
|
|
|
|
|
"rest_framework_simplejwt",
|
|
|
|
|
"rest_framework_simplejwt.token_blacklist",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
LOCAL_APPS = [
|
|
|
|
|
"apps.account",
|
|
|
|
|
"apps.banker",
|
|
|
|
|
"apps.messaging",
|
|
|
|
|
"apps.bizapps",
|
|
|
|
|
"apps.billing",
|
|
|
|
|
"apps.notifications",
|
|
|
|
|
"apps.landing",
|
|
|
|
|
"apps.orders",
|
2026-09-04 13:05:54 +03:30
|
|
|
"apps.telemetry",
|
2026-09-15 23:18:05 +03:30
|
|
|
"apps.reviews",
|
2026-08-24 15:54:08 +03:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Middleware
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
MIDDLEWARE = [
|
|
|
|
|
"django.middleware.security.SecurityMiddleware",
|
|
|
|
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
|
|
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
|
|
|
"corsheaders.middleware.CorsMiddleware",
|
|
|
|
|
"django.middleware.common.CommonMiddleware",
|
|
|
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
|
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
|
|
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|
|
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
2026-09-04 13:05:54 +03:30
|
|
|
# آخر از همه: زمانِ پاسخ را وقتی همهی میدلورهای دیگر کارشان تمام شده اندازه میگیرد.
|
|
|
|
|
"apps.telemetry.middleware.ApiCallLoggingMiddleware",
|
2026-08-24 15:54:08 +03:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
ROOT_URLCONF = "config.urls"
|
|
|
|
|
WSGI_APPLICATION = "config.wsgi.application"
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Templates
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
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",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Database
|
|
|
|
|
# ============================================
|
|
|
|
|
DATABASES = {
|
|
|
|
|
"default": {
|
|
|
|
|
"ENGINE": config("DB_ENGINE", default="django.db.backends.sqlite3"),
|
|
|
|
|
"NAME": config("DB_NAME", default=BASE_DIR / "db.sqlite3"),
|
|
|
|
|
"USER": config("DB_USER", default=""),
|
|
|
|
|
"PASSWORD": config("DB_PASSWORD", default=""),
|
|
|
|
|
"HOST": config("DB_HOST", default=""),
|
|
|
|
|
"PORT": config("DB_PORT", default=""),
|
2026-08-24 15:54:08 +03:30
|
|
|
}
|
2026-08-31 15:11:52 +03:30
|
|
|
}
|
2026-08-24 15:54:08 +03:30
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Auth
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
AUTH_PASSWORD_VALIDATORS = [
|
|
|
|
|
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
|
|
|
|
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
2026-08-31 15:11:52 +03:30
|
|
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|
|
|
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
2026-08-24 15:54:08 +03:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
AUTH_USER_MODEL = "account.User"
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# بینالملل
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
LANGUAGE_CODE = "fa-ir"
|
|
|
|
|
TIME_ZONE = "Asia/Tehran"
|
|
|
|
|
USE_I18N = True
|
|
|
|
|
USE_TZ = True
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# فایلهای استاتیک
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
STATIC_URL = "/static/"
|
|
|
|
|
STATIC_ROOT = BASE_DIR / "staticfiles"
|
|
|
|
|
STORAGES = {
|
|
|
|
|
"staticfiles": {
|
|
|
|
|
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Landing Page
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
WHITENOISE_ROOT = BASE_DIR / "landing"
|
|
|
|
|
WHITENOISE_INDEX_FILE = True
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# فایلهای رسانه
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
MEDIA_URL = "/media/"
|
|
|
|
|
MEDIA_ROOT = BASE_DIR / "media"
|
|
|
|
|
|
|
|
|
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# CORS
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
CORS_ALLOWED_ORIGINS = config("CORS_ALLOWED_ORIGINS", default="", cast=Csv())
|
|
|
|
|
CORS_ALLOW_ALL_ORIGINS = config("CORS_ALLOW_ALL_ORIGINS", default=False, cast=bool)
|
|
|
|
|
CSRF_TRUSTED_ORIGINS = config("CSRF_TRUSTED_ORIGINS", default="", cast=Csv())
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Django REST Framework
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
REST_FRAMEWORK = {
|
|
|
|
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
|
|
|
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
|
|
|
),
|
|
|
|
|
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
|
|
|
|
"DEFAULT_THROTTLE_CLASSES": [
|
|
|
|
|
"rest_framework.throttling.ScopedRateThrottle",
|
|
|
|
|
],
|
|
|
|
|
"DEFAULT_THROTTLE_RATES": {
|
|
|
|
|
"otp_send": "5/hour",
|
|
|
|
|
"otp_verify": "10/hour",
|
|
|
|
|
"backup": "30/hour",
|
|
|
|
|
"messaging_send": "30/hour",
|
|
|
|
|
"billing_write": "30/hour",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# Simple JWT
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
SIMPLE_JWT = {
|
|
|
|
|
"ACCESS_TOKEN_LIFETIME": timedelta(days=config("JWT_ACCESS_LIFETIME_DAYS", default=1, cast=int)),
|
|
|
|
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=config("JWT_REFRESH_LIFETIME_DAYS", default=30, cast=int)),
|
|
|
|
|
"ROTATE_REFRESH_TOKENS": True,
|
|
|
|
|
"BLACKLIST_AFTER_ROTATION": True,
|
|
|
|
|
"USER_ID_FIELD": "id",
|
|
|
|
|
"USER_ID_CLAIM": "user_id",
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# ✅ تنظیمات پیامک (فقط OTP با کاوهنگار)
|
|
|
|
|
# ============================================
|
|
|
|
|
KAVENEGAR_API_KEY = config("KAVENEGAR_API_KEY", default="")
|
|
|
|
|
SMS_PATTERN_NAME = config("SMS_PATTERN_NAME", default="shopshipotp")
|
2026-09-15 23:18:05 +03:30
|
|
|
# قالبِ پیامکِ «هدیهی جم از پنل ادمین» — باید جدا از SMS_PATTERN_NAME در
|
|
|
|
|
# کاوهنگار ثبت شود (لوکاپ/پترن فقط با یک token عدد کار میکند، همان
|
|
|
|
|
# تعداد جمِ هدیهدادهشده).
|
|
|
|
|
SMS_GIFT_PATTERN_NAME = config("SMS_GIFT_PATTERN_NAME", default="")
|
2026-08-24 15:54:08 +03:30
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# برای سازگاری با کدهای قدیمی (استفاده نمیشود)
|
|
|
|
|
SMS_TOKEN_URL = config("SMS_TOKEN_URL", default="")
|
2026-08-24 15:54:08 +03:30
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# تنظیمات کانالهای پیامرسان (در صورت نیاز)
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
KAVENEGAR_SENDER_LINE = config("KAVENEGAR_SENDER_LINE", default="")
|
|
|
|
|
WHATSAPP_ACCESS_TOKEN = config("WHATSAPP_ACCESS_TOKEN", default="")
|
|
|
|
|
WHATSAPP_PHONE_NUMBER_ID = config("WHATSAPP_PHONE_NUMBER_ID", default="")
|
|
|
|
|
WHATSAPP_TEMPLATE_NAME = config("WHATSAPP_TEMPLATE_NAME", default="")
|
|
|
|
|
TELEGRAM_BOT_TOKEN = config("TELEGRAM_BOT_TOKEN", default="")
|
|
|
|
|
BALE_BOT_TOKEN = config("BALE_BOT_TOKEN", default="")
|
|
|
|
|
EITAA_BOT_TOKEN = config("EITAA_BOT_TOKEN", default="")
|
|
|
|
|
|
2026-08-31 15:11:52 +03:30
|
|
|
# ============================================
|
|
|
|
|
# لاگینگ
|
|
|
|
|
# ============================================
|
2026-08-24 15:54:08 +03:30
|
|
|
LOGGING = {
|
|
|
|
|
"version": 1,
|
|
|
|
|
"disable_existing_loggers": False,
|
|
|
|
|
"handlers": {
|
2026-08-31 15:11:52 +03:30
|
|
|
"console": {
|
|
|
|
|
"class": "logging.StreamHandler",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"loggers": {
|
|
|
|
|
"apps.account": {
|
|
|
|
|
"handlers": ["console"],
|
|
|
|
|
"level": "DEBUG",
|
|
|
|
|
"propagate": True,
|
|
|
|
|
},
|
|
|
|
|
"django": {
|
|
|
|
|
"handlers": ["console"],
|
|
|
|
|
"level": "INFO",
|
|
|
|
|
"propagate": True,
|
|
|
|
|
},
|
2026-08-24 15:54:08 +03:30
|
|
|
},
|
|
|
|
|
"root": {
|
|
|
|
|
"handlers": ["console"],
|
|
|
|
|
"level": "INFO",
|
|
|
|
|
},
|
2026-08-31 15:11:52 +03:30
|
|
|
}
|