- http: a request whose 401 arrived after another request's refresh had finished refreshed again; it now resends with the new token. A refresh finishing after logout no longer restores tokens. - logout sends the refresh token current at send time (after a 401 -> refresh it had sent the rotated-out one and left the new session alive on the server for 30 days). - Sign-out/sign-in in another browser tab is followed (storage event). - Navigator removes its history entries when the main app unmounts (Back did nothing after logout). - Login: +98 / 0098 / 9xxxxxxxxx phone input (autofill, typing, paste) normalised; incomplete OTP is not sent (each wrong guess burns one of five). - Quiz resumes at the same question after hearts refill instead of restarting; the no-hearts screen re-checks while due (phone clock ahead of the server left it stuck at zero). - Payment return: a pending order is re-checked for about a minute instead of shown as failed; the pay button is reset when the page comes back from bfcache. - Tutor: retry keeps the quiz question id; a network error no longer forgets the conversation; daily quota detected by code ai_daily_quota. - Root error boundary and vite:preloadError reload instead of a white screen; deploy.sh keeps hashed assets of earlier builds for 30 days so open apps can still load their chunks. - Sheet: labelled dialog, focus moves in and back, Escape closes only the top sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.9 KiB
Bash
Executable File
58 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# انتشارِ فرانت روی سرور: گرفتنِ کدِ تازه، build، و جابهجاییِ بیوقفهی dist.
|
||
#
|
||
# ./deploy.sh # شاخهی main
|
||
# ./deploy.sh <branch>
|
||
#
|
||
# build در dist.next ساخته میشود و فقط اگر کامل موفق شد جای dist را میگیرد؛
|
||
# پس سایت حتی چند ثانیه هم بیفایل نمیماند. buildِ قبلی در dist.prev میماند:
|
||
# برگشت: rm -rf dist && mv dist.prev dist
|
||
#
|
||
# dist/config.js (آدرسِ API در زمانِ اجرا) اگر روی سرور دستی ویرایش شده باشد،
|
||
# به buildِ تازه منتقل میشود تا با هر انتشار از دست نرود.
|
||
set -euo pipefail
|
||
cd "$(dirname "$0")"
|
||
|
||
BRANCH="${1:-main}"
|
||
step() { printf '\n\033[0;32m▸ %s\033[0m\n' "$1"; }
|
||
fail() { printf '\033[0;31m✖ %s\033[0m\n' "$1" >&2; exit 1; }
|
||
|
||
step "گرفتنِ کدِ تازه ($BRANCH)"
|
||
git fetch origin "$BRANCH" || fail "به gitea وصل نشد."
|
||
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
|
||
mkdir -p .deploy-backups
|
||
PATCH=".deploy-backups/local-changes-$(date +%Y%m%d-%H%M%S).patch"
|
||
git diff > "$PATCH"
|
||
printf ' تغییرِ دستی روی سرور بود؛ در %s ذخیره شد.\n' "$PATCH"
|
||
fi
|
||
git reset --hard "origin/$BRANCH"
|
||
git log --oneline -1
|
||
|
||
step "نصبِ وابستگیها"
|
||
npm ci --no-audit --no-fund
|
||
|
||
step "type-check و build"
|
||
rm -rf dist.next
|
||
npx tsc --noEmit || fail "type-check شکست خورد؛ سایت دست نخورد."
|
||
npx vite build --outDir dist.next --emptyOutDir || fail "build شکست خورد؛ سایت دست نخورد."
|
||
|
||
if [ -f dist/config.js ] && ! cmp -s dist/config.js public/config.js; then
|
||
cp dist/config.js dist.next/config.js
|
||
printf ' config.jsِ ویرایششدهی سرور نگه داشته شد.\n'
|
||
fi
|
||
|
||
# فایلهای buildهای قبلی (نامشان hash دارد و با هم برخورد نمیکنند) تا ۳۰ روز میمانند: گوشیای که اپ از قبل
|
||
# در آن باز است هنوز index.htmlِ قبلی را دارد و با باز کردنِ صفحهای که بار نشده همان نامها را میخواهد؛ بیاین
|
||
# ۴۰۴ و صفحهی سفید میگرفت. cp -p زمانِ فایل را نگه میدارد، پس هر فایل ۳۰ روز بعد از buildِ خودش کنار میرود.
|
||
if [ -d dist/assets ]; then
|
||
find dist/assets -type f -mtime -30 -exec cp -pn {} dist.next/assets/ \;
|
||
fi
|
||
|
||
step "جابهجایی"
|
||
rm -rf dist.prev
|
||
[ -d dist ] && mv dist dist.prev
|
||
mv dist.next dist
|
||
API="$(grep -oh 'https\?://[a-zA-Z0-9.-]*/api' dist/assets/*.js | head -1)"
|
||
printf ' منتشر شد. آدرسِ API در build: %s\n' "${API:-(همدامنه /api یا config.js)}"
|
||
printf ' کشِ آروان را برای index.html و config.js پاک کنید اگر تغییری دیده نشد.\n'
|