- TriggerAuditLog 모델 (casts, accessors, scopes) - TriggerAuditController (목록/상세/이력/롤백 미리보기/롤백 실행) - index: 대시보드 통계 + 필터 + 목록 + 파티션 현황 - show: old/new diff 뷰 (변경 컬럼 하이라이트) - history: 레코드별 변경 타임라인 - rollback-preview: SQL 미리보기 + 확인 후 실행 - 라우트 5개 등록, 메뉴 시더 (시스템 관리 > DB 변경 추적) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
123 lines
5.5 KiB
PHP
123 lines
5.5 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '감사 로그 상세 #' . $log->id)
|
|
|
|
@section('content')
|
|
<div class="flex justify-between items-center mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-800">감사 로그 상세 #{{ $log->id }}</h1>
|
|
<p class="text-sm text-gray-500 mt-1">{{ $log->table_name }} / Row {{ $log->row_id }}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<a href="{{ route('trigger-audit.history', [$log->table_name, $log->row_id]) }}"
|
|
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg text-sm">이 레코드 전체 이력</a>
|
|
<a href="{{ route('trigger-audit.rollback-preview', $log->id) }}"
|
|
class="bg-orange-500 hover:bg-orange-600 text-white px-4 py-2 rounded-lg text-sm">롤백 미리보기</a>
|
|
<a href="{{ route('trigger-audit.index') }}"
|
|
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg text-sm">목록</a>
|
|
</div>
|
|
</div>
|
|
|
|
@if(session('success'))
|
|
<div class="bg-green-100 text-green-700 px-4 py-3 rounded-lg mb-6">{{ session('success') }}</div>
|
|
@endif
|
|
|
|
<!-- 기본 정보 -->
|
|
<div class="bg-white rounded-lg shadow-sm p-4 mb-6">
|
|
<h3 class="text-sm font-semibold text-gray-700 mb-3">기본 정보</h3>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
|
<div>
|
|
<span class="text-gray-500">DML 타입</span>
|
|
<div class="mt-1">
|
|
<span class="px-2 py-1 rounded text-xs font-medium
|
|
{{ $log->dml_type === 'INSERT' ? 'bg-green-100 text-green-700' : '' }}
|
|
{{ $log->dml_type === 'UPDATE' ? 'bg-yellow-100 text-yellow-700' : '' }}
|
|
{{ $log->dml_type === 'DELETE' ? 'bg-red-100 text-red-700' : '' }}">
|
|
{{ $log->dml_type }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500">테넌트 ID</span>
|
|
<div class="mt-1 font-medium">{{ $log->tenant_id ?? '-' }}</div>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500">DB 사용자</span>
|
|
<div class="mt-1 font-medium">{{ $log->db_user ?? '-' }}</div>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500">일시</span>
|
|
<div class="mt-1 font-medium">{{ $log->created_at->format('Y-m-d H:i:s') }}</div>
|
|
</div>
|
|
@if($log->actor_id)
|
|
<div>
|
|
<span class="text-gray-500">Actor ID</span>
|
|
<div class="mt-1 font-medium">{{ $log->actor_id }}</div>
|
|
</div>
|
|
@endif
|
|
@if($log->session_info)
|
|
<div>
|
|
<span class="text-gray-500">IP</span>
|
|
<div class="mt-1 font-medium">{{ $log->session_info['ip'] ?? '-' }}</div>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500">Route</span>
|
|
<div class="mt-1 font-medium text-xs">{{ $log->session_info['route'] ?? '-' }}</div>
|
|
</div>
|
|
@endif
|
|
@if($log->changed_columns)
|
|
<div>
|
|
<span class="text-gray-500">변경 컬럼</span>
|
|
<div class="mt-1">
|
|
@foreach($log->changed_columns as $col)
|
|
<span class="inline-block bg-yellow-100 text-yellow-700 px-2 py-0.5 rounded text-xs mr-1 mb-1">{{ $col }}</span>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diff 뷰 -->
|
|
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
|
<h3 class="text-sm font-semibold text-gray-700 px-4 py-3 bg-gray-50 border-b">데이터 변경 내역</h3>
|
|
<table class="w-full text-sm">
|
|
<thead class="bg-gray-50 text-gray-600">
|
|
<tr>
|
|
<th class="px-4 py-2 text-left w-1/5">컬럼</th>
|
|
<th class="px-4 py-2 text-left w-2/5">이전 값 (OLD)</th>
|
|
<th class="px-4 py-2 text-left w-2/5">이후 값 (NEW)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-100">
|
|
@foreach($diff as $d)
|
|
<tr class="{{ $d['changed'] ? 'bg-yellow-50' : '' }}">
|
|
<td class="px-4 py-2 font-medium {{ $d['changed'] ? 'text-yellow-700' : 'text-gray-600' }}">
|
|
{{ $d['column'] }}
|
|
@if($d['changed']) <span class="text-yellow-500">*</span> @endif
|
|
</td>
|
|
<td class="px-4 py-2 text-gray-600 break-all text-xs font-mono {{ $d['changed'] ? 'bg-red-50' : '' }}">
|
|
@if($d['old'] === null)
|
|
<span class="text-gray-400 italic">NULL</span>
|
|
@elseif(is_array($d['old']) || is_object($d['old']))
|
|
<pre class="whitespace-pre-wrap">{{ json_encode($d['old'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}</pre>
|
|
@else
|
|
{{ Str::limit((string) $d['old'], 500) }}
|
|
@endif
|
|
</td>
|
|
<td class="px-4 py-2 text-gray-600 break-all text-xs font-mono {{ $d['changed'] ? 'bg-green-50' : '' }}">
|
|
@if($d['new'] === null)
|
|
<span class="text-gray-400 italic">NULL</span>
|
|
@elseif(is_array($d['new']) || is_object($d['new']))
|
|
<pre class="whitespace-pre-wrap">{{ json_encode($d['new'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}</pre>
|
|
@else
|
|
{{ Str::limit((string) $d['new'], 500) }}
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endsection
|