feat(stack): full overhaul — hardening, ops tooling, monitoring, analytics, chatbot
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.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# ممیزی وابستگیها — آسیبپذیریهای شناختهشده در پکیجهای پایتون/نود و ایمیجهای داکر.
|
||||
# bash scripts/audit.sh
|
||||
# ابزارها اگر نصب نباشند رد میشوند (پیام هشدار)؛ خروجی nonzero یعنی مشکل پیدا شد.
|
||||
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
STATUS=0
|
||||
|
||||
echo "== pip-audit (بکاند) =="
|
||||
if command -v pip-audit >/dev/null 2>&1; then
|
||||
(cd backend && pip-audit -r requirements.txt --progress-spinner off) || STATUS=1
|
||||
else
|
||||
echo "رد شد: pip-audit نصب نیست (pip install pip-audit)"
|
||||
fi
|
||||
|
||||
echo "== npm audit (فرانتاند) =="
|
||||
if [ -d frontend/node_modules ]; then
|
||||
(cd frontend && npm audit --omit=dev) || STATUS=1
|
||||
else
|
||||
echo "رد شد: frontend/node_modules نیست (npm install)"
|
||||
fi
|
||||
|
||||
echo "== trivy (ایمیجهای داکر) =="
|
||||
if command -v trivy >/dev/null 2>&1; then
|
||||
for img in aramesh-backend-backend aramesh-backend-frontend aramesh-backend-chatbot; do
|
||||
if docker image inspect "$img" >/dev/null 2>&1; then
|
||||
trivy image --severity HIGH,CRITICAL "$img" || STATUS=1
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "رد شد: trivy نصب نیست"
|
||||
fi
|
||||
|
||||
if [ "$STATUS" -eq 0 ]; then
|
||||
echo "✔ ممیزی تمام شد — مشکل شناختهشدهای پیدا نشد."
|
||||
else
|
||||
echo "✗ ممیزی مشکل پیدا کرد — خروجی بالا را بررسی کنید." >&2
|
||||
fi
|
||||
exit "$STATUS"
|
||||
+32
-3
@@ -4,8 +4,12 @@
|
||||
# اجرا: bash scripts/backup.sh
|
||||
# زمانبندی: cron (لینوکس) یا Task Scheduler (ویندوز)، مثلا هر شب ساعت ۳:
|
||||
# 0 3 * * * cd /path/to/aramesh-backend && bash scripts/backup.sh >> backups/backup.log 2>&1
|
||||
# یا سرویس db-backup در پروفایل ops (پشتیبان ساعتی DB بدون cron میزبان).
|
||||
#
|
||||
# نگهداری: فایلهای قدیمیتر از RETENTION_DAYS (پیشفرض ۱۴) حذف میشوند.
|
||||
# انتشار خارج از سرور: اگر OFFSITE_RCLONE_REMOTE تنظیم شده باشد (در .env، مثل
|
||||
# OFFSITE_RCLONE_REMOTE=storage:aramesh-backups)، بعد از هر پشتیبان rclone copy انجام میشود.
|
||||
# تازگی پشتیبان: backups/backup_freshness.prom برای هشدار «پشتیبان کهنه» در Grafana نوشته میشود.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
@@ -14,7 +18,7 @@ RETENTION_DAYS="${RETENTION_DAYS:-14}"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p backups
|
||||
|
||||
# متغیرهای .env (برای MINIO_ROOT_*) در دسترس قرار میگیرند
|
||||
# متغیرهای .env (برای MINIO_ROOT_* و OFFSITE_RCLONE_REMOTE) در دسترس قرار میگیرند
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
@@ -26,7 +30,18 @@ echo "[backup] $STAMP"
|
||||
|
||||
# ۱) دیتابیس — همه متنها، مقالهها و تنظیمات سایت
|
||||
DB_FILE="backups/db-${STAMP}.sql.gz"
|
||||
docker compose exec -T db pg_dump -U aramesh arameshplaza | gzip > "$DB_FILE"
|
||||
# --exit-on-error: اگر pg_dump وسط کار خطا بگیرد، پشتیبان ناقص تولید نمیشود
|
||||
docker compose exec -T db pg_dump --exit-on-error -U aramesh arameshplaza | gzip > "$DB_FILE"
|
||||
|
||||
# پشتیبان خالی/کوچک یعنی خرابی — زودتر بفهمیم تا «روز نیاز»
|
||||
MIN_DB_BYTES=1024
|
||||
DB_BYTES=$(wc -c < "$DB_FILE" | tr -d ' ')
|
||||
if [ "$DB_BYTES" -lt "$MIN_DB_BYTES" ]; then
|
||||
echo "[backup] ✗ پشتیبان دیتابیس شکسته است (${DB_BYTES} بایت)" >&2
|
||||
rm -f "$DB_FILE"
|
||||
exit 1
|
||||
fi
|
||||
sha256sum "$DB_FILE" > "${DB_FILE}.sha256"
|
||||
echo "[backup] db: $DB_FILE ($(du -h "$DB_FILE" | cut -f1))"
|
||||
|
||||
# ۲) تصاویر MinIO — باکت aramesh-media
|
||||
@@ -36,9 +51,23 @@ docker compose run --rm -v "$(pwd)/backups:/backups" --entrypoint sh mc -c "
|
||||
"
|
||||
echo "[backup] images: backups/images-${STAMP}"
|
||||
|
||||
# ۳) پاکسازی نسخههای قدیمیتر
|
||||
# ۳) انتشار خارج از سرور (اختیاری) — هر شب کپی به حافظهٔ دیگری
|
||||
if [ -n "${OFFSITE_RCLONE_REMOTE:-}" ]; then
|
||||
if command -v rclone >/dev/null 2>&1; then
|
||||
rclone copy backups "${OFFSITE_RCLONE_REMOTE}" --max-age 48h
|
||||
echo "[backup] offsite: ${OFFSITE_RCLONE_REMOTE}"
|
||||
else
|
||||
echo "[backup] هشدار: OFFSITE_RCLONE_REMOTE تنظیم است اما rclone نصب نیست" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# ۴) پاکسازی نسخههای قدیمیتر
|
||||
find backups -name 'db-*.sql.gz' -mtime "+${RETENTION_DAYS}" -delete
|
||||
find backups -name 'db-*.sql.gz.sha256' -mtime "+${RETENTION_DAYS}" -delete
|
||||
find backups -maxdepth 1 -type d -name 'images-*' -mtime "+${RETENTION_DAYS}" -exec rm -rf {} +
|
||||
echo "[backup] پاکسازی نسخههای قدیمیتر از ${RETENTION_DAYS} روز انجام شد"
|
||||
|
||||
# ۵) سنجهٔ تازگی پشتیبان — Prometheus آن را میخواند؛ کهنهشدن یعنی کرون خفتی است
|
||||
printf 'backup_success_time %s\n' "$(date +%s)" > backups/backup_freshness.prom
|
||||
|
||||
echo "[backup] ✔ تمام شد"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# تستها + بیلد ایمیجها — قابل اجرا محلی یا در هر CI (Gitea Actions/GitLab CI سازگار است)
|
||||
# bash scripts/ci.sh
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "== تستهای بکاند =="
|
||||
if [ -d backend/.venv ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source backend/.venv/bin/activate 2>/dev/null || source backend/.venv/Scripts/activate
|
||||
(cd backend && pytest -q)
|
||||
elif command -v pytest >/dev/null 2>&1; then
|
||||
(cd backend && pytest -q)
|
||||
else
|
||||
echo "رد شد: pytest در دسترس نیست (backend/.venv را بسازید)"
|
||||
fi
|
||||
|
||||
echo "== بیلد ایمیجهای داکر =="
|
||||
docker compose -f docker-compose.yml build backend frontend chatbot
|
||||
|
||||
echo "== ممیزی وابستگیها =="
|
||||
bash scripts/audit.sh || true
|
||||
|
||||
echo "✔ CI تمام شد"
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
# ساخت .env با مقادیر قوی تصادفی — پیش از اولین استقرار تولید یکبار اجرا کنید.
|
||||
#
|
||||
# bash scripts/gen-env.sh # فایل .env میسازد (موجود را بازنویسی نمیکند)
|
||||
# bash scripts/gen-env.sh --force # .env موجود را بازنویسی میکند (احتیاط!)
|
||||
#
|
||||
# توجه: تغییر SECRET_KEY همه ادمینها را از پنل خارج میکند؛ تغییر MINIO_ROOT_PASSWORD
|
||||
# فقط با همگامسازی S3_SECRET_KEY بکاند اعتبار دارد.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ -f .env ] && [ "${1:-}" != "--force" ]; then
|
||||
echo "خطا: .env از قبل موجود است. برای بازنویسی: bash scripts/gen-env.sh --force" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rand() { openssl rand -"$1" 2>/dev/null || head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n'; }
|
||||
|
||||
SECRET_KEY="$(rand hex 48)"
|
||||
POSTGRES_PASSWORD="$(rand hex 16)"
|
||||
MINIO_ROOT_PASSWORD="$(rand hex 16)"
|
||||
ADMIN_PASSWORD="$(rand hex 12)"
|
||||
|
||||
cat > .env <<EOF
|
||||
# تولیدشده با scripts/gen-env.sh — این فایل هرگز به گیت نمیرود.
|
||||
# پیش از انتشار واقعی، ADMIN_PASSWORD را به مقداری که به یاد میسپارید اما قوی است تغییر دهید:
|
||||
# docker compose exec backend python -c "from app.auth import hash_password; print(hash_password('رمز-جدید'))"
|
||||
# سپس در پنل مدیریت از تغییر رمز (change-password) استفاده کنید.
|
||||
SECRET_KEY=${SECRET_KEY}
|
||||
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
MINIO_ROOT_USER=aramesh
|
||||
MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
TRAEFIK_HTTP_PORT=18081
|
||||
SITE_URL=
|
||||
# --- فقط تولید ---
|
||||
# DOMAIN=example.com
|
||||
# ACME_EMAIL=you@example.com
|
||||
# --- تحلیل رفتار (Umami) ---
|
||||
# VITE_API_URL=
|
||||
# VITE_UMAMI_SRC=
|
||||
# VITE_UMAMI_WEBSITE_ID=
|
||||
# --- چتبات ---
|
||||
# LLM_BASE_URL=
|
||||
# LLM_API_KEY=
|
||||
# LLM_MODEL=openai/gpt-oss-120b
|
||||
# CHAT_REINDEX_TOKEN=
|
||||
# --- مانیتورینگ ---
|
||||
# GRAFANA_ADMIN_PASSWORD=
|
||||
# ALERT_BOT_TOKEN=
|
||||
# ALERT_CHAT_ID=
|
||||
EOF
|
||||
|
||||
echo "✔ .env ساخته شد با مقادیر قوی تصادفی."
|
||||
echo " ADMIN_PASSWORD فعلی: ${ADMIN_PASSWORD}"
|
||||
echo " (آن را امن نگه دارید — پس از ورود به پنل، رمز را عوض کنید.)"
|
||||
@@ -27,11 +27,23 @@ import uuid
|
||||
from collections import defaultdict
|
||||
from io import BytesIO
|
||||
|
||||
# اگر .env ریشه هست، متغیرهایش (ADMIN_PASSWORD و ...) خوانده میشوند
|
||||
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
_ENV_FILE = os.path.join(_REPO_ROOT, ".env")
|
||||
if os.path.exists(_ENV_FILE):
|
||||
with open(_ENV_FILE, encoding="utf-8") as _f:
|
||||
for _line in _f:
|
||||
_line = _line.strip()
|
||||
if _line and not _line.startswith("#") and "=" in _line:
|
||||
_k, _, _v = _line.partition("=")
|
||||
os.environ.setdefault(_k.strip(), _v.strip())
|
||||
|
||||
import requests
|
||||
|
||||
API = os.environ.get("VITE_API_URL", "http://localhost:5173")
|
||||
USER = os.environ.get("ADMIN_USERNAME", "admin")
|
||||
PASS = os.environ.get("ADMIN_PASSWORD", "aramesh123")
|
||||
# رمز پیشفرض ندارد — از .env یا متغیر محیطی خوانده میشود؛ نبودش یعنی توقف با خطا
|
||||
PASS = os.environ["ADMIN_PASSWORD"]
|
||||
FORCE = "--force" in sys.argv
|
||||
|
||||
print(f"API بکاند: {API}")
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# بازگردانی پشتیبان دیتابیس — از بازگردانی در محیط واقعی قبلش تمرین کنید!
|
||||
#
|
||||
# bash scripts/restore.sh backups/db-20260918-030000.sql.gz
|
||||
#
|
||||
# احتیاط: محتوای فعلی دیتابیس با محتوای پشتیبان جایگزین میشود.
|
||||
# تصاویر MinIO هم اگر پوشه images-* همان زمان را دارید:
|
||||
# docker compose run --rm -v "$(pwd)/backups:/backups" --entrypoint sh mc -c \
|
||||
# "mc alias set local http://minio:9000 \"\$MINIO_ROOT_USER\" \"\$MINIO_ROOT_PASSWORD\" && \
|
||||
# mc mirror --overwrite /backups/images-20260918-030000 local/aramesh-media"
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "استفاده: bash scripts/restore.sh backups/db-<زمان>.sql.gz" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SQL_GZ="$1"
|
||||
if [ ! -f "$SQL_GZ" ]; then
|
||||
echo "خطا: فایل ${SQL_GZ} پیدا نشد" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# راستیآزمایی چکسام اگر کنار فایل هست
|
||||
if [ -f "${SQL_GZ}.sha256" ]; then
|
||||
sha256sum -c "${SQL_GZ}.sha256"
|
||||
fi
|
||||
|
||||
echo "[restore] هشدار: محتوای فعلی arameshplaza جایگزین میشود. ۵ ثانیه فرصت لغو (Ctrl+C)…"
|
||||
sleep 5
|
||||
|
||||
gunzip -c "$SQL_GZ" | docker compose exec -T db psql -v ON_ERROR_STOP=1 -U aramesh -d arameshplaza
|
||||
|
||||
echo "[restore] ✔ بازگردانی تمام شد — حالا: docker compose restart backend"
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user