209 lines
9.2 KiB
HTML
209 lines
9.2 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% set active_page = "logs" %}
|
|
|
|
{% block title %}HAProxy • Logs{% endblock %}
|
|
|
|
{% block breadcrumb %}
|
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{{ url_for('main.index') }}"><i class="bi bi-house"></i></a></li>
|
|
<li class="breadcrumb-item active" aria-current="page">Access Logs</li>
|
|
</ol>
|
|
</nav>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-header bg-info text-white">
|
|
<h5 class="mb-0"><i class="bi bi-file-text me-2"></i>HAProxy Access Logs</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
|
|
<!-- Error/Info Messages -->
|
|
{% if error_message %}
|
|
<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
|
<i class="bi bi-exclamation-triangle me-2"></i>
|
|
<strong>Warning:</strong> {{ error_message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Filter Section (kompaktnie, jak było) -->
|
|
{% if logs and logs|length > 0 %}
|
|
<div class="row mb-3 g-2">
|
|
<div class="col-auto">
|
|
<input type="text" class="form-control form-control-sm" id="filter_ip" placeholder="Filter by IP">
|
|
</div>
|
|
<div class="col-auto">
|
|
<select class="form-select form-select-sm" id="filter_status" style="width: auto;">
|
|
<option value="">All Status</option>
|
|
<option value="2">2xx (Success)</option>
|
|
<option value="3">3xx (Redirect)</option>
|
|
<option value="4">4xx (Client Error)</option>
|
|
<option value="5">5xx (Server Error)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<select class="form-select form-select-sm" id="filter_method" style="width: auto;">
|
|
<option value="">All Methods</option>
|
|
<option value="GET">GET</option>
|
|
<option value="POST">POST</option>
|
|
<option value="PUT">PUT</option>
|
|
<option value="DELETE">DELETE</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="filter_threats" checked>
|
|
<label class="form-check-label" for="filter_threats" style="margin-top: 5px;">
|
|
Show Threats
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto ms-auto">
|
|
<button class="btn btn-sm btn-secondary" id="reset_filters">Reset</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Cards (kompaktnie) -->
|
|
<div class="row mb-3 g-2">
|
|
<div class="col-md-2">
|
|
<div class="card text-center" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">Total</div>
|
|
<strong id="stat_total">{{ logs|length }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="card text-center text-danger" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">Threats</div>
|
|
<strong id="stat_threats">0</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="card text-center text-success" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">2xx</div>
|
|
<strong id="stat_2xx">0</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="card text-center text-warning" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">4xx</div>
|
|
<strong id="stat_4xx">0</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="card text-center text-danger" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">5xx</div>
|
|
<strong id="stat_5xx">0</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="card text-center" style="font-size: 0.9rem;">
|
|
<div class="card-body p-2">
|
|
<div class="text-muted small">Unique IPs</div>
|
|
<strong id="stat_ips">0</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Timestamp</th>
|
|
<th>IP Address</th>
|
|
<th>HTTP Method</th>
|
|
<th>Requested URL</th>
|
|
<th>Status Code</th>
|
|
<th>Alerts</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="logs_table">
|
|
{% for entry in logs %}
|
|
<tr class="log-row"
|
|
data-ip="{{ entry['ip_address'] }}"
|
|
data-status="{{ entry['status_code'] }}"
|
|
data-method="{{ entry['http_method'] }}"
|
|
data-threats="{% if entry['xss_alert'] or entry['sql_alert'] or entry['put_method'] or entry['webshell_alert'] or entry['illegal_resource'] %}1{% else %}0{% endif %}">
|
|
<td>{{ entry['timestamp'] }}</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ entry['ip_address'] }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ entry['http_method'] }}</span>
|
|
</td>
|
|
<td class="text-truncate" style="max-width: 300px;" title="{{ entry['requested_url'] }}">
|
|
{{ entry['requested_url'] }}
|
|
</td>
|
|
<td>
|
|
<span class="badge {% if entry['status_code']|int >= 200 and entry['status_code']|int < 300 %}bg-success{% elif entry['status_code']|int >= 300 and entry['status_code']|int < 400 %}bg-secondary{% elif entry['status_code']|int >= 400 and entry['status_code']|int < 500 %}bg-warning{% else %}bg-danger{% endif %}">
|
|
{{ entry['status_code'] }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if entry['xss_alert'] %}
|
|
<span class="badge bg-danger">XSS</span>
|
|
{% endif %}
|
|
{% if entry['sql_alert'] %}
|
|
<span class="badge bg-danger">SQL</span>
|
|
{% endif %}
|
|
{% if entry['put_method'] %}
|
|
<span class="badge bg-warning">PUT</span>
|
|
{% endif %}
|
|
{% if entry['webshell_alert'] %}
|
|
<span class="badge bg-danger">Webshell</span>
|
|
{% endif %}
|
|
{% if entry['illegal_resource'] %}
|
|
<span class="badge bg-warning">403</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% elif logs %}
|
|
<!-- Logs exist but empty after filtering -->
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle me-2"></i>No log entries match your filters.
|
|
</div>
|
|
{% else %}
|
|
<!-- No logs at all -->
|
|
<div class="alert alert-danger" role="alert">
|
|
<h4 class="alert-heading"><i class="bi bi-exclamation-circle me-2"></i>No logs available</h4>
|
|
<hr>
|
|
<p class="mb-2"><strong>Possible reasons:</strong></p>
|
|
<ul class="mb-0">
|
|
<li>Log file does not exist or is not readable</li>
|
|
<li>HAProxy is not configured to log requests</li>
|
|
<li>Log file path is incorrect in configuration</li>
|
|
<li>No requests have been processed yet</li>
|
|
</ul>
|
|
<hr class="my-2">
|
|
<p class="small text-muted mb-0">Check HAProxy configuration and log file permissions.</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ url_for('static', filename='js/logs.js') }}"></script>
|
|
|
|
{% endblock %}
|