PolGuixe
11/11/2016 - 10:54 AM

Debbuging AWS S3 integration

Debbuging AWS S3 integration

import {FilesCollection} from 'meteor/ostrio:files';
import {Meteor} from 'meteor/meteor';
import {Random} from 'meteor/random';
import _ from 'lodash';

export const MAX_ALLOWED_SIZE_MB = 2;
export const MAX_ALLOWED_SIZE_KB = MAX_ALLOWED_SIZE_MB * 1024;
export const MAX_ALLOWED_SIZE_B = MAX_ALLOWED_SIZE_KB * 1024;
export const ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg'];
export const ALLOWED_FILE_TYPES_REGEXP = new RegExp(ALLOWED_FILE_TYPES.join('|'), 'i');

let knox, bound, client, Request, cfdomain;


if (Meteor.isServer) {
  // Fix CloudFront certificate issue Read:
  // https://github.com/chilts/awssum/issues/164
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
  knox = Npm.require('knox');
  Request = Npm.require('request');
  bound = Meteor.bindEnvironment(function (callback) {
    return callback();
  });

  // TODO add it from settings
  cfdomain = 'https://XXXXXX.cloudfront.net' //
  client = knox.createClient({key: 'YYYYYYYY', secret: 'ZZZZZZZZZZ', bucket: 'my-app-test', region: 'eu-central-1'});
}

console.log(knox, cfdomain, client);

const ProductImages = new FilesCollection({
  debug: true,
  throttle: false,
  storagePath: 'assets/app/uploads/productImages',
  collectionName: 'product_images',
  allowClientCode: false, // Disallow remove files from Client
  onBeforeUpload(file) {
    if (file.size > MAX_ALLOWED_SIZE_B) {
      return 'exceed-max-allowed-size';
    }
    if (!ALLOWED_FILE_TYPES_REGEXP.test(file.extension)) {
      return 'invalid-file-type';
    }

    return true;
  },
  onAfterUpload: function (fileRef) {
    // In onAfterUpload callback we will move file to AWS:S3
    const self = this;
    _.each(fileRef.versions, function (vRef, version) {
      // 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 = "files/" + (Random.id()) + "-" + version + "." + fileRef.extension;
      console.log(filePath);
      client.putFile(vRef.path, filePath, function (error, res) {
        bound(function () {
          let upd;
          if (error) {
            console.error(error);
          } else {
            upd = {
              $set: {}
            };
            upd['$set']["versions." + version + ".meta.pipeFrom"] = cfdomain + '/' + filePath;
            upd['$set']["versions." + version + ".meta.pipePath"] = filePath;
            self.collection.update({
              _id: fileRef._id
            }, upd, function (error) {
              if (error) {
                console.error(error);
              } else {
                console.log('Success');
                // Unlink original files from FS after successful upload to AWS:S3
                self.unlink(self.collection.findOne(fileRef._id), version);
              }
            });
          }
        });
      });
    });
  },
  interceptDownload: function (http, fileRef, version) {
    let path,
      ref,
      ref1,
      ref2;
    path = (ref = fileRef.versions) != null
      ? (ref1 = ref[version]) != null
        ? (ref2 = ref1.meta) != null
          ? ref2.pipeFrom
          : void 0
        : void 0
      : void 0;
    if (path) {
      // If file is moved to S3 We will pipe request to S3 So, original link will stay
      // always secure
      Request({
        url: path,
        headers: _.pick(http.request.headers, 'range', 'accept-language', 'accept', 'cache-control', 'pragma', 'connection', 'upgrade-insecure-requests', 'user-agent')
      }).pipe(http.response);
      return true;
    } else {
      // While file is not yet uploaded to S3 We will serve file from FS
      return false;
    }
  }
});

if (Meteor.isServer) {
  // Intercept File's collection remove method to remove file from S3
  const _origRemove = ProductImages.remove;

  ProductImages.remove = function (search) {
    const cursor = this.collection.find(search);
    cursor.forEach(function (fileRef) {
      _.each(fileRef.versions, function (vRef) {
        let ref;
        if (vRef != null
          ? (ref = vRef.meta) != null
            ? ref.pipePath
            : void 0
          : void 0) {
          client.deleteFile(vRef.meta.pipePath, function (error) {
            bound(function () {
              if (error) {
                console.error(error);
              }
            });
          });
        }
      });
    });
    // Call original method
    _origRemove.call(this, search);
  };
}

export default ProductImages;
I20161110-20:03:03.573(1)? [FilesCollection] [find({"meta":{"sessionId":"P9FeBQXF6dpQheBEv"}}, undefined)]
I20161110-20:06:27.578(1)? [FilesCollection] [File Start Method] IMG_3189.PNG - AFGLPexSm5ihbC9EA
I20161110-20:06:27.580(1)? [FilesCollection] [Upload] [Start Method] Got #-1/1 chunks, dst: IMG_3189.PNG
I20161110-20:06:28.117(1)? [FilesCollection] [Upload] [DDP] Got #1/1 chunks, dst: IMG_3189.PNG
I20161110-20:06:28.128(1)? [FilesCollection] [Upload] [DDP] Got #-1/1 chunks, dst: IMG_3189.PNG
I20161110-20:06:28.130(1)? [FilesCollection] [Upload] [finish(ing)Upload] -> assets/app/uploads/productImages/AFGLPexSm5ihbC9EA.PNG
I20161110-20:06:28.158(1)? [FilesCollection] [Upload] [finish(ed)Upload] -> assets/app/uploads/productImages/AFGLPexSm5ihbC9EA.PNG
I20161110-20:06:28.159(1)? files/4Q4NdzA7C8bWT7Qkh-original.PNG
I20161110-20:06:28.221(1)? [FilesCollection] [_preCollectionCursor.observeChanges] [removed]: AFGLPexSm5ihbC9EA
I20161110-20:06:28.948(1)? Success
I20161110-20:06:28.956(1)? [FilesCollection] [unlink(AFGLPexSm5ihbC9EA, original)]