This commit is contained in:
CaffeineFueled 2025-04-10 21:40:30 +02:00
commit 0e3323b7ab
13 changed files with 1625 additions and 0 deletions

170
templates/base.html Normal file
View file

@ -0,0 +1,170 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>VPN Log Viewer</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1, h2, h3 {
color: #0066cc;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
padding: 8px 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.filter-section {
margin: 20px 0;
padding: 10px;
background-color: #f8f8f8;
border-radius: 5px;
}
.filter-form {
display: flex;
flex-direction: column;
}
.filter-row {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-end;
}
.filter-item {
display: flex;
flex-direction: column;
min-width: 200px;
}
.filter-item label {
margin-bottom: 5px;
font-weight: bold;
}
.filter-item select, .filter-item input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
input[type="datetime-local"] {
min-width: 220px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px;
}
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
font-weight: normal;
margin-top: 10px;
}
.checkbox-label input[type="checkbox"] {
margin-right: 8px;
cursor: pointer;
}
.filter-button, .reset-button {
padding: 8px 16px;
background-color: #0066cc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
text-align: center;
}
.reset-button {
background-color: #666;
}
.filter-button:hover, .reset-button:hover {
opacity: 0.9;
}
.table-responsive {
overflow-x: auto;
max-width: 100%;
}
.log-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9em;
}
.log-table th {
position: sticky;
top: 0;
background-color: #f2f2f2;
z-index: 10;
}
.api-info {
margin: 15px 0;
padding: 15px;
background-color: #f8f8f8;
border-radius: 5px;
}
.api-info h3 {
margin-top: 0;
margin-bottom: 10px;
}
.api-info p {
margin: 5px 0;
}
.navigation-links {
margin: 15px 0;
}
.nav-link {
display: inline-block;
padding: 8px 16px;
background-color: #0066cc;
color: white;
border-radius: 4px;
text-decoration: none;
margin-right: 10px;
}
.nav-link:hover {
background-color: #0055aa;
text-decoration: none;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
white-space: pre-wrap;
font-family: monospace;
}
</style>
</head>
<body>
<header>
<h1><a href="/">VPN Log Viewer</a></h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p><small>VPN Log Viewer - Logs updated every 5 minutes</small></p>
</footer>
</body>
</html>

133
templates/combined.html Normal file
View file

@ -0,0 +1,133 @@
{% extends "base.html" %}
{% block content %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const defaultTimeCheckbox = document.getElementById('use-default-time');
const startDateInput = document.getElementById('start-date');
const endDateInput = document.getElementById('end-date');
// Function to toggle date fields enabled/disabled based on checkbox
function toggleDateFields() {
if (defaultTimeCheckbox.checked) {
startDateInput.disabled = true;
endDateInput.disabled = true;
} else {
startDateInput.disabled = false;
endDateInput.disabled = false;
}
}
// Initial setup
toggleDateFields();
// Set up event listener
defaultTimeCheckbox.addEventListener('change', toggleDateFields);
});
</script>
<h2>Combined VPN Sessions View</h2>
<div class="navigation-links">
<a href="/" class="nav-link">Back to Log Files</a>
</div>
<div class="api-info">
<h3>API Endpoints</h3>
<p>Get combined data via API: <a href="/api/all-entries" target="_blank">/api/all-entries</a></p>
<p>Filter by gateway: <a href="/api/all-entries?gateway={{ selected_gateway }}" target="_blank">/api/all-entries?gateway={{ selected_gateway }}</a></p>
<p>Filter by date range: <a href="/api/all-entries?start_date={{ start_date }}&end_date={{ end_date }}" target="_blank">/api/all-entries?start_date={{ start_date }}&end_date={{ end_date }}</a></p>
<p>Use default time (last 30 min): <a href="/api/all-entries?use_default_time" target="_blank">/api/all-entries?use_default_time</a></p>
<p>Search: <a href="/api/all-entries?search={{ search_term }}" target="_blank">/api/all-entries?search={{ search_term }}</a></p>
<p>Combined filters: <a href="/api/all-entries?gateway={{ selected_gateway }}&start_date={{ start_date | replace("T", "T") }}&end_date={{ end_date | replace("T", "T") }}&search={{ search_term }}" target="_blank">/api/all-entries?gateway={{ selected_gateway }}&start_date={{ start_date }}&end_date={{ end_date }}&search={{ search_term }}</a></p>
<p><small>Note: For API calls, date/time must be in ISO format (YYYY-MM-DDThh:mm:ss)</small></p>
</div>
<div class="filter-section">
<form method="get" class="filter-form">
<div class="filter-row">
<div class="filter-item">
<label for="gateway-select">Gateway:</label>
<select id="gateway-select" name="gateway">
<option value="">All Gateways</option>
{% for gateway in gateways %}
<option value="{{ gateway }}" {% if gateway == selected_gateway %}selected{% endif %}>{{ gateway }}</option>
{% endfor %}
</select>
</div>
<div class="filter-item">
<label for="start-date">From Date/Time:</label>
<input type="datetime-local" id="start-date" name="start_date" value="{{ start_date }}" step="60">
</div>
<div class="filter-item">
<label for="end-date">To Date/Time:</label>
<input type="datetime-local" id="end-date" name="end_date" value="{{ end_date }}" step="60">
</div>
<div class="filter-item">
<label for="search-input">Search:</label>
<input type="text" id="search-input" name="search" value="{{ search_term or '' }}" placeholder="Search in all fields...">
</div>
<div class="filter-item">
<label for="use-default-time" class="checkbox-label">
<input type="checkbox" id="use-default-time" name="use_default_time" {% if not start_date and not end_date %}checked{% endif %}>
Use default time range (last 30 min)
</label>
</div>
<div class="filter-item">
<button type="submit" class="filter-button">Apply Filters</button>
<a href="/combined" class="reset-button">Reset</a>
</div>
</div>
</form>
</div>
<div class="table-responsive">
<table class="log-table">
<thead>
<tr>
{% for col in columns %}
<th>
{% if col.startswith('_') %}
{{ col[1:] | capitalize }}
{% else %}
{{ col }}
{% endif %}
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for col in columns %}
<td>
{% if col == '_source_file' %}
<a href="/view/{{ row[col] }}">{{ row[col] }}</a>
{% elif col == '_timestamp' and row[col] %}
{{ row[col].strftime('%Y-%m-%d %H:%M:%S UTC') }}
{% else %}
{% if row[col] is string %}
{{ row[col].strip() }}
{% elif row[col] is none %}
<!-- Empty for None values -->
{% else %}
{{ row[col] }}
{% endif %}
{% endif %}
</td>
{% endfor %}
</tr>
{% else %}
<tr>
<td colspan="{{ columns|length }}">No matching logs found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

53
templates/index.html Normal file
View file

@ -0,0 +1,53 @@
{% extends "base.html" %}
{% block content %}
<h2>VPN Session Logs</h2>
<div class="navigation-links">
<a href="/combined" class="nav-link">View Combined Logs</a>
</div>
<div class="api-info">
<h3>API Endpoints</h3>
<p>Get all logs: <a href="/api/logs" target="_blank">/api/logs</a></p>
<p>Filter logs by gateway: <a href="/api/logs?gateway={{ selected_gateway }}" target="_blank">/api/logs?gateway={{ selected_gateway }}</a></p>
<p>Get all gateways: <a href="/api/gateways" target="_blank">/api/gateways</a></p>
<p>Get log content: <a href="/api/log-content/{{ logs[0].filename if logs else 'filename.logs' }}" target="_blank">/api/log-content/{filename}</a></p>
</div>
<div class="filter-section">
<form method="get">
<label for="gateway-select">Filter by Gateway:</label>
<select id="gateway-select" onchange="this.form.submit()" name="gateway">
<option value="">All Gateways</option>
{% for gateway in gateways %}
<option value="{{ gateway }}" {% if gateway == selected_gateway %}selected{% endif %}>{{ gateway }}</option>
{% endfor %}
</select>
</form>
</div>
<table>
<thead>
<tr>
<th>Gateway</th>
<th>Timestamp</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log.gateway }}</td>
<td>{{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S UTC') }}</td>
<td><a href="/view/{{ log.filename }}">View</a></td>
</tr>
{% else %}
<tr>
<td colspan="3">No logs found</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

99
templates/view.html Normal file
View file

@ -0,0 +1,99 @@
{% extends "base.html" %}
{% block content %}
<h2>VPN Session Details</h2>
<p><a href="/">&larr; Back to all logs</a></p>
<div class="api-info">
<h3>API Endpoints</h3>
<p>Get log content via API: <a href="/api/log-content/{{ filename }}" target="_blank">/api/log-content/{{ filename }}</a></p>
</div>
<div class="log-info">
<p><strong>Gateway:</strong> {{ gateway }}</p>
<p><strong>Timestamp:</strong> {{ timestamp.strftime('%Y-%m-%d %H:%M:%S UTC') if timestamp else 'Unknown' }}</p>
<p><strong>Filename:</strong> {{ filename }}</p>
</div>
<h3>Log Content</h3>
{% if parsed_rows %}
<div class="table-container">
<table class="log-table">
<thead>
<tr>
{% for col in columns %}
<th>{{ col }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in parsed_rows %}
<tr>
{% if 'Index' in columns %}
<td>{{ row.index }}</td>
{% endif %}
{% if 'User' in columns %}
<td>{{ row.user.strip() if row.user else "" }}</td>
{% endif %}
{% if 'Group' in columns %}
<td>{{ row.group.strip() if row.group else "" }}</td>
{% endif %}
{# VPN Login Users fields #}
{% if 'Auth Type' in columns %}
<td>{{ row.auth_type.strip() if row.auth_type else "" }}</td>
{% endif %}
{% if 'Timeout' in columns %}
<td>{{ row.timeout.strip() if row.timeout else "" }}</td>
{% endif %}
{% if 'Auth-Timeout' in columns %}
<td>{{ row.auth_timeout.strip() if row.auth_timeout else "" }}</td>
{% endif %}
{% if 'From' in columns %}
<td>{{ row.from_ip.strip() if row.from_ip else "" }}</td>
{% endif %}
{% if 'HTTP in/out' in columns %}
<td>{{ row.http.strip() if row.http else "" }}</td>
{% endif %}
{% if 'HTTPS in/out' in columns %}
<td>{{ row.https.strip() if row.https else "" }}</td>
{% endif %}
{% if 'Two-factor Auth' in columns %}
<td>{{ row.two_factor.strip() if row.two_factor else "" }}</td>
{% endif %}
{# VPN Sessions fields #}
{% if 'Source IP' in columns %}
<td>{{ row.source_ip.strip() if row.source_ip else "" }}</td>
{% endif %}
{% if 'Duration' in columns %}
<td>{{ row.duration.strip() if row.duration else "" }}</td>
{% endif %}
{% if 'I/O Bytes' in columns %}
<td>{{ row.io_bytes.strip() if row.io_bytes else "" }}</td>
{% endif %}
{% if 'Tunnel/Dest IP' in columns %}
<td>{{ (row.tunnel_dest_ip.strip() if row.tunnel_dest_ip else "") }}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<pre>{{ raw_content }}</pre>
{% endif %}
{% endblock %}