Files
khatibi/run.sh
T
2026-10-10 14:21:04 +03:30

186 lines
7.7 KiB
Bash

#!/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