323 lines
17 KiB
HTML
323 lines
17 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Logs - Email Server{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.log-entry {
|
|
border-left: 4px solid var(--bs-border-color);
|
|
padding: 0.75rem;
|
|
margin-bottom: 0.5rem;
|
|
background-color: var(--bs-body-bg);
|
|
border-radius: 0.375rem;
|
|
}
|
|
.log-email { border-left-color: #0d6efd; }
|
|
.log-auth { border-left-color: #198754; }
|
|
.log-error { border-left-color: #dc3545; }
|
|
.log-success { border-left-color: #198754; }
|
|
.log-failed { border-left-color: #dc3545; }
|
|
|
|
.log-content {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.875rem;
|
|
background-color: var(--bs-gray-100);
|
|
border-radius: 0.25rem;
|
|
padding: 0.5rem;
|
|
max-height: 150px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>
|
|
<i class="bi bi-journal-text me-2"></i>
|
|
Emails Log
|
|
</h2>
|
|
<div class="btn-group">
|
|
<a href="{{ url_for('email.logs', type='all') }}"
|
|
class="btn {{ 'btn-primary' if filter_type == 'all' else 'btn-outline-primary' }}">
|
|
<i class="bi bi-list-ul me-1"></i>
|
|
All Logs
|
|
</a>
|
|
<a href="{{ url_for('email.logs', type='emails') }}"
|
|
class="btn {{ 'btn-primary' if filter_type == 'emails' else 'btn-outline-primary' }}">
|
|
<i class="bi bi-envelope me-1"></i>
|
|
Email Logs
|
|
</a>
|
|
<a href="{{ url_for('email.logs', type='auth') }}"
|
|
class="btn {{ 'btn-primary' if filter_type == 'auth' else 'btn-outline-primary' }}">
|
|
<i class="bi bi-shield-lock me-1"></i>
|
|
Auth Logs
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
{% if filter_type == 'emails' %}
|
|
<i class="bi bi-envelope me-2"></i>
|
|
Email Activity
|
|
{% elif filter_type == 'auth' %}
|
|
<i class="bi bi-shield-lock me-2"></i>
|
|
Authentication Activity
|
|
{% else %}
|
|
<i class="bi bi-list-ul me-2"></i>
|
|
Recent Activity
|
|
{% endif %}
|
|
</h5>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="refreshLogs()">
|
|
<i class="bi bi-arrow-clockwise me-1"></i>
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if logs %}
|
|
{% if filter_type == 'all' %}
|
|
<!-- Combined logs view -->
|
|
{% for log_entry in logs %}
|
|
{% if log_entry.type == 'email' %}
|
|
{% set log = log_entry.data %}
|
|
<div class="log-entry log-email log-{{ 'success' if log.status == 'relayed' else 'failed' }}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div>
|
|
<span class="badge bg-primary me-2">EMAIL</span>
|
|
<strong>{{ log.mail_from }}</strong> → {{ log.rcpt_tos }}
|
|
{% if log.dkim_signed %}
|
|
<span class="badge bg-success ms-2">
|
|
<i class="bi bi-shield-check me-1"></i>
|
|
DKIM
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
<small class="text-muted">{{ log.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</small>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<strong>Status:</strong>
|
|
{% if log.status == 'relayed' %}
|
|
<span class="text-success">Sent Successfully</span>
|
|
{% else %}
|
|
<span class="text-danger">Failed</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<strong>Message ID:</strong> <code>{{ log.message_id }}</code>
|
|
</div>
|
|
</div>
|
|
{% if log.subject %}
|
|
<div class="mt-2">
|
|
<strong>Subject:</strong> {{ log.subject }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
{% set log = log_entry.data %}
|
|
<div class="log-entry log-auth log-{{ 'success' if log.success else 'failed' }}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div>
|
|
<span class="badge bg-success me-2">AUTH</span>
|
|
<strong>{{ log.identifier }}</strong>
|
|
<span class="badge {{ 'bg-success' if log.success else 'bg-danger' }} ms-2">
|
|
{{ 'Success' if log.success else 'Failed' }}
|
|
</span>
|
|
</div>
|
|
<small class="text-muted">{{ log.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</small>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<strong>Type:</strong> {{ log.auth_type.upper() }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<strong>IP:</strong> <code>{{ log.ip_address or 'N/A' }}</code>
|
|
</div>
|
|
</div>
|
|
{% if log.message %}
|
|
<div class="mt-2">
|
|
<strong>Message:</strong> {{ log.message }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% elif filter_type == 'emails' %}
|
|
<!-- Email logs only -->
|
|
{% for log in logs %}
|
|
<div class="log-entry log-email log-{{ 'success' if log.status == 'relayed' else 'failed' }}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div>
|
|
<strong>{{ log.mail_from }}</strong> → {{ log.rcpt_tos }}
|
|
{% if log.dkim_signed %}
|
|
<span class="badge bg-success ms-2">
|
|
<i class="bi bi-shield-check me-1"></i>
|
|
DKIM
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
<small class="text-muted">{{ log.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</small>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<strong>Status:</strong>
|
|
{% if log.status == 'relayed' %}
|
|
<span class="text-success">Sent</span>
|
|
{% else %}
|
|
<span class="text-danger">Failed</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="col-md-3">
|
|
<strong>Peer:</strong> <code>{{ log.peer }}</code>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<strong>Message ID:</strong> <code>{{ log.message_id }}</code>
|
|
</div>
|
|
</div>
|
|
{% if log.subject %}
|
|
<div class="mt-2">
|
|
<strong>Subject:</strong> {{ log.subject }}
|
|
</div>
|
|
{% endif %}
|
|
{% if log.content and log.content|length > 50 %}
|
|
<div class="mt-2">
|
|
<button class="btn btn-outline-secondary btn-sm" type="button"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#content-{{ log.id }}">
|
|
<i class="bi bi-eye me-1"></i>
|
|
View Content
|
|
</button>
|
|
<div class="collapse mt-2" id="content-{{ log.id }}">
|
|
<div class="log-content">{{ log.content }}</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<!-- Auth logs only -->
|
|
{% for log in logs %}
|
|
<div class="log-entry log-auth log-{{ 'success' if log.success else 'failed' }}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div>
|
|
<strong>{{ log.identifier }}</strong>
|
|
<span class="badge {{ 'bg-success' if log.success else 'bg-danger' }} ms-2">
|
|
{{ 'Success' if log.success else 'Failed' }}
|
|
</span>
|
|
</div>
|
|
<small class="text-muted">{{ log.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</small>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<strong>Type:</strong> {{ log.auth_type.upper() }}
|
|
</div>
|
|
<div class="col-md-4">
|
|
<strong>IP:</strong> <code>{{ log.ip_address or 'N/A' }}</code>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<strong>Result:</strong>
|
|
{% if log.success %}
|
|
<span class="text-success">Authenticated</span>
|
|
{% else %}
|
|
<span class="text-danger">Rejected</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% if log.message %}
|
|
<div class="mt-2">
|
|
<strong>Details:</strong> {{ log.message }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<!-- Pagination -->
|
|
{% if has_prev or has_next %}
|
|
<nav aria-label="Log pagination" class="mt-4">
|
|
<ul class="pagination justify-content-center">
|
|
{% if has_prev %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('email.logs', type=filter_type, page=page-1) }}">
|
|
<i class="bi bi-chevron-left"></i>
|
|
Previous
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
<li class="page-item active">
|
|
<span class="page-link">Page {{ page }}</span>
|
|
</li>
|
|
{% if has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('email.logs', type=filter_type, page=page+1) }}">
|
|
Next
|
|
<i class="bi bi-chevron-right"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-journal-text text-muted" style="font-size: 4rem;"></i>
|
|
<h4 class="text-muted mt-3">No Logs Found</h4>
|
|
<p class="text-muted">
|
|
{% if filter_type == 'emails' %}
|
|
No email activity has been logged yet.
|
|
{% elif filter_type == 'auth' %}
|
|
No authentication attempts have been logged yet.
|
|
{% else %}
|
|
No activity has been logged yet.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
function refreshLogs() {
|
|
window.location.reload();
|
|
}
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setInterval(function() {
|
|
// Only auto-refresh if the user is viewing the page
|
|
if (document.visibilityState === 'visible') {
|
|
const button = document.querySelector('[onclick="refreshLogs()"]');
|
|
if (button) {
|
|
// Add visual indicator that refresh is happening
|
|
const originalText = button.innerHTML;
|
|
button.innerHTML = '<i class="bi bi-arrow-clockwise me-1 spin"></i>Refreshing...';
|
|
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
}
|
|
}
|
|
}, 30000);
|
|
|
|
// Add CSS for spinning icon
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
.spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
</script>
|
|
{% endblock %}
|