163 lines
7.5 KiB
HTML
163 lines
7.5 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% set active_page = "logs" %}
|
|
|
|
{% block title %}HAProxy • Access Logs{% endblock %}
|
|
|
|
{% block breadcrumb %}Access Logs{% 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 & Security Analysis</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
|
|
{% if logs %}
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Total Requests</h6>
|
|
<div class="fs-3 fw-bold text-primary">{{ logs|length }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Threats Detected</h6>
|
|
<div class="fs-3 fw-bold text-danger">
|
|
{{ logs|selectattr('is_threat')|list|length }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Unique IPs</h6>
|
|
<div class="fs-3 fw-bold text-warning">
|
|
{{ logs|map(attribute='ip_address')|unique|list|length }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Success Rate</h6>
|
|
<div class="fs-3 fw-bold text-success">
|
|
{% set success_count = logs|selectattr('status_code')|selectattr('status_code', 'ge', 200)|selectattr('status_code', 'lt', 300)|list|length %}
|
|
{{ ((success_count / logs|length * 100)|round(1)) if logs else 0 }}%
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="card mb-4 bg-light">
|
|
<div class="card-body">
|
|
<h6 class="mb-3"><i class="bi bi-funnel me-2"></i>Filters</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label small">Status Code</label>
|
|
<select class="form-select form-select-sm" id="filter_status">
|
|
<option value="">All</option>
|
|
{% for log in logs %}
|
|
<option value="{{ log.status_code }}">{{ log.status_code }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">Threat Level</label>
|
|
<select class="form-select form-select-sm" id="filter_threat">
|
|
<option value="">All</option>
|
|
<option value="danger">Danger</option>
|
|
<option value="warning">Warning</option>
|
|
<option value="info">Info</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">HTTP Method</label>
|
|
<select class="form-select form-select-sm" id="filter_method">
|
|
<option value="">All</option>
|
|
{% set methods = logs|map(attribute='http_method')|unique %}
|
|
{% for method in methods %}
|
|
<option value="{{ method }}">{{ method }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">Show Threats Only</label>
|
|
<div class="form-check mt-2">
|
|
<input class="form-check-input" type="checkbox" id="filter_threats_only">
|
|
<label class="form-check-label" for="filter_threats_only">
|
|
Threats
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Table -->
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover table-sm">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Timestamp</th>
|
|
<th>IP Address</th>
|
|
<th>Method</th>
|
|
<th>URL</th>
|
|
<th>Status</th>
|
|
<th>Threats</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="logs_table_body">
|
|
{% for log in logs %}
|
|
<tr class="log-row" data-status="{{ log.status_code }}" data-threat="{{ log.threat_level }}" data-method="{{ log.http_method }}" data-threat-count="{{ 1 if log.is_threat else 0 }}">
|
|
<td class="small">{{ log.timestamp }}</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ log.ip_address }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ log.http_method }}</span>
|
|
</td>
|
|
<td class="text-truncate" style="max-width: 300px;" title="{{ log.requested_url }}">
|
|
{{ log.requested_url }}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ log.status_category }}">{{ log.status_code }}</span>
|
|
</td>
|
|
<td>
|
|
{% if log.is_threat %}
|
|
{% for threat in log.threats %}
|
|
<span class="badge bg-{{ log.threat_level }} me-1">{{ threat }}</span>
|
|
{% endfor %}
|
|
{% else %}
|
|
<span class="text-muted small">—</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% else %}
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle me-2"></i>No log entries found.
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ url_for('static', filename='js/logs.js') }}"></script>
|
|
|
|
{% endblock %}
|