import { FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports' import { AppState } from 'app/application' import { shell } from 'electron' import { StoreKeys } from '../Store' import { ensureDirectoryExists, moveDirContents, openDirectoryPicker, readJSONFile, writeFile, writeJSONFile, } from '../Utils/FileUtils' import { FileDownloader } from './FileDownloader' export const FileBackupsConstantsV1 = { Version: '1.0.0', MetadataFileName: 'metadata.sn.json', BinaryFileName: 'file.encrypted', } export class FilesBackupManager implements FileBackupsDevice { constructor(private appState: AppState) {} public isFilesBackupsEnabled(): Promise { return Promise.resolve(this.appState.store.get(StoreKeys.FileBackupsEnabled)) } public async enableFilesBackups(): Promise { const currentLocation = await this.getFilesBackupsLocation() if (!currentLocation) { const result = await this.changeFilesBackupsLocation() if (!result) { return } } this.appState.store.set(StoreKeys.FileBackupsEnabled, true) const mapping = this.getMappingFileFromDisk() if (!mapping) { await this.saveFilesBackupsMappingFile(this.defaultMappingFileValue()) } } public disableFilesBackups(): Promise { this.appState.store.set(StoreKeys.FileBackupsEnabled, false) return Promise.resolve() } public async changeFilesBackupsLocation(): Promise { const newPath = await openDirectoryPicker() if (!newPath) { return undefined } const oldPath = await this.getFilesBackupsLocation() if (oldPath) { await moveDirContents(oldPath, newPath) } this.appState.store.set(StoreKeys.FileBackupsLocation, newPath) return newPath } public getFilesBackupsLocation(): Promise { return Promise.resolve(this.appState.store.get(StoreKeys.FileBackupsLocation)) } private getMappingFileLocation(): string { const base = this.appState.store.get(StoreKeys.FileBackupsLocation) return `${base}/info.json` } private async getMappingFileFromDisk(): Promise { return readJSONFile(this.getMappingFileLocation()) } private defaultMappingFileValue(): FileBackupsMapping { return { version: FileBackupsConstantsV1.Version, files: {} } } async getFilesBackupsMappingFile(): Promise { const data = await this.getMappingFileFromDisk() if (!data) { return this.defaultMappingFileValue() } return data } async openFilesBackupsLocation(): Promise { const location = await this.getFilesBackupsLocation() void shell.openPath(location) } async saveFilesBackupsMappingFile(file: FileBackupsMapping): Promise<'success' | 'failed'> { await writeJSONFile(this.getMappingFileLocation(), file) return 'success' } async saveFilesBackupsFile( uuid: string, metaFile: string, downloadRequest: { chunkSizes: number[] valetToken: string url: string }, ): Promise<'success' | 'failed'> { const backupsDir = await this.getFilesBackupsLocation() const fileDir = `${backupsDir}/${uuid}` const metaFilePath = `${fileDir}/${FileBackupsConstantsV1.MetadataFileName}` const binaryPath = `${fileDir}/${FileBackupsConstantsV1.BinaryFileName}` await ensureDirectoryExists(fileDir) await writeFile(metaFilePath, metaFile) const downloader = new FileDownloader( downloadRequest.chunkSizes, downloadRequest.valetToken, downloadRequest.url, binaryPath, ) const result = await downloader.run() if (result === 'success') { const mapping = await this.getFilesBackupsMappingFile() mapping.files[uuid] = { backedUpOn: new Date(), absolutePath: fileDir, relativePath: uuid, metadataFileName: FileBackupsConstantsV1.MetadataFileName, binaryFileName: FileBackupsConstantsV1.BinaryFileName, version: FileBackupsConstantsV1.Version, } await this.saveFilesBackupsMappingFile(mapping) } return result } }