diff --git a/src/features/upload-file/providers/index.ts b/src/features/upload-file/providers/index.ts index 3eb84f4..a62d83b 100644 --- a/src/features/upload-file/providers/index.ts +++ b/src/features/upload-file/providers/index.ts @@ -2,3 +2,4 @@ export * from './aws-provider.js' export * from './base-provider.js' export * from './gcp-provider.js' export * from './local-provider.js' +export * from './minio-provider.js' diff --git a/src/features/upload-file/providers/minio-provider.ts b/src/features/upload-file/providers/minio-provider.ts new file mode 100644 index 0000000..ee17404 --- /dev/null +++ b/src/features/upload-file/providers/minio-provider.ts @@ -0,0 +1,95 @@ +import fs from 'fs' +// eslint-disable-next-line import/no-extraneous-dependencies +import { S3 } from 'aws-sdk' +import { UploadedFile } from 'adminjs' +import { ERROR_MESSAGES, DAY_IN_MINUTES } from '../constants' + +import { BaseProvider } from './base-provider' + +/** + * AWS Credentials which can be set for S3 file upload. + * If not given, 'aws-sdk' will try to fetch them from + * environmental variables. + * @memberof module:@adminjs/upload + */ +export type MinIoOptions = { + /** + * MinIo endpoint + */ + endpoint: string; + /** + * AWS IAM accessKeyId. By default its value is taken from AWS_ACCESS_KEY_ID env variable + */ + accessKeyId?: string; + /** + * AWS IAM secretAccessKey. By default its value is taken from AWS_SECRET_ACCESS_KEY env variable + */ + secretAccessKey?: string; + + /** + * S3 Bucket where files will be stored + */ + bucket: string; + /** + * indicates how long links should be available after page load (in minutes). + * Default to 24h. If set to 0 adapter will mark uploaded files as PUBLIC ACL. + */ + expires?: number; +} + +export class MinIoProvider extends BaseProvider { + private s3: S3 + + private endpoint: string + + public expires: number + + constructor(options: MinIoOptions) { + super(options.bucket) + + let AWS_S3: typeof S3 + try { + // eslint-disable-next-line + const AWS = require('aws-sdk') + AWS_S3 = AWS.S3 + } catch (error) { + throw new Error(ERROR_MESSAGES.NO_AWS_SDK) + } + this.expires = options.expires ?? DAY_IN_MINUTES + this.endpoint = options.endpoint + this.s3 = new AWS_S3({ + s3ForcePathStyle: true, // needed with minio? + signatureVersion: 'v4', + ...options }) + } + + public async upload(file: UploadedFile, key: string): Promise { + const uploadOptions = { partSize: 5 * 1024 * 1024, queueSize: 10 } + const tmpFile = fs.createReadStream(file.path) + const params: S3.PutObjectRequest = { + Bucket: this.bucket, + Key: key, + Body: tmpFile, + } + if (!this.expires) { + params.ACL = 'public-read' + } + return this.s3.upload(params, uploadOptions).promise() + } + + public async delete(key: string, bucket: string): Promise { + return this.s3.deleteObject({ Key: key, Bucket: bucket }).promise() + } + + public async path(key: string, bucket: string): Promise { + if (this.expires) { + return this.s3.getSignedUrl('getObject', { + Key: key, + Bucket: bucket, + Expires: this.expires, + }) + } + // https://bucket.s3.amazonaws.com/key + return `${this.endpoint}/${bucket}/${key}` + } +} diff --git a/src/features/upload-file/types/upload-options.type.ts b/src/features/upload-file/types/upload-options.type.ts index 104e403..4835498 100644 --- a/src/features/upload-file/types/upload-options.type.ts +++ b/src/features/upload-file/types/upload-options.type.ts @@ -3,6 +3,7 @@ import { AWSOptions } from '../providers/aws-provider.js' import { BaseProvider } from '../providers/base-provider.js' import { GCPOptions } from '../providers/gcp-provider.js' import { LocalUploadOptions } from '../providers/local-provider.js' +import { MinIoOptions } from '../providers/minio-provider.js' /** * Function which defines where in the bucket file should be stored. @@ -41,6 +42,7 @@ export type UploadOptions = { * Options for the provider */ provider: { + minio?: MinIoOptions; /** AWS Credentials */ aws?: AWSOptions, /** GCP Credentials */ diff --git a/src/features/upload-file/utils/get-provider.ts b/src/features/upload-file/utils/get-provider.ts index f6d7495..244f26f 100644 --- a/src/features/upload-file/utils/get-provider.ts +++ b/src/features/upload-file/utils/get-provider.ts @@ -1,6 +1,7 @@ import { ERROR_MESSAGES } from '../constants.js' import { AWSProvider } from '../providers/aws-provider.js' import { GCPProvider } from '../providers/gcp-provider.js' +import { MinIoProvider } from '../providers/minio-provider' import { BaseProvider } from '../providers/index.js' import { LocalProvider } from '../providers/local-provider.js' import { AvailableDefaultProviders, UploadOptions } from '../types/upload-options.type.js' @@ -33,6 +34,7 @@ export const getProvider = (options: UploadOptions['provider']): GetProviderRetu const providerOptions = options[providerName] const providerMap: Record BaseProvider> = { + minio: () => new MinIoProvider(providerOptions), aws: () => new AWSProvider(providerOptions), gcp: () => new GCPProvider(providerOptions), local: () => new LocalProvider(providerOptions),