Files
krawl.es/src/templates/jinja2/dashboard/index.html

171 lines
7.4 KiB
HTML

{% extends "base.html" %}
{% block content %}
<div class="container" x-data="dashboardApp()" x-init="init()">
{# GitHub logo #}
<a href="https://github.com/BlessedRebuS/Krawl" target="_blank" class="github-logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
<span class="github-logo-text">Krawl</span>
</a>
{# Banlist export dropdown - Alpine.js #}
<div class="download-section">
<div class="banlist-dropdown" @click.outside="banlistOpen = false">
<button class="banlist-dropdown-btn" @click="banlistOpen = !banlistOpen">
Export IPs Banlist ▾
</button>
<div class="banlist-dropdown-menu" :class="{ 'show': banlistOpen }">
<a :href="dashboardPath + '/api/get_banlist?fwtype=raw'" download>
<span class="banlist-icon">📄</span> Raw IPs List
</a>
<a :href="dashboardPath + '/api/get_banlist?fwtype=iptables'" download>
<span class="banlist-icon">🔥</span> IPTables Rules
</a>
</div>
</div>
</div>
<h1>Krawl Dashboard</h1>
{# Stats cards - server-rendered #}
{% include "dashboard/partials/stats_cards.html" %}
{# Tab navigation - Alpine.js #}
<div class="tabs-container">
<a class="tab-button" :class="{ active: tab === 'overview' }" @click.prevent="switchToOverview()" href="#overview">Overview</a>
<a class="tab-button" :class="{ active: tab === 'attacks' }" @click.prevent="switchToAttacks()" href="#ip-stats">Attacks</a>
<a class="tab-button" :class="{ active: tab === 'ip-insight', disabled: !insightIp }" @click.prevent="insightIp && switchToIpInsight()" href="#ip-insight">
IP Insight<span x-show="insightIp" x-text="' (' + insightIp + ')'"></span>
</a>
</div>
{# ==================== OVERVIEW TAB ==================== #}
<div x-show="tab === 'overview'" x-init="$nextTick(() => { if (!mapInitialized && typeof initializeAttackerMap === 'function') { initializeAttackerMap(); mapInitialized = true; } })">
{# Map section #}
{% include "dashboard/partials/map_section.html" %}
{# Top IPs + Top User-Agents side by side #}
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
<div class="table-container" style="flex: 1; min-width: 300px;">
<h2>Top IP Addresses</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/top-ips?page=1"
hx-trigger="load"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
<div class="table-container" style="flex: 1; min-width: 300px;">
<h2>Top User-Agents</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/top-ua?page=1"
hx-trigger="load"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
</div>
{# Top Paths #}
<div class="table-container">
<h2>Top Paths</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/top-paths?page=1"
hx-trigger="load"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{# Suspicious Activity - server-rendered #}
{% include "dashboard/partials/suspicious_table.html" %}
</div>
{# ==================== ATTACKS TAB ==================== #}
<div x-show="tab === 'attacks'" x-cloak>
{# Attackers table - HTMX loaded #}
<div class="table-container alert-section">
<h2>Attackers by Total Requests</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/attackers?page=1"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{# Credentials table #}
<div class="table-container alert-section">
<h2>Captured Credentials</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/credentials?page=1"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{# Honeypot Triggers - HTMX loaded #}
<div class="table-container alert-section">
<h2>Honeypot Triggers by IP</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/honeypot?page=1"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{# Attack Types table #}
<div class="table-container alert-section">
<h2>Detected Attack Types</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/attacks?page=1"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{# Charts + Patterns side by side #}
<div class="charts-container">
<div class="table-container chart-section">
<h2>Most Recurring Attack Types</h2>
<div class="chart-wrapper">
<canvas id="attack-types-chart"></canvas>
</div>
</div>
<div class="table-container chart-section">
<h2>Most Recurring Attack Patterns</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/patterns?page=1"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
</div>
</div>
{# ==================== IP INSIGHT TAB ==================== #}
<div x-show="tab === 'ip-insight'" x-cloak>
{# IP Insight content - loaded via HTMX when IP is selected #}
<div id="ip-insight-container">
<template x-if="!insightIp">
<div class="table-container" style="text-align: center; padding: 60px 20px;">
<p style="color: #8b949e; font-size: 16px;">Select an IP address from any table to view detailed insights.</p>
</div>
</template>
<div x-show="insightIp" id="ip-insight-htmx-container"></div>
</div>
</div>
{# Raw request modal - Alpine.js #}
{% include "dashboard/partials/raw_request_modal.html" %}
</div>
{% endblock %}