diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index e65df86e1c..fd88e67738 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -283,10 +283,17 @@ export function devTool ( program .command('backup-restore [date]') + .option('-m, --merge', 'Enable merge of remote and backup content.', false) .description('dump workspace transactions and minio resources') - .action(async (dirName: string, workspace: string, date, cmd) => { + .action(async (dirName: string, workspace: string, date, cmd: { merge: boolean }) => { const storage = await createFileBackupStorage(dirName) - return await restore(transactorUrl, getWorkspaceId(workspace, productId), storage, parseInt(date ?? '-1')) + return await restore( + transactorUrl, + getWorkspaceId(workspace, productId), + storage, + parseInt(date ?? '-1'), + cmd.merge + ) }) program diff --git a/pods/backup/src/config.ts b/pods/backup/src/config.ts index e9caa797cc..3c9c859437 100644 --- a/pods/backup/src/config.ts +++ b/pods/backup/src/config.ts @@ -25,8 +25,6 @@ interface Config { MinioEndpoint: string MinioAccessKey: string MinioSecretKey: string - - ProductId: string } const envMap: { [key in keyof Config]: string } = { @@ -38,8 +36,7 @@ const envMap: { [key in keyof Config]: string } = { Interval: 'INTERVAL', MinioEndpoint: 'MINIO_ENDPOINT', MinioAccessKey: 'MINIO_ACCESS_KEY', - MinioSecretKey: 'MINIO_SECRET_KEY', - ProductId: 'PRODUCT_ID' + MinioSecretKey: 'MINIO_SECRET_KEY' } const required: Array = [ @@ -63,8 +60,7 @@ const config: Config = (() => { Interval: parseInt(process.env[envMap.Interval] ?? '3600'), MinioEndpoint: process.env[envMap.MinioEndpoint], MinioAccessKey: process.env[envMap.MinioAccessKey], - MinioSecretKey: process.env[envMap.MinioSecretKey], - ProductId: process.env[envMap.ProductId] ?? '' + MinioSecretKey: process.env[envMap.MinioSecretKey] } const missingEnv = required.filter((key) => params[key] === undefined).map((key) => envMap[key]) diff --git a/pods/backup/src/platform.ts b/pods/backup/src/platform.ts index a4b33622d6..d7c08eb6e5 100644 --- a/pods/backup/src/platform.ts +++ b/pods/backup/src/platform.ts @@ -19,9 +19,21 @@ import { setMetadata } from '@hcengineering/platform' import { backup, createMinioBackupStorage } from '@hcengineering/server-backup' import serverToken from '@hcengineering/server-token' import got from 'got' +import { ObjectId } from 'mongodb' import config from './config' -async function getWorkspaces (): Promise { +/** + * @public + */ +export interface Workspace { + _id: ObjectId + workspace: string + organisation: string + accounts: ObjectId[] + productId: string +} + +async function getWorkspaces (): Promise { const { body }: { body: { error?: string, result?: any[] } } = await got.post(config.AccountsURL, { json: { method: 'listWorkspaces', @@ -34,7 +46,7 @@ async function getWorkspaces (): Promise { throw Error(body.error) } - return (body.result ?? []).map((x) => x.workspace) + return (body.result as Workspace[]) ?? [] } export class PlatformWorker { @@ -79,8 +91,12 @@ export class PlatformWorker { for (const ws of workspaces) { console.log('\n\nBACKUP WORKSPACE ', ws) try { - const storage = await createMinioBackupStorage(this.minio, getWorkspaceId('backups', config.ProductId), ws) - await backup(config.TransactorURL, getWorkspaceId(ws, config.ProductId), storage) + const storage = await createMinioBackupStorage( + this.minio, + getWorkspaceId('backups', ws.productId), + ws.workspace + ) + await backup(config.TransactorURL, getWorkspaceId(ws.workspace, ws.productId), storage) } catch (err: any) { console.error('\n\nFAILED to BACKUP', ws, err) } diff --git a/server/account/src/index.ts b/server/account/src/index.ts index a05c1d8d09..171241e9f7 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -377,7 +377,9 @@ export async function createAccount ( * @public */ export async function listWorkspaces (db: Db, productId: string): Promise { - return await db.collection(WORKSPACE_COLLECTION).find(withProductId(productId, {})).toArray() + return (await db.collection(WORKSPACE_COLLECTION).find(withProductId(productId, {})).toArray()).map( + (it) => ({ ...it, productId }) + ) } /** diff --git a/server/backup/src/index.ts b/server/backup/src/index.ts index 397e8095ee..f92598c0f0 100644 --- a/server/backup/src/index.ts +++ b/server/backup/src/index.ts @@ -326,7 +326,8 @@ export async function restore ( transactorUrl: string, workspaceId: WorkspaceId, storage: BackupStorage, - date: number + date: number, + merge?: boolean ): Promise { const infoFile = 'backup.json.gz' @@ -357,6 +358,7 @@ export async function restore ( mode: 'backup', model: 'upgrade' })) as unknown as CoreClient & BackupClient + try { for (const c of domains) { console.log('loading server changeset for', c) @@ -514,7 +516,7 @@ export async function restore ( } await sendChunk(undefined, 0) - if (docsToRemove.length > 0) { + if (docsToRemove.length > 0 && merge !== true) { console.log('cleanup', docsToRemove.length) await connection.clean(c, docsToRemove) }