getPathInfo(), '/install')) { /** * Once the application is fully installed, the installer and its API endpoints must be * unreachable. Previously AJAX requests were exempted here, but whether a request is * "AJAX" is decided solely by the client supplied `X-Requested-With` header, which let an * unauthenticated request reach the installer endpoints on a live application. The * completion state is recorded only when the installation finishes, so an installation in * progress is unaffected. */ if ($this->isInstallationComplete()) { if ($request->ajax()) { abort(403); } return redirect()->route('admin.dashboard.index'); } } elseif (! $this->isAlreadyInstalled()) { return redirect()->route('installer.index'); } return $next($request); } /** * Whether the installation has fully completed. * * Delegates to the single authority, which consults the completion marker, the database flag and * finally the database itself, while excluding an installation that is genuinely under way. */ public function isInstallationComplete(): bool { return app(DatabaseManager::class)->isInstallationComplete(); } /** * Check if application is already installed. */ public function isAlreadyInstalled(): bool { if (file_exists(storage_path('installed'))) { return true; } if (($databaseManager = app(DatabaseManager::class))->isInstalled()) { touch(storage_path('installed')); /** * Backfill for an application installed before completion was recorded in the database. * Reaching here means the marker file was missing while the database was fully * installed, so the flag is written once and then travels with the data, keeping the * installer closed on every later deploy that arrives without `storage/`. */ $databaseManager->markInstallationCompleted(); Event::dispatch('krayin.installed'); return true; } return false; } }