@php $agentCount = $hosts->filter(fn($h) => !empty($h->agent_token))->count(); @endphp
新增主機
匯入
{{-- ── Stat Cards(可篩選) ─────────────────────────── --}}
@foreach([
['key'=>'all', 'icon'=>'bi-grid-3x3-gap-fill', 'bg'=>'#f1f5f9','color'=>'#64748b','label'=>'全部', 'id'=>'cnt-all'],
['key'=>'online', 'icon'=>'bi-check-circle-fill', 'bg'=>'#dcfce7','color'=>'#10b981','label'=>'正常運作','id'=>'cnt-online'],
['key'=>'degraded', 'icon'=>'bi-exclamation-triangle-fill','bg'=>'#fef3c7','color'=>'#f59e0b','label'=>'回應異常','id'=>'cnt-degraded'],
['key'=>'offline', 'icon'=>'bi-x-circle-fill', 'bg'=>'#fee2e2','color'=>'#ef4444','label'=>'無法連線','id'=>'cnt-offline'],
['key'=>'unknown', 'icon'=>'bi-question-circle-fill','bg'=>'#f1f5f9','color'=>'#adb5bd','label'=>'尚未檢測','id'=>'cnt-unknown'],
['key'=>'paused', 'icon'=>'bi-pause-circle-fill', 'bg'=>'#f8f9fa','color'=>'#6c757d','label'=>'暫停監控','id'=>'cnt-paused'],
['key'=>'retired', 'icon'=>'bi-archive-fill', 'bg'=>'#e9ecef','color'=>'#adb5bd','label'=>'過保不維護','id'=>'cnt-retired'],
] as $s)
@endforeach
{{-- ════════════════════════════════════════════
地圖模式
════════════════════════════════════════════ --}}
@php
$noLoc = $hosts->filter(fn($h) => !$h->lat || !$h->lng);
$noLocPrivate = $noLoc->filter(fn($h) => $h->ip_address &&
!filter_var($h->ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE));
$noLocNoIp = $noLoc->filter(fn($h) => !$h->ip_address);
@endphp
@if($noLoc->isNotEmpty())
@if($noLocNoIp->isNotEmpty())
尚未設定 IP 位址(無法定位):{{ $noLocNoIp->pluck('name')->join('、') }}
@endif
@if($noLocNoIp->isNotEmpty() && $noLocPrivate->isNotEmpty())
@endif
@if($noLocPrivate->isNotEmpty())
私有 IP 無法定位:{{ $noLocPrivate->pluck('name')->join('、') }}
@endif
@endif
{{-- ════════════════════════════════════════════
清單模式(依客戶→專案分組)
════════════════════════════════════════════ --}}
共 {{ $hosts->count() }} 台主機
即時監控
@php
$grouped = $hosts->groupBy(fn($h) => $h->project?->client?->name ?? '未分類')
->map(fn($g) => $g->groupBy(fn($h) => $h->project?->name ?? '未分類專案'))
->sortKeys();
$uncategorised = $grouped->pull('未分類');
@endphp
@forelse($grouped as $clientName => $projects)
{{-- 客戶標頭 --}}
{{-- 每個專案 --}}
@foreach($projects as $projName => $projHosts)
{{ $projName }}
{{ $projHosts->count() }} 台
@foreach($projHosts as $host)
@php
$st = $host->is_retired ? 'retired' : ($host->isMonitorPaused() ? 'paused' : ($host->monitor_status ?? 'unknown'));
$srch = strtolower($host->name.' '.$clientName.' '.$projName.' '.($host->ip_address ?? '').' '.($host->typeLabel()));
$agentSort = !empty($host->agent_token)
? ($host->agent_last_report_at
? ($host->agent_last_report_at->gt(now()->subHours(3)) ? 3 : 2)
: 1)
: 0;
$statusSort = match($host->monitor_status ?? 'unknown') {
'online' => 3, 'degraded' => 2, 'offline' => 1, default => 0
};
@endphp
|
|
{{ $host->name }}
@if($host->lat && $host->lng)
@elseif($host->ip_address)
@endif
@if($host->ip_address)
{{ $host->ip_address }}
@endif
@if($host->lat && $host->lng)
{{ number_format($host->lat,4) }}, {{ number_format($host->lng,4) }}
@endif
|
@if($host->host_type)
{{ $host->typeLabel() }}
@endif
|
{{ $host->response_time_ms ? $host->response_time_ms.'ms' : '–' }}
|
{{ $host->last_checked_at?->format('m/d H:i') ?? '尚未檢測' }}
|
@php
$hasReport = !empty($host->report_data);
$hasApi = !empty($host->api_url);
$hasAgent = !empty($host->agent_token);
$lastRpt = $host->agent_last_report_at;
// Agent:運作中→實心綠;異常→空心黃;未安裝/未回報→灰空心
$agentBtnClass = !$hasAgent
? 'btn-outline-secondary'
: ($lastRpt
? ($lastRpt->gt(now()->subHours(3)) ? 'btn-success' : 'btn-outline-warning')
: 'btn-outline-secondary');
$agentTitle = !$hasAgent
? '安裝 Agent'
: ($lastRpt
? ($lastRpt->gt(now()->subHours(3))
? 'Agent 運作中(最後回報:'.$lastRpt->format('m/d H:i').')'
: 'Agent 異常(最後回報:'.$lastRpt->format('m/d H:i').')')
: 'Agent 已安裝,尚未收到回報');
$reportBtnClass = $hasReport ? 'btn-success' : ($hasApi ? 'btn-primary' : 'btn-outline-secondary');
$reportTitle = $hasReport
? 'Agent 報告(最後回報:'.($host->agent_last_report_at?->format('m/d H:i') ?? '—').')'
: ($hasApi ? 'API 報告' : '尚未設定資料來源');
$avStatus = $host->av_last_scan['status'] ?? null;
$hasSbom = !empty($host->sbom_data);
// SBOM:有資料→實心藍;無資料→灰空心
$sbomBtnClass = $hasSbom ? 'btn-info' : 'btn-outline-secondary';
$sbomTitle = $hasSbom
? 'SBOM('.$host->sbom_collected_at?->format('m/d H:i').')'
: 'SBOM(尚未收集)';
// 掃毒:乾淨→實心綠;感染→空心紅(異常);未掃→灰空心
$avBtnClass = $avStatus === 'clean' ? 'btn-success'
: ($avStatus === 'infected' ? 'btn-outline-danger'
: 'btn-outline-secondary');
$avBtnIcon = $avStatus === 'clean' ? 'shield-fill-check'
: ($avStatus === 'infected' ? 'shield-fill-exclamation'
: 'shield');
$avTitle = $avStatus === 'clean'
? '掃毒:乾淨('.$host->av_last_scan_at?->format('m/d').')'
: ($avStatus === 'infected'
? '掃毒:⚠ 發現威脅('.$host->av_last_scan_at?->format('m/d').')'
: '掃毒(尚未執行)');
@endphp
{{-- 1. 安裝 Agent --}}
{{-- 2. 定位 --}}
@if($host->ip_address)
@endif
{{-- 3. SBOM --}}
{{-- 4. 掃毒 --}}
{{-- 5. 立即檢測 --}}
{{-- 6. 暫停監控 --}}
{{-- 7. 主機報告 --}}
{{-- 8. 修改 --}}
{{-- 9. 刪除 --}}
|
@endforeach
@endforeach
@empty
@endforelse
{{-- 未分類 --}}
@if($uncategorised && $uncategorised->flatten()->isNotEmpty())
@foreach($uncategorised as $projName => $projHosts)
@if($projName !== '未分類專案')
{{ $projName }}
@endif
@foreach($projHosts as $host)
@php
$st = $host->is_retired ? 'retired' : ($host->isMonitorPaused() ? 'paused' : ($host->monitor_status ?? 'unknown'));
$srch = strtolower($host->name.' 未分類 '.$projName.' '.($host->ip_address ?? '').' '.($host->typeLabel()));
$agentSort = !empty($host->agent_token)
? ($host->agent_last_report_at
? ($host->agent_last_report_at->gt(now()->subHours(3)) ? 3 : 2)
: 1)
: 0;
$statusSort = match($host->monitor_status ?? 'unknown') {
'online' => 3, 'degraded' => 2, 'offline' => 1, default => 0
};
@endphp
|
|
{{ $host->name }}
@if($host->lat && $host->lng)
@elseif($host->ip_address)
@endif
@if($host->ip_address)
{{ $host->ip_address }}
@endif
@if($host->lat && $host->lng)
{{ number_format($host->lat,4) }}, {{ number_format($host->lng,4) }}
@endif
|
@if($host->host_type)
{{ $host->typeLabel() }}
@endif
|
{{ $host->response_time_ms ? $host->response_time_ms.'ms' : '–' }}
|
{{ $host->last_checked_at?->format('m/d H:i') ?? '尚未檢測' }}
|
@php
$hasReport = !empty($host->report_data);
$hasApi = !empty($host->api_url);
$hasAgent = !empty($host->agent_token);
$lastRpt = $host->agent_last_report_at;
// Agent:運作中→實心綠;異常→空心黃;未安裝/未回報→灰空心
$agentBtnClass = !$hasAgent
? 'btn-outline-secondary'
: ($lastRpt
? ($lastRpt->gt(now()->subHours(3)) ? 'btn-success' : 'btn-outline-warning')
: 'btn-outline-secondary');
$agentTitle = !$hasAgent
? '安裝 Agent'
: ($lastRpt
? ($lastRpt->gt(now()->subHours(3))
? 'Agent 運作中(最後回報:'.$lastRpt->format('m/d H:i').')'
: 'Agent 異常(最後回報:'.$lastRpt->format('m/d H:i').')')
: 'Agent 已安裝,尚未收到回報');
$reportBtnClass = $hasReport ? 'btn-success' : ($hasApi ? 'btn-primary' : 'btn-outline-secondary');
$reportTitle = $hasReport
? 'Agent 報告(最後回報:'.($host->agent_last_report_at?->format('m/d H:i') ?? '—').')'
: ($hasApi ? 'API 報告' : '尚未設定資料來源');
$avStatus = $host->av_last_scan['status'] ?? null;
$hasSbom = !empty($host->sbom_data);
// SBOM:有資料→實心藍;無資料→灰空心
$sbomBtnClass = $hasSbom ? 'btn-info' : 'btn-outline-secondary';
$sbomTitle = $hasSbom
? 'SBOM('.$host->sbom_collected_at?->format('m/d H:i').')'
: 'SBOM(尚未收集)';
// 掃毒:乾淨→實心綠;感染→空心紅(異常);未掃→灰空心
$avBtnClass = $avStatus === 'clean' ? 'btn-success'
: ($avStatus === 'infected' ? 'btn-outline-danger'
: 'btn-outline-secondary');
$avBtnIcon = $avStatus === 'clean' ? 'shield-fill-check'
: ($avStatus === 'infected' ? 'shield-fill-exclamation'
: 'shield');
$avTitle = $avStatus === 'clean'
? '掃毒:乾淨('.$host->av_last_scan_at?->format('m/d').')'
: ($avStatus === 'infected'
? '掃毒:⚠ 發現威脅('.$host->av_last_scan_at?->format('m/d').')'
: '掃毒(尚未執行)');
@endphp
{{-- 1. 安裝 Agent --}}
{{-- 2. 定位 --}}
@if($host->ip_address)
@endif
{{-- 3. SBOM --}}
{{-- 4. 掃毒 --}}
{{-- 5. 立即檢測 --}}
{{-- 6. 暫停監控 --}}
{{-- 7. 主機報告 --}}
{{-- 8. 修改 --}}
{{-- 9. 刪除 --}}
|
@endforeach
@endforeach
@endif
@if($hosts->isEmpty())
@endif
@endsection
@push('scripts')
{{-- Quick-view Modal --}}