v0.4.5: Add dedicated Backup page (Biztonsági mentés)

New /backups page with full backup system visibility:
- Status overview cards (local/remote backup, DB count, repo size)
- Schedule section with next-run times and retention policy
- Database table with type, size, validation (table count), status
- Snapshot history table with per-snapshot stats
- Repository info card with paths, integrity status, remote placeholder
- "Mentés most" button with auto-refresh polling
- Empty state when backup not configured

Backend: SnapshotRecord history (ring buffer), DumpValidation,
ListDumpFiles, ListSnapshots, GetFullStatus, restic check tracking.
Server accepts scheduler for next-run time calculation.

Sidebar nav updated with 3rd item, dashboard backup card title clickable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 07:43:24 +01:00
parent 0985339e6c
commit 37ff296a0d
12 changed files with 1064 additions and 16 deletions
+251
View File
@@ -1226,6 +1226,257 @@ a.stat-card:hover {
.backup-status-fail { color: var(--red); }
.backup-status-none { color: var(--text-muted); }
/* Dashboard backup card link */
.backup-card-link {
color: var(--text-primary);
text-decoration: none;
transition: color 0.2s;
}
.backup-card-link:hover {
color: var(--accent-light);
}
/* --- Backup page --- */
.backup-page-cards {
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.backup-empty-state {
text-align: center;
padding: 4rem 2rem;
background: var(--bg-card);
border-radius: var(--radius);
border: 1px solid var(--border-color);
}
.backup-empty-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.backup-empty-state h3 {
margin-bottom: .5rem;
}
.backup-empty-state p {
color: var(--text-secondary);
font-size: .9rem;
line-height: 1.6;
}
.schedule-card {
background: var(--bg-card);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--border-color);
margin-bottom: 1.5rem;
}
.schedule-card h3 {
margin-bottom: .75rem;
}
.schedule-rows {
margin-bottom: 1rem;
}
.schedule-row {
display: flex;
align-items: center;
gap: 1rem;
padding: .4rem 0;
font-size: .85rem;
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
}
.schedule-row:last-child { border-bottom: none; }
.schedule-task {
color: var(--text-primary);
font-weight: 500;
min-width: 160px;
}
.schedule-time {
color: var(--accent-light);
font-family: 'JetBrains Mono', monospace;
font-size: .8rem;
min-width: 80px;
}
.schedule-next {
color: var(--text-secondary);
font-size: .8rem;
}
.schedule-summary {
border-top: 1px solid var(--border-color);
padding-top: .75rem;
margin-bottom: .75rem;
}
.schedule-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .25rem 0;
font-size: .85rem;
color: var(--text-secondary);
}
.schedule-summary-value {
color: var(--text-primary);
font-family: 'JetBrains Mono', monospace;
font-size: .8rem;
}
.schedule-actions {
padding-top: .5rem;
}
.backup-section-card {
background: var(--bg-card);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--border-color);
margin-bottom: 1.5rem;
}
.backup-section-card h3 {
margin-bottom: .75rem;
}
.backup-table-wrap {
overflow-x: auto;
}
.db-table, .snapshot-table {
width: 100%;
border-collapse: collapse;
font-size: .85rem;
}
.db-table th, .snapshot-table th {
text-align: left;
padding: .5rem .75rem;
color: var(--text-muted);
font-size: .75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
border-bottom: 1px solid var(--border-color);
}
.db-table td, .snapshot-table td {
padding: .5rem .75rem;
color: var(--text-primary);
border-bottom: 1px solid rgba(48, 54, 61, 0.3);
}
.db-table tr:nth-child(even), .snapshot-table tr:nth-child(even) {
background: var(--bg-secondary);
}
.db-table tr:nth-child(odd), .snapshot-table tr:nth-child(odd) {
background: var(--bg-card);
}
.db-table td.mono, .snapshot-table td.mono {
font-family: 'JetBrains Mono', monospace;
font-size: .8rem;
}
.db-type-badge {
display: inline-block;
padding: .1rem .5rem;
border-radius: 999px;
font-size: .75rem;
font-weight: 500;
}
.db-type-postgres {
background: rgba(0, 136, 204, 0.15);
color: var(--accent-light);
}
.db-type-mariadb {
background: rgba(210, 153, 34, 0.15);
color: var(--yellow);
}
.validation-badge {
display: inline-block;
padding: .1rem .5rem;
border-radius: 999px;
font-size: .75rem;
font-weight: 500;
}
.validation-ok {
background: var(--green-bg);
color: var(--green);
}
.validation-fail {
background: var(--red-bg);
color: var(--red);
cursor: help;
}
.validation-na {
color: var(--text-muted);
}
.snapshot-footer {
padding: .75rem .75rem 0;
font-size: .8rem;
color: var(--text-secondary);
}
.backup-table-empty {
padding: 1.5rem;
text-align: center;
color: var(--text-muted);
font-size: .85rem;
}
.repo-card {
background: var(--bg-card);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--border-color);
margin-bottom: 1.5rem;
}
.repo-card h3 {
margin-bottom: .75rem;
}
.repo-info-rows {
margin-bottom: 1rem;
}
.repo-info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .3rem 0;
font-size: .85rem;
}
.repo-label {
color: var(--text-secondary);
}
.repo-value {
color: var(--text-primary);
}
.repo-value.mono {
font-family: 'JetBrains Mono', monospace;
font-size: .8rem;
}
.repo-paths {
border-top: 1px solid var(--border-color);
padding-top: .75rem;
margin-bottom: .75rem;
}
.repo-path-list {
list-style: disc;
padding-left: 1.5rem;
margin-top: .5rem;
}
.repo-path-list li {
font-family: 'JetBrains Mono', monospace;
font-size: .8rem;
color: var(--text-secondary);
padding: .15rem 0;
}
.repo-remote {
border-top: 1px solid var(--border-color);
padding-top: .75rem;
}
.repo-remote-status {
margin-top: .25rem;
display: flex;
flex-direction: column;
gap: .15rem;
}
.relative-time {
color: var(--text-muted);
font-size: .8rem;
}
/* Responsive */
@media(max-width: 768px) {
.sidebar { width: 100%; height: auto; position: relative; border-right: none; border-bottom: 1px solid var(--border-color); }