mirror of https://github.com/msgbyte/tailchat
feat: add alioss or other s3 protocol external storage support #104
parent
cb927ae079
commit
584997826b
@ -1,22 +0,0 @@
|
||||
diff --git a/src/errors/MinioInitializationError.js b/src/errors/MinioInitializationError.js
|
||||
index 2022b251c5bc9c036c7456d6937c270f8f920d3a..ae9d3808d87f8fe0ee5e82076802da09a1a7f676 100644
|
||||
--- a/src/errors/MinioInitializationError.js
|
||||
+++ b/src/errors/MinioInitializationError.js
|
||||
@@ -1,4 +1,5 @@
|
||||
-const {MoleculerError} = require("moleculer/src/errors");
|
||||
+const {Errors} = require('moleculer');
|
||||
+const MoleculerError = Errors.MoleculerError;
|
||||
|
||||
/**
|
||||
* Error that should be thrown when the Minio Service can not be Initialized
|
||||
diff --git a/src/errors/MinioPingError.js b/src/errors/MinioPingError.js
|
||||
index f73f9423f3407fe828ba99db556b2f8367483fa3..d03e31c2a223b6182c659b0c13beb7e1f11751d4 100644
|
||||
--- a/src/errors/MinioPingError.js
|
||||
+++ b/src/errors/MinioPingError.js
|
||||
@@ -1,4 +1,5 @@
|
||||
-const {MoleculerRetryableError} = require("moleculer/src/errors");
|
||||
+const {Errors} = require('moleculer');
|
||||
+const MoleculerRetryableError = Errors.MoleculerRetryableError;
|
||||
|
||||
/**
|
||||
* Error that should be thrown when the Minio Backend can not be pinged
|
@ -0,0 +1,822 @@
|
||||
import { Client as MinioClient, CopyConditions } from 'minio';
|
||||
import { isString, isUndefined } from 'ramda-adjunct';
|
||||
import { Errors } from 'moleculer';
|
||||
|
||||
class MinioInitializationError extends Errors.MoleculerError {
|
||||
/**
|
||||
* Creates an instance of MinioInitializationError.
|
||||
*
|
||||
* @param {String?} message
|
||||
* @param {Number?} code
|
||||
* @param {String?} type
|
||||
* @param {any} data
|
||||
*
|
||||
* @memberof MinioInitializationError
|
||||
*/
|
||||
constructor(
|
||||
message = 'Minio can not be initialized',
|
||||
code = 500,
|
||||
type = 'MINIO_INITIALIZATION_ERROR',
|
||||
data = {}
|
||||
) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
class MinioPingError extends Errors.MoleculerRetryableError {
|
||||
/**
|
||||
* Creates an instance of MinioPingError.
|
||||
*
|
||||
* @param {String?} message
|
||||
* @param {Number?} code
|
||||
* @param {String?} type
|
||||
* @param {any} data
|
||||
*
|
||||
* @memberof MinioPingError
|
||||
*/
|
||||
constructor(
|
||||
message = 'Minio Backend not reachable',
|
||||
code = 502,
|
||||
type = 'MINIO_PING_ERROR',
|
||||
data = {}
|
||||
) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Service mixin for managing files in a Minio S3 backend
|
||||
*
|
||||
* @name moleculer-minio
|
||||
* @module Service
|
||||
*/
|
||||
|
||||
export const TcMinioService = {
|
||||
// Service name
|
||||
name: 'minio',
|
||||
|
||||
// Default settings
|
||||
settings: {
|
||||
/** @type {String} The Hostname minio is running on and available at. Hostname or IP-Address */
|
||||
endPoint: undefined,
|
||||
/** @type {Number} TCP/IP port number minio is listening on. Default value set to 80 for HTTP and 443 for HTTPs.*/
|
||||
port: undefined,
|
||||
/** @type {Boolean?} If set to true, https is used instead of http. Default is true.*/
|
||||
useSSL: true,
|
||||
/** @type {String} The AccessKey to use when connecting to minio */
|
||||
accessKey: undefined,
|
||||
/** @type {String} The SecretKey to use when connecting to minio */
|
||||
secretKey: undefined,
|
||||
/** @type {String?} Set this value to override region cache*/
|
||||
region: undefined,
|
||||
/** @type {String?} Set this value to pass in a custom transport. (Optional)*/
|
||||
transport: undefined,
|
||||
/** @type {String?} Set this value to provide x-amz-security-token (AWS S3 specific). (Optional)*/
|
||||
sessionToken: undefined,
|
||||
/** @type {Number?} This service will perform a periodic healthcheck of Minio. Use this setting to configure the inverval in which the healthcheck is performed. Set to `0` to turn healthcheks of */
|
||||
minioHealthCheckInterval: 5000,
|
||||
|
||||
/**
|
||||
* Path Style: <Schema>://<S3 Endpoint>/<Bucket>/<Object>
|
||||
* Virtual hosted style: <Schema>://<Bucket>.<S3 Endpoint>/<Object>
|
||||
*/
|
||||
pathStyle: true,
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Creates and returns a new Minio client
|
||||
*
|
||||
* @methods
|
||||
*
|
||||
* @returns {Client}
|
||||
*/
|
||||
createMinioClient() {
|
||||
return new MinioClient({
|
||||
endPoint: this.settings.endPoint,
|
||||
port: this.settings.port,
|
||||
useSSL: this.settings.useSSL,
|
||||
accessKey: this.settings.accessKey,
|
||||
secretKey: this.settings.secretKey,
|
||||
region: this.settings.region,
|
||||
transport: this.settings.transport,
|
||||
sessionToken: this.settings.sessionToken,
|
||||
pathStyle: this.settings.pathStyle,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Pings the configured minio backend
|
||||
*
|
||||
* @param {number} timeout - Amount of miliseconds to wait for a ping response
|
||||
* @returns {PromiseLike<boolean|MinioPingError>}
|
||||
*/
|
||||
ping({ timeout = 5000 } = {}) {
|
||||
return this.Promise.race([
|
||||
this.client.listBuckets().then(() => true),
|
||||
this.Promise.delay(timeout).then(() => {
|
||||
throw new MinioPingError();
|
||||
}),
|
||||
]);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* Creates a new Bucket
|
||||
*
|
||||
* @actions
|
||||
*
|
||||
* @param {string} bucketName - The name of the bucket
|
||||
* @param {string} region - The region to create the bucket in. Defaults to "us-east-1"
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
makeBucket: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
region: { type: 'string', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, region = '' }) =>
|
||||
this.client.makeBucket(bucketName, region)
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Lists all buckets.
|
||||
*
|
||||
* @actions
|
||||
*
|
||||
* @returns {PromiseLike<Bucket[]|Error>}
|
||||
*/
|
||||
listBuckets: {
|
||||
handler() {
|
||||
return this.client
|
||||
.listBuckets()
|
||||
.then((buckets) => (isUndefined(buckets) ? [] : buckets));
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Checks if a bucket exists.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
*
|
||||
* @returns {PromiseLike<boolean|Error>}
|
||||
*/
|
||||
bucketExists: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.bucketExists(ctx.params.bucketName);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Removes a bucket.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
*
|
||||
* @returns {PromiseLike<boolean|Error>}
|
||||
*/
|
||||
removeBucket: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.removeBucket(ctx.params.bucketName);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Lists all objects in a bucket.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
|
||||
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
|
||||
*
|
||||
* @returns {PromiseLike<Object[]|Error>}
|
||||
*/
|
||||
listObjects: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
prefix: { type: 'string', optional: true },
|
||||
recursive: { type: 'boolean', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, prefix = '', recursive = false }) => {
|
||||
return new this.Promise((resolve, reject) => {
|
||||
try {
|
||||
const stream = this.client.listObjects(
|
||||
bucketName,
|
||||
prefix,
|
||||
recursive
|
||||
);
|
||||
const objects = [];
|
||||
stream.on('data', (el) => objects.push(el));
|
||||
stream.on('end', () => resolve(objects));
|
||||
stream.on('error', reject);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Lists all objects in a bucket using S3 listing objects V2 API
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
|
||||
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
|
||||
* @param {string} startAfter - Specifies the object name to start after when listing objects in a bucket. (optional, default '').
|
||||
*
|
||||
* @returns {PromiseLike<Object[]|Error>}
|
||||
*/
|
||||
listObjectsV2: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
prefix: { type: 'string', optional: true },
|
||||
recursive: { type: 'boolean', optional: true },
|
||||
startAfter: { type: 'string', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, prefix = '', recursive = false, startAfter = '' }) => {
|
||||
return new this.Promise((resolve, reject) => {
|
||||
try {
|
||||
const stream = this.client.listObjectsV2(
|
||||
bucketName,
|
||||
prefix,
|
||||
recursive,
|
||||
startAfter
|
||||
);
|
||||
const objects = [];
|
||||
stream.on('data', (el) => objects.push(el));
|
||||
stream.on('end', () => resolve(objects));
|
||||
stream.on('error', reject);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Lists partially uploaded objects in a bucket.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
|
||||
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
|
||||
*
|
||||
* @returns {PromiseLike<Object[]|Error>}
|
||||
*/
|
||||
listIncompleteUploads: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
prefix: { type: 'string', optional: true },
|
||||
recursive: { type: 'boolean', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, prefix = '', recursive = false }) => {
|
||||
return new this.Promise((resolve, reject) => {
|
||||
try {
|
||||
const stream = this.client.listIncompleteUploads(
|
||||
bucketName,
|
||||
prefix,
|
||||
recursive
|
||||
);
|
||||
const objects = [];
|
||||
stream.on('data', (el) => objects.push(el));
|
||||
stream.on('end', () => resolve(objects));
|
||||
stream.on('error', reject);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Downloads an object as a stream.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket
|
||||
* @param {string} objectName - Name of the object.
|
||||
*
|
||||
* @returns {PromiseLike<ReadableStream|Error>}
|
||||
*/
|
||||
getObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.getObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Downloads the specified range bytes of an object as a stream.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {number} offset - `offset` of the object from where the stream will start.
|
||||
* @param {number} length - `length` of the object that will be read in the stream (optional, if not specified we read the rest of the file from the offset).
|
||||
*
|
||||
* @returns {PromiseLike<ReadableStream|Error>}
|
||||
*/
|
||||
getPartialObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
offset: { type: 'number' },
|
||||
length: { type: 'number', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.getPartialObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName,
|
||||
ctx.params.offset,
|
||||
ctx.params.length
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Downloads and saves the object as a file in the local filesystem.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {string} filePath - Path on the local filesystem to which the object data will be written.
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
fGetObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
filePath: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.fGetObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName,
|
||||
ctx.params.filePath
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Uploads an object from a stream/Buffer.
|
||||
*
|
||||
* @actions
|
||||
* @param {ReadableStream} params - Readable stream.
|
||||
*
|
||||
* @meta
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {number} size - Size of the object (optional).
|
||||
* @param {object} metaData - metaData of the object (optional).
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
putObject: {
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve({
|
||||
stream: ctx.params,
|
||||
meta: ctx.meta,
|
||||
}).then(({ stream, meta }) =>
|
||||
this.client.putObject(
|
||||
meta.bucketName,
|
||||
meta.objectName,
|
||||
stream,
|
||||
meta.size,
|
||||
meta.metaData
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Uploads contents from a file to objectName.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {string} filePath - Path of the file to be uploaded.
|
||||
* @param {object} metaData - metaData of the object (optional).
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
fPutObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
filePath: { type: 'string' },
|
||||
metaData: { type: 'object', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.fPutObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName,
|
||||
ctx.params.filePath,
|
||||
ctx.params.metaData
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Copy a source object into a new object in the specified bucket.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {string} sourceObject - Path of the file to be copied.
|
||||
* @param {object} conditions - Conditions to be satisfied before allowing object copy.
|
||||
* @param {object} metaData - metaData of the object (optional).
|
||||
*
|
||||
* @returns {PromiseLike<{etag: {string}, lastModified: {string}}|Error>}
|
||||
*/
|
||||
copyObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
sourceObject: { type: 'string' },
|
||||
conditions: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
modified: { type: 'string', optional: true },
|
||||
unmodified: { type: 'string', optional: true },
|
||||
matchETag: { type: 'string', optional: true },
|
||||
matchETagExcept: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, objectName, sourceObject, conditions }) => {
|
||||
const _conditions = new CopyConditions();
|
||||
if (conditions.modified) {
|
||||
_conditions.setModified(new Date(conditions.modified));
|
||||
}
|
||||
if (conditions.unmodified) {
|
||||
_conditions.setUnmodified(new Date(conditions.unmodified));
|
||||
}
|
||||
if (conditions.matchETag) {
|
||||
_conditions.setMatchETag(conditions.matchETag);
|
||||
}
|
||||
if (conditions.matchETagExcept) {
|
||||
_conditions.setMatchETagExcept(conditions.matchETagExcept);
|
||||
}
|
||||
conditions = _conditions;
|
||||
return this.client.copyObject(
|
||||
bucketName,
|
||||
objectName,
|
||||
sourceObject,
|
||||
conditions
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Gets metadata of an object.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
*
|
||||
* @returns {PromiseLike<{size: {number}, metaData: {object}, lastModified: {string}, etag: {string}}|Error>}
|
||||
*/
|
||||
statObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.statObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Removes an Object
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
removeObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.removeObject(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectName
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Removes a list of Objects
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string[]} objectNames - Names of the objects.
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
removeObjects: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectNames: { type: 'array', items: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.client.removeObjects(
|
||||
ctx.params.bucketName,
|
||||
ctx.params.objectNames
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Removes a partially uploaded object.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
*
|
||||
* @returns {PromiseLike<undefined|Error>}
|
||||
*/
|
||||
removeIncompleteUpload: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, objectName }) =>
|
||||
this.client.removeIncompleteUpload(bucketName, objectName)
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Generates a presigned URL for the provided HTTP method, 'httpMethod'. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This
|
||||
* presigned URL can have an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} httpMethod - The HTTP-Method (eg. `GET`).
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
|
||||
* @param {object} reqParams - request parameters. (optional)
|
||||
* @param {string} requestDate - An ISO date string, the url will be issued at. Default value is now. (optional)
|
||||
* @returns {PromiseLike<String|Error>}
|
||||
*/
|
||||
presignedUrl: {
|
||||
params: {
|
||||
httpMethod: { type: 'string' },
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
expires: { type: 'number', integer: true, optional: true },
|
||||
reqParams: { type: 'object', optional: true },
|
||||
requestDate: { type: 'string', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({
|
||||
httpMethod,
|
||||
bucketName,
|
||||
objectName,
|
||||
expires,
|
||||
reqParams,
|
||||
requestDate,
|
||||
}) => {
|
||||
if (isString(requestDate)) {
|
||||
requestDate = new Date(requestDate);
|
||||
}
|
||||
|
||||
return new this.Promise((resolve, reject) => {
|
||||
this.client.presignedUrl(
|
||||
httpMethod,
|
||||
bucketName,
|
||||
objectName,
|
||||
expires,
|
||||
reqParams,
|
||||
requestDate,
|
||||
(error, url) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(url);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Generates a presigned URL for HTTP GET operations. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This presigned URL can have an
|
||||
* associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
|
||||
* @param {object} reqParams - request parameters. (optional)
|
||||
* @param {string} requestDate - An ISO date string, the url will be issued at. Default value is now. (optional)
|
||||
* @returns {PromiseLike<String|Error>}
|
||||
*/
|
||||
presignedGetObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
expires: { type: 'number', integer: true, optional: true },
|
||||
reqParams: { type: 'object', optional: true },
|
||||
requestDate: { type: 'string', optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, objectName, expires, reqParams, requestDate }) => {
|
||||
if (isString(requestDate)) {
|
||||
requestDate = new Date(requestDate);
|
||||
}
|
||||
|
||||
return new this.Promise((resolve, reject) => {
|
||||
this.client.presignedGetObject(
|
||||
bucketName,
|
||||
objectName,
|
||||
expires,
|
||||
reqParams,
|
||||
requestDate,
|
||||
(error, url) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(url);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Generates a presigned URL for HTTP PUT operations. Browsers/Mobile clients may point to this URL to upload objects directly to a bucket even if it is private. This presigned URL can have
|
||||
* an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
|
||||
*
|
||||
* @actions
|
||||
* @param {string} bucketName - Name of the bucket.
|
||||
* @param {string} objectName - Name of the object.
|
||||
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
|
||||
* @returns {PromiseLike<String|Error>}
|
||||
*/
|
||||
presignedPutObject: {
|
||||
params: {
|
||||
bucketName: { type: 'string' },
|
||||
objectName: { type: 'string' },
|
||||
expires: { type: 'number', integer: true, optional: true },
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(
|
||||
({ bucketName, objectName, expires }) => {
|
||||
return new this.Promise((resolve, reject) => {
|
||||
this.client.presignedPutObject(
|
||||
bucketName,
|
||||
objectName,
|
||||
expires,
|
||||
(error, url) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(url);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Allows setting policy conditions to a presigned URL for POST operations. Policies such as bucket name to receive object uploads, key name prefixes, expiry policy may be set.
|
||||
*
|
||||
* @actions
|
||||
* @param {object} policy - Policy object created by minioClient.newPostPolicy()
|
||||
* @returns {PromiseLike<{postURL: {string}, formData: {object}}|Error>}
|
||||
*/
|
||||
presignedPostPolicy: {
|
||||
params: {
|
||||
policy: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
expires: { type: 'string', optional: true },
|
||||
key: { type: 'string', optional: true },
|
||||
keyStartsWith: { type: 'string', optional: true },
|
||||
bucket: { type: 'string', optional: true },
|
||||
contentType: { type: 'string', optional: true },
|
||||
contentLengthRangeMin: {
|
||||
type: 'number',
|
||||
integer: true,
|
||||
optional: true,
|
||||
},
|
||||
contentLengthRangeMax: {
|
||||
type: 'number',
|
||||
integer: true,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
handler(ctx) {
|
||||
return this.Promise.resolve(ctx.params).then(({ policy }) => {
|
||||
const _policy = this.client.newPostPolicy();
|
||||
if (policy.expires) {
|
||||
_policy.setExpires(new Date(policy.expires));
|
||||
}
|
||||
if (policy.key) {
|
||||
_policy.setKey(policy.key);
|
||||
}
|
||||
if (policy.keyStartsWith) {
|
||||
_policy.setKeyStartsWith(policy.keyStartsWith);
|
||||
}
|
||||
if (policy.bucket) {
|
||||
_policy.setBucket(policy.bucket);
|
||||
}
|
||||
if (policy.contentType) {
|
||||
_policy.setContentType(policy.contentType);
|
||||
}
|
||||
if (policy.contentLengthRangeMin && policy.contentLengthRangeMax) {
|
||||
_policy.setContentLengthRange(
|
||||
policy.contentLengthRangeMin,
|
||||
policy.contentLengthRangeMax
|
||||
);
|
||||
}
|
||||
return this.client.presignedPostPolicy(_policy);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Service created lifecycle event handler.
|
||||
* Constructs a new minio client entity
|
||||
*/
|
||||
created() {
|
||||
this.client = this.createMinioClient();
|
||||
},
|
||||
/**
|
||||
* Service started lifecycle event handler. Resolves when:
|
||||
* * ping of S3 backend has been successful
|
||||
* * a healthCheck has been registered, given minioHealthCheckInterval > 0
|
||||
* @returns {PromiseLike<undefined|MinioInitializationError>}
|
||||
*/
|
||||
started() {
|
||||
/* istanbul ignore next */
|
||||
return this.Promise.resolve()
|
||||
.then(() => this.ping())
|
||||
.then(() => {
|
||||
this.settings.minioHealthCheckInterval
|
||||
? (this.healthCheckInterval = setInterval(
|
||||
() =>
|
||||
this.ping().catch((e) =>
|
||||
this.logger.error('Minio backend can not be reached', e)
|
||||
),
|
||||
this.settings.minioHealthCheckInterval
|
||||
))
|
||||
: undefined;
|
||||
return undefined;
|
||||
})
|
||||
.catch((e) => {
|
||||
throw new MinioInitializationError(e.message);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Service stopped lifecycle event handler.
|
||||
* Removes the healthCheckInterval
|
||||
*/
|
||||
stopped() {
|
||||
this.healthCheckInterval && clearInterval(this.healthCheckInterval);
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue