Files
standardnotes-app-web/packages/files/src/Domain/Operations/DownloadAndDecrypt.ts
2022-07-05 20:36:00 +02:00

76 lines
2.4 KiB
TypeScript

import { ClientDisplayableError } from '@standardnotes/responses'
import { AbortFunction, FileDownloader } from '../UseCase/FileDownloader'
import { FileDecryptor } from '../UseCase/FileDecryptor'
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
import { FilesApiInterface } from '@standardnotes/services'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { FileContent } from '@standardnotes/models'
import { DecryptedBytes, EncryptedBytes } from '@standardnotes/filepicker'
export type DownloadAndDecryptResult = { success: boolean; error?: ClientDisplayableError; aborted?: boolean }
type OnBytesCallback = (results: {
decrypted: DecryptedBytes
encrypted: EncryptedBytes
progress: FileDownloadProgress
}) => Promise<void>
export class DownloadAndDecryptFileOperation {
private downloader: FileDownloader
constructor(
private readonly file: {
encryptedChunkSizes: FileContent['encryptedChunkSizes']
encryptionHeader: FileContent['encryptionHeader']
remoteIdentifier: FileContent['remoteIdentifier']
key: FileContent['key']
},
private readonly crypto: PureCryptoInterface,
private readonly api: FilesApiInterface,
) {
this.downloader = new FileDownloader(this.file, this.api)
}
private createDecryptor(): FileDecryptor {
return new FileDecryptor(this.file, this.crypto)
}
public async run(onBytes: OnBytesCallback): Promise<DownloadAndDecryptResult> {
const decryptor = this.createDecryptor()
let decryptError: ClientDisplayableError | undefined
const onDownloadBytes = async (
encryptedBytes: Uint8Array,
progress: FileDownloadProgress,
abortDownload: AbortFunction,
) => {
const result = decryptor.decryptBytes(encryptedBytes)
if (!result || result.decryptedBytes.length === 0) {
decryptError = new ClientDisplayableError('Failed to decrypt chunk')
abortDownload()
return
}
const decryptedBytes = result.decryptedBytes
await onBytes({ decrypted: { decryptedBytes }, encrypted: { encryptedBytes }, progress })
}
const downloadResult = await this.downloader.run(onDownloadBytes)
return {
success: downloadResult instanceof ClientDisplayableError ? false : true,
error: downloadResult === 'aborted' ? undefined : downloadResult || decryptError,
aborted: downloadResult === 'aborted',
}
}
abort(): void {
this.downloader.abort()
}
}