Files
BC-bak/templates/history.html
Malin b651eca285 feat: Business Central backup Docker container with web management panel
Python backup module (Azure AD OAuth2 → BC API v2.0 → GPG AES256 → S3),
Flask web UI on port 1890 with SSE live log streaming, dual incremental +
full backup schedules, chain restore merging full + incrementals, and
per-entity file browser with ZIP download.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 20:09:32 +02:00

132 lines
5.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %} — History{% endblock %}
{% block content %}
<div class="page-header">
<h4><i class="bi bi-clock-history me-2 text-primary"></i>Backup History</h4>
<div class="d-flex align-items-center gap-3">
<span class="text-muted small">{{ total }} total backup{{ 's' if total != 1 }}</span>
<button class="btn btn-primary btn-sm px-3" onclick="runBackupNow()">
<i class="bi bi-cloud-upload-fill me-1"></i>Backup Now
</button>
</div>
</div>
<div class="card stat-card">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>#</th>
<th>Started</th>
<th>Completed</th>
<th>Duration</th>
<th>Type</th>
<th>Trigger</th>
<th>Companies</th>
<th>Entities OK</th>
<th>Records</th>
<th>Size</th>
<th>Status</th>
<th>Archive</th>
<th></th>
</tr>
</thead>
<tbody>
{% for r in runs %}
<tr {% if r['status'] == 'failed' %}class="table-danger bg-opacity-25"{% endif %}>
<td class="text-muted">{{ r['id'] }}</td>
<td>{{ r['started_at'] | fmtdt }}</td>
<td>{{ r['completed_at'] | fmtdt }}</td>
<td>{{ r['started_at'] | duration(r['completed_at']) }}</td>
<td>
{% if r['backup_type'] == 'full' %}
<span class="badge bg-primary text-white"><i class="bi bi-database-fill-up me-1"></i>Full</span>
{% else %}
<span class="badge bg-success text-white"><i class="bi bi-arrow-repeat me-1"></i>Incr</span>
{% endif %}
</td>
<td>
<span class="badge rounded-pill {% if r['triggered_by'] == 'manual' %}bg-info{% else %}bg-secondary{% endif %} text-white">
{{ r['triggered_by'] }}
</span>
</td>
<td class="text-muted small">
{% if r['companies'] %}
{{ r['companies'] | replace('["','') | replace('"]','') | replace('","',' · ') }}
{% else %}—{% endif %}
</td>
<td class="text-success fw-semibold">{{ r['entities_ok'] or '—' }}</td>
<td class="text-muted">{{ r['total_records'] or '—' }}</td>
<td>{% if r['archive_size_mb'] %}{{ '%.1f' | format(r['archive_size_mb']) }} MB{% else %}—{% endif %}</td>
<td>
{% if r['status'] == 'success' %}
<span class="badge badge-success"><i class="bi bi-check me-1"></i>Success</span>
{% elif r['status'] == 'failed' %}
<span class="badge badge-failed" title="{{ r['error_message'] or '' }}">
<i class="bi bi-x me-1"></i>Failed
</span>
{% else %}
<span class="badge badge-running"><i class="bi bi-arrow-repeat me-1"></i>Running</span>
{% endif %}
</td>
<td class="text-muted" style="font-size:.7rem; max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap">
{% if r['s3_key'] %}
<span title="{{ r['s3_key'] }}">{{ r['s3_key'].split('/')[-1] }}</span>
{% else %}—{% endif %}
</td>
<td>
{% if r['s3_key'] and r['status'] == 'success' %}
<a href="{{ url_for('restore_list') }}"
class="btn btn-outline-warning btn-sm py-0"
onclick="event.preventDefault(); sessionStorage.setItem('restoreKey','{{ r['s3_key'] }}'); window.location='{{ url_for('restore_list') }}'">
<i class="bi bi-arrow-counterclockwise me-1"></i>Restore
</a>
{% endif %}
</td>
</tr>
{% if r['status'] == 'failed' and r['error_message'] %}
<tr class="table-danger">
<td colspan="13" class="text-danger small py-1 ps-4">
<i class="bi bi-exclamation-triangle me-1"></i>{{ r['error_message'] }}
</td>
</tr>
{% endif %}
{% else %}
<tr>
<td colspan="13" class="text-center text-muted py-5">
<i class="bi bi-inbox fs-2 d-block mb-2"></i>No backup history yet
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if pages > 1 %}
<div class="card-footer bg-white border-0 d-flex justify-content-between align-items-center">
<span class="text-muted small">Page {{ page }} of {{ pages }}</span>
<nav>
<ul class="pagination pagination-sm mb-0">
<li class="page-item {% if page <= 1 %}disabled{% endif %}">
<a class="page-link" href="?page={{ page - 1 }}"> Prev</a>
</li>
{% for p in range(1, pages + 1) %}
{% if p == page or p == 1 or p == pages or (p >= page - 2 and p <= page + 2) %}
<li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% elif p == page - 3 or p == page + 3 %}
<li class="page-item disabled"><span class="page-link"></span></li>
{% endif %}
{% endfor %}
<li class="page-item {% if page >= pages %}disabled{% endif %}">
<a class="page-link" href="?page={{ page + 1 }}">Next </a>
</li>
</ul>
</nav>
</div>
{% endif %}
</div>
{% endblock %}