signal-desktop/ts/services/backups/api.ts

169 lines
4.3 KiB
TypeScript
Raw Normal View History

2024-04-22 14:11:36 +00:00
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2024-08-08 19:22:48 +00:00
import { type Readable } from 'node:stream';
2024-10-31 17:01:03 +00:00
2024-04-22 14:11:36 +00:00
import { strictAssert } from '../../util/assert';
import type {
WebAPIType,
AttachmentUploadFormResponseType,
2024-04-22 14:11:36 +00:00
GetBackupInfoResponseType,
BackupMediaItemType,
BackupMediaBatchResponseType,
BackupListMediaResponseType,
} from '../../textsecure/WebAPI';
import type { BackupCredentials } from './credentials';
2024-10-31 17:01:03 +00:00
import { BackupCredentialType } from '../../types/backups';
2024-05-20 19:29:20 +00:00
import { uploadFile } from '../../util/uploadAttachment';
2024-04-22 14:11:36 +00:00
2024-08-27 21:00:41 +00:00
export type DownloadOptionsType = Readonly<{
downloadOffset: number;
onProgress: (currentBytes: number, totalBytes: number) => void;
2024-09-11 18:03:18 +00:00
abortSignal?: AbortSignal;
2024-08-27 21:00:41 +00:00
}>;
2024-04-22 14:11:36 +00:00
export class BackupAPI {
2024-10-31 17:01:03 +00:00
private cachedBackupInfo = new Map<
BackupCredentialType,
GetBackupInfoResponseType
>();
constructor(private readonly credentials: BackupCredentials) {}
2024-04-22 14:11:36 +00:00
public async refresh(): Promise<void> {
2024-10-31 17:01:03 +00:00
const headers = await Promise.all(
[BackupCredentialType.Messages, BackupCredentialType.Media].map(type =>
this.credentials.getHeadersForToday(type)
)
2024-04-22 14:11:36 +00:00
);
2024-10-31 17:01:03 +00:00
await Promise.all(headers.map(h => this.server.refreshBackup(h)));
2024-04-22 14:11:36 +00:00
}
2024-10-31 17:01:03 +00:00
public async getInfo(
credentialType: BackupCredentialType
): Promise<GetBackupInfoResponseType> {
const backupInfo = await this.server.getBackupInfo(
2024-10-31 17:01:03 +00:00
await this.credentials.getHeadersForToday(credentialType)
2024-04-22 14:11:36 +00:00
);
2024-10-31 17:01:03 +00:00
this.cachedBackupInfo.set(credentialType, backupInfo);
return backupInfo;
}
2024-10-31 17:01:03 +00:00
private async getCachedInfo(
credentialType: BackupCredentialType
): Promise<GetBackupInfoResponseType> {
const cached = this.cachedBackupInfo.get(credentialType);
if (cached) {
return cached;
}
2024-10-31 17:01:03 +00:00
return this.getInfo(credentialType);
}
public async getMediaDir(): Promise<string> {
2024-10-31 17:01:03 +00:00
return (await this.getCachedInfo(BackupCredentialType.Media)).mediaDir;
}
public async getBackupDir(): Promise<string> {
2024-10-31 17:01:03 +00:00
return (await this.getCachedInfo(BackupCredentialType.Media))?.backupDir;
2024-04-22 14:11:36 +00:00
}
2024-05-14 17:04:50 +00:00
public async upload(filePath: string, fileSize: number): Promise<void> {
const form = await this.server.getBackupUploadForm(
2024-10-31 17:01:03 +00:00
await this.credentials.getHeadersForToday(BackupCredentialType.Messages)
2024-05-14 17:04:50 +00:00
);
2024-05-20 19:29:20 +00:00
await uploadFile({
absoluteCiphertextPath: filePath,
ciphertextFileSize: fileSize,
uploadForm: form,
});
2024-04-22 14:11:36 +00:00
}
2024-08-27 21:00:41 +00:00
public async download({
downloadOffset,
onProgress,
2024-09-11 18:03:18 +00:00
abortSignal,
2024-08-27 21:00:41 +00:00
}: DownloadOptionsType): Promise<Readable> {
2024-10-31 17:01:03 +00:00
const { cdn, backupDir, backupName } = await this.getInfo(
BackupCredentialType.Messages
);
const { headers } = await this.credentials.getCDNReadCredentials(
cdn,
BackupCredentialType.Messages
);
2024-08-08 19:22:48 +00:00
return this.server.getBackupStream({
cdn,
backupDir,
backupName,
headers,
2024-08-27 21:00:41 +00:00
downloadOffset,
2024-10-18 17:15:03 +00:00
onProgress,
abortSignal,
});
}
public async downloadEphemeral({
downloadOffset,
onProgress,
abortSignal,
}: DownloadOptionsType): Promise<Readable> {
const { cdn, key } = await this.server.getTransferArchive({
abortSignal,
});
return this.server.getEphemeralBackupStream({
cdn,
key,
downloadOffset,
2024-08-27 21:00:41 +00:00
onProgress,
2024-09-11 18:03:18 +00:00
abortSignal,
2024-08-08 19:22:48 +00:00
});
}
public async getMediaUploadForm(): Promise<AttachmentUploadFormResponseType> {
2024-04-22 14:11:36 +00:00
return this.server.getBackupMediaUploadForm(
2024-10-31 17:01:03 +00:00
await this.credentials.getHeadersForToday(BackupCredentialType.Media)
2024-04-22 14:11:36 +00:00
);
}
public async backupMediaBatch(
items: ReadonlyArray<BackupMediaItemType>
): Promise<BackupMediaBatchResponseType> {
return this.server.backupMediaBatch({
2024-10-31 17:01:03 +00:00
headers: await this.credentials.getHeadersForToday(
BackupCredentialType.Media
),
2024-04-22 14:11:36 +00:00
items,
});
}
public async listMedia({
cursor,
limit,
}: {
cursor?: string;
limit: number;
}): Promise<BackupListMediaResponseType> {
return this.server.backupListMedia({
2024-10-31 17:01:03 +00:00
headers: await this.credentials.getHeadersForToday(
BackupCredentialType.Media
),
2024-04-22 14:11:36 +00:00
cursor,
limit,
});
}
public clearCache(): void {
2024-10-31 17:01:03 +00:00
this.cachedBackupInfo.clear();
}
2024-04-22 14:11:36 +00:00
private get server(): WebAPIType {
const { server } = window.textsecure;
strictAssert(server, 'server not available');
return server;
}
}