feat: enhance backup and restore functionality with integrity checks and progress tracking

- Added support for backup integrity verification during export and restore processes.
- Introduced progress dispatching for backup export and restore operations.
- Implemented new API endpoints for inspecting remote backup integrity.
- Enhanced user interface with progress indicators and warning dialogs for integrity issues.
- Updated localization strings for new features and user feedback.
- Refactored backup-related functions for better clarity and maintainability.
This commit is contained in:
shuaiplus
2026-03-28 05:52:47 +08:00
parent bd8e26d2ab
commit 2a7879efaa
18 changed files with 2250 additions and 225 deletions
+257
View File
@@ -80,6 +80,11 @@ body {
color var(--dur-medium) var(--ease-smooth);
}
body.dialog-open {
overflow: hidden;
overscroll-behavior: contain;
}
body::before {
content: none;
}
@@ -2929,6 +2934,148 @@ input[type='file'].input::file-selector-button:hover {
font-weight: 700;
}
.restore-progress-card {
margin: 8px 0 12px;
padding: 14px 16px;
border-radius: 12px;
border: 1px solid #d7e2f1;
background: #ffffff;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.10);
}
.restore-progress-overlay {
position: fixed;
inset: 0;
z-index: 1250;
display: grid;
place-items: center;
padding: 20px;
background: rgba(15, 23, 42, 0.30);
}
.restore-progress-modal {
width: min(520px, 100%);
margin: 0;
}
.restore-progress-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.restore-progress-kicker {
font-size: 12px;
font-weight: 600;
letter-spacing: 0.02em;
color: #64748b;
}
.restore-progress-title {
margin: 4px 0 2px;
font-size: 20px;
line-height: 1.2;
}
.restore-progress-subtitle {
margin: 0;
color: #6b7280;
font-size: 13px;
}
.restore-progress-elapsed {
flex: 0 0 auto;
min-width: 88px;
padding: 6px 8px;
border-radius: 10px;
background: #f8fbff;
border: 1px solid #d7e2f1;
color: #475569;
font-weight: 600;
font-size: 13px;
text-align: center;
}
.restore-progress-meter {
height: 6px;
border-radius: 999px;
background: #e7eef8;
overflow: hidden;
}
.restore-progress-meter-bar {
display: block;
height: 100%;
border-radius: inherit;
background: #3a71d8;
transition: width 280ms ease;
}
.restore-progress-current {
margin-top: 12px;
padding: 10px 12px;
border-radius: 10px;
background: #f8fbff;
border: 1px solid #d7e2f1;
}
.restore-progress-current strong {
display: block;
color: #0f172a;
font-size: 14px;
}
.restore-progress-current p {
margin: 4px 0 0;
color: #64748b;
line-height: 1.45;
font-size: 13px;
}
.restore-progress-list {
list-style: none;
margin: 12px 0 0;
padding: 0;
display: grid;
gap: 6px;
}
.restore-progress-item {
display: flex;
align-items: center;
gap: 8px;
min-height: 30px;
color: #64748b;
font-weight: 500;
font-size: 13px;
}
.restore-progress-item.active {
color: #1d4ed8;
}
.restore-progress-item.done {
color: #475569;
}
.restore-progress-dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: #cbd5e1;
flex: 0 0 auto;
}
.restore-progress-item.active .restore-progress-dot {
background: #1d4ed8;
}
.restore-progress-item.done .restore-progress-dot {
background: #94a3b8;
}
.kv-line strong {
overflow-wrap: anywhere;
}
@@ -3022,6 +3169,8 @@ input[type='file'].input::file-selector-button:hover {
.dialog-mask {
position: fixed;
inset: 0;
width: 100vw;
height: 100dvh;
background: rgba(15, 23, 42, 0.5);
display: grid;
place-items: center;
@@ -3029,6 +3178,8 @@ input[type='file'].input::file-selector-button:hover {
padding: 20px;
opacity: 0;
animation: fade-in var(--dur-medium) var(--ease-smooth) both;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
}
.dialog-card {
@@ -3043,6 +3194,54 @@ input[type='file'].input::file-selector-button:hover {
animation: dialog-in 240ms var(--ease-out-strong) both;
}
.dialog-mask.warning {
background:
radial-gradient(circle at top, rgba(255, 237, 213, 0.32), transparent 34%),
linear-gradient(180deg, rgba(127, 29, 29, 0.36), rgba(15, 23, 42, 0.72));
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.dialog-card.warning {
width: min(520px, 100%);
border: 1px solid rgba(220, 38, 38, 0.22);
background:
linear-gradient(180deg, rgba(255, 246, 246, 0.98), rgba(255, 255, 255, 0.99));
box-shadow:
0 36px 90px rgba(69, 10, 10, 0.28),
0 0 0 1px rgba(255, 255, 255, 0.7) inset;
}
.dialog-warning-head {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 8px;
}
.dialog-warning-badge {
width: 48px;
height: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 16px;
background: linear-gradient(180deg, #fff1f2, #ffe4e6);
color: #dc2626;
box-shadow:
0 12px 30px rgba(220, 38, 38, 0.18),
0 0 0 1px rgba(220, 38, 38, 0.08) inset;
}
.dialog-warning-kicker {
font-size: 12px;
font-weight: 800;
letter-spacing: 0.16em;
text-transform: uppercase;
color: #b91c1c;
}
.dialog-mask.closing {
animation: fade-out 220ms var(--ease-smooth) both;
}
@@ -3070,6 +3269,22 @@ input[type='file'].input::file-selector-button:hover {
margin-bottom: 10px;
}
.dialog-card.warning .dialog-title {
color: #7f1d1d;
margin-bottom: 10px;
}
.dialog-message.warning {
margin-bottom: 16px;
padding: 14px 16px;
border-radius: 16px;
border: 1px solid rgba(220, 38, 38, 0.16);
background: linear-gradient(180deg, rgba(255, 241, 242, 0.94), rgba(255, 247, 237, 0.9));
color: #7a2832;
line-height: 1.65;
box-shadow: 0 10px 28px rgba(248, 113, 113, 0.08) inset;
}
.dialog-btn {
width: 100%;
height: 50px;
@@ -4132,6 +4347,14 @@ input[type='file'].input::file-selector-button:hover {
padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
}
.dialog-card.warning {
max-width: 520px;
}
.dialog-warning-strip {
margin: -18px -16px 16px;
}
.dialog-title {
font-size: 24px;
}
@@ -4252,6 +4475,40 @@ input[type='file'].input::file-selector-button:hover {
color: var(--text);
}
:root[data-theme='dark'] .dialog-card.warning {
border-color: rgba(248, 113, 113, 0.36);
background: linear-gradient(180deg, rgba(39, 16, 16, 0.98), rgba(27, 12, 12, 0.98));
box-shadow:
0 36px 90px rgba(5, 5, 5, 0.56),
0 0 0 1px rgba(248, 113, 113, 0.12) inset;
}
:root[data-theme='dark'] .dialog-mask.warning {
background:
radial-gradient(circle at top, rgba(127, 29, 29, 0.28), transparent 34%),
linear-gradient(180deg, rgba(20, 12, 12, 0.64), rgba(2, 6, 23, 0.82));
}
:root[data-theme='dark'] .dialog-warning-badge {
background: linear-gradient(180deg, rgba(127, 29, 29, 0.8), rgba(69, 10, 10, 0.86));
color: #fda4af;
box-shadow:
0 12px 30px rgba(0, 0, 0, 0.32),
0 0 0 1px rgba(248, 113, 113, 0.14) inset;
}
:root[data-theme='dark'] .dialog-warning-kicker,
:root[data-theme='dark'] .dialog-card.warning .dialog-title {
color: #fecaca;
}
:root[data-theme='dark'] .dialog-message.warning {
border-color: rgba(248, 113, 113, 0.18);
background: linear-gradient(180deg, rgba(69, 10, 10, 0.54), rgba(67, 20, 7, 0.46));
color: #fecdd3;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18) inset;
}
:root[data-theme='dark'] .app-side,
:root[data-theme='dark'] .sidebar,
:root[data-theme='dark'] .mobile-sidebar-sheet .sidebar-block {