Skip to content

Instantly share code, notes, and snippets.

@Apollinaire
Created February 6, 2019 14:23
Show Gist options
  • Save Apollinaire/37bac78ea3cc9b8c01ef05e5079c0f8a to your computer and use it in GitHub Desktop.
Save Apollinaire/37bac78ea3cc9b8c01ef05e5079c0f8a to your computer and use it in GitHub Desktop.
AWS-SDK update for vulcan-files-s3
import S3 from 'aws-sdk/clients/s3';
import curryUploadToS3 from './curryUploadToS3';
import curryDeleteFromS3 from './curryDeleteFromS3';
import serveFilesFromS3 from './serveFilesFromS3';
/**
* Creates an S3 storage provider, as needed by {@link createFSCollection}.
*
* @param {{key: String, secret: String, region: String, bucket: String}} config
* @param {String} cfdomain
* @return {{S3Client: Object, upload: curriedUploadToS3, delete: curriedDeleteFromS3, serve: serveFilesFromS3}}
*/
export default function createS3StorageProvider(config, cfdomain) {
const S3Client = new S3({
secretAccessKey: config.secret,
accessKeyId: config.key,
region: config.region,
// sslEnabled: true, // optional
httpOptions: {
timeout: 6000,
agent: false
}
});
return {
S3Client,
upload: curryUploadToS3(S3Client, cfdomain, config.region, config.bucket),
delete: curryDeleteFromS3(S3Client, config.region, config.bucket),
serve: serveFilesFromS3,
};
}
import has from 'lodash/has';
let bound;
if (Meteor.isServer) {
bound = Meteor.bindEnvironment(callback => (callback()));
}
/**
* curryDeleteFromS3 - curries a function that deletes a file from AWS S3 with
* from an S3 client
*
* @param {object} S3Client A Knox S3 client or any client with similar API.
*
* @return {function} returns curriedDeleteFromS3
*/
export default function curryDeleteFromS3(S3Client, bucket) {
/**
* curriedDeleteFromS3 - Deletes a file from S3 and updates the doc's versions
* that contains that file.
*
* @param {FilesCollection} FilesCollection Collection that needs to be updated
* once the files is deleted from S3
* @param {string} docId The id of the doc that needs to be updated
* once the file is deleted
* @param {object} versionReference The information of the version that wants
* to be deleted. The refernce of the file in S3 needs to be in `versionReference.meta.pipePath`
*
* @return {promise} returns a promise - resolve returns the same version reference
* - reject returns the error that caused it.
*/
return function curriedDeleteFromS3(FilesCollection, docId, versionReference) {
return new Promise((resolve, reject) => {
if (has(versionReference, 'meta.pipePath')) {
// using aws-sdk but never really tested that since I can't fire a delete from the FScollection
S3Client.deleteObject({Key: versionReference.meta.pipePath, Bucket: bucket}, (deleteFromS3Error) => {
bound(() => {
if (deleteFromS3Error) {
reject(deleteFromS3Error);
} else {
FilesCollection.collection.update({ _id: docId }, {
$unset: { [`versions.${versionReference.version}`]: '' },
}, (collectionUpadteError) => {
if (collectionUpadteError) {
reject(collectionUpadteError);
}
// TODO remove console.log and add it to a log collection
console.log(
`${versionReference.name ||
`${docId}-${versionReference.version}`}: deleted from S3`
);
resolve(versionReference);
});
}
});
});
} else {
// console.warn(`This ${versionReference.name} has no linked file in S3`);
resolve(versionReference);
}
});
};
}
import snakeCase from 'lodash/snakeCase';
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import fs from 'fs';
let bound;
if (Meteor.isServer) {
bound = Meteor.bindEnvironment(callback => callback());
}
/**
* curryUploadToS3 - it curries the curriedUploadToS3 with an S3Client and a
* CloudFront domain
*
* @param {Object} S3Client A Knox S3 client
* @param {String} cfdomain A CloudFront domain linked to the S3 client
*
* @return {function} returns curriedUploadToS3
*/
export default function curryUploadToS3(S3Client, cfdomain, region, bucket) {
/**
* curriedUploadToS3 - Uploads a file to S3 and updates the collection with
* the new metadata. It also unlinks the original file from the internal FS.
*
* @param {FileCollection} FilesCollection The FilesCollection from where the
* reference docs belongs to. It is used to update the doc with the new metadata
* @param {string} docId The id from the reference doc that needs to be updated
* @param {object} versionRef The information of the version that will be
* uploaded to S3. It need to contain the path of the file version that needs to be uploaded.
*
* @return {promise} returns a promise - resolve: returns the same versionRef
* as the input - reject: returns the error that cause it
*/
return function curriedUploadToS3(FilesCollection, docId, versionRef) {
return new Promise((resolve, reject) => {
const { version } = versionRef;
const url = cfdomain ? cfdomain : `https://s3.${region}.amazonaws.com/${bucket}`
// console.log(versionRef);
// We use Random.id() instead of real file's _id to secure files from reverse
// engineering as after viewing this code it will be easy to get access to
// unlisted and protected files
const filePath = `${snakeCase(FilesCollection.collectionName)}/${version}/${Random.id()}-${version}.${versionRef.extension}`;
const fileStream = fs.createReadStream(versionRef.path);
fileStream.on('error', function(err) {
console.log('File Error', err);
});
// Here we upoload the file
S3Client.putObject({Bucket: 'liveforgooddev', Key: filePath, Body: fileStream, ContentType: versionRef.type }, (S3error, res) => {
bound(() => {
if (S3error) {
reject(S3error); // Reject
} else {
// If no error, update the mongo document
const upd = {
$set: {
[`versions.${version}.uploadedTo3rdParty`]: true,
[`versions.${version}.meta.pipeFrom`]: `${url}/${filePath}`,
[`versions.${version}.meta.pipePath`]: filePath,
},
};
// Here we perform the document update
FilesCollection.collection.update(
{
_id: docId,
},
upd,
collectionError => {
if (collectionError) {
reject(collectionError);
} else {
// Unlink original files from FS after successful upload to AWS:S3
FilesCollection.unlink(FilesCollection.collection.findOne(docId), version);
// TODO remove console.log and add it to a log collection
console.log(`${versionRef.name || `${docId}-${version}`}: uploaded`);
resolve(versionRef);
}
}
);
}
});
});
});
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment