Repository: Lepozepo/S3 Branch: master Commit: 0dc2d6ba0c33 Files: 35 Total size: 41.8 KB Directory structure: gitextract_nbdvszgl/ ├── .gitignore ├── .npm/ │ └── package/ │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── .versions ├── LICENSE.md ├── README.md ├── client/ │ └── functions.coffee ├── coffeelint.json ├── example/ │ ├── basic/ │ │ ├── .meteor/ │ │ │ ├── .finished-upgraders │ │ │ ├── .gitignore │ │ │ ├── .id │ │ │ ├── packages │ │ │ ├── platforms │ │ │ ├── release │ │ │ └── versions │ │ ├── basic.coffee │ │ ├── basic.html │ │ └── packages/ │ │ └── .gitignore │ └── mdg_camera/ │ ├── .meteor/ │ │ ├── .finished-upgraders │ │ ├── .gitignore │ │ ├── .id │ │ ├── cordova-plugins │ │ ├── packages │ │ ├── platforms │ │ ├── release │ │ └── versions │ ├── mdg_camera.coffee │ ├── mdg_camera.html │ └── packages/ │ └── .gitignore ├── package.js ├── server/ │ ├── delete_object.coffee │ ├── sign_request.coffee │ └── startup.coffee └── versions.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .build* .DS_store .aws_keys.txt ================================================ FILE: .npm/package/.gitignore ================================================ node_modules ================================================ FILE: .npm/package/README ================================================ This directory and the files immediately inside it are automatically generated when you change this package's NPM dependencies. Commit the files in this directory (npm-shrinkwrap.json, .gitignore, and this README) to source control so that others run the same versions of sub-dependencies. You should NOT check in the node_modules directory that Meteor automatically creates; if you are using git, the .gitignore file tells git to ignore it. ================================================ FILE: .npm/package/npm-shrinkwrap.json ================================================ { "lockfileVersion": 1, "dependencies": { "aws-sdk": { "version": "2.1.14", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1.14.tgz", "integrity": "sha1-cPct9Y1GaiItTDK/IbYarX5QRpg=", "dependencies": { "xml2js": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.6.tgz", "integrity": "sha1-0gnE5N2h/JxFIUHvQcB39a399sQ=", "dependencies": { "sax": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/sax/-/sax-0.4.2.tgz", "integrity": "sha1-OfO2AXM9a+yXEFskKipA/Wl4rDw=" } } }, "xmlbuilder": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.2.tgz", "integrity": "sha1-F3bWXz/brUcKCNhgTN6xxOVA/4M=" } } }, "crypto-js": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.6.tgz", "integrity": "sha1-YUJlGyMtu469+pcWpwooiDWdpsk=" }, "knox": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/knox/-/knox-0.9.2.tgz", "integrity": "sha1-NzZZNmniTwJP2vcjtqHcSv2DmnE=", "dependencies": { "debug": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "integrity": "sha1-W5wla9VLbsAigxdvqKDt5tFUy/g=", "dependencies": { "ms": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "integrity": "sha1-2JwhJMb9wTU9Zai3e/GqxLGTcIw=" } } }, "mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" }, "once": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "integrity": "sha1-8/Pk2lt9J7XHMpae4+Z+cpRXsx8=", "dependencies": { "wrappy": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", "integrity": "sha1-HmWWmWXMvC20VIxrhKbyxa7dRzk=" } } }, "stream-counter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-1.0.0.tgz", "integrity": "sha1-kc8lac5NxQYf6816yyY5SloRR1E=" }, "xml2js": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.5.tgz", "integrity": "sha1-/EJnUbfPiQqqkJp1bu3jHH84qPw=", "dependencies": { "sax": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=" }, "xmlbuilder": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.1.tgz", "integrity": "sha1-umkhZQEz5YCCiPNdyrDbaWqbqaA=", "dependencies": { "lodash": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.3.1.tgz", "integrity": "sha1-O5FNShuyfvzuB24N+lgVIBjiBC4=" } } } } } } }, "moment": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.13.0.tgz", "integrity": "sha1-JBYtmVIebUD5muaTnoBtITnqrFI=" }, "stream-buffers": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.1.0.tgz", "integrity": "sha1-gsG8imgVvwAQje2I6lU/D4vLOzo=" } } } ================================================ FILE: .versions ================================================ accounts-base@1.4.2 allow-deny@1.1.0 autoupdate@1.4.0 babel-compiler@7.0.4 babel-runtime@1.2.0 base64@1.0.10 binary-heap@1.0.10 boilerplate-generator@1.4.0 caching-compiler@1.1.9 callback-hook@1.1.0 check@1.3.0 coffeescript@1.0.17 ddp@1.4.0 ddp-client@2.3.1 ddp-common@1.4.0 ddp-rate-limiter@1.0.7 ddp-server@2.1.2 diff-sequence@1.1.0 dynamic-import@0.3.0 ecmascript@0.10.0 ecmascript-runtime@0.5.0 ecmascript-runtime-client@0.6.0 ecmascript-runtime-server@0.5.0 ejson@1.1.0 es5-shim@4.7.3 geojson-utils@1.0.10 hot-code-push@1.0.4 http@1.4.0 id-map@1.1.0 lepozepo:s3@5.2.8 livedata@1.0.18 localstorage@1.2.0 logging@1.1.19 meteor@1.8.2 meteor-base@1.3.0 minimongo@1.4.3 modules@0.11.3 modules-runtime@0.9.1 mongo@1.4.2 mongo-dev-server@1.1.0 mongo-id@1.0.6 npm-mongo@2.2.33 ordered-dict@1.1.0 promise@0.10.1 random@1.1.0 rate-limit@1.0.8 reactive-var@1.0.11 reload@1.2.0 retry@1.1.0 routepolicy@1.0.12 server-render@0.3.0 service-configuration@1.0.11 shim-common@0.1.0 socket-stream-client@0.1.0 tracker@1.1.3 underscore@1.0.10 url@1.2.0 webapp@1.5.0 webapp-hashing@1.0.9 ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2015 Marcelo Reyna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Amazon S3 Uploader S3 provides a simple way for uploading files to the Amazon S3 service with a progress bar. This is useful for uploading images and files that you want accesible to the public. S3 is built on [Knox](https://github.com/LearnBoost/knox) and [AWS-SDK](https://github.com/aws/aws-sdk-js). Both modules are made available on the server after installing this package. If you want to keep using the older version of this package (pre 0.9.0) check it out using `meteor add lepozepo:s3@=3.0.1` If you want to keep using the version of this package that uses server resources to upload files check it out using `meteor add lepozepo:s3@=4.1.3` **S3 now uploads directly from the client to Amazon. Client files will not touch your server.** # Show your support! Star my code in github or atmosphere if you like my code or shoot me a dollar or two! [DONATE HERE](https://cash.me/$lepozepo) ## Moving Forward In line with Meteor's move towards NPM, I'm moving this package to NPM. Along with this move, I'll resolve issues with importing the uploader and making it easy to use in React and React Native. I've already started migrating but I'm only half way there, have a look at the repo [here](https://github.com/Lepozepo/S3-uploader) ## NEW IN 5.2.1 * AWS Signature V4!! This means more regions can use this package ## Installation ``` sh $ meteor add lepozepo:s3 ``` ## How to use ### Step 1 Define your Amazon S3 credentials. SERVER SIDE. ``` javascript S3.config = { key: 'amazonKey', secret: 'amazonSecret', bucket: 'bucketName', region: 'eu-west-1' // Only needed if not "us-east-1" or "us-standard" }; ``` ### Step 2 Create a file input and progress indicator. CLIENT SIDE. ``` handlebars ``` ### Step 3 Create a function to upload the files and a helper to see the uploads progress. CLIENT SIDE. ``` javascript Template.s3_tester.events({ "click button.upload": function(){ var files = $("input.file_bag")[0].files S3.upload({ files:files, path:"subfolder" },function(e,r){ console.log(r); }); } }) Template.s3_tester.helpers({ "files": function(){ return S3.collection.find(); } }) ``` ## Create your Amazon S3 For all of this to work you need to create an aws account. ### 1. Create an S3 bucket in your preferred region. ### 2. Access Key Id and Secret Key 1. Navigate to your bucket 2. On the top right side you'll see your account name. Click it and go to Security Credentials. 3. Create a new access key under the Access Keys (Access Key ID and Secret Access Key) tab. 4. Enter this information into your app as defined in "How to Use" "Step 1". 5. Your region can be found under "Properties" button and "Static Website Hosting" tab. * bucketName.s3-website-**eu-west-1**.amazonaws.com. * If your region is "us-east-1" or "us-standard" then you don't need to specify this in the config. ### 3. Hosting 1. Upload a blank `index.html` file (anywhere is ok, I put it in root). 2. Select the bucket's properties by clicking on the bucket (from All Buckets) then the "Properties" button at the top right. 3. Click **"Static Website Hosting"** tab. 4. Click **Enable Website Hosting**. 5. Fill the `Index Document` input with the path to your `index.html` without a trailing slash. E.g. `afolder/index.html`, `index.html` 6. **Click "Save"** ### 4. CORS You need to set permissions so that everyone can see what's in there. 1. Select the bucket's properties and go to the "Permissions" tab. 2. Click "Edit CORS Configuration" and paste this: ``` xml * PUT POST GET HEAD 3000 * ``` 5. Click "Edit bucket policy" and paste this (**Replace the bucket name with your own**): ``` javascript { "Version": "2008-10-17", "Statement": [ { "Sid": "AllowPublicRead", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::YOURBUCKETNAMEHERE/*" } ] } ``` 7. **Click Save** ### Note It might take a couple of hours before you can actually start uploading to S3. Amazon takes some time to make things work. Enjoy, this took me a long time to figure out and I'm sharing it so that nobody has to go through all that. ## API ### S3 (CLIENT SIDE) #### S3.collection This is a null Meteor.Collection that exists only on the users client. After the user leaves the page or refreshes, the collection disappears forever. #### S3.upload(ops,callback) This is the upload function that manages all the dramatic things you need to do for something so essentially simple. __Parameters:__ * __ops.file [OPTIONAL]:__ Must be a File object. You can create this via ```new File()```. Either this otpion or 'files' just be provided. * __ops.files [OPTIONAL]:__ Must be a FileList object. You can get this via jQuery via $("input[type='file']")[0].files. * __ops.path [DEFAULT: ""]:__ Must be in this format ("folder/other_folder"). So basically never start with "/" and never end with "/". Defaults to ROOT folder. * __ops.unique_name [DEFAULT: true]:__ If set to true, the uploaded file name will be set to a uuid without changing the files' extension. If set to false, the uploaded file name will be set to the original name of the file. * __ops.encoding [OPTIONAL: "base64"]:__ If set to "base64", the uploaded file will be uploaded as a base64 string. The uploader will enforce a unique_name if this option is set. * __ops.expiration [DEFAULT: 1800000 (30 mins)]:__ Defines how much time the file has before Amazon denies the upload. Must be in milliseconds. Defaults to 1800000 (30 minutes). * __ops.uploader [DEFAULT: "default"]:__ Defines the name of the uploader. Useful for forms that use multiple uploaders. * __ops.acl [DEFAULT: "public-read"]:__ Access Control List. Describes who has access to the file. Can only be one of the following options: * "private" * "public-read" * "public-read-write" * "authenticated-read" * "bucket-owner-read" * "bucket-owner-full-control" * "log-delivery-write" * __Support for signed GET is still pending so uploads that require authentication won't be easily reachable__ * __ops.bucket [DEFAULT: SERVER SETTINGS]:__ Overrides the bucket that will be used for the upload. * __ops.region [DEFAULT: SERVER SETTINGS]:__ Overrides the region that will be used for the upload. Only accepts the following regions: * "us-west-2" * "us-west-1" * "eu-west-1" * "eu-central-1" * "ap-southeast-1" * "ap-southeast-2" * "ap-northeast-1" * "sa-east-1" * __file.upload_name [OPTIONAL]:__ A function that returns the name with which you want to upload the file. It takes the file object as the only parameter. eg. ``` javascript // The following function simply replicates the default behavior. function(f) { var extension = f.type.split("/")[1]; return Meteor.uuid() + "." + extension; } ``` * __callback:__ A function that is run after the upload is complete returning an Error as the first parameter (if there is one), and a Result as the second. * __Result:__ The returned value of the callback function if there is no error. It returns an object with these keys: * __loaded:__ Integer (bytes) * __total:__ Integer (bytes) * __percent_uploaded:__ Integer (out of 100) * __uploader:__ String (describes which uploader was used to upload the file) * __url:__ String (S3 hosted URL) * __secure_url:__ String (S3 hosted URL for https) * __relative_url:__ String (S3 URL for delete operations, this is what you should save in your DB to control delete) #### S3.delete(path,callback) This function permanently destroys a file located in your S3 bucket. __Parameters:__ * __path:__ Must be in this format ("/folder/other_folder/file.extension"). So basically always start with "/" and never end with "/". This is required. * __callback:__ A function that is run after the delete operation is complete returning an Error as the first parameter (if there is one), and a Result as the second. ### S3 (SERVER SIDE) #### S3.config(ops) This is where you define your key, secret, bucket, and other account wide settings. __Parameters:__ * __ops.key [REQUIRED]:__ Your Amazon AWS Key. * __ops.secret [REQUIRED]:__ Your Amazon AWS Secret. * __ops.bucket [REQUIRED]:__ Your Amazon AWS S3 bucket. * __ops.denyDelete [DEFAULT: undefined]:__ If set to true, will block delete calls. This is to enable secure deployment of this package before a more granular permissions system is developed. * __ops.region [DEFAULT: "us-east-1"]:__ Your Amazon AWS S3 Region. Defaults to US Standard. Can be any of the following: * "us-west-2" * "us-west-1" * "eu-west-1" * "eu-central-1" * "ap-southeast-1" * "ap-southeast-2" * "ap-northeast-1" * "sa-east-1" ``` javascript S3.config = { key: 'amazonKey', secret: 'amazonSecret', bucket: 'bucketName' }; ``` #### S3.rules ##### S3.rules.delete This is a function that runs every time someone uses the delete function on the client side. The context of `this` for the function has access to the `path` and `this` from a Meteor.method. #### S3.knox The current knox client. #### S3.aws The current aws-sdk client. #### Developer Notes http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/frames.html https://github.com/Differential/meteor-uploader/blob/master/lib/UploaderFile.coffee#L169-L178 http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html https://github.com/CulturalMe/meteor-slingshot/blob/master/services/aws-s3.js ================================================ FILE: client/functions.coffee ================================================ @S3 = collection: new Meteor.Collection(null) # file.name # file.type # file.size # loaded # total # percent_uploaded # uploader # status: ["signing","uploading","complete"] # url # secure_url # relative_url upload: (ops = {},callback) -> # ops.files [OPTIONAL] # each needs to run file.type, store in a variable, then send. Either files or ops.file must be provided. # ops.file [OPTIONAL] # single file upload of javascript type File # ops.path [DEFAULT: ""] # the folder to upload to: blank string for root folder "" # ops.unique_name [DEFAULT: true] # modifies the file name to a unique string, if false takes the name of the file. Uploads will overwrite existing files instead. # ops.encoding [OPTIONAL: only supports "base64"] # overrides file encoding, only supports base64 right now # ops.content_disposition [DEFAULT: "inline"] # overrides file disposition (inline or attachment) # ops.server_side_encryption # if true, use server side encryption # ops.expiration [DEFAULT: 1800000 (30 mins)] # How long before uploads to the file are disabled in ms # ops.acl [DEFAULT: "public-read"] # Access Control List. Describes who has access to the file. Any of these options: # "private", # "public-read", # "public-read-write", # "authenticated-read", # "bucket-owner-read", # "bucket-owner-full-control", # "log-delivery-write" # ops.bucket [OVERRIDE REQUIRED SERVER-SIDE] # ops.region [OVERRIDE DEFAULT: "us-east-1"] # Accepts the following regions: # "us-west-2" # "us-west-1" # "eu-west-1" # "eu-central-1" # "ap-southeast-1" # "ap-southeast-2" # "ap-northeast-1" # "sa-east-1" # ops.uploader [DEFAULT: "default"] # key to differentiate multiple uploaders on the same form _.defaults ops, expiration:1800000 path:"" acl:"public-read" uploader:"default" unique_name:true connection:Meteor server_side_encryption:false content_disposition:"inline" if ops.file uploadFile(ops.file, ops, callback) else _.each ops.files, (file) -> uploadFile(file, ops, callback) delete: (path, callback, connection) -> conn = if connection then connection else Meteor conn.call "_s3_delete", path, callback b64toBlob: (b64Data, contentType, sliceSize) -> data = b64Data.split("base64,") if not contentType contentType = data[0].replace("data:","").replace(";","") contentType = contentType sliceSize = sliceSize or 512 byteCharacters = atob data[1] byteArrays = [] for offset in [0...byteCharacters.length] by sliceSize slice = byteCharacters.slice offset, offset + sliceSize byteNumbers = new Array slice.length for i in [0...slice.length] byteNumbers[i] = slice.charCodeAt(i) byteArray = new Uint8Array byteNumbers byteArrays.push byteArray blob = new Blob(byteArrays, {type: contentType}) return blob uploadFile = (file, ops, callback) -> if ops.encoding is "base64" if _.isString file file = S3.b64toBlob file if ops.unique_name or ops.encoding is "base64" extension = _.last file.name?.split(".") if not extension extension = file.type.split("/")[1] # a library of extensions based on MIME types would be better file_name = "#{Random.id()}.#{extension}" else if _.isFunction(file.upload_name) file_name = file.upload_name(file) else if !_.isEmpty(file.upload_name) file_name = file.upload_name else file_name = file.name initial_file_data = file: name:file_name type:file.type size:file.size original_name:file.name loaded:0 total:file.size percent_uploaded:0 uploader:ops.uploader status:"signing" id = S3.collection.insert initial_file_data ops.connection.call "_s3_sign", path:ops.path file_name: initial_file_data.file.name file_type:file.type file_size:file.size acl:ops.acl bucket:ops.bucket region:ops.region expiration:ops.expiration server_side_encryption:ops.server_side_encryption content_disposition:ops.content_disposition (error,result) -> if result # Mark as signed S3.collection.update id, $set: status:"uploading" # Prepare data form_data = new FormData() form_data.append "key", result.key form_data.append "acl", result.acl form_data.append "Content-Type", result.file_type if ops.content_disposition form_data.append "Content-Disposition", ops.content_disposition form_data.append "X-Amz-Date", result.meta_date if ops.server_side_encryption form_data.append "x-amz-server-side-encryption", "AES256" form_data.append "x-amz-meta-uuid", result.meta_uuid form_data.append "X-Amz-Algorithm", "AWS4-HMAC-SHA256" form_data.append "X-Amz-Credential", result.meta_credential form_data.append "X-Amz-Signature",result.signature form_data.append "Policy",result.policy form_data.append "file",file # Send data xhr = new XMLHttpRequest() xhr.upload.addEventListener "progress", (event) -> S3.collection.update id, $set: status:"uploading" loaded:event.loaded total:event.total percent_uploaded: Math.floor ((event.loaded / event.total) * 100) ,false xhr.addEventListener "load", -> if xhr.status < 400 S3.collection.update id, $set: status:"complete" percent_uploaded: 100 url:result.url secure_url:result.secure_url relative_url:result.relative_url callback and callback null,S3.collection.findOne id else callback and callback true,null xhr.addEventListener "error", -> callback and callback true,null xhr.addEventListener "abort", -> console.log "aborted by user" xhr.open "POST",result.post_url,true xhr.send form_data else callback and callback error,null ================================================ FILE: coffeelint.json ================================================ { "arrow_spacing": { "level": "error" }, "braces_spacing": { "level": "error", "spaces": 0, "empty_object_spaces": 0 }, "camel_case_classes": { "level": "error" }, "coffeescript_error": { "level": "error" }, "colon_assignment_spacing": { "level": "ignore", "spacing": { "left": 0, "right": 0 } }, "cyclomatic_complexity": { "level": "ignore", "value": 10 }, "duplicate_key": { "level": "error" }, "empty_constructor_needs_parens": { "level": "ignore" }, "ensure_comprehensions": { "level": "warn" }, "eol_last": { "level": "ignore" }, "indentation": { "value": 1, "level": "error" }, "line_endings": { "level": "ignore", "value": "unix" }, "max_line_length": { "value": 80, "level": "ignore", "limitComments": true }, "missing_fat_arrows": { "level": "ignore", "is_strict": false }, "newlines_after_classes": { "value": 3, "level": "ignore" }, "no_backticks": { "level": "error" }, "no_debugger": { "level": "warn", "console": false }, "no_empty_functions": { "level": "ignore" }, "no_empty_param_list": { "level": "ignore" }, "no_implicit_braces": { "level": "ignore", "strict": true }, "no_implicit_parens": { "level": "ignore", "strict": true }, "no_interpolation_in_single_quotes": { "level": "ignore" }, "no_nested_string_interpolation": { "level": "warn" }, "no_plusplus": { "level": "ignore" }, "no_private_function_fat_arrows": { "level": "warn" }, "no_stand_alone_at": { "level": "ignore" }, "no_tabs": { "level": "ignore" }, "no_this": { "level": "ignore" }, "no_throwing_strings": { "level": "error" }, "no_trailing_semicolons": { "level": "error" }, "no_trailing_whitespace": { "level": "error", "allowed_in_comments": false, "allowed_in_empty_lines": true }, "no_unnecessary_double_quotes": { "level": "ignore" }, "no_unnecessary_fat_arrows": { "level": "warn" }, "non_empty_constructor_needs_parens": { "level": "ignore" }, "prefer_english_operator": { "level": "ignore", "doubleNotLevel": "ignore" }, "space_operators": { "level": "ignore" }, "spacing_after_comma": { "level": "ignore" }, "transform_messes_up_line_numbers": { "level": "warn" } } ================================================ FILE: example/basic/.meteor/.finished-upgraders ================================================ # This file contains information which helps Meteor properly upgrade your # app when you run 'meteor update'. You should check it into version control # with your project. notices-for-0.9.0 notices-for-0.9.1 0.9.4-platform-file notices-for-facebook-graph-api-2 1.2.0-standard-minifiers-package 1.2.0-meteor-platform-split 1.2.0-cordova-changes 1.2.0-breaking-changes 1.3.0-split-minifiers-package 1.4.0-remove-old-dev-bundle-link 1.4.1-add-shell-server-package ================================================ FILE: example/basic/.meteor/.gitignore ================================================ local ================================================ FILE: example/basic/.meteor/.id ================================================ # This file contains a token that is unique to your project. # Check it into your repository along with the rest of this directory. # It can be used for purposes such as: # - ensuring you don't accidentally deploy one app on top of another # - providing package authors with aggregated statistics n8dvpiz2lro616zjewc ================================================ FILE: example/basic/.meteor/packages ================================================ # Meteor packages used by this project, one per line. # # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. standard-app-packages@1.0.9 autopublish@1.0.7 insecure@1.0.7 coffeescript@1.11.1_4 lepozepo:s3 standard-minifier-css standard-minifier-js shell-server ================================================ FILE: example/basic/.meteor/platforms ================================================ server browser ================================================ FILE: example/basic/.meteor/release ================================================ METEOR@1.4.2.3 ================================================ FILE: example/basic/.meteor/versions ================================================ accounts-base@1.2.14 allow-deny@1.0.5 autopublish@1.0.7 autoupdate@1.3.12 babel-compiler@6.13.0 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 blaze@2.2.0 blaze-tools@1.0.10 boilerplate-generator@1.0.11 caching-compiler@1.1.9 caching-html-compiler@1.0.7 callback-hook@1.0.10 check@1.2.4 coffeescript@1.11.1_4 ddp@1.2.5 ddp-client@1.3.2 ddp-common@1.2.8 ddp-rate-limiter@1.0.6 ddp-server@1.3.12 deps@1.0.12 diff-sequence@1.0.7 ecmascript@0.6.1 ecmascript-runtime@0.3.15 ejson@1.0.13 fastclick@1.0.13 geojson-utils@1.0.10 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 http@1.2.10 id-map@1.0.9 insecure@1.0.7 jquery@1.11.10 launch-screen@1.1.0 lepozepo:s3@5.2.4 livedata@1.0.18 localstorage@1.0.12 logging@1.1.16 meteor@1.6.0 meteor-base@1.0.4 meteor-platform@1.2.6 minifier-css@1.2.15 minifier-js@1.2.15 minimongo@1.0.19 mobile-status-bar@1.0.13 modules@0.7.7 modules-runtime@0.7.7 mongo@1.1.14 mongo-id@1.0.6 npm-mongo@2.2.11_2 observe-sequence@1.0.14 ordered-dict@1.0.9 promise@0.8.8 random@1.0.10 rate-limit@1.0.6 reactive-dict@1.1.8 reactive-var@1.0.11 reload@1.1.11 retry@1.0.9 routepolicy@1.0.12 service-configuration@1.0.11 session@1.1.7 shell-server@0.2.1 spacebars@1.0.13 spacebars-compiler@1.0.13 standard-app-packages@1.0.9 standard-minifier-css@1.3.2 standard-minifier-js@1.2.1 templating@1.2.15 templating-compiler@1.2.15 templating-runtime@1.2.15 templating-tools@1.0.5 tracker@1.1.1 ui@1.0.12 underscore@1.0.10 url@1.0.11 webapp@1.3.12 webapp-hashing@1.0.9 ================================================ FILE: example/basic/basic.coffee ================================================ if Meteor.isClient Template.basic.helpers "files": -> S3.collection.find() Template.basic.events "click button.upload": (event) -> S3.upload files:$("input.file_bag")[0].files (error,result) -> if error console.log "Unable to upload" else console.log result "click button.delete": (event) -> S3.delete @relative_url, (error,res) => if not error console.log res S3.collection.remove @_id else console.log error if Meteor.isServer S3.config = key:"yourkey" secret:"yoursecret" bucket:"yourbucket" # region:"us-standard" #default ================================================ FILE: example/basic/basic.html ================================================ basic {{> basic}} ================================================ FILE: example/basic/packages/.gitignore ================================================ /s3 ================================================ FILE: example/mdg_camera/.meteor/.finished-upgraders ================================================ # This file contains information which helps Meteor properly upgrade your # app when you run 'meteor update'. You should check it into version control # with your project. notices-for-0.9.0 notices-for-0.9.1 0.9.4-platform-file ================================================ FILE: example/mdg_camera/.meteor/.gitignore ================================================ local ================================================ FILE: example/mdg_camera/.meteor/.id ================================================ # This file contains a token that is unique to your project. # Check it into your repository along with the rest of this directory. # It can be used for purposes such as: # - ensuring you don't accidentally deploy one app on top of another # - providing package authors with aggregated statistics n8dvpiz2lro616zjewc ================================================ FILE: example/mdg_camera/.meteor/cordova-plugins ================================================ ================================================ FILE: example/mdg_camera/.meteor/packages ================================================ # Meteor packages used by this project, one per line. # # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. standard-app-packages autopublish insecure coffeescript lepozepo:s3 mdg:camera ================================================ FILE: example/mdg_camera/.meteor/platforms ================================================ server browser ios ================================================ FILE: example/mdg_camera/.meteor/release ================================================ METEOR@1.0.4.1 ================================================ FILE: example/mdg_camera/.meteor/versions ================================================ accounts-base@1.2.0 autopublish@1.0.3 autoupdate@1.2.0 base64@1.0.3 binary-heap@1.0.3 blaze@2.1.0 blaze-tools@1.0.3 boilerplate-generator@1.0.3 callback-hook@1.0.3 check@1.0.5 coffeescript@1.0.6 ddp@1.1.0 deps@1.0.7 ejson@1.0.6 fastclick@1.0.3 geojson-utils@1.0.3 html-tools@1.0.4 htmljs@1.0.4 http@1.1.0 id-map@1.0.3 insecure@1.0.3 jquery@1.11.3_2 json@1.0.3 launch-screen@1.0.2 lepozepo:s3@5.1.0 less@1.0.13 livedata@1.0.13 localstorage@1.0.3 logging@1.0.7 mdg:camera@1.1.4 meteor@1.1.5 meteor-platform@1.2.2 minifiers@1.1.4 minimongo@1.0.7 mobile-status-bar@1.0.3 mongo@1.1.0 observe-sequence@1.0.5 ordered-dict@1.0.3 random@1.0.3 reactive-dict@1.1.0 reactive-var@1.0.5 reload@1.1.3 retry@1.0.3 routepolicy@1.0.5 service-configuration@1.0.4 session@1.1.0 spacebars@1.0.6 spacebars-compiler@1.0.5 standard-app-packages@1.0.5 templating@1.1.0 tracker@1.0.6 ui@1.0.6 underscore@1.0.3 url@1.0.4 webapp@1.2.0 webapp-hashing@1.0.3 ================================================ FILE: example/mdg_camera/mdg_camera.coffee ================================================ if Meteor.isClient Template.basic.helpers "files": -> S3.collection.find() Template.basic.events "click button.upload": (event) -> S3.upload files:[$("textarea").val()] path:"tester" encoding:"base64" (error,result) -> if error console.log error else console.log result "click button.delete": (event) -> S3.delete @relative_url, (error,res) -> if not error console.log res else console.log error if Meteor.isServer S3.config = key:"yourkey" secret:"yousecret" bucket:"yourbucket" ================================================ FILE: example/mdg_camera/mdg_camera.html ================================================ mdg:camera {{> basic}} ================================================ FILE: example/mdg_camera/packages/.gitignore ================================================ /s3 ================================================ FILE: package.js ================================================ Package.describe({ name:"lepozepo:s3", summary: "Upload files to S3. Allows use of Knox Server-Side.", version:"5.2.8", git:"https://github.com/Lepozepo/S3" }); Npm.depends({ knox: "0.9.2", "stream-buffers":"2.1.0", "aws-sdk":"2.1.14", "crypto-js":"3.1.6", "moment":"2.13.0" }); Package.on_use(function (api) { api.versionsFrom('METEOR@1.0'); api.use(["meteor-base@1.0.1","coffeescript","service-configuration"], ["client", "server"]); api.use(["check","random"], ["client","server"]); // Client api.add_files("client/functions.coffee", "client"); // Server api.add_files("server/startup.coffee", "server"); api.add_files("server/sign_request.coffee", "server"); api.add_files("server/delete_object.coffee", "server"); //Allows user access to Knox api.export && api.export("Knox","server"); //Allows user access to AWS-SDK api.export && api.export("AWS","server"); }); ================================================ FILE: server/delete_object.coffee ================================================ Future = Npm.require 'fibers/future' Meteor.methods _s3_delete: (path) -> @unblock() check path,String future = new Future() if S3.rules?.delete delete_context = _.extend this, s3_delete_path:path auth_function = _.bind S3.rules.delete,delete_context if not auth_function() throw new Meteor.Error "Unauthorized", "Delete not allowed" S3.knox.deleteFile path, (e,r) -> if e future.return e else future.return true future.wait() ================================================ FILE: server/sign_request.coffee ================================================ Meteor.methods _s3_sign: (ops={}) -> @unblock() # ops.expiration: the signature expires after x milliseconds | defaults to 30 minutes # ops.path # ops.file_type # ops.file_name # ops.file_size # ops.acl # ops.bucket # ops.server_side_encryption # ops.content_disposition _.defaults ops, expiration:1800000 path:"" bucket:S3.config.bucket acl:"public-read" region:S3.config.region server_side_encryption:false content_disposition:"inline" check ops, expiration:Number path:String bucket:String acl:String region:String server_side_encryption:Boolean file_type:String file_name:String file_size:Number content_disposition:String expiration = new Date Date.now() + ops.expiration expiration = expiration.toISOString() if _.isEmpty ops.path key = "#{ops.file_name}" else key = "#{ops.path}/#{ops.file_name}" meta_uuid = Random.id() meta_date = "#{moment().format('YYYYMMDD')}T000000Z" meta_credential = "#{S3.config.key}/#{moment().format('YYYYMMDD')}/#{ops.region}/s3/aws4_request" policy = "expiration":expiration "conditions":[ ["content-length-range",0,ops.file_size] {"key":key} {"bucket":ops.bucket} {"Content-Type":ops.file_type} {"acl":ops.acl} {"x-amz-algorithm": "AWS4-HMAC-SHA256"} {"x-amz-credential": meta_credential} {"x-amz-date": meta_date } {"x-amz-meta-uuid": meta_uuid} ] if ops.content_disposition policy["conditions"].push({"Content-Disposition": ops.content_disposition}) if ops.server_side_encryption policy["conditions"].push({"x-amz-server-side-encryption": "AES256"}) # Encode the policy policy = new Buffer(JSON.stringify(policy), "utf-8").toString("base64") # Sign the policy signature = calculate_signature policy, ops.region # Identify post_url if ops.region is "us-east-1" or ops.region is "us-standard" post_url = "https://s3.amazonaws.com/#{ops.bucket}" else post_url = "https://s3-#{ops.region}.amazonaws.com/#{ops.bucket}" # Return results policy:policy signature:signature access_key:S3.config.key post_url:post_url url:"#{post_url}/#{key}".replace("https://","http://") secure_url:"#{post_url}/#{key}" relative_url:"/#{key}" bucket:ops.bucket acl:ops.acl key:key file_type:ops.file_type file_name:ops.file_name meta_uuid:meta_uuid meta_date:meta_date meta_credential:meta_credential # crypto = Npm.require("crypto") Crypto = Npm.require "crypto-js" moment = Npm.require "moment" {HmacSHA256} = Crypto calculate_signature = (policy, region) -> kDate = HmacSHA256(moment().format("YYYYMMDD"), "AWS4" + S3.config.secret); kRegion = HmacSHA256(region, kDate); kService = HmacSHA256("s3", kRegion); signature_key = HmacSHA256("aws4_request", kService); HmacSHA256 policy, signature_key .toString Crypto.enc.Hex ================================================ FILE: server/startup.coffee ================================================ #Get Knox and AWS libraries Knox = Npm.require "knox" processBrowser = process.browser process.browser = false AWS = Npm.require "aws-sdk" process.browser = processBrowser #Server side configuration variables @S3 = config:{} knox:{} aws:{} rules:{} Meteor.startup -> if not _.has S3.config,"key" console.log "S3: AWS key is undefined" if not _.has S3.config,"secret" console.log "S3: AWS secret is undefined" if not _.has S3.config,"bucket" console.log "S3: AWS bucket is undefined" if not _.has(S3.config,"bucket") or not _.has(S3.config,"secret") or not _.has(S3.config,"key") return _.defaults S3.config, region:"us-east-1" # us-standard S3.knox = Knox.createClient S3.config S3.aws = new AWS.S3 accessKeyId:S3.config.key secretAccessKey:S3.config.secret region:S3.config.region ================================================ FILE: versions.json ================================================ { "dependencies": [ [ "accounts-base", "1.1.2" ], [ "application-configuration", "1.0.3" ], [ "base64", "1.0.1" ], [ "binary-heap", "1.0.1" ], [ "callback-hook", "1.0.1" ], [ "check", "1.0.2" ], [ "coffeescript", "1.0.4" ], [ "ddp", "1.0.11" ], [ "ejson", "1.0.4" ], [ "follower-livedata", "1.0.2" ], [ "geojson-utils", "1.0.1" ], [ "id-map", "1.0.1" ], [ "json", "1.0.1" ], [ "localstorage", "1.0.1" ], [ "logging", "1.0.5" ], [ "meteor", "1.1.3" ], [ "minimongo", "1.0.5" ], [ "mongo", "1.0.8" ], [ "ordered-dict", "1.0.1" ], [ "random", "1.0.1" ], [ "retry", "1.0.1" ], [ "service-configuration", "1.0.2" ], [ "tracker", "1.0.3" ], [ "underscore", "1.0.1" ] ], "pluginDependencies": [], "toolVersion": "meteor-tool@1.0.35", "format": "1.0" }