ifnex/DEPLOYMENT_HANDOFF.md
Kazem Alghasi f1d5667fe8 feat(deploy): preflight + smoke-test + UAT checklist (phases 3.9.1–3.11)
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.
2026-10-31 22:18:15 +03:30

19 KiB
Raw Blame History

راهنمای استقرار IFNEX — ویژهٔ تیم IT کارفرما

مخاطب: مسئول IT شرکت IFNEX تاریخ تحویل: ۲۰۲۶-۱۰-۰۴ نسخه: ۱.۰ تهیه‌کننده: VernaSoft Group — Kazem Alghasi


۱. معماری استقرار

این سیستم از دو بخش مستقل تشکیل شده است که هرکدام روی دامنهٔ جداگانه میزبانی می‌شوند و فقط از طریق REST API با هم ارتباط دارند.

بخش دامنه روش تحویل مسیر وب‌سرور
لاراول (API + پنل مدیریت Filament) system.ifnex.ir کلون گیت /var/www/system.ifnex.ir/public
وردپرس (وب‌سایت + پورتال مشتری) ifnex.ir فایل ZIP + فایل SQL /var/www/ifnex.ir

پیش‌نیازهای هر دو بخش:

  • PHP 8.2 یا بالاتر با اکستنشن‌های pdo_mysql، mbstring، xml، gd، zip
  • MySQL 8 یا بالاتر
  • Composer 2.x (فقط برای لاراول)
  • Nginx یا Apache + PHP-FPM
  • گواهی SSL از Let's Encrypt برای هر دو دامنه

۲. استقرار لاراول — system.ifnex.ir

۲.۱ کلون مخزن

cd /var/www/system.ifnex.ir

git clone https://www.git.vernahost.ir/gitmodir110/ifnex.git tmp

cp -r tmp/04_Laravel/* .
cp tmp/04_Laravel/.env.example .env

rm -rf tmp

۲.۲ نصب پکیج‌ها

composer install --no-dev --optimize-autoloader

۲.۳ تنظیم فایل .env

nano .env

مقادیر زیر را حتماً تنظیم کنید:

APP_NAME=IFNEX
APP_ENV=production
APP_DEBUG=false
APP_URL=https://system.ifnex.ir

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ifnex_laravel
DB_USERNAME=YOUR_DB_USER
DB_PASSWORD=YOUR_DB_PASSWORD

IFNEX_API_KEY=change-this-secret-key
IFNEX_BRIDGE_API_KEY=change-this-secret-key
CORS_ALLOWED_ORIGINS=https://ifnex.ir

ZARINPAL_SANDBOX=false
ZARINPAL_MERCHANT_ID=YOUR_REAL_MERCHANT_ID
ZARINPAL_CALLBACK_URL=https://system.ifnex.ir/api/v1/payment/callback
ZARINPAL_FRONTEND_FAILURE_URL=https://ifnex.ir/wallet

KAVENEGAR_API_KEY=
KAVENEGAR_SENDER=

⚠️ نکتهٔ امنیتی — الزامی: هر دو مقدار IFNEX_API_KEY و IFNEX_BRIDGE_API_KEY باید پیش از استقرار با یک رشتهٔ تصادفی و طولانی (مثلاً خروجی openssl rand -hex 32) جایگزین شوند.

مقدار change-this-secret-key عمداً انتخاب شده است، چون کد سمت لاراول (BridgeAuthController) دقیقاً همین مقدار را رد می‌کند و خطای ۵۰۰ می‌دهد. یعنی اگر جایگزینی فراموش شود، سیستم بی‌سروصدا کار نمی‌کند و به‌جای آن لو می‌دهد. هر مقدار دیگری — از جمله مواردی که شبیه placeholder باشند — به‌عنوان یک کلید معتبر پذیرفته می‌شود.

IFNEX_BRIDGE_API_KEY باید عیناً در پلاگین وردپرس هم تنظیم شود. IFNEX_API_KEY کلید عمومی بخش رهگیری است و آن هم باید در پلاگین تنظیم شود.

۲.۴ راه‌اندازی اولیه

# فاز ۳.۱۰ — پیش‌از-همه: بررسی سخت‌سازی محیط (fail-fast)
php artisan ifnex:preflight --skip-db

php artisan key:generate
php artisan migrate --force

# داده‌های مرجع — بدون این مراحل قیمت‌گذاری و فرم سفارش کار نمی‌کنند
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

# دریافت نرخ ارز اولیه (auto-approve) — فاز ۳.۱۰
php artisan ifnex:import-fx-rates --source=all --auto-approve

# بررسی نهایی preflight (شامل currencies)
php artisan ifnex:preflight

php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache

ℹ️ دربارهٔ seedها: جدول countries فقط توسط CountriesTableSeeder پر می‌شود و موتور قیمت‌گذاری با Country::where('iso_code', …)->firstOrFail() کشور را پیدا می‌کند؛ اگر این seed اجرا نشود، همهٔ استعلام‌های قیمت با خطای 404 مواجه می‌شوند و لیست کشورها در فرم سفارش وردپرس خالی می‌ماند. جدول system_settings نیز فقط توسط SystemSettingSeeder پر می‌شود.

⚠️ فاز ۳.۱۰ — جدول currencies: CurrencySeeder پنج ارز پایه (USD/AED/EUR/CNY/IRR) را با نرخ صفر (یا ۱ برای ریال) ایجاد می‌کند تا ExchangeRateService::getCurrentRate() خطای RuntimeException ندهد. سپس ifnex:import-fx-rates --source=all --auto-approve نرخ‌های واقعی را از Exir.io (USD/AED) و FreeCurrencyAPI (EUR/CNY) دریافت و در currencies.rate_to_rial می‌نویسد.

⚠️ دربارهٔ DatabaseSeeder: فایل DatabaseSeeder.php در فاز ۳.۱۰ به‌روزرسانی شد تا در محیط Production، حساب super_admin با رمز عبور password نساخته شود (در local/testing/staging همچنان ساخته می‌شود). برای ساخت ادمین Production از make:filament-user + assignRole استفاده کنید.

💡 اسکریپت خودکار: می‌توانید به‌جای اجرای دستی همهٔ مراحل بالا، از اسکریپت scripts/deploy.sh استفاده کنید:

bash scripts/deploy.sh

این اسکریپت همهٔ مراحل بالا را به‌ترتیب اجرا می‌کند و در صورت شکست هر مرحله با کد غیرصفر متوقف می‌شود.

تنظیم دسترسی پوشه‌ها:

chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

۲.۵ ساخت حساب ادمین پنل

پنل مدیریت فقط به کاربرانی اجازهٔ ورود می‌دهد که نقش Spatie یکی از super_admin، admin یا staff را داشته باشند. برای ساخت اولین مدیر:

php artisan make:filament-user

سپس در یک محیط تعاملی (Tinker) نقش را اعطا کنید:

php artisan tinker
$user = User::where('email', 'admin@ifnex.ir')->first();
$user->assignRole('super_admin');

⚠️ اگر این مرحله انجام نشود، صفحهٔ /panel/login باز می‌شود ولی هیچ حسابی نمی‌تواند وارد شود.

۲.۶ همگام‌سازی کاربران وردپرس

ورود مشتری از طریق Bridge انجام می‌شود و کاربر را بر اساس ایمیل در دیتابیس لاراول پیدا می‌کند. برای اینکه مشتریان وردپرس بتوانند وارد شوند، باید یک‌بار کاربران را همگام کنید (این دستور پس از راه‌اندازی هر دو دیتابیس اجرا می‌شود):

php artisan ifnex:sync-wp-users \
  --wp-db-host=127.0.0.1 \
  --wp-db-name=ifnex_wordpress \
  --wp-db-user=YOUR_WP_DB_USER \
  --wp-db-pass=YOUR_WP_DB_PASSWORD

اگر پیشوند جدول‌های وردپرس شما wp_ نیست، گزینهٔ --wp-table-prefix را هم اضافه کنید.

۲.۷ تنظیمات پیامک (کاوه‌نگار)

ارسال پیامک در این سیستم از جدول system_settings خوانده می‌شود، نه از فایل .env. بنابراین مقداردهی KAVENEGAR_API_KEY در .env به‌تنهایی کافی نیست و باید از پنل مدیریت وارد شود:

پنل ادمین ← تنظیمات سیستم ← سرویس پیامک

فیلد مقدار
Kavenegar API Key کلید دریافتی از پنل کاوه‌نگار
Kavenegar Sender شمارهٔ فرستندهٔ تأییدشده

پس از وارد کردن، سوییچ‌های ارسال برای هر رویداد (تأیید سفارش، رد سفارش، پرداخت موفق، تغییر ترکینگ) را نیز در همان صفحه فعال کنید.

۲.۸ تنظیم Cron

این پروژه در حال حاضر هیچ زمان‌بندی ثبت‌شده‌ای (Schedule) ندارد. تنها وظیفهٔ زمان‌بندی‌شده، به‌روزرسانی نرخ ارز است که باید مستقیم صدا زده شود:

# فاز ۳.۱۰ — دستور ارتقایافته (جایگزین ifnex:update-rates):
#   - USD و AED از Exir.io
#   - EUR و CNY از FreeCurrencyAPI (نیازمند CURRENCY_API_KEY)
#   - auto-approve نرخ‌های با تغییر زیر ۵٪
0 2 * * * cd /var/www/system.ifnex.ir && bash scripts/cron-exir-rates.sh >> storage/logs/cron.log 2>&1

⚠️ دستور قبلی ifnex:update-rates همچنان برای backward compatibility موجود است، اما فقط USD و AED را پشتیبانی می‌کند و auto-approve ندارد. توصیه می‌شود از ifnex:import-fx-rates استفاده کنید.

📋 اسکریپت کرون: scripts/cron-exir-rates.sh به‌صورت wrapper برای ifnex:import-fx-rates --source=all --auto-approve کار می‌کند. متغیر محیطی IFNEX_PROJECT_DIR را اگر پروژه در مسیر دیگری نصب است، در crontab تنظیم کنید.

ℹ️ دربارهٔ صف: در نسخهٔ فعلی هیچ Job یا اعلانی که ShouldQueue را پیاده‌سازی کند وجود ندارد و QUEUE_CONNECTION روی database است. بنابراین نیازی به queue:work نیست. اگر در آینده صف اضافه شد، آن را با systemd یا supervisor مدیریت کنید، نه با کرون.


۳. استقرار وردپرس — ifnex.ir

۳.۱ نصب فایل‌ها

  1. فایل ZIP وردپرس را در /var/www/ifnex.ir اکسترکت کنید.
  2. فایل SQL دیتابیس را ایمپورت کنید.

۳.۲ تنظیم wp-config.php

define('DB_NAME', 'ifnex_wordpress');
define('DB_USER', 'YOUR_WP_DB_USER');
define('DB_PASSWORD', 'YOUR_WP_DB_PASSWORD');
define('DB_HOST', '127.0.0.1');
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', '');

define('WP_HOME', 'https://ifnex.ir');
define('WP_SITEURL', 'https://ifnex.ir');

// غیرفعال کردن دیباگ در Production
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

// امنیت پیشخوان
define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', true);
define('FS_METHOD', 'direct');

ساخت کلیدها و Saltهای اختصاصی — الزامی:

فایل wp-config.php که همراه بسته تحویل داده می‌شود دارای WP_DEBUG = true و کلیدها و Saltهای ثابت است که در مخزن گیت قرار دارند. این مقادیر نباید در Production استفاده شوند. پیش از راه‌اندازی، مقادیر AUTH_KEY، SECURE_AUTH_KEY، LOGGED_IN_KEY، NONCE_KEY، AUTH_SALT، SECURE_AUTH_SALT، LOGGED_IN_SALT و NONCE_SALT را با مقادیر تصادفی اختصاصی جایگزین کنید. ساده‌ترین روش، دریافت هشت مقدار آماده از سرویس رسمی وردپرس است:

https://api.wordpress.org/secret-key/1.1/salt/

⚠️ استفاده از Saltهای مشترک بین چند نصب، امنیت کوکی‌های احراز هویت را به‌طور کامل از بین می‌برد.

۳.۳ فعال‌سازی قالب و پلاگین

قالب IFNEX Theme و پلاگین IFNEX Bridge را از بخش افزونه‌های وردپرس فعال کنید.

۳.۴ تنظیمات پلاگین IFNEX Bridge

از مسیر پنل وردپرس → تنظیمات IFNEX، هر سه فیلد زیر را پر کنید:

تنظیم مقدار
API URL https://system.ifnex.ir/api/v1
API Key (عمومی) همان مقدار IFNEX_API_KEY در فایل .env لاراول
Bridge API Key همان مقدار IFNEX_BRIDGE_API_KEY در فایل .env لاراول

⚠️ اگر API Key (عمومی) خالی بماند، صفحهٔ /tracking/ خطای «API Key تنظیم نشده است» نشان می‌دهد و اندپوینت رهگیری کار نمی‌کند. این مقدار را با یک رشتهٔ تصادفی در .env تنظیم کنید.

۳.۵ اصلاح آدرس‌ها در دیتابیس

اگر سایت از محیط لوکال به سرور منتقل می‌شود، آدرس‌های ذخیره‌شده را اصلاح کنید:

UPDATE wp_options
   SET option_value = REPLACE(option_value, 'http://localhost/IFNEX-Logistics/03_WordPress', 'https://ifnex.ir')
 WHERE option_name IN ('home', 'siteurl');

UPDATE wp_posts
   SET guid = REPLACE(guid, 'http://localhost/IFNEX-Logistics/03_WordPress', 'https://ifnex.ir');

UPDATE wp_posts
   SET post_content = REPLACE(post_content, 'http://localhost/IFNEX-Logistics/03_WordPress', 'https://ifnex.ir');

UPDATE wp_postmeta
   SET meta_value = REPLACE(meta_value, 'http://localhost/IFNEX-Logistics/03_WordPress', 'https://ifnex.ir');

سپس کش وردپرس را پاک کنید (اگر افزونهٔ کش دارید) و یک‌بار به پیشخوان وردپرس بروید.


۴. تنظیمات وب‌سرور

۴.۱ لاراول — system.ifnex.ir

  • Document Root: /var/www/system.ifnex.ir/public

۴.۲ وردپرس — ifnex.ir

  • Document Root: /var/www/ifnex.ir

۴.۳ گواهی SSL

هر دو دامنه به گواهی SSL از Let's Encrypt نیاز دارند. پس از نصب گواهی، مطمئن شوید ریدایرکت HTTP به HTTPS فعال است.

۴.۴ 🔐 چک‌لیست امنیتی سخت‌سازی (فاز ۳.۸.۱ — اجباری قبل از انتشار)

زمینه: در فاز سخت‌سازی Production این تغییرات امنیتی اعمال شد. پیش از رفتن روی سرور، موارد زیر را انجام دهید:

# اقدام چرا؟
۱ چرخش کلیدها — IFNEX_API_KEY و IFNEX_BRIDGE_API_KEY با رشتهٔ تصادفی جدید (هر دو طرف لاراول و وردپرس یکسان) مقادیر قبلی در تاریخچهٔ گیت ثبت شده‌اند و باید لو‌رفته فرض شوند: php -r "echo bin2hex(random_bytes(32));"
۲ Saltهای وردپرس — همهٔ ۸ کلید/salt را از api.wordpress.org/secret-key بازتولید کنید wp-config.php با saltهای لوکال قبلاً در مخزن کامیت شده بود — اکنون از گیت حذف شده و دیگر کامیت نمی‌شود
۳ درگاه پرداخت — ZARINPAL_MERCHANT_ID واقعی + ZARINPAL_SANDBOX=false اکنون کد در APP_ENV=production اگر درگاه تستی باشد، با خطای صریح از پردازش جلوگیری می‌کند (Log::critical در لاگ)
۴ لاگ Production — LOG_STACK=daily، LOG_LEVEL=warning، LOG_DAILY_DAYS=14، APP_DEBUG=false لاگ روزانه و بدون اطلاعات حساس
۵ کش‌ها — config:cache + route:cache + view:cache سرعت + عدم افشای متغیرهای محیطی
۶ حذف فایل‌های دیباگ — مطمئن شوید نسخهٔ جدید پلاگین (ifnex-bridge ≥ 2.1.0) روی سرور است هک‌های opcache_reset() / header() / cache-buster با time() حذف شدند و با filemtime جایگزین شدند

۵. چک‌لیست نهایی

۵.۱ لاراول

  • کلون مخزن انجام شد
  • composer install اجرا شد
  • فایل .env تنظیم شد (دیتابیس، APP_URL، CORS، Zarinpal)
  • IFNEX_API_KEY و IFNEX_BRIDGE_API_KEY با رشتهٔ تصادفی جایگزین شدند (مقدار change-this-secret-key باقی نمانده)
  • php artisan key:generate اجرا شد
  • php artisan migrate اجرا شد (شامل مایگریشن جدید discount_code_usages — سخت‌سازی فاز ۳.۸.۱)
  • تست کد تخفیف: ثبت سفارش با کد فعال → used_count افزایش یافت؛ سفارش دوم با کد تک‌مصرف → خطای 422
  • seedها اجرا شدند (CountriesTableSeeder، SystemSettingSeeder، RoleAndPermissionSeeder)
  • جدول countries پر است (۱۹۲ کشور)
  • حساب ادمین با نقش super_admin ساخته شد
  • php artisan storage:link اجرا شد
  • دسترسی storage/ و bootstrap/cache/ تنظیم شد
  • php artisan config:cache اجرا شد
  • کاوه‌نگار در «تنظیمات سیستم» پنل تنظیم شد
  • کرون به‌روزرسانی نرخ ارز اضافه شد
  • آدرس https://system.ifnex.ir/panel/login باز می‌شود و ورود ادمین کار می‌کند

۵.۲ وردپرس

  • فایل‌ها اکسترکت شدند
  • دیتابیس ایمپورت شد
  • wp-config.php تنظیم شد
  • WP_DEBUG روی false تنظیم شد
  • کلیدها و Saltهای وردپرس با مقادیر اختصاصی جایگزین شدند
  • آدرس‌ها در دیتابیس جایگزین شدند
  • قالب و پلاگین IFNEX Bridge فعال شدند
  • پلاگین تنظیم شد (API URL + API Key عمومی + Bridge API Key)
  • ifnex:sync-wp-users اجرا شد و کاربران مشتری به لاراول منتقل شدند
  • آدرس https://ifnex.ir باز می‌شود
  • لاگین مشتری کار می‌کند

۵.۳ ارتباط بین‌سامانه‌ای

  • وردپرس به API لاراول وصل می‌شود (تست: لاگین مشتری)
  • خطای CORS داده نمی‌شود
  • صفحهٔ /tracking/ با یک شمارهٔ AWB نتیجه برمی‌گرداند
  • پرداخت درگاه کار می‌کند (ZARINPAL_MERCHANT_ID تنظیم شده و ZARINPAL_SANDBOX=false) — توجه: از فاز ۳.۸.۱ کد در Production با درگاه تستی اصلاً اجرا نمی‌شود (گارد سخت‌سازی)
  • ارسال پیامک Kavenegar تست شد
  • بارگذاری فایل (آپلود تعهدنامه) کار می‌کند
  • بکاپ دیتابیس تنظیم شده است

۶. اطلاعات تماس

مورد مقدار
توسعه‌دهنده VernaSoft Group — Kazem Alghasi
ایمیل kazem@vernasoft.group
مخزن گیت git.vernahost.ir/gitmodir110/ifnex