// // Copyright © 2022 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import { BucketItem, BucketItemStat, Client, ItemBucketMetadata, UploadedObjectInfo } from 'minio' import { Readable as ReadableStream } from 'stream' import { toWorkspaceString, WorkspaceId } from '@hcengineering/core' /** * @public */ export type MinioWorkspaceItem = BucketItem & { metaData: ItemBucketMetadata } /** * @public */ export function getBucketId (workspaceId: WorkspaceId): string { return toWorkspaceString(workspaceId, '.') } /** * @public */ export class MinioService { client: Client constructor (opt: { endPoint: string, port: number, accessKey: string, secretKey: string, useSSL: boolean }) { this.client = new Client(opt) } async exists (workspaceId: WorkspaceId): Promise { return await this.client.bucketExists(getBucketId(workspaceId)) } async make (workspaceId: WorkspaceId): Promise { await this.client.makeBucket(getBucketId(workspaceId), 'k8s') } async remove (workspaceId: WorkspaceId, objectNames: string[]): Promise { await this.client.removeObjects(getBucketId(workspaceId), objectNames) } async delete (workspaceId: WorkspaceId): Promise { await this.client.removeBucket(getBucketId(workspaceId)) } async list (workspaceId: WorkspaceId, prefix?: string): Promise { const items = new Map() const list = await this.client.listObjects(getBucketId(workspaceId), prefix, true) await new Promise((resolve) => { list.on('data', (data) => { items.set(data.name, { metaData: {}, ...data }) }) list.on('end', () => { resolve(null) }) }) return Array.from(items.values()) } async stat (workspaceId: WorkspaceId, objectName: string): Promise { return await this.client.statObject(getBucketId(workspaceId), objectName) } async get (workspaceId: WorkspaceId, objectName: string): Promise { return await this.client.getObject(getBucketId(workspaceId), objectName) } async put ( workspaceId: WorkspaceId, objectName: string, stream: ReadableStream | Buffer | string, size?: number, metaData?: ItemBucketMetadata ): Promise { return await this.client.putObject(getBucketId(workspaceId), objectName, stream, size, metaData) } async read (workspaceId: WorkspaceId, name: string): Promise { const data = await this.client.getObject(getBucketId(workspaceId), name) const chunks: Buffer[] = [] await new Promise((resolve) => { data.on('readable', () => { let chunk while ((chunk = data.read()) !== null) { const b = chunk as Buffer chunks.push(b) } }) data.on('end', () => { resolve(null) }) }) return chunks } async partial ( workspaceId: WorkspaceId, objectName: string, offset: number, length?: number ): Promise { return await this.client.getPartialObject(getBucketId(workspaceId), objectName, offset, length) } }