2025-06-07 10:48:03 +01:00
|
|
|
{% extends "base.html" %}
|
|
|
|
|
|
|
|
|
|
{% block title %}Dashboard - Email Server Management{% endblock %}
|
|
|
|
|
{% block page_title %}Dashboard{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block content %}
|
|
|
|
|
<div class="row">
|
|
|
|
|
<!-- Statistics Cards -->
|
|
|
|
|
<div class="col-lg-3 col-md-6 mb-4">
|
2025-06-10 01:50:35 +01:00
|
|
|
<a href="{{ url_for('email.domains_list') }}" class="dashboard-card-link text-decoration-none">
|
2025-06-07 10:48:03 +01:00
|
|
|
<div class="card border-primary">
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="d-flex align-items-center">
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
|
<h5 class="card-title text-primary mb-1">
|
|
|
|
|
<i class="bi bi-globe me-2"></i>
|
|
|
|
|
Domains
|
|
|
|
|
</h5>
|
|
|
|
|
<h3 class="mb-0">{{ domain_count }}</h3>
|
|
|
|
|
<small class="text-muted">Active domains</small>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="fs-2 text-primary opacity-50">
|
|
|
|
|
<i class="bi bi-globe"></i>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-06-10 01:50:35 +01:00
|
|
|
</a>
|
2025-06-07 10:48:03 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="col-lg-3 col-md-6 mb-4">
|
2025-06-10 01:50:35 +01:00
|
|
|
<a href="{{ url_for('email.senders_list') }}" class="dashboard-card-link text-decoration-none">
|
2025-06-07 10:48:03 +01:00
|
|
|
<div class="card border-success">
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="d-flex align-items-center">
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
|
<h5 class="card-title text-success mb-1">
|
|
|
|
|
<i class="bi bi-people me-2"></i>
|
2025-06-10 01:50:35 +01:00
|
|
|
Senders
|
2025-06-07 10:48:03 +01:00
|
|
|
</h5>
|
2025-06-10 01:50:35 +01:00
|
|
|
<h3 class="mb-0">{{ sender_count }}</h3>
|
|
|
|
|
<small class="text-muted">Authenticated senders</small>
|
2025-06-07 10:48:03 +01:00
|
|
|
</div>
|
|
|
|
|
<div class="fs-2 text-success opacity-50">
|
|
|
|
|
<i class="bi bi-people"></i>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-06-10 01:50:35 +01:00
|
|
|
</a>
|
2025-06-07 10:48:03 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="col-lg-3 col-md-6 mb-4">
|
2025-06-10 01:50:35 +01:00
|
|
|
<a href="{{ url_for('email.dkim_list') }}" class="dashboard-card-link text-decoration-none">
|
2025-06-07 10:48:03 +01:00
|
|
|
<div class="card border-warning">
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="d-flex align-items-center">
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
|
<h5 class="card-title text-warning mb-1">
|
|
|
|
|
<i class="bi bi-shield-check me-2"></i>
|
|
|
|
|
DKIM Keys
|
|
|
|
|
</h5>
|
|
|
|
|
<h3 class="mb-0">{{ dkim_count }}</h3>
|
|
|
|
|
<small class="text-muted">Active DKIM keys</small>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="fs-2 text-warning opacity-50">
|
|
|
|
|
<i class="bi bi-shield-check"></i>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-06-10 01:50:35 +01:00
|
|
|
</a>
|
2025-06-07 10:48:03 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="col-lg-3 col-md-6 mb-4">
|
|
|
|
|
<div class="card border-info">
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="d-flex align-items-center">
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
|
<h5 class="card-title text-info mb-1">
|
|
|
|
|
<i class="bi bi-activity me-2"></i>
|
|
|
|
|
Status
|
|
|
|
|
</h5>
|
2025-06-10 01:50:35 +01:00
|
|
|
{% set health = check_health() %}
|
|
|
|
|
<h6 class="{% if health.status == 'healthy' %}text-success{% else %}text-warning{% endif %} mb-0 status-indicator"
|
|
|
|
|
data-bs-toggle="tooltip"
|
|
|
|
|
data-bs-html="true"
|
|
|
|
|
data-bs-placement="top"
|
|
|
|
|
title="{{ (
|
|
|
|
|
"<div class='text-start'><strong>Service Status:</strong><br>" +
|
|
|
|
|
'SMTP Server: ' + health.services.smtp_server|title + '<br>' +
|
|
|
|
|
'Web Frontend: ' + health.services.web_frontend|title + '<br>' +
|
|
|
|
|
'Database: ' + health.services.database|title + '</div>'
|
|
|
|
|
) | safe }}">
|
2025-06-07 10:48:03 +01:00
|
|
|
<i class="bi bi-circle-fill me-1" style="font-size: 0.5rem;"></i>
|
2025-06-10 01:50:35 +01:00
|
|
|
{{ health.status|title }}
|
2025-06-07 10:48:03 +01:00
|
|
|
</h6>
|
2025-06-10 01:50:35 +01:00
|
|
|
<small class="text-muted">
|
|
|
|
|
{% if health.services.smtp_server == 'running' and health.services.database == 'ok' %}
|
|
|
|
|
All services running
|
|
|
|
|
{% else %}
|
|
|
|
|
{% if health.services.smtp_server == 'stopped' %}SMTP Server stopped{% endif %}
|
|
|
|
|
{% if health.services.database == 'error' %}Database error{% endif %}
|
|
|
|
|
{% endif %}
|
|
|
|
|
</small>
|
2025-06-07 10:48:03 +01:00
|
|
|
</div>
|
|
|
|
|
<div class="fs-2 text-info opacity-50">
|
|
|
|
|
<i class="bi bi-activity"></i>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
<!-- Recent Email Activity -->
|
|
|
|
|
<div class="col-lg-8 mb-4">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
|
|
|
<h5 class="mb-0">
|
|
|
|
|
<i class="bi bi-envelope me-2"></i>
|
|
|
|
|
Recent Email Activity
|
|
|
|
|
</h5>
|
|
|
|
|
<a href="{{ url_for('email.logs', type='emails') }}" class="btn btn-outline-light btn-sm">
|
|
|
|
|
View All
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-body p-0">
|
|
|
|
|
{% if recent_emails %}
|
|
|
|
|
<div class="table-responsive">
|
|
|
|
|
<table class="table table-dark table-hover mb-0">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Time</th>
|
|
|
|
|
<th>From</th>
|
|
|
|
|
<th>To</th>
|
|
|
|
|
<th>Status</th>
|
|
|
|
|
<th>DKIM</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{% for email in recent_emails %}
|
|
|
|
|
<tr>
|
|
|
|
|
<td>
|
|
|
|
|
<small class="text-muted">
|
|
|
|
|
{{ email.created_at.strftime('%H:%M:%S') }}
|
|
|
|
|
</small>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<span class="text-truncate d-inline-block" style="max-width: 150px;" title="{{ email.mail_from }}">
|
|
|
|
|
{{ email.mail_from }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<span class="text-truncate d-inline-block" style="max-width: 150px;" title="{{ email.to_address }}">
|
|
|
|
|
{{ email.to_address }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
{% if email.status == 'relayed' %}
|
|
|
|
|
<span class="badge bg-success">
|
|
|
|
|
<i class="bi bi-check-circle me-1"></i>
|
|
|
|
|
Sent
|
|
|
|
|
</span>
|
|
|
|
|
{% else %}
|
|
|
|
|
<span class="badge bg-danger">
|
|
|
|
|
<i class="bi bi-x-circle me-1"></i>
|
|
|
|
|
Failed
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
{% if email.dkim_signed %}
|
|
|
|
|
<span class="text-success">
|
|
|
|
|
<i class="bi bi-shield-check" title="DKIM Signed"></i>
|
|
|
|
|
</span>
|
|
|
|
|
{% else %}
|
|
|
|
|
<span class="text-muted">
|
|
|
|
|
<i class="bi bi-shield-x" title="Not DKIM Signed"></i>
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="text-center py-4">
|
|
|
|
|
<i class="bi bi-envelope text-muted fs-1"></i>
|
|
|
|
|
<p class="text-muted mt-2">No email activity yet</p>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Recent Authentication Activity -->
|
|
|
|
|
<div class="col-lg-4 mb-4">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
|
|
|
<h5 class="mb-0">
|
|
|
|
|
<i class="bi bi-shield-lock me-2"></i>
|
|
|
|
|
Recent Auth Activity
|
|
|
|
|
</h5>
|
|
|
|
|
<a href="{{ url_for('email.logs', type='auth') }}" class="btn btn-outline-light btn-sm">
|
|
|
|
|
View All
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-body p-0">
|
|
|
|
|
{% if recent_auths %}
|
|
|
|
|
<div class="list-group list-group-flush">
|
|
|
|
|
{% for auth in recent_auths %}
|
|
|
|
|
<div class="list-group-item list-group-item-dark d-flex justify-content-between align-items-start">
|
|
|
|
|
<div class="ms-2 me-auto">
|
|
|
|
|
<div class="fw-bold">
|
|
|
|
|
{% if auth.success %}
|
|
|
|
|
<i class="bi bi-check-circle text-success me-1"></i>
|
|
|
|
|
{% else %}
|
|
|
|
|
<i class="bi bi-x-circle text-danger me-1"></i>
|
|
|
|
|
{% endif %}
|
|
|
|
|
{{ auth.auth_type|title }}
|
|
|
|
|
</div>
|
|
|
|
|
<small class="text-muted">
|
|
|
|
|
{{ auth.identifier }}
|
|
|
|
|
</small>
|
|
|
|
|
<br>
|
|
|
|
|
<small class="text-muted">
|
|
|
|
|
{{ auth.created_at.strftime('%H:%M:%S') }}
|
|
|
|
|
</small>
|
|
|
|
|
</div>
|
|
|
|
|
<small class="text-muted">
|
|
|
|
|
{{ auth.ip_address }}
|
|
|
|
|
</small>
|
|
|
|
|
</div>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</div>
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="text-center py-4">
|
|
|
|
|
<i class="bi bi-shield-lock text-muted fs-1"></i>
|
|
|
|
|
<p class="text-muted mt-2">No authentication activity yet</p>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Quick Actions -->
|
|
|
|
|
<div class="row">
|
|
|
|
|
<div class="col-12">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<h5 class="mb-0">
|
|
|
|
|
<i class="bi bi-lightning me-2"></i>
|
|
|
|
|
Quick Actions
|
|
|
|
|
</h5>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="row">
|
|
|
|
|
<div class="col-md-3 mb-3">
|
|
|
|
|
<div class="d-grid">
|
|
|
|
|
<a href="{{ url_for('email.add_domain') }}" class="btn btn-outline-primary">
|
|
|
|
|
<i class="bi bi-plus-circle me-2"></i>
|
|
|
|
|
Add Domain
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-3 mb-3">
|
|
|
|
|
<div class="d-grid">
|
2025-06-08 22:51:07 +01:00
|
|
|
<a href="{{ url_for('email.add_sender') }}" class="btn btn-outline-success">
|
2025-06-07 10:48:03 +01:00
|
|
|
<i class="bi bi-person-plus me-2"></i>
|
2025-06-10 01:50:35 +01:00
|
|
|
Add Sender
|
2025-06-07 10:48:03 +01:00
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-3 mb-3">
|
|
|
|
|
<div class="d-grid">
|
|
|
|
|
<a href="{{ url_for('email.add_ip') }}" class="btn btn-outline-warning">
|
|
|
|
|
<i class="bi bi-shield-plus me-2"></i>
|
|
|
|
|
Whitelist IP
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-3 mb-3">
|
|
|
|
|
<div class="d-grid">
|
|
|
|
|
<a href="{{ url_for('email.settings') }}" class="btn btn-outline-info">
|
|
|
|
|
<i class="bi bi-gear me-2"></i>
|
|
|
|
|
Settings
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block extra_js %}
|
|
|
|
|
<script>
|
|
|
|
|
// Auto-refresh dashboard every 30 seconds
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
location.reload();
|
|
|
|
|
}, 30000);
|
|
|
|
|
</script>
|
2025-06-10 01:50:35 +01:00
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.status-indicator {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.dashboard-card-link {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
.dashboard-card-link:hover .card {
|
|
|
|
|
box-shadow: 0 0 0 2px #0d6efd33;
|
|
|
|
|
filter: brightness(1.05);
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
|
|
|
|
tooltipTriggerList.forEach(function(tooltipTriggerEl) {
|
|
|
|
|
new bootstrap.Tooltip(tooltipTriggerEl, {
|
|
|
|
|
html: true,
|
|
|
|
|
placement: 'top',
|
|
|
|
|
trigger: 'hover'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2025-06-07 10:48:03 +01:00
|
|
|
{% endblock %}
|