#!/usr/bin/env bash # پیش‌نمایش خصوصی و انتشار عمومی سایت. # # bash scripts/preview-mode.sh status # وضعیت فعلی # bash scripts/preview-mode.sh on # سایت آنلاین ولی خصوصی (گیت ورود + noindex) # bash scripts/preview-mode.sh off # انتشار عمومی (حذف گیت + اجازه ایندکس) # # در حالت «on» کل سایت پشت Basic Auth است: تیم با نام کاربری/رمز مشترک وارد می‌شود، # محتوا را در همان سایت آنلاین ویرایش می‌کند (ویرایش درجای ایمیل/تلفن و بقیه محتوا)، # اما نه عموم به سایت دسترسی دارند و نه گوگل چیزی ایندکس می‌کند. # # پس از هر تغییر، استک را دوباره بالا بیاورید: # bash scripts/stack.sh deploy-prod # # با «فعال شدن» اولین بار، نام کاربری/رمز ساخته و در .env ذخیره می‌شود # (پیش‌فرض: preview / یک رمز تصادفی — در خروجی چاپ می‌شود). set -euo pipefail cd "$(dirname "$0")/.." ENV_FILE=.env [ -f "$ENV_FILE" ] || { echo "فایل .env پیدا نشد — اول bash scripts/gen-env.sh" >&2; exit 1; } # مقدار فعلی یک کلید در .env (یا خالی) — کوتیشن تک‌نویسه‌ای هم برداشته می‌شود env_get() { local key="$1" sed -n "s/^${key}=//p" "$ENV_FILE" | tail -1 | sed -e "s/^'//" -e "s/'\$//" } # تنظیم/جایگزینی یک کلید در .env (بدون آسیب به بقیه خطوط) # نکتهٔ مهم: docker compose مقادیر .env را خودش تفسیر می‌کند؛ هش htpasswd شامل # $apr1$... است و اگر کوتیشن نخورد، $apr1 و بخش‌های بعدی به‌عنوان متغیر خالی # جایگزین می‌شوند و Basic Auth هرگز رمز را نمی‌پذیرد. پس هر مقدار دارای $ را # تک‌کوتیشن می‌گذاریم (compose کوتیشن را برمی‌دارد و مقدار را عیناً می‌خواند). env_set() { local key="$1" value="$2" if grep -q "^${key}=" "$ENV_FILE"; then python3 - "$ENV_FILE" "$key" "$value" <<'PY' import io, sys path, key, value = sys.argv[1], sys.argv[2], sys.argv[3] if "$" in value: value = "'" + value + "'" lines = io.open(path, encoding="utf-8").read().split("\n") out = [f"{key}={value}" if line.startswith(f"{key}=") else line for line in lines] io.open(path, "w", encoding="utf-8", newline="\n").write("\n".join(out)) PY else if [ "${value#*\$}" != "$value" ]; then value="'$value'"; fi printf '\n%s=%s\n' "$key" "$value" >> "$ENV_FILE" fi } # ساخت هش htpasswd — اول با openssl، در نبودش با ایمیج httpd (Docker لازم است) make_htpasswd() { local user="$1" pass="$2" hash if command -v openssl >/dev/null 2>&1; then hash=$(openssl passwd -apr1 "$pass") else hash=$(docker run --rm httpd:2.4-alpine htpasswd -nbB "$user" "$pass" | cut -d: -f2) fi printf '%s:%s' "$user" "$hash" } # رمز تصادفی خوانا (بدون نویسه‌های گیج‌کننده) random_password() { python3 - <<'PY' import secrets, string alphabet = string.ascii_letters + string.digits print("".join(secrets.choice(alphabet) for _ in range(16))) PY } PREVIEW_MIDDLEWARES_VALUE="preview-auth@docker,preview-noindex@docker" case "${1:-status}" in on) users="$(env_get PREVIEW_AUTH_USERS)" if [ -z "$users" ]; then password="$(random_password)" users="$(make_htpasswd preview "$password")" env_set PREVIEW_AUTH_USERS "$users" echo "نام کاربری/رمز پیش‌نمایش ساخته شد:" echo " کاربر: preview" echo " رمز: $password" echo " (این رمز فقط همین یک‌بار چاپ می‌شود — در .env ذخیره شده است)" echo "" fi env_set PREVIEW_MODE on env_set PREVIEW_MIDDLEWARES "$PREVIEW_MIDDLEWARES_VALUE" # /api و /admin گیت Basic Auth نمی‌گیرند (پنل با ورود خودش کار کند)؛ فقط noindex env_set PREVIEW_MIDDLEWARES_API "preview-noindex@docker" # در پیش‌نمایش، دامنه دوم به اصلی ریدایرکت نمی‌شود تا هر دو قابل بررسی باشند env_set ALIAS_REDIRECT_MIDDLEWARES "" echo "پیش‌نمایش خصوصی فعال شد — سایت آنلاین است، ورود با نام کاربری/رمز، بدون ایندکس." echo "برای اعمال: bash scripts/stack.sh deploy-prod" ;; off) env_set PREVIEW_MODE off env_set PREVIEW_MIDDLEWARES "" env_set PREVIEW_MIDDLEWARES_API "" # در انتشار، دامنه دوم به اصلی ریدایرکت می‌شود تا محتوای تکراری ایجاد نشود env_set ALIAS_REDIRECT_MIDDLEWARES "to-primary@docker" echo "حالت انتشار عمومی فعال شد — گیت ورود حذف و ایندکس مجاز می‌شود." echo "پس از deploy، نقشه سایت را در Search Console ثبت کنید:" echo " https://\$(grep '^DOMAIN=' .env | cut -d= -f2)/sitemap.xml" echo "برای اعمال: bash scripts/stack.sh deploy-prod" ;; status) mode="$(env_get PREVIEW_MODE)" mws="$(env_get PREVIEW_MIDDLEWARES)" alias_mws="$(env_get ALIAS_REDIRECT_MIDDLEWARES)" if [ "$mode" = "on" ]; then echo "وضعیت: پیش‌نمایش خصوصی (آنلاین، غیرعمومی، noindex)" elif [ "$mode" = "off" ]; then echo "وضعیت: انتشار عمومی" else echo "وضعیت: تعیین‌نشده (PREVIEW_MODE=${mode:-خالی}) — پیش‌فرض compose: off" fi echo " PREVIEW_MIDDLEWARES=${mws:-(خالی — بدون گیت)}" echo " ALIAS_REDIRECT_MIDDLEWARES=${alias_mws:-(خالی — بدون ریدایرکت دامنه دوم)}" if [ -n "$(env_get PREVIEW_AUTH_USERS)" ]; then echo " کاربر پیش‌نمایش: $(env_get PREVIEW_AUTH_USERS | cut -d: -f1)" else echo " کاربر پیش‌نمایش: ساخته نشده (اولین «on» می‌سازد)" fi ;; *) echo "استفاده: bash scripts/preview-mode.sh [on|off|status]" >&2 exit 1 ;; esac