@extends('layouts.app') @section('title', 'Состояние системы') {{-- Technical metrics for this instance (SystemHealthController). Every section arrives as ['ok' => bool, 'error' => ?string, ...]: a probe that failed renders as a red card here rather than taking the page down with it, which is the whole point — see App\Support\SystemHealth's docblock. Formatting helpers are defined once below instead of per section; they're display-only (bytes -> КБ/МБ/ГБ, seconds -> "3 д 4 ч") and deliberately live in the view rather than in SystemHealth, which returns raw numbers so the ?format=json output stays machine-readable. --}} @php $bytes = static function ($value): string { $value = (float) $value; foreach (['Б', 'КБ', 'МБ', 'ГБ', 'ТБ'] as $unit) { if ($value < 1024 || $unit === 'ТБ') { return number_format($value, $unit === 'Б' ? 0 : 1, ',', ' ').' '.$unit; } $value /= 1024; } return (string) $value; }; $duration = static function (int $seconds): string { $days = intdiv($seconds, 86400); $hours = intdiv($seconds % 86400, 3600); $minutes = intdiv($seconds % 3600, 60); return $days > 0 ? "{$days} д {$hours} ч" : ($hours > 0 ? "{$hours} ч {$minutes} мин" : "{$minutes} мин"); }; $number = static fn ($value): string => $value === null ? '—' : number_format((float) $value, 0, ',', ' '); @endphp @section('content')

Состояние системы

Показатели считаются в момент открытия страницы и нигде не кэшируются. Данные технические — они не о работе центра, а о самом сервере.

{{-- Верхняя сводка: четыре ответа на «всё ли работает» --}} @php $statuses = [ ['label' => 'База данных', 'ok' => ($database['ok'] ?? false), 'detail' => ($database['ok'] ?? false) ? $database['latency_ms'].' мс' : 'недоступна'], ['label' => 'Кэш / Redis', 'ok' => ($cache['ok'] ?? false) && ($cache['round_trip'] ?? false), 'detail' => ($cache['ok'] ?? false) ? ($cache['store'].', '.$cache['latency_ms'].' мс') : 'недоступен'], ['label' => 'Планировщик', 'ok' => ($scheduler['ok'] ?? false) && ($scheduler['alive'] ?? false), 'detail' => ($scheduler['last_run'] ?? null) ? $scheduler['minutes_ago'].' мин назад' : 'нет сигнала'], ['label' => 'Ошибки за 24 ч', 'ok' => ($logs['ok'] ?? false) && (($logs['errors_24h'] ?? 0) === 0), 'detail' => $number($logs['errors_24h'] ?? null)], ]; @endphp
@foreach ($statuses as $status)
{{ $status['label'] }}
{{ $status['ok'] ? 'ОК' : 'Внимание' }} {{ $status['detail'] }}
@endforeach
{{-- Приложение --}}
Приложение
@if (! $application['ok'])
{{ $application['error'] }}
@else
Бренд{{ $application['brand'] }}
Окружение {{ $application['env'] }} @if ($application['debug']) APP_DEBUG включён @endif
Версия PHP / Laravel{{ $application['php'] }} / {{ $application['laravel'] }}
Часовой пояс{{ $application['timezone'] }} — сейчас {{ $application['server_time'] }}
Развёрнутая версия @if ($application['commit']) {{ $application['commit'] }} @if ($application['branch']) ({{ $application['branch'] }}) @endif @if ($application['deployed_at']) — обновлено {{ $application['deployed_at'] }} @endif @else неизвестно (нет доступа к .git) @endif
@endif
{{-- Планировщик --}}
Планировщик задач

Ежеминутный сигнал пишется самим планировщиком. Если сигнала нет дольше {{ $scheduler['stale_after_minutes'] ?? 5 }} минут, значит cron на сервере не запускает schedule:run — тогда не выполняются ни начисления, ни напоминания об оплате.

@if (! $scheduler['ok'])
{{ $scheduler['error'] }}
@else @if ($scheduler['last_run'] === null)
Сигнала ещё не было. Это нормально в первую минуту после обновления — если он не появится, проверьте cron на сервере.
@else

Последний сигнал: {{ $scheduler['last_run'] }} ({{ $scheduler['minutes_ago'] }} мин назад)

@endif
@forelse ($scheduler['jobs'] as $job) @empty @endforelse
ЗадачаРасписаниеСледующий запуск
{{ $job['command'] }} {{ $job['expression'] }} ({{ $job['timezone'] }}) {{ $job['next_run'] ?? '—' }}
Задач нет.
@endif
{{-- База данных --}}
База данных
@if (! $database['ok'])
{{ $database['error'] }}
@else
Название{{ $database['name'] }}
MySQL{{ $database['version'] }}, аптайм {{ $duration($database['uptime_seconds']) }}, соединений: {{ $database['connections'] }}
Размер{{ $bytes($database['total_bytes']) }} в {{ $database['table_count'] }} таблицах
Отклик{{ $database['latency_ms'] }} мс
Ключевые таблицы (точный счёт)
@foreach ($database['exact_counts'] as $table => $count) @endforeach
{{ $table }}
{{ $number($count) }}
10 самых больших таблиц
@foreach (array_slice($database['tables'], 0, 10) as $table) @endforeach
ТаблицаСтрок (≈)Размер
{{ $table->name }} {{ $number($table->approx_rows) }} {{ $bytes($table->size_bytes) }}
Число строк здесь — оценка InnoDB, а не точный счёт.
@endif
{{-- Кэш и хранилище --}}
Кэш и сессии
@if (! $cache['ok'])
{{ $cache['error'] }}
@else
Хранилище кэша{{ $cache['store'] }}
Сессии{{ $cache['session_driver'] }}
Запись и чтение @if ($cache['round_trip']) ОК {{ $cache['latency_ms'] }} мс @else не совпало @endif
@endif
Диск
@if (! $storage['ok'])
{{ $storage['error'] }}
@else @php $usedPct = $storage['total_bytes'] > 0 ? (int) round(100 * ($storage['total_bytes'] - $storage['free_bytes']) / $storage['total_bytes']) : 0; @endphp

Занято {{ $usedPct }}% — свободно {{ $bytes($storage['free_bytes']) }} из {{ $bytes($storage['total_bytes']) }}

Каталог storage @if ($storage['writable']) доступен для записи @else только чтение @endif
Размер laravel.log{{ $bytes($storage['log_bytes']) }}
Загруженный логотип{{ $storage['logo_uploaded'] ? 'есть' : 'стандартный' }}
@endif
{{-- Ошибки --}}
Ошибки в логе

Считаются по последним {{ 256 }} КБ файла laravel.log — уровни ERROR и выше.

@if (! $logs['ok'])
{{ $logs['error'] }}
@elseif (! ($logs['exists'] ?? false))

Файл лога ещё не создан — ошибок не было.

@else
Ошибок за 24 часа
{{ $number($logs['errors_24h']) }}
Ошибок за 7 дней
{{ $number($logs['errors_7d']) }}
Предупреждений за 24 часа
{{ $number($logs['warnings_24h']) }}
Последняя ошибка
{{ $logs['last_error_at'] ?? '—' }}
@if ($logs['truncated'] ?? false)
Файл больше прочитанного отрезка ({{ $bytes($logs['size_bytes']) }}) — счётчики охватывают только его конец.
@endif @endif
@endsection