feat: Add base HTML template and implement dashboard, logs, and service views

- 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.
This commit is contained in:
Peter Wood
2025-06-18 08:06:08 -04:00
parent d066f32b10
commit 6d726cb015
34 changed files with 6006 additions and 26 deletions

228
templates/service.html Normal file
View File

@@ -0,0 +1,228 @@
{% extends "base.html" %}
{% block title %}Service: {{ service.service | title }} - Backup Monitor{% endblock %}
{% block content %}
<div class="container mt-4">
<!-- Header -->
<div class="row mb-4">
<div class="col-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('index') }}">Dashboard</a></li>
<li class="breadcrumb-item active">{{ service.service | title }}</li>
</ol>
</nav>
<h1 class="display-5">
<i class="fas fa-{{ service.icon | default('database') }} text-primary me-3"></i>
{{ service.service | title }} Service
</h1>
<p class="lead text-muted">{{ service.description }}</p>
</div>
</div>
<!-- Service Status Card -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Current Status</h5>
<span class="badge bg-{{ 'success' if service.status == 'success' else 'warning' if service.status == 'partial' else 'danger' if service.status == 'failed' else 'secondary' }} fs-6">
{{ service.status | title }}
</span>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Backup Information</h6>
<table class="table table-sm">
<tr>
<td><strong>Service:</strong></td>
<td>{{ service.service }}</td>
</tr>
<tr>
<td><strong>Status:</strong></td>
<td>
<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>
</td>
</tr>
<tr>
<td><strong>Current Operation:</strong></td>
<td>{{ service.current_operation | default('N/A') }}</td>
</tr>
<tr>
<td><strong>Backup Path:</strong></td>
<td><code>{{ service.backup_path | default('N/A') }}</code></td>
</tr>
{% if service.hostname %}
<tr>
<td><strong>Hostname:</strong></td>
<td>{{ service.hostname }}</td>
</tr>
{% endif %}
</table>
</div>
<div class="col-md-6">
<h6>Timing Information</h6>
<table class="table table-sm">
<tr>
<td><strong>Start Time:</strong></td>
<td>{{ service.start_time | default('N/A') }}</td>
</tr>
<tr>
<td><strong>End Time:</strong></td>
<td>{{ service.end_time | default('In Progress') }}</td>
</tr>
{% if service.duration_seconds %}
<tr>
<td><strong>Duration:</strong></td>
<td>{{ (service.duration_seconds / 60) | round(1) }} minutes</td>
</tr>
{% endif %}
<tr>
<td><strong>Last Updated:</strong></td>
<td>{{ service.last_updated | default('N/A') }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Statistics -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card text-center">
<div class="card-body">
<h2 class="text-primary">{{ service.files_processed | default(0) }}</h2>
<p class="text-muted mb-0">Files Processed</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center">
<div class="card-body">
<h2 class="text-info">
{% if service.total_size_bytes %}
{{ (service.total_size_bytes / 1024 / 1024 / 1024) | round(2) }}GB
{% else %}
0GB
{% endif %}
</h2>
<p class="text-muted mb-0">Total Size</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center">
<div class="card-body">
<h2 class="text-success">
{% if service.duration_seconds %}
{{ (service.duration_seconds / 60) | round(1) }}m
{% else %}
0m
{% endif %}
</h2>
<p class="text-muted mb-0">Duration</p>
</div>
</div>
</div>
</div>
<!-- Backup Files Information -->
{% if service.backup_path %}
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-folder me-2"></i>Backup Location
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<label class="form-label fw-bold">Backup Directory:</label>
<div class="p-2 bg-light rounded">
<code>{{ service.backup_path }}</code>
</div>
</div>
</div>
{% if service.latest_backup %}
<div class="row mt-3">
<div class="col-12">
<label class="form-label fw-bold">Latest Backup:</label>
<div class="p-2 bg-light rounded">
<code>{{ service.latest_backup }}</code>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Message/Error Information -->
{% if service.message %}
<div class="row mb-4">
<div class="col-12">
<div class="alert alert-{{ 'success' if service.status == 'success' else 'warning' if service.status == 'partial' else 'danger' if service.status == 'failed' else 'info' }}">
<h6 class="alert-heading">
{% if service.status == 'success' %}
<i class="fas fa-check-circle me-2"></i>Success
{% elif service.status == 'partial' %}
<i class="fas fa-exclamation-triangle me-2"></i>Warning
{% elif service.status == 'failed' %}
<i class="fas fa-times-circle me-2"></i>Error
{% else %}
<i class="fas fa-info-circle me-2"></i>Information
{% endif %}
</h6>
{{ service.message }}
</div>
</div>
</div>
{% endif %}
<!-- Actions -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Actions</h5>
</div>
<div class="card-body">
<div class="btn-group" role="group">
<button class="btn btn-primary" onclick="refreshService()">
<i class="fas fa-sync-alt me-1"></i>Refresh Status
</button>
<a href="{{ url_for('logs_view', service=service.service) }}" class="btn btn-outline-info">
<i class="fas fa-file-alt me-1"></i>View Logs
</a>
<a href="{{ url_for('index') }}" class="btn btn-outline-dark">
<i class="fas fa-arrow-left me-1"></i>Back to Dashboard
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function refreshService() {
location.reload();
}
// Auto-refresh every 10 seconds for individual service view
setInterval(function() {
location.reload();
}, 10000);
</script>
{% endblock %}