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
+30 -5
View File
@@ -1,4 +1,4 @@
import type { Env } from '../types';
import type { Env, User } from '../types';
import { StorageService } from './storage';
import {
type BackupSettingsPortableEnvelope,
@@ -422,20 +422,45 @@ export async function saveBackupSettings(storage: StorageService, env: Env, sett
export async function normalizeImportedBackupSettings(storage: StorageService, env: Env, fallbackTimezone: string = 'UTC'): Promise<void> {
const raw = await storage.getConfigValue(BACKUP_SETTINGS_CONFIG_KEY);
if (!raw) return;
const users = await storage.getAllUsers();
const normalized = await normalizeImportedBackupSettingsValue(raw, env, users, fallbackTimezone);
if (normalized !== null) {
await storage.setConfigValue(BACKUP_SETTINGS_CONFIG_KEY, normalized);
}
}
export async function normalizeImportedBackupSettingsValue(
raw: string | null,
env: Env,
users: Pick<User, 'id' | 'publicKey' | 'role' | 'status'>[],
fallbackTimezone: string = 'UTC'
): Promise<string | null> {
if (!raw) return null;
const envelope = parseBackupSettingsEnvelope(raw);
if (envelope) {
try {
const decrypted = await decryptBackupSettingsRuntime(raw, env);
const settings = parseBackupSettings(decrypted, fallbackTimezone);
await saveBackupSettings(storage, env, settings);
return;
const hasPortableAdmins = users.some(
(user) => user.role === 'admin' && user.status === 'active' && typeof user.publicKey === 'string' && user.publicKey.trim().length > 0
);
if (!hasPortableAdmins) {
return serializeBackupSettings(settings);
}
return encryptBackupSettingsEnvelope(serializeBackupSettings(settings), env, users);
} catch {
// Keep imported portable recovery data intact until an admin signs in and repairs it.
return;
return raw;
}
}
const settings = parseBackupSettings(raw, fallbackTimezone);
await saveBackupSettings(storage, env, settings);
const hasPortableAdmins = users.some(
(user) => user.role === 'admin' && user.status === 'active' && typeof user.publicKey === 'string' && user.publicKey.trim().length > 0
);
if (!hasPortableAdmins) {
return serializeBackupSettings(settings);
}
return encryptBackupSettingsEnvelope(serializeBackupSettings(settings), env, users);
}
export async function getBackupSettingsRepairState(storage: StorageService, env: Env, fallbackTimezone: string = 'UTC'): Promise<BackupSettingsRepairState> {