Files
log-analyzer-backend/frontend/js/app.js
T

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');