Prepare the project for production deployment and final client
acceptance testing. Adds preflight fail-fast command, hardened FX rate
importer, currency seeder, deploy scripts, E2E smoke test, and the
43-item UAT checklist document with sign-off form.
Key changes:
- Phase 3.9.1 — hotfix (AddressBookEntry):
- Add $attributes defaults (use_count=0, is_default=false) so
instance values are populated right after create() without
refresh(). Resolves the red test
`it_increments_use_count_and_sets_last_used_at`.
- Phase 3.10 — deploy infrastructure (Laravel):
- Add `ifnex:preflight` command: fail-fast audit of production
environment (APP_ENV, APP_DEBUG, APP_KEY, IFNEX_API_KEY,
IFNEX_BRIDGE_API_KEY, ZARINPAL_SANDBOX, DB_CONNECTION, CORS,
currencies seeded, PHP extensions, storage writable). Exits 1
if any check fails; supports --skip-db for pre-migrate runs.
- Add `ifnex:import-fx-rates` command: replacement for
`ifnex:update-rates`. Fetches USD/AED from Exir.io + EUR/CNY
from FreeCurrencyAPI. Supports --auto-approve (cron use) and
--threshold (max change % for auto-approve). Logs critical on
HTTP failure. URL configurable via EXIR_API_URL env.
- Add `CurrencySeeder`: creates 5 base currencies (USD/AED/EUR/
CNY/IRR) with rate=0 (IRR=1). Idempotent — does not overwrite
real rates already set by admin or cron.
- Update `DatabaseSeeder`: skip super_admin with password
'password' in production (still created in local/testing).
- Fix `bootstrap/app.php`: remove dead `use ApiKeyAuth` import
(actual class is ApiKeyMiddleware). Latent fatal if api_key
alias was used.
- Add `config/ifnex.php` exir block + preflight config.
- Harden `.env.example`: MySQL default, EXIR_API_URL,
SANCTUM_STATEFUL_DOMAINS, APP_TIMEZONE, LOG_DAILY_DAYS, fix
duplicate CURRENCY_API_KEY.
- Phase 3.10 — deploy scripts (Linux):
- Add `scripts/deploy.sh`: idempotent 13-step deploy (composer
install → key:generate → preflight → migrate → seed → FX import
→ cache → chown). Fail-fast with set -e.
- Add `scripts/cron-exir-rates.sh`: daily cron wrapper for
`ifnex:import-fx-rates --source=all --auto-approve`.
- Phase 3.10.1 — preflight hotfix:
- Make expected DB connection configurable
(ifnex.preflight.expected_db_connection) instead of hardcoded
'mysql'. Prevents cascade test failure when tests would have to
mutate database.default to mysql in a sqlite test environment
(Unknown database ':memory:' → broken connection pool → 22
previously-green tests fail with "There is already an active
transaction").
- Rewrite IfnexPreflightTest to use config() rather than mutating
database.default.
- Phase 3.11 — UAT:
- Add `ifnex:smoke-test` command: E2E post-deploy automated test
(25+ checks: DB connection, core tables, currencies with
non-zero rates, countries count, roles, admin user, system
settings, storage writable, config/route cache, 4 API
endpoints: /up, /countries, /track/{invalid}, /customer/profile
without token).
- Add 9 feature tests for smoke-test (PASS + 6 FAIL + custom URL
+ 422/500 handling + auth bypass detection).
- Add `UAT-3.11.md`: 43-item Persian checklist for client
acceptance testing across 15 feature areas. Includes sign-off
form for both parties (client + VernaSoft).
- Update `CLIENT_DELIVERY.md` to record phases 3.10 and 3.11.
Tests:
- Total: 51 → 82 tests, 150 → 220+ assertions, all green.
- New: IfnexPreflightTest (9), IfnexImportFxRatesTest (9),
CurrencySeederTest (4), IfnexSmokeTestTest (9) = 31 new tests.
Documentation:
- DEPLOYMENT_HANDOFF.md §2.4: add CurrencySeeder + import-fx-rates.
- DEPLOYMENT_HANDOFF.md §2.8: update cron to use cron-exir-rates.sh.
- UAT-3.11.md: new 43-item client acceptance checklist + sign-off.
- CLIENT_DELIVERY.md: record phases 3.10 and 3.11 completion.
143 lines
7.2 KiB
Bash
143 lines
7.2 KiB
Bash
#!/usr/bin/env bash
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# IFNEX — فاز ۳.۱۰ — اسکریپت استقرار Production
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# این اسکریپت را فقط یکبار روی سرور Production اجرا کنید.
|
||
# پیشفرض: در مسیر /var/www/system.ifnex.ir قرار دارید (یا هر مسیری که
|
||
# کلون کردهاید). اسکریپت idempotent است؛ دوباره اجرا هم مشکلی ندارد.
|
||
#
|
||
# مراحل (بهترتیب):
|
||
# 1. composer install (no-dev)
|
||
# 2. اگه APP_KEY خالی بود → key:generate
|
||
# 3. ifnex:preflight (fail-fast اگر محیط hardened نباشد)
|
||
# 4. migrate --force
|
||
# 5. seed های امن برای Production
|
||
# 6. import نرخ ارز اولیه (auto-approve)
|
||
# 7. storage:link
|
||
# 8. config/route/view cache
|
||
# 9. دسترسی پوشهها
|
||
#
|
||
# خروجی با کد غیرصفر در صورت شکست هر مرحله (set -e).
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
|
||
set -euo pipefail
|
||
|
||
# رنگها برای خروجی خوانا
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
RED='\033[0;31m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
step() {
|
||
echo -e "\n${BLUE}▶ $1${NC}"
|
||
}
|
||
|
||
ok() {
|
||
echo -e "${GREEN}✓ $1${NC}"
|
||
}
|
||
|
||
warn() {
|
||
echo -e "${YELLOW}⚠ $1${NC}"
|
||
}
|
||
|
||
err() {
|
||
echo -e "${RED}✗ $1${NC}" >&2
|
||
}
|
||
|
||
# بررسی حضور PHP و Composer
|
||
if ! command -v php >/dev/null 2>&1; then
|
||
err "PHP یافت نشد. نصب: apt install php8.2-cli php8.2-{mysql,mbstring,xml,gd,zip,curl,bcmath}"
|
||
exit 1
|
||
fi
|
||
|
||
if ! command -v composer >/dev/null 2>&1; then
|
||
err "Composer یافت نشد. نصب: https://getcomposer.org/download/"
|
||
exit 1
|
||
fi
|
||
|
||
# ─── مرحله ۱: نصب پکیجها ─────────────────────────────────────────
|
||
step "نصب وابستگیها (no-dev، optimize)"
|
||
composer install --no-dev --optimize-autoloader --no-interaction
|
||
ok "composer install کامل شد"
|
||
|
||
# ─── مرحله ۲: APP_KEY ─────────────────────────────────────────────
|
||
step "بررسی APP_KEY"
|
||
if ! grep -qE '^APP_KEY=base64:' .env 2>/dev/null; then
|
||
warn "APP_KEY خالی است → تولید..."
|
||
php artisan key:generate --force
|
||
ok "APP_KEY تولید شد"
|
||
else
|
||
ok "APP_KEY از قبل تنظیم شده"
|
||
fi
|
||
|
||
# ─── مرحله ۳: Preflight ──────────────────────────────────────────
|
||
step "اجرای ifnex:preflight — fail-fast اگر محیط آماده نباشد"
|
||
# در اولین اجرا جدول currencies هنوز ساخته نشده، پس --skip-db میزنیم
|
||
if ! php artisan ifnex:preflight --skip-db; then
|
||
err "preflight ناموفق بود — .env را اصلاح کنید و دوباره اجرا کنید"
|
||
exit 1
|
||
fi
|
||
ok "preflight سبز است"
|
||
|
||
# ─── مرحله ۴: مایگریشن ───────────────────────────────────────────
|
||
step "اجرای migrate --force"
|
||
php artisan migrate --force
|
||
ok "migrate کامل شد"
|
||
|
||
# ─── مرحله ۵: Seed های امن Production ────────────────────────────
|
||
step "Seed دادههای مرجع (امن برای Production)"
|
||
php artisan db:seed --class=CountriesTableSeeder --force
|
||
php artisan db:seed --class=SystemSettingSeeder --force
|
||
php artisan db:seed --class=CurrencySeeder --force
|
||
php artisan db:seed --class=RoleAndPermissionSeeder --force
|
||
ok "seed ها کامل شد"
|
||
|
||
# ─── مرحله ۵ب: دوباره preflight بدون --skip-db ───────────────────
|
||
step "preflight نهایی (شامل بررسی currencies)"
|
||
if ! php artisan ifnex:preflight; then
|
||
warn "preflight نهایی هشدار داد — ولی ادامه میدهیم (ممکن است نرخ ارز هنوز صفر باشد)"
|
||
fi
|
||
|
||
# ─── مرحله ۶: import نرخ ارز اولیه ────────────────────────────────
|
||
step "دریافت نرخ ارز اولیه از Exir.io + FreeCurrencyAPI"
|
||
php artisan ifnex:import-fx-rates --source=all --auto-approve || warn "دریافت نرخ ناموفق بود (ادامه میدهیم — بعداً دستی اجرا کنید)"
|
||
|
||
# ─── مرحله ۷: storage:link ───────────────────────────────────────
|
||
step "ساخت storage symlink"
|
||
php artisan storage:link --force || warn "storage:link از قبل موجود است"
|
||
|
||
# ─── مرحله ۸: Cache ها ────────────────────────────────────────────
|
||
step "ساخت cache برای سرعت Production"
|
||
php artisan config:cache
|
||
php artisan route:cache
|
||
php artisan view:cache
|
||
php artisan event:cache
|
||
ok "cache ها ساخته شدند"
|
||
|
||
# ─── مرحله ۹: دسترسی پوشهها ──────────────────────────────────────
|
||
step "تنظیم دسترسی storage و bootstrap/cache"
|
||
WEB_USER="${WEB_USER:-www-data}"
|
||
chown -R "${WEB_USER}:${WEB_USER}" storage bootstrap/cache
|
||
chmod -R 775 storage bootstrap/cache
|
||
ok "دسترسی تنظیم شد (owner: ${WEB_USER})"
|
||
|
||
# ─── مرحله ۱۰: حساب ادمین ───────────────────────────────────────
|
||
echo
|
||
warn "حساب ادمین Filament هنوز ساخته نشده — دستی اجرا کنید:"
|
||
echo " php artisan make:filament-user"
|
||
echo " php artisan tinker"
|
||
echo " >>> \$u = User::where('email','YOUR_EMAIL')->first();"
|
||
echo " >>> \$u->assignRole('super_admin');"
|
||
echo
|
||
|
||
ok "═══════════════════════════════════════════════════"
|
||
ok " دیپلوی IFNEX با موفقیت کامل شد ✓"
|
||
ok "═══════════════════════════════════════════════════"
|
||
echo
|
||
echo "گام بعدی:"
|
||
echo " ۱. ساخت حساب ادمین (بالا)"
|
||
echo " ۲. همگامسازی کاربران وردپرس: php artisan ifnex:sync-wp-users ..."
|
||
echo " ۳. تنظیم کرون روزانه نرخ ارز (scripts/cron-exir-rates.sh)"
|
||
echo " ۴. تست end-to-end از مرورگر (به DEPLOYMENT_HANDOFF.md §۵ مراجعه کنید)"
|