mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 04:30:13 -08:00
- Created a base HTML template for consistent layout across pages. - Developed a dashboard page to display backup service metrics and statuses. - Implemented a log viewer for detailed log file inspection. - Added error handling page for better user experience during failures. - Introduced service detail page to show specific service metrics and actions. - Enhanced log filtering and viewing capabilities. - Integrated auto-refresh functionality for real-time updates on metrics. - Created integration and unit test scripts for backup metrics functionality.
198 lines
7.7 KiB
HTML
198 lines
7.7 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Dashboard - Backup Monitor{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container mt-4">
|
|
<!-- Header -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h1 class="display-4">
|
|
<i class="fas fa-tachometer-alt text-primary me-3"></i>
|
|
Backup Dashboard
|
|
</h1>
|
|
<p class="lead text-muted">Monitor and manage your backup services</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Overview -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card bg-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4>{{ data.summary.successful }}</h4>
|
|
<p class="mb-0">Successful</p>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-check-circle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-warning text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4>{{ data.summary.partial }}</h4>
|
|
<p class="mb-0">Partial</p>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-exclamation-triangle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-danger text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4>{{ data.summary.failed }}</h4>
|
|
<p class="mb-0">Failed</p>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-times-circle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-info text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4>{{ data.summary.total }}</h4>
|
|
<p class="mb-0">Total Services</p>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-server fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Service Cards -->
|
|
<div class="row">
|
|
{% for service in data.services %}
|
|
<div class="col-lg-4 col-md-6 mb-4">
|
|
<div class="card h-100 service-card" data-service="{{ service.service }}">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-{{ service.icon | default('database') }} me-2"></i>
|
|
{{ service.service | title }}
|
|
</h5>
|
|
<span class="badge bg-{{ 'success' if service.status == 'success' else 'warning' if service.status == 'partial' else 'danger' if service.status == 'failed' else 'secondary' }}">
|
|
{{ service.status | title }}
|
|
</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text text-muted">{{ service.description }}</p>
|
|
|
|
{% if service.start_time %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<i class="fas fa-clock me-1"></i>
|
|
Last Run: {{ service.start_time | default('Never') }}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if service.duration_seconds %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<i class="fas fa-stopwatch me-1"></i>
|
|
Duration: {{ (service.duration_seconds / 60) | round(1) }} minutes
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if service.files_processed %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<i class="fas fa-file me-1"></i>
|
|
Files: {{ service.files_processed }}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if service.total_size_bytes %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<i class="fas fa-hdd me-1"></i>
|
|
Size: {{ (service.total_size_bytes / 1024 / 1024 / 1024) | round(2) }}GB
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if service.current_operation %}
|
|
<div class="mb-2">
|
|
<small class="text-muted">
|
|
<i class="fas fa-info-circle me-1"></i>
|
|
{{ service.current_operation }}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if service.message and service.status != 'success' %}
|
|
<div class="alert alert-{{ 'warning' if service.status == 'partial' else 'danger' }} py-1 px-2 mt-2">
|
|
<small>{{ service.message }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{{ url_for('service_detail', service_name=service.service) }}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-eye me-1"></i>Details
|
|
</a>
|
|
{% if service.backup_path %}
|
|
<small class="text-muted">
|
|
<i class="fas fa-folder me-1"></i>Backup Path: <code>{{ service.backup_path }}</code>
|
|
</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
{% if not data.services %}
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-database fa-4x text-muted mb-3"></i>
|
|
<h3 class="text-muted">No backup services found</h3>
|
|
<p class="text-muted">No backup metrics are available at this time.</p>
|
|
<button class="btn btn-primary" onclick="refreshMetrics()">
|
|
<i class="fas fa-sync-alt me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
function refreshMetrics() {
|
|
location.reload();
|
|
}
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setInterval(refreshMetrics, 30000);
|
|
|
|
// Update last updated time
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.getElementById('last-updated').textContent = 'Last updated: ' + new Date().toLocaleTimeString();
|
|
});
|
|
</script>
|
|
{% endblock %}
|