123 lines
4.0 KiB
HTML
123 lines
4.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Log Analyzer Dashboard</title>
|
|
<link rel="stylesheet" href="css/style.css">
|
|
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<header>
|
|
<h1>🔥 Log Analyzer</h1>
|
|
<span class="subtitle">Firewall & Proxy Log Dashboard</span>
|
|
</header>
|
|
|
|
<main>
|
|
<section class="upload-card">
|
|
<h2>📤 Log-Datei hochladen</h2>
|
|
<input type="file" @change="handleFile" accept=".log,.txt,.csv">
|
|
<button @click="uploadFile" :disabled="!selectedFile || uploading">
|
|
{{ uploading ? 'Hochladen...' : 'Hochladen & Parsen' }}
|
|
</button>
|
|
<div v-if="uploadMsg" :class="['msg', uploadOk ? 'ok' : 'err']">{{ uploadMsg }}</div>
|
|
</section>
|
|
|
|
<section class="stats-grid" v-if="stats.overview.total_entries > 0">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Gesamt Logs</div>
|
|
<div class="stat-value">{{ stats.overview.total_entries }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Firewall</div>
|
|
<div class="stat-value">{{ stats.overview.firewall_entries }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Proxy</div>
|
|
<div class="stat-value">{{ stats.overview.proxy_entries }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Unique Sources</div>
|
|
<div class="stat-value">{{ stats.unique_counts.unique_sources }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Unique Destinations</div>
|
|
<div class="stat-value">{{ stats.unique_counts.unique_destinations }}</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="charts-grid" v-if="stats.overview.total_entries > 0">
|
|
<div class="chart-card">
|
|
<h3>Top Quellen</h3>
|
|
<canvas id="chartSources"></canvas>
|
|
</div>
|
|
<div class="chart-card">
|
|
<h3>Top Ziele</h3>
|
|
<canvas id="chartDestinations"></canvas>
|
|
</div>
|
|
<div class="chart-card">
|
|
<h3>Top Ports</h3>
|
|
<canvas id="chartPorts"></canvas>
|
|
</div>
|
|
<div class="chart-card wide">
|
|
<h3>Zeitverlauf (pro Stunde)</h3>
|
|
<canvas id="chartTimeline"></canvas>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="tables" v-if="stats.top_urls.length > 0">
|
|
<h3>🔝 Top URLs</h3>
|
|
<table>
|
|
<thead><tr><th>URL</th><th>Hits</th></tr></thead>
|
|
<tbody>
|
|
<tr v-for="u in stats.top_urls.slice(0,20)" :key="u.url">
|
|
<td class="url">{{ u.url }}</td>
|
|
<td>{{ u.count }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="actions" v-if="stats.actions.length > 0">
|
|
<h3>⚡ Actions</h3>
|
|
<div class="action-tags">
|
|
<span v-for="a in stats.actions" :key="a.action" class="tag">
|
|
{{ a.action }}: {{ a.count }}
|
|
</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="analyze">
|
|
<h2>🤖 LLM-Analyse</h2>
|
|
<div class="analyze-controls">
|
|
<label>Log-Typ:
|
|
<select v-model="analyzeType">
|
|
<option value="all">Alle</option>
|
|
<option value="firewall">Firewall</option>
|
|
<option value="proxy">Proxy</option>
|
|
</select>
|
|
</label>
|
|
<label>Zeilen:
|
|
<input type="number" v-model.number="analyzeLimit" min="10" max="500" step="10">
|
|
</label>
|
|
<button @click="runAnalysis" :disabled="analyzing || stats.overview.total_entries === 0">
|
|
{{ analyzing ? 'Analysiere...' : 'Analyse starten' }}
|
|
</button>
|
|
</div>
|
|
<div v-if="analysisResult" class="analysis-result">
|
|
<pre>{{ analysisResult }}</pre>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<small>Log Analyzer Backend — <a href="/docs">API Docs</a></small>
|
|
</footer>
|
|
</div>
|
|
<script src="js/app.js"></script>
|
|
</body>
|
|
</html>
|