166 lines
4.8 KiB
JavaScript
166 lines
4.8 KiB
JavaScript
const { createApp } = Vue;
|
|
|
|
const api = axios.create({ baseURL: '/api' });
|
|
|
|
const chartColors = [
|
|
'#38bdf8','#22d3ee','#818cf8','#c084fc','#f472b6','#fb7185','#34d399','#a3e635','#fbbf24','#f87171'
|
|
];
|
|
|
|
function destroyChart(chart) { if(chart) { chart.destroy(); } }
|
|
|
|
function makeBarConfig(label, labels, data) {
|
|
return {
|
|
type: 'bar',
|
|
data: {
|
|
labels,
|
|
datasets: [{
|
|
label,
|
|
data,
|
|
backgroundColor: chartColors,
|
|
borderRadius: 4,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: { beginAtZero: true, grid: { color: '#27303f' }, ticks: { color: '#94a3b8' } },
|
|
x: { grid: { display: false }, ticks: { color: '#94a3b8' } }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function makeLineConfig(labels, data) {
|
|
return {
|
|
type: 'line',
|
|
data: {
|
|
labels,
|
|
datasets: [{
|
|
label: 'Events',
|
|
data,
|
|
borderColor: '#38bdf8',
|
|
backgroundColor: 'rgba(56,189,248,0.15)',
|
|
fill: true,
|
|
tension: 0.3,
|
|
pointRadius: 3,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: { beginAtZero: true, grid: { color: '#27303f' }, ticks: { color: '#94a3b8' } },
|
|
x: { grid: { color: '#27303f' }, ticks: { color: '#94a3b8' } }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
createApp({
|
|
data() {
|
|
return {
|
|
selectedFile: null,
|
|
uploading: false,
|
|
uploadMsg: '',
|
|
uploadOk: false,
|
|
stats: {
|
|
overview: { total_entries: 0, firewall_entries: 0, proxy_entries: 0 },
|
|
top_sources: [],
|
|
top_destinations: [],
|
|
top_ports: [],
|
|
top_urls: [],
|
|
actions: [],
|
|
timeline: [],
|
|
unique_counts: { unique_sources: 0, unique_destinations: 0 }
|
|
},
|
|
analyzeType: 'all',
|
|
analyzeLimit: 100,
|
|
analyzing: false,
|
|
analysisResult: '',
|
|
charts: {}
|
|
};
|
|
},
|
|
mounted() {
|
|
this.fetchStats();
|
|
setInterval(() => this.fetchStats(), 15000);
|
|
},
|
|
methods: {
|
|
handleFile(e) {
|
|
this.selectedFile = e.target.files[0];
|
|
},
|
|
async uploadFile() {
|
|
if (!this.selectedFile) return;
|
|
this.uploading = true;
|
|
this.uploadMsg = '';
|
|
const form = new FormData();
|
|
form.append('file', this.selectedFile);
|
|
try {
|
|
const res = await api.post('/upload', form, { headers: { 'Content-Type': 'multipart/form-data' } });
|
|
this.uploadOk = true;
|
|
this.uploadMsg = res.data.message;
|
|
this.selectedFile = null;
|
|
this.$el.querySelector('input[type=file]').value = '';
|
|
this.fetchStats();
|
|
} catch (e) {
|
|
this.uploadOk = false;
|
|
this.uploadMsg = e.response?.data?.detail || 'Upload fehlgeschlagen.';
|
|
} finally {
|
|
this.uploading = false;
|
|
}
|
|
},
|
|
async fetchStats() {
|
|
try {
|
|
const res = await api.get('/stats', { params: { limit: 20 } });
|
|
this.stats = res.data;
|
|
this.$nextTick(() => this.renderCharts());
|
|
} catch (e) {
|
|
console.error('Stats fetch error', e);
|
|
}
|
|
},
|
|
renderCharts() {
|
|
const s = this.stats;
|
|
|
|
destroyChart(this.charts.sources);
|
|
if (s.top_sources.length) {
|
|
const cfg = makeBarConfig('Hits', s.top_sources.map(x=>x.source_ip||'N/A'), s.top_sources.map(x=>x.count));
|
|
this.charts.sources = new Chart(document.getElementById('chartSources'), cfg);
|
|
}
|
|
|
|
destroyChart(this.charts.dests);
|
|
if (s.top_destinations.length) {
|
|
const cfg = makeBarConfig('Hits', s.top_destinations.map(x=>x.destination_ip||'N/A'), s.top_destinations.map(x=>x.count));
|
|
this.charts.dests = new Chart(document.getElementById('chartDestinations'), cfg);
|
|
}
|
|
|
|
destroyChart(this.charts.ports);
|
|
if (s.top_ports.length) {
|
|
const cfg = makeBarConfig('Hits', s.top_ports.map(x=>x.destination_port||'N/A'), s.top_ports.map(x=>x.count));
|
|
this.charts.ports = new Chart(document.getElementById('chartPorts'), cfg);
|
|
}
|
|
|
|
destroyChart(this.charts.timeline);
|
|
if (s.timeline.length) {
|
|
const cfg = makeLineConfig(s.timeline.map(x=>x.time_bucket), s.timeline.map(x=>x.count));
|
|
this.charts.timeline = new Chart(document.getElementById('chartTimeline'), cfg);
|
|
}
|
|
},
|
|
async runAnalysis() {
|
|
this.analyzing = true;
|
|
this.analysisResult = '';
|
|
try {
|
|
const res = await api.post('/analyze', null, {
|
|
params: { log_type: this.analyzeType, limit: this.analyzeLimit }
|
|
});
|
|
this.analysisResult = res.data.analysis;
|
|
} catch (e) {
|
|
this.analysisResult = e.response?.data?.detail || 'Analyse fehlgeschlagen.';
|
|
} finally {
|
|
this.analyzing = false;
|
|
}
|
|
}
|
|
}
|
|
}).mount('#app');
|