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.
68 lines
3.2 KiB
PHP
68 lines
3.2 KiB
PHP
<?php
|
||
|
||
use App\Http\Middleware\ApiKeyMiddleware;
|
||
use Illuminate\Auth\AuthenticationException;
|
||
use Illuminate\Foundation\Application;
|
||
use Illuminate\Foundation\Configuration\Exceptions;
|
||
use Illuminate\Foundation\Configuration\Middleware;
|
||
use Illuminate\Http\Request;
|
||
|
||
return Application::configure(basePath: dirname(__DIR__))
|
||
->withRouting(
|
||
web: __DIR__.'/../routes/web.php',
|
||
api: __DIR__.'/../routes/api.php',
|
||
commands: __DIR__.'/../routes/console.php',
|
||
health: '/up',
|
||
)
|
||
->withCommands([
|
||
\App\Console\Commands\ImportShippingRates::class,
|
||
\App\Console\Commands\ImportTrackingData::class,
|
||
\App\Console\Commands\UpdateExchangeRates::class,
|
||
\App\Console\Commands\GenerateApiToken::class,
|
||
\App\Console\Commands\SyncWordPressUsers::class,
|
||
// فاز ۳.۱۰ — استقرار
|
||
\App\Console\Commands\IfnexPreflight::class,
|
||
\App\Console\Commands\IfnexImportFxRates::class,
|
||
])
|
||
->withMiddleware(function (Middleware $middleware): void {
|
||
$middleware->alias([
|
||
'api_key' => ApiKeyMiddleware::class,
|
||
]);
|
||
|
||
// پیشفرض لاراول ۱۱ مهمانها را به route('login') میفرستد که در این پروژه وجود ندارد
|
||
// و باعث 500 میشود؛ با null، AuthenticationException به هندلر exceptions میرسد
|
||
// و برای api/* پاسخ JSON 401 برگردانده میشود.
|
||
$middleware->redirectGuestsTo(fn () => null);
|
||
|
||
// Stateful API برای Sanctum
|
||
$middleware->statefulApi();
|
||
|
||
// API با CORS
|
||
$middleware->api(prepend: \Illuminate\Http\Middleware\HandleCors::class);
|
||
|
||
// گروه web برای Filament و صفحات عادی
|
||
$middleware->group('web', [
|
||
\Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||
\Illuminate\Session\Middleware\StartSession::class,
|
||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||
]);
|
||
})
|
||
->withExceptions(function (Exceptions $exceptions): void {
|
||
// برای APIها، به جای redirect به login، JSON 401 برگردان
|
||
$exceptions->render(function (AuthenticationException $e, Request $request) {
|
||
if ($request->is('api/*') || $request->expectsJson() || $request->wantsJson()) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'Unauthenticated',
|
||
'error' => 'توکن نامعتبر است یا منقضی شده است. لطفاً دوباره وارد شوید.',
|
||
], 401);
|
||
}
|
||
|
||
// مهمانهای وب (مثلاً بازکردن مستقیم روتهای دانلود ادمین) به ورود پنل هدایت شوند —
|
||
// هندلر پیشفرض لاراول به route('login') میرود که در این پروژه وجود ندارد
|
||
return redirect()->guest(route('filament.admin.auth.login'));
|
||
});
|
||
})->create(); |