diff --git a/native/backup/backup-handler.js b/native/backup/backup-handler.js --- a/native/backup/backup-handler.js +++ b/native/backup/backup-handler.js @@ -10,10 +10,13 @@ import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { useClientBackup } from './use-client-backup.js'; +import { useGetBackupSecretForLoggedInUser } from './use-get-backup-secret.js'; import { commCoreModule } from '../native-modules.js'; import { useSelector } from '../redux/redux-utils.js'; import { useStaffCanSee } from '../utils/staff-utils.js'; +const millisecondsPerDay = 24 * 60 * 60 * 1000; + function BackupHandler(): null { const loggedIn = useSelector(isLoggedIn); const staffCanSee = useStaffCanSee(); @@ -27,7 +30,9 @@ state => state.backupStore.latestBackupInfo, ); const dispatchActionPromise = useDispatchActionPromise(); - const { createUserKeysBackup } = useClientBackup(); + const { createUserKeysBackup, retrieveLatestBackupInfo, getBackupUserKeys } = + useClientBackup(); + const getBackupSecret = useGetBackupSecretForLoggedInUser(); const backupUploadInProgress = React.useRef(false); const [handlerStarted, setHandlerStarted] = React.useState(false); @@ -72,13 +77,62 @@ staffCanSee, ]); + const testUserKeysRestore = React.useCallback(async () => { + if (!latestBackupInfo?.backupID) { + return; + } + const { + latestBackupInfo: { backupID }, + userIdentifier, + } = await retrieveLatestBackupInfo(); + + const backupSecret = await getBackupSecret(); + const [ + { + backupDataKey: backupDataKeyFromBackup, + backupLogDataKey: backupLogDataKeyFromBackup, + }, + { + backupID: localBackupID, + backupDataKey: localBackupDataKey, + backupLogDataKey: localBackupLogDataKey, + }, + ] = await Promise.all([ + getBackupUserKeys(userIdentifier, backupSecret, backupID), + commCoreModule.getQRAuthBackupData(), + ]); + + const backupIDCheck = + latestBackupInfo.backupID === backupID && backupID === localBackupID; + const keysCheck = + backupDataKeyFromBackup === localBackupDataKey && + backupLogDataKeyFromBackup === localBackupLogDataKey; + + if (!backupIDCheck || !keysCheck) { + throw new Error( + '\n' + + `backupID: ${backupID}\n` + + `latestBackupInfo.backupID: ${latestBackupInfo.backupID}\n` + + `localBackupID: ${localBackupID}\n` + + `backupDataKeyFromBackup: ${backupDataKeyFromBackup}\n` + + `backupLogDataKeyFromBackup: ${backupLogDataKeyFromBackup}\n` + + `localBackupDataKey: ${localBackupDataKey}\n` + + `localBackupLogDataKey: ${localBackupLogDataKey}\n`, + ); + } + }, [ + getBackupSecret, + getBackupUserKeys, + latestBackupInfo?.backupID, + retrieveLatestBackupInfo, + ]); + React.useEffect(() => { if ( !staffCanSee || !canPerformBackupOperation || !handlerStarted || - backupUploadInProgress.current || - !!latestBackupInfo + backupUploadInProgress.current ) { return; } @@ -91,6 +145,23 @@ return; } + if (latestBackupInfo) { + const timestamp = latestBackupInfo.timestamp; + // If last upload one less than 24h ago ignore it + if (timestamp >= Date.now() - millisecondsPerDay) { + backupUploadInProgress.current = false; + return; + } + + try { + await testUserKeysRestore(); + } catch (err) { + const message = getMessageForException(err) ?? 'unknown error'; + showAlertToStaff('Error restoring User Keys backup', message); + console.log('Error restoring User Keys backup:', message); + } + } + try { const promise = (async () => { const backupID = await createUserKeysBackup(); @@ -117,6 +188,7 @@ latestBackupInfo, showAlertToStaff, staffCanSee, + testUserKeysRestore, ]); return null;