162 lines
7.2 KiB
PHP
162 lines
7.2 KiB
PHP
<?php
|
|
$pageTitle = "AI Knowledge Base";
|
|
ob_start();
|
|
?>
|
|
|
|
<div class="mb-6">
|
|
<h1 class="text-2xl font-bold text-slate-800"><i class="fas fa-brain text-indigo-600 mr-2"></i> AI Knowledge Base</h1>
|
|
<p class="text-slate-500 mt-1">History of AI-generated remediation guidelines. These insights are cached to accelerate future incident responses.</p>
|
|
</div>
|
|
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table id="aiHistoryTable" class="w-full text-left border-collapse table-fixed">
|
|
<thead>
|
|
<tr class="bg-slate-50 text-slate-600 border-b border-slate-200 text-sm">
|
|
<th class="px-5 py-4 font-semibold w-64"><?= __('Rule Info') ?></th>
|
|
<th class="px-5 py-4 font-semibold"><?= __('AI Recommendation') ?></th>
|
|
<th class="px-5 py-4 font-semibold text-center w-40"><?= __('Cached Date') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100 text-sm">
|
|
<?php foreach ($histories as $h): ?>
|
|
<?php
|
|
// Extract rule groups from raw_data JSON
|
|
$groups = [];
|
|
if (isset($h['raw_data'])) {
|
|
$raw = json_decode($h['raw_data'], true);
|
|
if (isset($raw['rule']['groups'])) {
|
|
$groups = $raw['rule']['groups'];
|
|
}
|
|
}
|
|
$desc = htmlspecialchars($h['rule_desc'] ?? 'Unknown Rule Description');
|
|
?>
|
|
<tr class="hover:bg-slate-50/50 transition-colors group">
|
|
<td class="px-5 py-4">
|
|
<div class="font-mono text-xs text-indigo-600 font-bold mb-1">ID: <?= htmlspecialchars($h['rule_id']) ?></div>
|
|
<div class="text-slate-700 font-medium text-xs mb-2 leading-tight"><?= $desc ?></div>
|
|
<div class="flex flex-wrap gap-1">
|
|
<?php foreach($groups as $g): $g = trim($g); if(empty($g)) continue; ?>
|
|
<span class="bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded text-[10px] uppercase font-semibold"><?= htmlspecialchars($g) ?></span>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</td>
|
|
<td class="px-5 py-4">
|
|
<div class="bg-indigo-50/50 border border-indigo-100 rounded-lg p-3 text-indigo-900 text-sm shadow-sm relative whitespace-normal break-words w-full" id="rec-container-<?= $h['rule_id'] ?>">
|
|
<div class="overflow-hidden line-clamp-3 max-h-[4.5rem]">
|
|
<?= $h['recommendation'] ?>
|
|
</div>
|
|
<div class="mt-2 pt-2 border-t border-indigo-100/50 flex justify-between items-center">
|
|
<button onclick="regenerateAI(<?= $h['rule_id'] ?>, '<?= addslashes($desc) ?>')" class="text-slate-400 hover:text-indigo-600 text-xs font-bold uppercase transition" id="regen-btn-<?= $h['rule_id'] ?>">
|
|
<i class="fas fa-sync-alt mr-1"></i> Regenerate
|
|
</button>
|
|
<button onclick="viewAIRecommendation(<?= $h['rule_id'] ?>)" class="text-indigo-600 hover:text-indigo-800 text-xs font-bold uppercase transition">
|
|
<i class="fas fa-expand-alt mr-1"></i> Read Full Analysis
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<template id="ai-rec-<?= $h['rule_id'] ?>">
|
|
<?= $h['recommendation'] ?>
|
|
</template>
|
|
</td>
|
|
<td class="px-5 py-4 text-center text-slate-500 whitespace-nowrap">
|
|
<?= date('Y-m-d H:i', strtotime($h['created_at'])) ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($histories)): ?>
|
|
<tr>
|
|
<td colspan="3" class="px-5 py-8 text-center text-slate-400">
|
|
<i class="fas fa-inbox text-3xl mb-3 block"></i>
|
|
<?= __('No AI insights cached yet.') ?>
|
|
</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DataTables setup -->
|
|
<script>
|
|
window.regenerateAI = function(ruleId, ruleDesc) {
|
|
let btn = document.getElementById('regen-btn-' + ruleId);
|
|
let container = document.getElementById('rec-container-' + ruleId);
|
|
let tmpl = document.getElementById('ai-rec-' + ruleId);
|
|
|
|
let originalIcon = btn.innerHTML;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i> Thinking...';
|
|
btn.disabled = true;
|
|
|
|
$.ajax({
|
|
url: '<?= $baseUrl ?>/api/ai-remediation',
|
|
method: 'POST',
|
|
data: {
|
|
rule_id: ruleId,
|
|
rule_desc: ruleDesc,
|
|
level: 0,
|
|
force: 'true'
|
|
},
|
|
success: function(res) {
|
|
btn.innerHTML = originalIcon;
|
|
btn.disabled = false;
|
|
if (res.status === 'success') {
|
|
tmpl.innerHTML = res.html;
|
|
container.querySelector('.line-clamp-3').innerHTML = res.html;
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Regenerated!',
|
|
text: 'The AI insight has been updated.',
|
|
timer: 1500,
|
|
showConfirmButton: false
|
|
});
|
|
} else {
|
|
Swal.fire('Error', res.message || 'Failed to regenerate.', 'error');
|
|
}
|
|
},
|
|
error: function() {
|
|
btn.innerHTML = originalIcon;
|
|
btn.disabled = false;
|
|
Swal.fire('Error', 'Connection failed.', 'error');
|
|
}
|
|
});
|
|
};
|
|
|
|
window.viewAIRecommendation = function(ruleId) {
|
|
let template = document.getElementById('ai-rec-' + ruleId);
|
|
if (!template) return;
|
|
|
|
let rawHtml = template.innerHTML;
|
|
Swal.fire({
|
|
title: '<div class="text-indigo-800 text-lg border-b border-indigo-200 pb-2"><i class="fas fa-robot mr-2"></i> AI Investigation & Remediation</div>',
|
|
html: `<div class="text-left text-sm text-indigo-900 bg-indigo-50 p-5 rounded-lg border border-indigo-100 leading-relaxed overflow-x-auto shadow-inner mt-2">${rawHtml}</div>`,
|
|
width: '800px',
|
|
showCloseButton: true,
|
|
showConfirmButton: false,
|
|
customClass: {
|
|
container: 'font-sans'
|
|
}
|
|
});
|
|
};
|
|
|
|
$(document).ready(function() {
|
|
if($('#aiHistoryTable tbody tr td').length > 1) {
|
|
$('#aiHistoryTable').DataTable({
|
|
responsive: true,
|
|
order: [[2, 'desc']],
|
|
pageLength: 10,
|
|
language: {
|
|
search: "",
|
|
searchPlaceholder: "Search insights..."
|
|
},
|
|
dom: '<"flex justify-between items-center mb-4 px-5 pt-4"lf>rt<"flex justify-between items-center mt-4 px-5 pb-4"ip>'
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<?php
|
|
$content = ob_get_clean();
|
|
require 'layout.php';
|
|
?>
|