Security: deep /api/health (DB+MinIO probes, 503 on failure) + /api/live; rate-limited public forms with honeypots; real client IP behind Traefik (--proxy-headers); TipTap URL sanitization on write (nh3); non-root containers + slim .dockerignore; prod MinIO console bound to 127.0.0.1; HSTS/nosniff on TLS routers; JWT 8h; no default credentials in code. Reliability: Alembic migrations (create_all baseline); graceful shutdown (exec uvicorn); hardened backups (checksums, sanity check, freshness gauge, optional rclone off-site) + restore.sh + hourly db-backup sidecar (ops profile); stack.sh ops tooling (deploy/logs/health/backup/restore/monitoring); .gitattributes LF for shell scripts. Orchestration: json-file log rotation, pinned images, per-service memory limits, edge/internal networks, Traefik ping/accesslog/metrics + flood limiter, conditional minio-setup, pgvector db image, one-shot db-init (umami + aramesh_chatbot databases). Chatbot: unified from site-chatbot branch (RAG + pgvector, SSE, ChatWidget). Monitoring: Prometheus/Alertmanager/Loki/Promtail/Grafana + cAdvisor, node/postgres/blackbox exporters, provisioned dashboards and 10 alert rules; alerts delivered to Bale (tapi.bale.ai — Telegram-compatible). Analytics: Umami on shared Postgres, build-time tracker injection, custom events (contact/newsletter/phone/chat/lang) via frontend/src/analytics.ts. Hygiene: CSV export for contact messages and subscribers; upload image optimization (Pillow); CI script + GitHub-compatible workflow; audit.sh; gen-env.sh for strong credentials.
160 lines
5.6 KiB
Bash
160 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
# ابزار orchestration استک — یک نقطه ورود برای همه عملیات رایج.
|
|
#
|
|
# bash scripts/stack.sh deploy # بیلد فرانتاند (روی هاست) + بیلد و بالا آوردن استک
|
|
# bash scripts/stack.sh deploy-prod # همین، با لایه TLS/دامنه (docker-compose.prod.yml)
|
|
# bash scripts/stack.sh logs [svc] # دنبالکردن لاگ (همه یا یک سرویس)
|
|
# bash scripts/stack.sh ps # وضعیت سرویسها
|
|
# bash scripts/stack.sh health # وضعیت healthcheck همه سرویسها
|
|
# bash scripts/stack.sh backup # پشتیبانگیری کامل (scripts/backup.sh)
|
|
# bash scripts/stack.sh restore FILE # بازگردانی دیتابیس (scripts/restore.sh)
|
|
# bash scripts/stack.sh update # کشیدن ایمیجهای پینشده و بالا آوردن مجدد
|
|
# bash scripts/stack.sh monitoring # لایه مانیتورینگ (Prometheus/Grafana/Loki/بله)
|
|
# bash scripts/stack.sh seed-force # seed مجدد محتوا (--force — دادههای جدولها ریست میشود)
|
|
#
|
|
# متغیرهای بیلد فرانتاند (VITE_API_URL، VITE_UMAMI_SRC، VITE_UMAMI_WEBSITE_ID)
|
|
# از .env ریشه خوانده و به npm run build پاس میشوند — متغیرهای Vite در زمان بیلد
|
|
# روی هاست معنا دارند، نه در ایمیج Caddy.
|
|
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
DEV_FILES=(-f docker-compose.yml)
|
|
[ -f docker-compose.override.yml ] && DEV_FILES+=(-f docker-compose.override.yml)
|
|
|
|
# compose dev ... | compose prod ... — انتخاب فایلهای لایهای
|
|
compose() {
|
|
local mode="$1"
|
|
shift
|
|
if [ "$mode" = "prod" ]; then
|
|
docker compose -f docker-compose.yml -f docker-compose.prod.yml "$@"
|
|
else
|
|
docker compose "${DEV_FILES[@]}" "$@"
|
|
fi
|
|
}
|
|
|
|
load_env_vars() {
|
|
# فقط متغیرهای بیلد فرانتاند — بدون خروجی
|
|
if [ -f .env ]; then
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
. ./.env
|
|
set +a
|
|
fi
|
|
}
|
|
|
|
render_alertmanager() {
|
|
# قالب alertmanager.yml با مقادیر .env رندر میشود — Alertmanager متغیر محیطی
|
|
# را در کانفیگ جایگزین نمیکند؛ خروجی (rendered) در گیت نمیآید.
|
|
load_env_vars
|
|
local tpl="monitoring/alertmanager/alertmanager.yml"
|
|
local out="monitoring/alertmanager/alertmanager.rendered.yml"
|
|
local bot="${ALERT_BOT_TOKEN:-0000000000:PLACEHOLDER-TOKEN}"
|
|
local chat="${ALERT_CHAT_ID:-0}"
|
|
sed -e "s|\${ALERT_BOT_TOKEN}|${bot}|g" \
|
|
-e "s|\${ALERT_CHAT_ID}|${chat}|g" \
|
|
"$tpl" > "$out"
|
|
if [ -n "${ALERT_BOT_TOKEN:-}" ]; then
|
|
echo "[stack] alertmanager config rendered (bale token set)"
|
|
else
|
|
echo "[stack] alertmanager config rendered (no bale token — alerts will not deliver)"
|
|
fi
|
|
}
|
|
|
|
build_frontend() {
|
|
load_env_vars
|
|
echo "[stack] بیلد فرانتاند روی هاست…"
|
|
(cd frontend \
|
|
&& VITE_API_URL="${VITE_API_URL:-}" \
|
|
VITE_UMAMI_SRC="${VITE_UMAMI_SRC:-}" \
|
|
VITE_UMAMI_WEBSITE_ID="${VITE_UMAMI_WEBSITE_ID:-}" \
|
|
npm run build)
|
|
if [ ! -f frontend/dist/index.html ]; then
|
|
echo "[stack] ✗ بیلد فرانتاند خروجی نداد (frontend/dist)" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
wait_healthy() {
|
|
local mode="$1"
|
|
echo "[stack] منتظر سلامت سرویسها (حداکثر ۱۸۰ ثانیه)…"
|
|
local deadline=$((SECONDS + 180))
|
|
local unhealthy=""
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
unhealthy=$(compose "$mode" ps --format '{{.Service}} {{.Health}}' 2>/dev/null \
|
|
| awk '$2=="starting" || $2=="unhealthy" {print $1":"$2}')
|
|
if [ -z "$unhealthy" ]; then
|
|
echo "[stack] ✔ همه سرویسها سالماند"
|
|
return 0
|
|
fi
|
|
sleep 5
|
|
done
|
|
echo "[stack] ✗ برخی سرویسها سالم نشدند: ${unhealthy:-نامشخص}" >&2
|
|
compose "$mode" ps
|
|
return 1
|
|
}
|
|
|
|
do_deploy() {
|
|
local mode="$1"
|
|
build_frontend
|
|
echo "[stack] بیلد و بالا آوردن استک…"
|
|
compose "$mode" up -d --build
|
|
wait_healthy "$mode"
|
|
docker system prune -f >/dev/null 2>&1 || true
|
|
echo "[stack] ✔ استقرار تمام شد"
|
|
if [ -z "${VITE_UMAMI_WEBSITE_ID:-}" ]; then
|
|
echo "[stack] یادآوری: VITE_UMAMI_WEBSITE_ID در .env خالی است — تحلیل رفتار غیرفعال میماند."
|
|
fi
|
|
}
|
|
|
|
case "${1:-}" in
|
|
deploy)
|
|
do_deploy dev
|
|
;;
|
|
deploy-prod)
|
|
load_env_vars
|
|
if [ -z "${DOMAIN:-}" ] || [ -z "${ACME_EMAIL:-}" ]; then
|
|
echo "[stack] ✗ برای deploy-prod، DOMAIN و ACME_EMAIL باید در .env تنظیم باشند" >&2
|
|
exit 1
|
|
fi
|
|
do_deploy prod
|
|
;;
|
|
logs)
|
|
shift || true
|
|
compose dev logs -f --tail=100 "$@"
|
|
;;
|
|
ps)
|
|
compose dev ps
|
|
;;
|
|
health)
|
|
compose dev ps --format '{{.Service}}: {{.Status}}'
|
|
;;
|
|
backup)
|
|
bash scripts/backup.sh
|
|
;;
|
|
restore)
|
|
shift
|
|
bash scripts/restore.sh "$@"
|
|
;;
|
|
update)
|
|
compose dev pull
|
|
compose dev up -d
|
|
wait_healthy dev
|
|
;;
|
|
monitoring)
|
|
# لایه مانیتورینگ — Prometheus/Grafana/Loki/Alertmanager (+ رندر کانفیگ بله)
|
|
render_alertmanager
|
|
echo "[stack] بالا آوردن لایه مانیتورینگ…"
|
|
docker compose "${DEV_FILES[@]}" -f docker-compose.monitoring.yml up -d --build
|
|
wait_healthy dev
|
|
echo "[stack] ✔ مانیتورینگ بالا آمد — داشبورد: http://grafana.localhost:${TRAEFIK_HTTP_PORT:-5173}"
|
|
;;
|
|
seed-force)
|
|
compose dev run --rm backend python -m app.seed --force
|
|
;;
|
|
*)
|
|
grep '^# ' "$0" | sed 's/^# //'
|
|
exit 1
|
|
;;
|
|
esac
|