mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-24 16:02:20 +08:00
Merge branch 'main' into next
This commit is contained in:
+50
-1
@@ -12,7 +12,7 @@ import { BackupManager, BackupSettings } from '../../managers/backup';
|
||||
import { MockServer } from '@nocobase/test';
|
||||
import path from 'path';
|
||||
import { storagePathJoin } from '@nocobase/utils';
|
||||
import { BACKUP_EXTENSION, METADATA_EXTENSION } from '../../utils';
|
||||
import { BACKUP_EXTENSION, METADATA_EXTENSION, SETTINGS } from '../../utils';
|
||||
import fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import PluginFileManagerServer from '@nocobase/plugin-file-manager';
|
||||
@@ -126,6 +126,55 @@ describe('BackupManager', async () => {
|
||||
expect(files).toContain(`${backupFileBaseName}.nbdata`);
|
||||
});
|
||||
|
||||
it('should upload using configured cloud storage when backup options omit storageId', async () => {
|
||||
const uploadFile = vi.fn().mockResolvedValue({ filename: 'cloud-backups/backup_for_unit_tests.nbdata' });
|
||||
const storageRepository = {
|
||||
findOne: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
title: 'Aliyun OSS',
|
||||
type: 'ali-oss',
|
||||
name: 'aliyun-oss',
|
||||
baseUrl: '',
|
||||
options: {},
|
||||
}),
|
||||
};
|
||||
const getRepository = app.db.getRepository.bind(app.db);
|
||||
const getRepositorySpy = vi.spyOn(app.db, 'getRepository').mockImplementation((name: string) => {
|
||||
if (name === 'storages') {
|
||||
return storageRepository as ReturnType<typeof app.db.getRepository>;
|
||||
}
|
||||
return getRepository(name);
|
||||
});
|
||||
const pmGetSpy = vi.spyOn(app.pm, 'get').mockReturnValue({
|
||||
uploadFile,
|
||||
} as unknown as PluginFileManagerServer);
|
||||
const settingsRepository = app.db.getRepository(SETTINGS);
|
||||
const settings = await settingsRepository.findOne();
|
||||
await settingsRepository.update({
|
||||
values: { storageId: 1 },
|
||||
filterByTk: settings.get('id'),
|
||||
});
|
||||
const backupSettings = await settingsRepository.findOne();
|
||||
const backupManager = new BackupManager(app, null, backupSettings);
|
||||
|
||||
try {
|
||||
await backupManager.backup(backupFileBaseName, {
|
||||
description: 'Manual backup from settings page',
|
||||
});
|
||||
|
||||
expect(storageRepository.findOne).toHaveBeenCalledWith({
|
||||
filterByTk: 1,
|
||||
});
|
||||
expect(uploadFile).toHaveBeenCalledWith({
|
||||
filePath: finalBackupFilePath,
|
||||
storageName: 'aliyun-oss',
|
||||
});
|
||||
} finally {
|
||||
getRepositorySpy.mockRestore();
|
||||
pmGetSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should honor enableFilesBackup from backup options', async () => {
|
||||
const backupManager = new BackupManager(app, null, defaultBackupSettings);
|
||||
const fileCollection = {
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
const BACKUP_METADATA_VERSION = 2;
|
||||
|
||||
export interface BackupSettings {
|
||||
storageId?: string;
|
||||
storageId?: string | number | null;
|
||||
encryptionPassword: string;
|
||||
enableFilesBackup: boolean;
|
||||
keep?: number;
|
||||
@@ -71,6 +71,10 @@ export interface BackupTaskResult {
|
||||
inProgress: boolean;
|
||||
}
|
||||
|
||||
type BackupSettingsInput = BackupSettings & {
|
||||
toJSON?: () => Partial<BackupSettings>;
|
||||
};
|
||||
|
||||
export class BackupManager {
|
||||
app: Application;
|
||||
ctx: ResourcerContext | null; // when triggered by cron job, ctx is null
|
||||
@@ -83,10 +87,10 @@ export class BackupManager {
|
||||
#uploadDir: string;
|
||||
#aesKeyPath: string;
|
||||
|
||||
constructor(app: Application, ctx: ResourcerContext | null, settings: BackupSettings) {
|
||||
constructor(app: Application, ctx: ResourcerContext | null, settings: BackupSettingsInput) {
|
||||
this.app = app;
|
||||
this.ctx = ctx;
|
||||
this.#settings = settings;
|
||||
this.#settings = this.#normalizeBackupSettings(settings);
|
||||
this.#dbAdapter = getDBAdapter(app.db.options);
|
||||
this.#backupTasksCacheName = BACKUP_TASKS_CACHE_NAME;
|
||||
this.#backupPrefix = 'backup_';
|
||||
@@ -116,6 +120,14 @@ export class BackupManager {
|
||||
this.#backupTasksCacheName = backupTasksCacheName;
|
||||
}
|
||||
|
||||
#normalizeBackupSettings(settings: BackupSettingsInput): BackupSettings {
|
||||
if (typeof settings.toJSON === 'function') {
|
||||
return settings.toJSON() as BackupSettings;
|
||||
}
|
||||
|
||||
return { ...settings };
|
||||
}
|
||||
|
||||
async createBackupName() {
|
||||
await this.#dbAdapter.check('backup');
|
||||
await fsPromises.mkdir(this.#backupDir, { recursive: true });
|
||||
@@ -454,7 +466,7 @@ export class BackupManager {
|
||||
return output;
|
||||
}
|
||||
|
||||
async #uploadFiles(filePath: string, storageId?: string) {
|
||||
async #uploadFiles(filePath: string, storageId?: BackupSettings['storageId']) {
|
||||
if (!storageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user