diff --git a/native/backup/api.js b/native/backup/api.js index 2ae5c6702..40676f6f6 100644 --- a/native/backup/api.js +++ b/native/backup/api.js @@ -1,75 +1,102 @@ // @flow import base64 from 'base-64'; import backupService from 'lib/facts/backup-service.js'; import type { BackupAuth, BackupEncrypted } from 'lib/types/backup-types.js'; import { makeBackupServiceEndpointURL } from 'lib/utils/backup-service.js'; import { toBase64URL } from 'lib/utils/base64.js'; +import { getBackupBytesFromBlob } from './conversion-utils.js'; import { commUtilsModule } from '../native-modules.js'; function getBackupFormData(backup: BackupEncrypted): FormData { const { backupID, userKeys, userData } = backup; const userKeysHash = commUtilsModule.sha256(userKeys.buffer); const userDataHash = commUtilsModule.sha256(userData.buffer); const userKeysStr = commUtilsModule.base64EncodeBuffer(userKeys.buffer); const userDataStr = commUtilsModule.base64EncodeBuffer(userData.buffer); const formData = new FormData(); formData.append('backup_id', backupID); formData.append('user_keys_hash', toBase64URL(userKeysHash)); formData.append('user_keys', userKeysStr); formData.append('user_data_hash', toBase64URL(userDataHash)); formData.append('user_data', userDataStr); formData.append('attachments', ''); return formData; } function getBackupAuthorizationHeader(auth: BackupAuth) { const authStr = JSON.stringify(auth); const authBase64 = base64.encode(authStr); return `Bearer ${authBase64}`; } async function uploadBackup(backup: BackupEncrypted, auth: BackupAuth) { const authHeader = getBackupAuthorizationHeader(auth); const uploadBackupEndpoint = backupService.httpEndpoints.UPLOAD_BACKUP; const sendBackupResponse = await fetch( makeBackupServiceEndpointURL(uploadBackupEndpoint), { method: uploadBackupEndpoint.method, body: getBackupFormData(backup), headers: { 'Content-Type': 'multipart/form-data', 'Authorization': authHeader, }, }, ); if (!sendBackupResponse.ok) { const { status, statusText } = sendBackupResponse; throw new Error(`Server responded with HTTP ${status}: ${statusText}`); } } async function getBackupID(username: string): Promise { const getBackupIDEndpoint = backupService.httpEndpoints.GET_LATEST_BACKUP_ID; const getBackupIDResponse = await fetch( makeBackupServiceEndpointURL(getBackupIDEndpoint, { username }), { method: getBackupIDEndpoint.method, }, ); if (!getBackupIDResponse.ok) { const { status, statusText } = getBackupIDResponse; throw new Error(`Server responded with HTTP ${status}: ${statusText}`); } const { backupID } = await getBackupIDResponse.json(); return backupID; } -export { uploadBackup, getBackupID }; +async function getUserKeys( + backupID: string, + auth: BackupAuth, +): Promise { + const authHeader = getBackupAuthorizationHeader(auth); + + const getUserKeysEndpoint = backupService.httpEndpoints.GET_USER_KEYS; + const getUserKeysResponse = await fetch( + makeBackupServiceEndpointURL(getUserKeysEndpoint, { backupID }), + { + method: getUserKeysEndpoint.method, + headers: { + Authorization: authHeader, + }, + }, + ); + + if (!getUserKeysResponse.ok) { + const { status, statusText } = getUserKeysResponse; + throw new Error(`Server responded with HTTP ${status}: ${statusText}`); + } + + const blob = await getUserKeysResponse.blob(); + return getBackupBytesFromBlob(blob); +} + +export { uploadBackup, getBackupID, getUserKeys }; diff --git a/native/backup/conversion-utils.js b/native/backup/conversion-utils.js new file mode 100644 index 000000000..da8826e1b --- /dev/null +++ b/native/backup/conversion-utils.js @@ -0,0 +1,13 @@ +// @flow + +import { commUtilsModule } from '../native-modules.js'; +import { arrayBufferFromBlob } from '../utils/blob-utils-module.js'; + +function getBackupBytesFromBlob(blob: Blob): Uint8Array { + const buffer = arrayBufferFromBlob(blob); + const str = commUtilsModule.decodeUTF8ArrayBufferToString(buffer); + const decodedBuffer = commUtilsModule.base64DecodeBuffer(str); + return new Uint8Array(decodedBuffer); +} + +export { getBackupBytesFromBlob };