Download backup on link

This commit is contained in:
Fedor Indutny 2024-08-08 12:22:48 -07:00 committed by GitHub
parent ec36ae7f26
commit 5c350a0e3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 67 additions and 2 deletions

View file

@ -1482,6 +1482,12 @@ export async function startApp(): Promise<void> {
)
);
// Now that we authenticated - time to download the backup!
if (isBackupEnabled()) {
backupsService.start();
drop(backupsService.download());
}
// Cancel throttled calls to refreshRemoteConfig since our auth changed.
window.Signal.RemoteConfig.maybeRefreshRemoteConfig.cancel();
drop(window.Signal.RemoteConfig.maybeRefreshRemoteConfig(server));

View file

@ -1,6 +1,7 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { type Readable } from 'node:stream';
import { strictAssert } from '../../util/assert';
import type {
WebAPIType,
@ -66,6 +67,18 @@ export class BackupAPI {
});
}
public async download(): Promise<Readable> {
const { cdn, backupDir, backupName } = await this.getInfo();
const { headers } = await this.credentials.getCDNReadCredentials(cdn);
return this.server.getBackupStream({
cdn,
backupDir,
backupName,
headers,
});
}
public async getMediaUploadForm(): Promise<AttachmentUploadFormResponseType> {
return this.server.getBackupMediaUploadForm(
await this.credentials.getHeadersForToday()

View file

@ -129,6 +129,21 @@ export class BackupsService {
return backupsService.importBackup(() => createReadStream(backupFile));
}
public async download(): Promise<void> {
const path = window.Signal.Migrations.getAbsoluteTempPath(
randomBytes(32).toString('hex')
);
const stream = await this.api.download();
await pipeline(stream, createWriteStream(path));
try {
await this.importFromDisk(path);
} finally {
await unlink(path);
}
}
public async importBackup(createBackupStream: () => Readable): Promise<void> {
strictAssert(!this.isRunning, 'BackupService is already running');
@ -281,7 +296,7 @@ export class BackupsService {
await this.api.refresh();
log.info('Backup: refreshed');
} catch (error) {
log.error('Backup: periodic refresh kufailed', Errors.toLogFormat(error));
log.error('Backup: periodic refresh failed', Errors.toLogFormat(error));
}
}
}

View file

@ -1147,8 +1147,15 @@ export type GetBackupCDNCredentialsResponseType = z.infer<
typeof getBackupCDNCredentialsResponseSchema
>;
export type GetBackupStreamOptionsType = Readonly<{
cdn: number;
backupDir: string;
backupName: string;
headers: Record<string, string>;
}>;
export const getBackupInfoResponseSchema = z.object({
cdn: z.number(),
cdn: z.literal(3),
backupDir: z.string(),
mediaDir: z.string(),
backupName: z.string(),
@ -1380,6 +1387,7 @@ export type WebAPIType = {
getBackupInfo: (
headers: BackupPresentationHeadersType
) => Promise<GetBackupInfoResponseType>;
getBackupStream: (options: GetBackupStreamOptionsType) => Promise<Readable>;
getBackupUploadForm: (
headers: BackupPresentationHeadersType
) => Promise<AttachmentUploadFormResponseType>;
@ -1707,6 +1715,7 @@ export function initialize({
getBackupCredentials,
getBackupCDNCredentials,
getBackupInfo,
getBackupStream,
getBackupMediaUploadForm,
getBackupUploadForm,
getBadgeImageFile,
@ -2764,6 +2773,20 @@ export function initialize({
return getBackupInfoResponseSchema.parse(res);
}
async function getBackupStream({
headers,
cdn,
backupDir,
backupName,
}: GetBackupStreamOptionsType): Promise<Readable> {
return _getAttachment({
cdnPath: `/backups/${encodeURIComponent(backupDir)}/${encodeURIComponent(backupName)}`,
cdnNumber: cdn,
redactor: _createRedactor(backupDir, backupName),
headers,
});
}
async function getBackupMediaUploadForm(
headers: BackupPresentationHeadersType
) {

View file

@ -2,7 +2,15 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as RemoteConfig from '../RemoteConfig';
import { Environment, getEnvironment } from '../environment';
import { isStaging } from './version';
export function isBackupEnabled(): boolean {
if (getEnvironment() === Environment.Staging) {
return true;
}
if (isStaging(window.getVersion())) {
return true;
}
return Boolean(RemoteConfig.isEnabled('desktop.backup.credentialFetch'));
}