Files
MathApp/config/settings.py

254 lines
10 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Django settings for config project.
Generated by 'django-admin startproject' using Django 5.2.17.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-jn!s*25lz^+bjm101tzmlz8jlj70i0qka++*h-(!0s^^vk-0ss'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 3rd party
'rest_framework',
'drf_spectacular',
# Tadrisino apps (فاز اول - ۴ از ۱۱ اپ)
'accounts',
'content',
'progress',
'ai_tutor',
# Tadrisino apps (فاز دوم - بخش ۱ از ۳: subscription, payment, discount)
'subscription',
'payment',
'discount',
# Tadrisino apps (فاز دوم - بخش ۲: notifications)
'notifications',
# Tadrisino apps (فاز دوم - بخش ۳: affiliate)
'affiliate',
# Tadrisino apps (فاز دوم - بخش ۴: support)
'support',
# Tadrisino apps (فاز دوم - بخش ۵: family)
'family',
# Tadrisino apps (سیستم پرداخت بازار + آفر/تخفیف + لاگ عمومی تغییر Status)
'statuslog',
]
MIDDLEWARE = [
'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.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'config.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
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',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'fa-ir'
TIME_ZONE = 'Asia/Tehran'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
# -----------------------------------------------------------------------
# Tadrisino — تنظیمات پروژه
# -----------------------------------------------------------------------
# این پروژه از مدل User جنگو (auth) استفاده نمی‌کنه؛ طبق طراحی سینیورت
# accounts.User یک مدل مستقل (business model) هست، نه AUTH_USER_MODEL.
# احراز هویت با یک TokenAuthentication سفارشی (accounts/authentication.py)
# روی همین مدل accounts.User انجام می‌شه؛ نه سشن/کوکی جنگو.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'accounts.authentication.TadrisinoTokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
SPECTACULAR_SETTINGS = {
'TITLE': 'Tadrisino API',
'DESCRIPTION': (
'بک‌اند اپلیکیشن آموزشی Tadrisino — احراز هویت با OTP، محتوای درسی '
'و پیشرفت کاربر، دستیار هوش مصنوعی، اشتراک و پرداخت، تخفیف، '
'اعلان‌ها، همکاری در فروش، پشتیبانی و حساب خانواده.'
),
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
'COMPONENT_SPLIT_REQUEST': True,
'SWAGGER_UI_SETTINGS': {
'deepLinking': True,
'displayRequestDuration': True,
'defaultModelsExpandDepth': -1,
'docExpansion': 'list',
'filter': True,
'persistAuthorization': True,
},
}
# اعتبار توکن دسترسی (ثانیه) و رفرش‌توکن (روز)
ACCESS_TOKEN_TTL_SECONDS = 3600
REFRESH_TOKEN_TTL_DAYS = 30
# در محیط توسعه/تست، این کد OTP همیشه معتبره تا بدون نیاز به SMS واقعی
# بشه لاگین رو با Swagger تست کرد. در پروداکشن حتماً حذف بشه.
DEV_MASTER_OTP = '0000'
# -----------------------------------------------------------------------
# Content progress (NodeProgress status)
# -----------------------------------------------------------------------
# بعد از چند روز بدون فعالیت روی یک Node که status اون in_progress هست،
# management command مربوطه (content/management/commands/
# mark_abandoned_node_progress.py) اونو abandoned علامت می‌زنه. ۱۴ روز
# (نه ۳-۷ روزِ رایج در اپ‌های مشابه) عمداً انتخاب شده چون قطعی/محدودیت
# اینترنت و دیتای موبایل در ایران باعث وقفه‌های چندروزه‌ی غیرارادی
# می‌شه که نباید فوراً به‌عنوان «رهاکردن درس» حساب بشه.
NODE_PROGRESS_ABANDON_DAYS = 14
# -----------------------------------------------------------------------
# Payment security
# -----------------------------------------------------------------------
# POST /api/payment/orders طبق سند یک فراخوانی سرور-به-سرور از اپ
# Subscription‌ه (نه از کلاینت). در عمل subscription/views.py مستقیماً
# تابع payment.services.create_order رو import و صدا می‌زنه، پس این
# endpoint اصلاً لازم نیست از بیرون صدا زده بشه؛ اما چون در urls.py
# رجیستر و AllowAny‌ه، هر کلاینتی می‌تونه با یک userId دلخواه سفارش بسازه.
# برای بستن این حفره، درخواست باید هدر X-Internal-Key رو برابر همین مقدار
# بفرسته؛ در پروداکشن حتماً از env خونده بشه و یک مقدار تصادفی/طولانی باشه.
INTERNAL_SERVICE_KEY = os.environ.get('INTERNAL_SERVICE_KEY', 'dev-internal-service-key-change-me')
# POST /api/payment/webhook قراره از درگاه پرداخت بیاد، نه از یک کلاینت
# دلخواه؛ بدون امضا هر کسی می‌تونه با یک orderId معتبر status=paid بفرسته
# و بدون پرداخت واقعی، اشتراک رو فعال کنه. درگاه‌های واقعی (زرین‌پال و غیره)
# امضای HMAC واقعی خودشون رو دارن؛ اینجا چون درگاه واقعی وصل نیست، به‌جای
# HMAC یک shared-secret ساده به اسم X-Webhook-Secret استفاده شده — همون
# سطح حفاظتی که در سند API برای این endpoint خواسته شده. برای اتصال واقعی
# این باید با بررسی امضای HMAC درگاه جایگزین بشه.
PAYMENT_WEBHOOK_SECRET = os.environ.get('PAYMENT_WEBHOOK_SECRET', 'dev-webhook-secret-change-me')
# -----------------------------------------------------------------------
# Bazaar In-App Billing (پرداخت درون‌برنامه‌ای کافه‌بازار — REST API v2)
# -----------------------------------------------------------------------
# طبق داکیومنت رسمی (developers.cafebazaar.ir/fa/guidelines/in-app-billing)،
# صحت‌سنجی پرداخت بازار یک OAuth2 سرور-به-سرور‌ه: یک بار با CLIENT_ID/
# CLIENT_SECRET و کد بازگشتی از پنل devapi، REFRESH_TOKEN گرفته می‌شه (یک‌بار،
# دستی)؛ از اون به بعد بک‌اند با همین REFRESH_TOKEN، ACCESS_TOKEN تازه
# می‌گیره و با اون به endpoint وضعیت خرید/اشتراک درخواست می‌زنه.
# ⚠️ کافه‌بازار اواخر ۱۴۰۳ روش صحت‌سنجی رو تغییر داد؛ قبل از اتصال واقعی،
# حتماً آخرین مسیر endpointها رو از همون صفحه‌ی رسمی/پنل devapi خودتون
# تایید کنید (BAZAAR_API_BASE پایینی رو بر همون اساس ست کنید).
BAZAAR_PACKAGE_NAME = os.environ.get('BAZAAR_PACKAGE_NAME', 'ir.tadrisino.app')
BAZAAR_CLIENT_ID = os.environ.get('BAZAAR_CLIENT_ID', '')
BAZAAR_CLIENT_SECRET = os.environ.get('BAZAAR_CLIENT_SECRET', '')
BAZAAR_REFRESH_TOKEN = os.environ.get('BAZAAR_REFRESH_TOKEN', '')
BAZAAR_API_BASE = os.environ.get('BAZAAR_API_BASE', 'https://pardakht.cafebazaar.ir/devapi/v2')
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'