#!/usr/bin/env bash # # اجرای کامل کتیبه در محیط توسعهٔ محلی — بدون Docker. # # چهار سرویس را با هم بالا می‌آورد: # web → runserver روی 0.0.0.0:8000 (طبق قرارداد پروژه، برای دسترسی از LAN) # worker → تخلیهٔ صف پیامک (sms_worker) # scanner → اسکن دوره‌ای و صف‌کردن یادآوری‌ها (sms_scan --loop) # delivery → استعلام وضعیت تحویل پیامک‌ها (sms_delivery --sleep 30) # # استفاده: # ./run.sh همه چیز # ./run.sh --no-scan بدون اسکنر # ./run.sh --no-worker بدون worker پیامک # ./run.sh --no-delivery بدون استعلام دلیوری # ./run.sh --no-web فقط سرویس‌های پس‌زمینه # ./run.sh --no-migrate مایگریشن اجرا نشود # ./run.sh --reload runserver با auto-reload (دو پروسه، فقط برای کدنویسی) # ./run.sh -f پس از بالا آمدن، لاگ‌ها را دنبال کن (Ctrl+C فقط دنبال‌کردن را قطع می‌کند) # ./run.sh -h راهنما # # لاگ‌ها و فایل‌های PID در .run/ قرار می‌گیرند. برای توقف: ./stop.sh set -euo pipefail cd "$(dirname "$(readlink -f "$0")")" RUN_DIR=".run" WEB_PORT="8000" WEB_HOST="0.0.0.0" WORKER_SLEEP="${WORKER_SLEEP:-3}" SCAN_INTERVAL="${SCAN_INTERVAL:-300}" START_WEB=1 START_WORKER=1 START_SCAN=1 START_DELIVERY=1 DO_MIGRATE=1 RELOAD=0 FOLLOW=0 usage() { sed -n '3,20p' "$0" | sed 's/^# \{0,1\}//' } while [ $# -gt 0 ]; do case "$1" in --no-web) START_WEB=0 ;; --no-worker) START_WORKER=0 ;; --no-scan) START_SCAN=0 ;; --no-delivery) START_DELIVERY=0 ;; --no-migrate) DO_MIGRATE=0 ;; --reload) RELOAD=1 ;; -f|--follow|--logs) FOLLOW=1 ;; -h|--help) usage; exit 0 ;; *) echo "گزینهٔ ناشناخته: $1" >&2; echo; usage; exit 2 ;; esac shift done # --------------------------------------------------------------------------- # ۱) پیدا کردن مفسر پایتون از venv مخزن (هرگز پایتون سیستم) # --------------------------------------------------------------------------- PY="" for candidate in ".venv/Scripts/python.exe" ".venv/bin/python" ".venv/bin/python3"; do if [ -x "$candidate" ]; then PY="$candidate"; break; fi done if [ -z "$PY" ]; then echo "خطا: مفسر پایتون در .venv پیدا نشد." >&2 echo " ابتدا محیط مجازی مخزن را بساز/فعال کن (.venv)." >&2 exit 1 fi mkdir -p "$RUN_DIR" echo "==> کتیبه — اجرای محلی (dev)" echo " python : $PY" echo " run dir : $RUN_DIR/" [ "$START_WEB" = "1" ] && echo " web : http://$WEB_HOST:$WEB_PORT" [ "$START_WORKER" = "1" ] && echo " worker : sms_worker --sleep $WORKER_SLEEP" [ "$START_SCAN" = "1" ] && echo " scanner : sms_scan --loop --interval $SCAN_INTERVAL" [ "$START_DELIVERY" = "1" ] && echo " delivery: sms_delivery --sleep 30" echo # --------------------------------------------------------------------------- # ۲) توقف نسخهٔ قبلی — جلوگیری از «database is locked» و پورت اشغال # --------------------------------------------------------------------------- echo "==> توقف نسخهٔ قبلی (اگر بالا باشد)" bash ./stop.sh --quiet || true echo # --------------------------------------------------------------------------- # ۳) مایگریشن‌ها (idempotent) # --------------------------------------------------------------------------- if [ "$DO_MIGRATE" = "1" ]; then echo "==> اعمال مایگریشن‌ها" "$PY" manage.py migrate --noinput echo fi # --------------------------------------------------------------------------- # ۴) بالا آوردن سرویس‌ها # --------------------------------------------------------------------------- start_service() { local name="$1"; shift local log="$RUN_DIR/$name.log" local pidfile="$RUN_DIR/$name.pid" : > "$log" # -X utf8: حالت UTF-8 پایتون (PEP 540). روی ویندوز کنسول cp1252 است و بدون این، # نوشتن متن فارسی (مثلاً در provider کنسول پیامک) خطای 'charmap' می‌دهد. # دقت: PYTHONIOENCODING از طریق WSL interop به پروسهٔ ویندوزی منتقل نمی‌شود، ولی # آرگومان خط فرمان منتقل می‌شود؛ برای همین به -X utf8 تکیه می‌کنیم. PYTHONUNBUFFERED=1 PYTHONIOENCODING=utf-8 \ nohup "$PY" -X utf8 -u manage.py "$@" >>"$log" 2>&1 & echo $! > "$pidfile" printf ' %-8s pid %s\n' "$name" "$(cat "$pidfile")" } echo "==> بالا آوردن سرویس‌ها" if [ "$START_WEB" = "1" ]; then if [ "$RELOAD" = "1" ]; then start_service web runserver "$WEB_HOST:$WEB_PORT" else start_service web runserver "$WEB_HOST:$WEB_PORT" --noreload fi fi [ "$START_WORKER" = "1" ] && start_service worker sms_worker --sleep "$WORKER_SLEEP" [ "$START_SCAN" = "1" ] && start_service scanner sms_scan --loop --interval "$SCAN_INTERVAL" [ "$START_DELIVERY" = "1" ] && start_service delivery sms_delivery --sleep 30 echo # --------------------------------------------------------------------------- # ۵) تأیید سلامت وب‌سرور (فقط اگر درخواست شده باشد) # --------------------------------------------------------------------------- if [ "$START_WEB" = "1" ]; then echo "==> بررسی سلامت وب‌سرور" healthy=0 for _ in $(seq 1 30); do # --noproxy: در این محیط یک پروکسی محلی (127.0.0.1:10808) هست و درخواست به # localhost را از خودش رد می‌کند و کد 503 می‌دهد؛ باید صریحاً دور زده شود. code="$(curl -s --noproxy '*' -o /dev/null -w '%{http_code}' \ "http://127.0.0.1:$WEB_PORT/panel/p_login/" || true)" if [ "$code" = "200" ]; then healthy=1; break; fi sleep 1 done if [ "$healthy" = "1" ]; then echo " http://127.0.0.1:$WEB_PORT/panel/p_login/ → HTTP 200" else echo " هشدار: وب‌سرور ظرف ۳۰ ثانیه پاسخ نداد؛ لاگ را ببین: $RUN_DIR/web.log" >&2 fi echo fi # --------------------------------------------------------------------------- # ۶) هشدار ایمنی پیامک — جلوگیری از ارسال واقعی در حین توسعه # --------------------------------------------------------------------------- provider_line="$("$PY" - <<'PYEOF' 2>/dev/null || true import os, django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "khatibi.settings") try: django.setup() from admin_panel.models import SMSProviderConfig c = SMSProviderConfig.load() print("%s|%s" % (c.provider, "on" if c.is_enabled else "off")) except Exception: print("unknown|unknown") PYEOF )" provider="${provider_line%%|*}" enabled="${provider_line##*|}" if [ "$enabled" = "on" ] && [ "$provider" != "console" ]; then echo "⚠️ هشدار: provider پیامک روی «$provider» و ارسال فعال است." echo " worker ممکن است پیامک واقعی بفرستد. برای تست محلی، از" echo " /panel/sms/settings آن را روی console بگذار." echo fi echo "✅ راه‌اندازی تمام شد." echo " لاگ‌ها : $RUN_DIR/{web,worker,scanner,delivery}.log → tail -f $RUN_DIR/worker.log" echo " توقف : ./stop.sh" if [ "$FOLLOW" = "1" ]; then echo echo "==> دنبال‌کردن لاگ‌ها (Ctrl+C فقط همین را قطع می‌کند؛ سرویس‌ها بالا می‌مانند)" tail -n 0 -f "$RUN_DIR"/*.log fi