پیش‌نمایش خصوصی: سایت آنلاین اما غیرعمومی تا زمان انتشار

- گیت ورود Traefik (Basic Auth) روی همه مسیرها: سایت، API، تصاویر و چت — بدون رمز فقط ۴۰۱
- لایه دوم noindex: هدر X-Robots-Tag روی همه پاسخ‌ها + robots.txt مسدودکننده + نقشه سایت خالی
- اسکریپت scripts/preview-mode.sh on|off|status — ساخت خودکار نام کاربری/رمز و هماهنگ‌کردن .env
- دو دامنه (DOMAIN + DOMAIN_ALIAS) با گواهی TLS و ریدایرکت ۳۰۱ دامنه دوم به اصلی در زمان انتشار
- اسکیمای FAQPage در پوسته سئوی سمت سرور (فارسی و انگلیسی) برای خزنده‌های بدون جاوااسکریپت
- مستندسازی جریان پیش‌نمایش و چک‌لیست انتشار در README و .env.example
This commit is contained in:
MMD
2026-09-26 22:47:04 +03:30
parent 1d29e50852
commit d25db9367c
8 changed files with 485 additions and 9 deletions
+125
View File
@@ -0,0 +1,125 @@
#!/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
}
# تنظیم/جایگزینی یک کلید در .env (بدون آسیب به بقیه خطوط)
env_set() {
local key="$1" value="$2"
if grep -q "^${key}=" "$ENV_FILE"; then
# جداکننده | تا کاراکترهای ویژه مقدار (مثل / و $) مشکلی نسازند
python - "$ENV_FILE" "$key" "$value" <<'PY'
import io, sys
path, key, value = sys.argv[1], sys.argv[2], sys.argv[3]
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
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() {
python - <<'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"
# در پیش‌نمایش، دامنه دوم به اصلی ریدایرکت نمی‌شود تا هر دو قابل بررسی باشند
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 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