import { WorkspaceId } from '@hcengineering/core' import { MinioService } from '@hcengineering/minio' import { createReadStream, createWriteStream, existsSync } from 'fs' import { mkdir, readFile, writeFile } from 'fs/promises' import { dirname, join } from 'path' import { PassThrough, Readable, Writable } from 'stream' /** * @public */ export interface BackupStorage { loadFile: (name: string) => Promise load: (name: string) => Promise write: (name: string) => Promise writeFile: (name: string, data: string | Buffer) => Promise exists: (name: string) => Promise } class FileStorage implements BackupStorage { constructor (readonly root: string) {} async loadFile (name: string): Promise { return await readFile(join(this.root, name)) } async write (name: string): Promise { const fileName = join(this.root, name) const dir = dirname(fileName) if (!existsSync(dir)) { await mkdir(dir, { recursive: true }) } return createWriteStream(join(this.root, name)) } async load (name: string): Promise { return createReadStream(join(this.root, name)) } async exists (name: string): Promise { return existsSync(join(this.root, name)) } async writeFile (name: string, data: string | Buffer): Promise { const fileName = join(this.root, name) const dir = dirname(fileName) if (!existsSync(dir)) { await mkdir(dir, { recursive: true }) } await writeFile(fileName, data) } } class MinioStorage implements BackupStorage { constructor (readonly client: MinioService, readonly workspaceId: WorkspaceId, readonly root: string) {} async loadFile (name: string): Promise { const data = await this.client.read(this.workspaceId, join(this.root, name)) return Buffer.concat(data) } async write (name: string): Promise { const wr = new PassThrough() void this.client.put(this.workspaceId, join(this.root, name), wr) return wr } async load (name: string): Promise { return await this.client.get(this.workspaceId, join(this.root, name)) } async exists (name: string): Promise { try { await this.client.stat(this.workspaceId, join(this.root, name)) return true } catch (err) { return false } } async writeFile (name: string, data: string | Buffer): Promise { void this.client.put(this.workspaceId, join(this.root, name), data, data.length) } } /** * @public */ export async function createFileBackupStorage (fileName: string): Promise { if (!existsSync(fileName)) { await mkdir(fileName, { recursive: true }) } return new FileStorage(fileName) } /** * @public */ export async function createMinioBackupStorage ( client: MinioService, workspaceId: WorkspaceId, root: string ): Promise { if (!(await client.exists(workspaceId))) { await client.make(workspaceId) } return new MinioStorage(client, workspaceId, root) }