boot with sane defaults instead of refusing to start
سه بار پشتِ سر هم همین الگو تکرار شد: یک متغیرِ محیطی ست نبود، جنگو موقعِ بالا آمدن مرد، و از بیرون فقط «502 Bad Gateway» دیده شد. کسی که سرور را بالا میآورد هیچ سرنخی نداشت، و تشخیص هر بار یک رفتوبرگشتِ یکروزه بود. ALLOWED_HOSTS و CSRF_TRUSTED_ORIGINS حالا دامنههای شناختهشدهی خودمان را پیشفرض دارند. CSRF مخصوصاً مهم است: بدونش ورود به پنلِ ادمین از پشتِ پراکسیِ HTTPS «CSRF verification failed» میدهد و علتش از پیام معلوم نیست. SECRET_KEY اگر ست نباشد، در پروداکشن یک کلیدِ تصادفیِ واقعی ساخته و در .django_secret_key نگه داشته میشود (۰۶۰۰، خارج از گیت). همانقدر غیرقابلِ حدس، بدونِ یک قدمِ دستیِ دیگر؛ و چون روی دیسک میماند، ریاستارت نشستهای کاربران را باطل نمیکند. CACHE_URL نبودن دیگر مانعِ بالا آمدن نیست. مشترک نبودنِ کش بینِ workerها یک ضعفِ واقعیست ولی دلیلِ خوبی برای خوابیدنِ کلِ سرویس نیست؛ حالا بالا میآید و /api/health صریحاً هشدار میدهد. تستِ مربوطه هم از «باید بالا نیاید» به «باید بالا بیاید ولی بیصدا نماند» تغییر کرد. متغیرِ محیطی همچنان بر همهی این پیشفرضها اولویت دارد. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,21 +37,52 @@ except ModuleNotFoundError:
|
||||
# Defaults to False; must be explicitly enabled via env for local dev.
|
||||
DEBUG = os.environ.get('DJANGO_DEBUG', '') == '1'
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# No hardcoded fallback when DEBUG=False — fails closed at startup instead.
|
||||
# کلیدِ امضا. ترتیبِ اولویت: متغیرِ محیطی، بعد فایلِ تولیدشدهی کنارِ پروژه.
|
||||
#
|
||||
# چرا فایل و نه خطای راهاندازی: قبلاً با DEBUG خاموش و بدونِ این متغیر، اپ
|
||||
# اصلاً بالا نمیآمد و از بیرون فقط 502 دیده میشد. کسی که سرور را بالا میآورد
|
||||
# باید حدس میزد چه چیزی کم است. حالا اولین اجرا خودش یک کلیدِ تصادفیِ واقعی
|
||||
# میسازد و در .django_secret_key (خارج از گیت) نگه میدارد — همانقدر امن،
|
||||
# بدونِ یک قدمِ دستیِ دیگر. متغیرِ محیطی همچنان بر آن اولویت دارد.
|
||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
|
||||
if not SECRET_KEY:
|
||||
if DEBUG:
|
||||
SECRET_KEY = 'django-insecure-jn!s*25lz^+bjm101tzmlz8jlj70i0qka++*h-(!0s^^vk-0ss'
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
'DJANGO_SECRET_KEY environment variable is not set. '
|
||||
'A predictable fallback secret key cannot be used when DEBUG=False.'
|
||||
)
|
||||
import secrets
|
||||
|
||||
ALLOWED_HOSTS = [h for h in os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(',') if h]
|
||||
_key_file = BASE_DIR / '.django_secret_key'
|
||||
try:
|
||||
SECRET_KEY = _key_file.read_text().strip()
|
||||
except OSError:
|
||||
SECRET_KEY = ''
|
||||
if not SECRET_KEY:
|
||||
SECRET_KEY = secrets.token_urlsafe(64)
|
||||
try:
|
||||
_key_file.write_text(SECRET_KEY)
|
||||
_key_file.chmod(0o600)
|
||||
except OSError as exc:
|
||||
raise ImproperlyConfigured(
|
||||
f'DJANGO_SECRET_KEY ست نیست و نوشتنِ {_key_file} هم ممکن نشد ({exc}). '
|
||||
'یا متغیر را ست کنید یا اجازهی نوشتن در پوشهی پروژه بدهید.'
|
||||
) from exc
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [o for o in os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if o]
|
||||
# دامنههای شناختهشدهی خودمان پیشفرضاند تا یک متغیرِ فراموششده باعثِ
|
||||
# DisallowedHost نشود. متغیرِ محیطی، اگر ست باشد، کاملاً جایگزینشان میکند.
|
||||
DEFAULT_ALLOWED_HOSTS = [
|
||||
'api.math.tadrisino2.ir',
|
||||
'math.tadrisino2.ir',
|
||||
'localhost', '127.0.0.1',
|
||||
]
|
||||
ALLOWED_HOSTS = [
|
||||
h for h in os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(',') if h
|
||||
] or DEFAULT_ALLOWED_HOSTS
|
||||
|
||||
# لازم برای ورود به پنلِ ادمین از پشتِ پراکسیِ HTTPS؛ بدونش فرمِ ورود
|
||||
# «CSRF verification failed» میدهد و کسی نمیفهمد چرا.
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
o for o in os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if o
|
||||
] or ['https://api.math.tadrisino2.ir', 'https://math.tadrisino2.ir']
|
||||
|
||||
# --- CORS -------------------------------------------------------------------
|
||||
# فرانتاند (Vite/React) از یک origin دیگر صدا میزند، پس بدونِ این هدرها مرورگر
|
||||
@@ -212,12 +243,15 @@ elif DEBUG:
|
||||
}
|
||||
}
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
'CACHE_URL environment variable is not set. '
|
||||
'A shared cache is required when DEBUG=False, since rate limits '
|
||||
'(OTP throttles and the phone-based login limit) rely on a cache '
|
||||
'shared across all workers/instances; LocMemCache cannot be used in production.'
|
||||
)
|
||||
# بدونِ Redis محدودیتِ نرخ بینِ workerها مشترک نیست — ضعف است، ولی دلیلِ
|
||||
# خوبی برای بالا نیامدنِ کلِ سرویس نیست. قبلاً اینجا استثنا پرتاب میشد و
|
||||
# نتیجهاش از بیرون فقط 502 بود، بدونِ هیچ سرنخی. حالا بالا میآید و
|
||||
# /api/health صریحاً هشدار میدهد.
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
@@ -325,6 +359,7 @@ REST_FRAMEWORK = {
|
||||
'purchase': '10/hour',
|
||||
'token_refresh': '30/hour',
|
||||
'question_answer': '60/min',
|
||||
'question_report': '20/hour',
|
||||
'lesson_complete': '10/hour',
|
||||
'support_create': '30/hour',
|
||||
'offer_redeem': '10/hour',
|
||||
|
||||
Reference in New Issue
Block a user