186 lines
7.7 KiB
Bash
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
|