reportable(function (Throwable $e) { $this->attemptAutoRecovery($e); $this->handleExceptionAlert($e); }); $this->renderable(function (Throwable $e) { $recovered = $this->attemptAutoRecovery($e); if ($recovered) { return redirect()->to(request()->url()); } $this->handleExceptionAlert($e); }); } private function attemptAutoRecovery(Throwable $e): bool { $message = $e->getMessage() ?: ''; $exceptionClass = $e::class; $isRecoverable = array_any(self::AUTO_RECOVERABLE_ERRORS, fn ($pattern) => str_contains($exceptionClass, (string) $pattern) || str_contains($message, (string) $pattern)); if (! $isRecoverable) { return false; } $cacheKey = 'auto_recovery_cooldown'; if (Cache::has($cacheKey)) { return false; } Cache::put($cacheKey, true, self::AUTO_RECOVER_COOLDOWN); Log::warning('Attempting auto-recovery from error', [ 'exception' => $exceptionClass, 'message' => $message, ]); try { Artisan::call('view:clear'); Artisan::call('cache:clear'); Artisan::call('config:clear'); Artisan::call('route:clear'); Artisan::call('config:cache'); Artisan::call('view:cache'); if (str_contains($exceptionClass, 'ViteManifestNotFoundException') || str_contains($message, 'Vite manifest')) { $this->rebuildViteManifest(); } if (function_exists('opcache_reset')) { @opcache_reset(); } Log::info('Auto-recovery completed successfully'); return true; } catch (Throwable $recoveryError) { Log::error('Auto-recovery failed', [ 'original_error' => $message, 'recovery_error' => $recoveryError->getMessage(), ]); return false; } } private function rebuildViteManifest(): void { $manifestPath = public_path('build/manifest.json'); if (! file_exists($manifestPath)) { Log::warning('Vite manifest missing, attempting rebuild'); $result = Process::timeout(120)->run('npm run build'); if ($result->successful()) { Log::info('Vite manifest rebuilt successfully'); if (file_exists('/var/www/atomcms/public/build')) { Process::run('chown -R www-data:www-data /var/www/atomcms/public/build'); Process::run('chmod -R 775 /var/www/atomcms/public/build'); } } else { Log::error('Vite manifest rebuild failed', [ 'output' => $result->output(), 'error' => $result->errorOutput(), ]); } } } private function handleExceptionAlert(Throwable $e): void { if (! $this->shouldAlertException($e)) { return; } try { $alertService = app(AlertService::class); $errorMessage = $e->getMessage() ?: $e::class; $alertService->sendCriticalError($errorMessage, $e); $this->trackErrorRate(); Log::channel('emergency')->error('Critical exception reported via alert', [ 'exception' => $e::class, 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), ]); } catch (\Exception $alertException) { Log::error('Failed to send exception alert: ' . $alertException->getMessage()); } } private function shouldAlertException(Throwable $e): bool { if (! app()->isBooted()) { return false; } if (! (bool) setting('alert_errors_enabled', true)) { return false; } $criticalExceptions = [ QueryException::class, RconConnectionException::class, ]; foreach ($criticalExceptions as $criticalException) { if ($e instanceof $criticalException) { return true; } } if ($this->isHighErrorRate()) { return true; } $minSeverity = setting('alert_min_severity', 'error'); return $this->isSeverityHighEnough($e, $minSeverity); } private function trackErrorRate(): void { if (! app()->isBooted()) { return; } $key = self::CACHE_KEY_ERROR_COUNT . now()->format('YmdHi'); $count = Cache::get($key, 0); Cache::put($key, $count + 1, self::ERROR_COUNT_DURATION); } private function isHighErrorRate(): bool { if (! app()->isBooted()) { return false; } $threshold = (int) setting('alert_error_threshold', 10); $key = self::CACHE_KEY_ERROR_COUNT . now()->format('YmdHi'); $count = Cache::get($key, 0); return $count >= $threshold; } private function isSeverityHighEnough(Throwable $e, string $minSeverity): bool { $severityLevel = [ 'info' => 0, 'warning' => 1, 'error' => 2, 'critical' => 3, ]; $exceptionSeverity = $this->determineExceptionSeverity($e); $minLevel = $severityLevel[$minSeverity] ?? 2; return $exceptionSeverity >= $minLevel; } private function determineExceptionSeverity(Throwable $e): int { if ($e instanceof \Error) { return 3; } if ($e instanceof ValidationException) { return 0; } if ($e instanceof AuthenticationException) { return 1; } if ($e instanceof AuthorizationException) { return 1; } return 2; } }