stop a deploy from silently blocking the frontend or leaving 0000 open

سه حالتِ خراب که هیچ‌کدام در لاگِ سرور ردی نمی‌گذارند و فقط وقتی کشف می‌شدند
که یک آدم از بیرون اپ را دستی امتحان کند.

CORS_ALLOWED_ORIGINS فقط از env خوانده می‌شد و با DEBUG روشن به localhost
محدود می‌ماند. یعنی بک‌اندِ کاملاً سالم، ولی مرورگر همه‌ی درخواست‌های
math.tadrisino2.ir را بلاک می‌کند — خطایی که سمتِ سرور دیده نمی‌شود و فقط در
کنسولِ مرورگر پیداست. origin های شناخته‌شده حالا پیش‌فرضِ داخلِ کدند تا یک
متغیرِ فراموش‌شده اتصال را نشکند؛ env همچنان بازنویسی‌شان می‌کند.

/api/health حالا warnings هم می‌دهد. مهم‌ترینش: با DEBUG روشن روی سرورِ عمومی،
DEV_MASTER_OTP یعنی هر کسی با کدِ 0000 می‌تواند به‌جای هر شماره‌ای وارد شود.
این تا امروز فقط با خواندنِ تنظیماتِ سرور معلوم می‌شد، و ما به سرور دسترسی
نداریم. allowedOrigins هم برگردانده می‌شود تا مشکلِ CORS از بیرون قابلِ
تشخیص باشد.

deploy.sh قبل از دست زدن به دیتابیس متغیرهای لازم را بررسی می‌کند و با پیامِ
روشن می‌ایستد، به‌جای اینکه وسطِ کار بشکند و 502 بدهد.
.env.production.example همان‌ها را یک‌جا مستند می‌کند.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 20:46:23 +03:30
parent 190bff3d0f
commit eaa4b761dd
4 changed files with 116 additions and 7 deletions

View File

@@ -60,6 +60,41 @@ python -m pip install "${PIP_ARGS[@]}" \
|| fail "نصبِ وابستگی‌ها شکست خورد. اگر pypi.org تایم‌اوت می‌دهد، این را امتحان کنید:
PIP_INDEX_URL=https://mirror-pypi.runflare.com/simple ./deploy.sh"
# ─── ۳.۵ بررسیِ متغیرهای محیطی، قبل از دست زدن به دیتابیس ──────────────────
# چرا اینجا: اگر SECRET_KEY یا CACHE_URL نباشد، جنگو موقعِ بالا آمدن می‌میرد و
# از بیرون فقط 502 دیده می‌شود. بهتر است همین‌جا و با پیامِ روشن متوقف شویم.
step "بررسیِ متغیرهای محیطی"
python - <<'PYCHECK' || fail "متغیرهای محیطیِ لازم ست نیستند؛ هیچ تغییری اعمال نشد."
import os, sys
debug = os.environ.get('DJANGO_DEBUG', '').strip().lower() in {'1', 'true', 'yes', 'on'}
missing = []
if not os.environ.get('DJANGO_ALLOWED_HOSTS', '').strip():
missing.append(('DJANGO_ALLOWED_HOSTS', 'دامنه‌هایی که سرو می‌شوند، مثلاً api.math.tadrisino2.ir'))
if not debug:
if not os.environ.get('DJANGO_SECRET_KEY', '').strip():
missing.append(('DJANGO_SECRET_KEY', 'کلیدِ امضا؛ با DEBUG خاموش اجباری است'))
if not os.environ.get('CACHE_URL', '').strip():
missing.append(('CACHE_URL', 'آدرسِ Redis؛ محدودیتِ نرخ بدونش بینِ workerها مشترک نیست'))
if missing:
print(' این متغیرها ست نیستند:')
for name, why in missing:
print(f' {name:24} {why}')
print()
print(' در فایلِ .env سرور اضافه‌شان کنید (نمونه: .env.production.example).')
sys.exit(1)
if debug:
print(' ⚠ DJANGO_DEBUG روشن است.')
print(' روی سرورِ عمومی یعنی: صفحه‌های خطا کلِ تنظیمات را نشان می‌دهند،')
print(' و کدِ ورودِ 0000 برای هر شماره‌ای پذیرفته می‌شود.')
else:
print(' ✓ حالتِ پروداکشن')
PYCHECK
# ─── ۴. دیتابیس ────────────────────────────────────────────────────────────
step "اعمالِ مهاجرت‌های دیتابیس"
python manage.py migrate --noinput || fail "migrate شکست خورد؛ سرویس ری‌استارت نشد."