stop an existing .env from cancelling the defaults meant to replace it
پیشفرضهای دیروز روی یک سرورِ تازه کار میکردند ولی روی سرورِ واقعی نه، و تستِ با .env واقعی نشانش داد: هر دو حالتِ شایع خنثیشان میکرد. مقدارِ موجود ولی ناقص: .env سرور از قبل DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 دارد. چون مقدار خالی نبود، کاملاً جایگزینِ پیشفرض میشد و دامنهی api.math.tadrisino2.ir باز هم غایب میماند — یعنی همان «Disallowed Host» قبلی، و باز هم ویرایشِ دستیِ فایل. حالا دامنههای خودمان به مقدارِ محیطی اضافه میشوند نه جایگزینش؛ همین برای CSRF_TRUSTED_ORIGINS و CORS_ALLOWED_ORIGINS هم اعمال شد. مقدارِ موجود ولی خالی: PUBLIC_BASE_URL در .env هست و خالی است، و os.environ.get فقط وقتی کلید *نباشد* پیشفرض میدهد. نتیجهاش paymentUrl نسبی و callbackِ خالیِ زرینپال بود — خرابیای که فقط موقعِ اولین پرداختِ واقعی معلوم میشود. حالا مقدارِ خالی هم به پیشفرض میافتد. این سهتا همانهاییاند که تا امروز مسئولِ سرور باید دستی به .env اضافه میکرد و چون در .env.example هم نبودند، هر بار از نو کشف میشدند. حالا در همان فایل مستند شدهاند و تست نگهبانشان است. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -67,22 +67,42 @@ if not SECRET_KEY:
|
||||
'یا متغیر را ست کنید یا اجازهی نوشتن در پوشهی پروژه بدهید.'
|
||||
) from exc
|
||||
|
||||
# دامنههای شناختهشدهی خودمان پیشفرضاند تا یک متغیرِ فراموششده باعثِ
|
||||
# DisallowedHost نشود. متغیرِ محیطی، اگر ست باشد، کاملاً جایگزینشان میکند.
|
||||
def _env_list(name: str) -> list[str]:
|
||||
return [v.strip() for v in os.environ.get(name, '').split(',') if v.strip()]
|
||||
|
||||
|
||||
def _with_defaults(name: str, defaults: list[str]) -> list[str]:
|
||||
"""
|
||||
مقدارِ محیطی + پیشفرضهای خودمان، بدونِ تکرار و با حفظِ ترتیب.
|
||||
|
||||
چرا اضافه و نه جایگزین: فایلِ .env روی سرور از قبل وجود دارد و مقدارهای
|
||||
ناقص دارد (مثلاً DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1). اگر مقدارِ
|
||||
موجود پیشفرض را کنار میزد، همان خطای قدیمی برمیگشت و باز کسی باید
|
||||
دستی فایل را ویرایش میکرد. دامنههای خودمان همیشه باید مجاز باشند.
|
||||
"""
|
||||
seen, out = set(), []
|
||||
for value in _env_list(name) + defaults:
|
||||
if value not in seen:
|
||||
seen.add(value)
|
||||
out.append(value)
|
||||
return out
|
||||
|
||||
|
||||
# دامنههای شناختهشدهی خودمان همیشه مجازند تا یک .env قدیمی یا ناقص باعثِ
|
||||
# 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
|
||||
ALLOWED_HOSTS = _with_defaults('DJANGO_ALLOWED_HOSTS', 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']
|
||||
CSRF_TRUSTED_ORIGINS = _with_defaults(
|
||||
'CSRF_TRUSTED_ORIGINS',
|
||||
['https://api.math.tadrisino2.ir', 'https://math.tadrisino2.ir'],
|
||||
)
|
||||
|
||||
# --- CORS -------------------------------------------------------------------
|
||||
# فرانتاند (Vite/React) از یک origin دیگر صدا میزند، پس بدونِ این هدرها مرورگر
|
||||
@@ -101,9 +121,7 @@ CORS_DEFAULT_ORIGINS = [
|
||||
'http://localhost:3000', 'http://127.0.0.1:3000',
|
||||
'http://localhost:5173', 'http://127.0.0.1:5173', # Vite/CRA محلی
|
||||
]
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
o for o in os.environ.get('CORS_ALLOWED_ORIGINS', '').split(',') if o
|
||||
] or CORS_DEFAULT_ORIGINS
|
||||
CORS_ALLOWED_ORIGINS = _with_defaults('CORS_ALLOWED_ORIGINS', CORS_DEFAULT_ORIGINS)
|
||||
CORS_ALLOW_CREDENTIALS = False
|
||||
# فقط مسیرهای API؛ /admin/ و فایلهای استاتیک لازم نیست از origin دیگر خوانده شوند.
|
||||
CORS_URLS_REGEX = r'^/api/.*$'
|
||||
@@ -490,8 +508,18 @@ BAZAAR_API_BASE = os.environ.get('BAZAAR_API_BASE', 'https://pardakht.cafebazaar
|
||||
# -----------------------------------------------------------------------
|
||||
# آدرسِ عمومیِ خودِ بکاند؛ هم برای ساختنِ paymentUrlی که به کلاینت برمیگردد
|
||||
# (PaymentRedirectView) و هم برای callback_url که به زرینپال میفرستیم لازم
|
||||
# است. بدونش این دو URL شکسته میمانند — سیگنالِ واضحی که هنوز ست نشده.
|
||||
PUBLIC_BASE_URL = os.environ.get('PUBLIC_BASE_URL', '').rstrip('/')
|
||||
# است.
|
||||
#
|
||||
# چرا پیشفرض دارد: خالی بودنش سرویس را نمیخواباند، فقط مسیرِ پرداخت را
|
||||
# بیسروصدا میشکند — paymentUrl نسبی میشود و زرینپال callback خالی
|
||||
# میگیرد. این دقیقاً همان دسته خطاست که فقط موقعِ اولین پرداختِ واقعی معلوم
|
||||
# میشود. دامنهی خودمان از قبل معلوم است، پس حدس زدنی نیست.
|
||||
# .strip() اول لازم است: کلید در .env سرور وجود دارد ولی خالی است، و
|
||||
# os.environ.get فقط وقتی کلید *نباشد* پیشفرض میدهد — مقدارِ خالی آن را
|
||||
# خنثی میکرد و مسیرِ پرداخت باز هم بیصدا میشکست.
|
||||
PUBLIC_BASE_URL = (
|
||||
os.environ.get('PUBLIC_BASE_URL', '').strip() or 'https://api.math.tadrisino2.ir'
|
||||
).rstrip('/')
|
||||
|
||||
ZARINPAL_MERCHANT_ID = os.environ.get('ZARINPAL_MERCHANT_ID', '')
|
||||
ZARINPAL_SANDBOX = os.environ.get('ZARINPAL_SANDBOX') == '1'
|
||||
|
||||
Reference in New Issue
Block a user