Skip to content

Commit

Permalink
Merge pull request #44 from CulturalMe/stip-rackspace
Browse files Browse the repository at this point in the history
Strip rackspace
  • Loading branch information
gsuess committed Jan 11, 2015
2 parents 4eee2ca + 521e04d commit 73055a7
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 133 deletions.
56 changes: 36 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ Template.myPicture.helpers({
});
```

This will use [Blob URLs](http://caniuse.com/#feat=bloburls) to show the image from the local source until it is uploaded to the server. If Blob URL's are not available it will attempt to use `FileReader` to generate a base64 encoded url representing the data as a fallback.
This will use [Blob URLs](http://caniuse.com/#feat=bloburls) to show the image
from the local source until it is uploaded to the server. If Blob URL's are not
available it will attempt to use `FileReader` to generate a base64-encoded url
representing the data as a fallback.

### AWS S3

Expand Down Expand Up @@ -243,45 +246,58 @@ Meteor core packages:

### Directives

#### General

`authorize`: Function (**required** unless set in File Restrictions)

`maxSize`: Number (**required** unless set in File Restrictions)

`allowedFileTypes` RegExp, String or Array (**required** unless set in File
Restrictions)

`cdn` String (optional) - CDN domain for downloads.
i.e. `"https://d111111abcdef8.cloudfront.net"`

`expire` Number (optional) - Number of milliseconds in which an upload
authorization will expire after the request was made. Default is 5 minutes.

#### AWS S3 and Google Cloud

`bucket` String (**required**) - Name of bucket to use.
For Google Cloud the default is `Meteor.settings.GoogleCloudBucket`. For AWS S3
the default bucket is `Meteor.settings.S3Bucket`.

`bucketUrl` String or Function (optional) - Override URL to which files are
uploaded. If it is a function, then the first argument is the bucket name. This
url also used for downloads unless a cdn is given.

`key` String or Function (**required**) - Name of the file on the cloud storage
service. If a function is provided, it will be called with `userId` in the
context and its return value is used as the key. First argument is file info and
the second is the meta-information that can be passed by the client.

`acl` String (optional)

`cacheControl` String (optional) - RFC 2616 Cache-Control directive

`contentDisposition` String (**required**) - RFC 2616 Content-Disposition directive.
`contentDisposition` String (optional) - RFC 2616 Content-Disposition directive.
Default is the uploaded file's name (inline). Use null to disable.

`bucket` String (**required**) - Name of bucket to use. Google Cloud it default is
`Meteor.settings.GoogleCloudBucket`. For AWS S3 the default bucket is
`Meteor.settings.S3Bucket`.

`domain` String (optional) - Override domain to use to access bucket. Useful for
CDN.

`key` String or Function (**required**) - Name of the file on the cloud storage
service. If a function is provided, it will be called with `userId` in the
context and its return value is used as the key.

`expire` Number (optional) - Number of milliseconds in which an upload
authorization will expire after the request was made. Default is 5 minutes.
#### AWS S3 specific

`acl` String (optional)
`AWSAccessKeyId` String (**required**) - Can also be set in `Meteor.settings`.

`AWSAccessKeyId` String (**required** for AWS S3) - Can also be set in
`Meteor.settings`
`AWSSecretAccessKey` String (**required**) - Can also be set in `Meteor.settings`.

`AWSSecretAccessKey` String (**required** for AWS S3) - Can also be set in
`Meteor.settings`
#### Google Cloud Storage specific

`GoogleAccessId` String (**required** for Google Cloud Storage) - Can also be set in
`Meteor.settings`
`GoogleAccessId` String (**required**) - Can also be set in `Meteor.settings`.

`GoogleSecretKey` String (**required** for Google Cloud Storage) - Can also be set
in `Meteor.settings`
`GoogleSecretKey` String (**required**) - Can also be set in `Meteor.settings`.

### File restrictions

Expand Down
20 changes: 11 additions & 9 deletions lib/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,15 @@ Slingshot.Directive = function (service, directive) {

_.defaults(directive, service.directiveDefault);

check(directive, _.defaults({
check(directive, _.extend({
authorize: Function,
maxSize: Match.OneOf(Number, null),
maxSize: Match.Where(function (size) {
check(size, Match.OneOf(Number, null));

return !size || size > 0 && size <= (service.maxSize || Infinity);
}),
allowedFileTypes: matchAllowedFileTypes,
cacheControl: Match.Optional(String),
contentDisposition: Match.Optional(Match.OneOf(String, null))
cdn: Match.Optional(String)
}, service.directiveMatch));

/**
Expand Down Expand Up @@ -144,18 +147,17 @@ _.extend(Slingshot.Directive.prototype, {
*/

getInstructions: function (method, file, meta) {
var instructions = this.storageService().upload(method, _.extend({
contentDisposition: file.name && "inline; filename=" +
quoteString(file.name, '"')
}, this._directive), file, meta);
var instructions = this.storageService().upload(method, this._directive,
file, meta);

check(instructions, {
upload: String,
download: String,
postData: [{
name: String,
value: Match.OneOf(String, Number, null)
}]
}],
headers: Match.Optional(Object)
});

return instructions;
Expand Down
10 changes: 5 additions & 5 deletions lib/restrictions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ Slingshot.fileRestrictions = function (name, restrictions) {
check(restrictions, {
authorize: Match.Optional(Function),
maxSize: Match.Optional(Match.OneOf(Number, null)),
allowedFileTypes: Match.Optional(matchAllowedFileTypes),
allowedFileTypes: Match.Optional(matchAllowedFileTypes)
});

if (Meteor.isServer) {
var directive = Slingshot.getDirective(name);
if (directive) {
_.extend(directive._directive, restrictions);
}
}
return (Slingshot._restrictions[name] =

return (Slingshot._restrictions[name] =
_.extend(Slingshot._restrictions[name] || {}, restrictions));
};

Expand All @@ -50,4 +50,4 @@ Slingshot.fileRestrictions = function (name, restrictions) {

Slingshot.getRestrictions = function (name) {
return this._restrictions[name] || {};
};
};
5 changes: 5 additions & 0 deletions lib/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ Slingshot.Upload = function (directive, metaData) {
});

xhr.open("POST", self.instructions.upload, true);

_.each(self.instructions.headers, function (value, key) {
xhr.setRequestHeader(key, value);
});

xhr.send(buildFormData());

return self;
Expand Down
46 changes: 31 additions & 15 deletions services/aws-s3.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
Slingshot.S3Storage = {

accessId: "AWSAccessKeyId",

secretKey: "AWSSecretAccessKey",

directiveMatch: {
bucket: String,
domain: Match.Optional(String),
bucketUrl: Match.OneOf(String, Function),

AWSAccessKeyId: String,
AWSSecretAccessKey: String,
Expand All @@ -27,13 +31,19 @@ Slingshot.S3Storage = {
check(expire, Number);

return expire > 0;
})
}),

cacheControl: Match.Optional(String),
contentDisposition: Match.Optional(Match.OneOf(String, null))
},

directiveDefault: _.chain(Meteor.settings)
.pick("AWSAccessKeyId", "AWSSecretAccessKey")
.extend({
bucket: Meteor.settings.S3Bucket,
bucketUrl: function (bucket) {
return "https://" + bucket + ".s3.amazonaws.com"
},
expire: 5 * 60 * 1000 //in 5 minutes
})
.value(),
Expand All @@ -50,6 +60,7 @@ Slingshot.S3Storage = {

upload: function (method, directive, file, meta) {
var url = Npm.require("url"),

policy = new Slingshot.StoragePolicy()
.expireIn(directive.expire)
.contentLength(0, Math.min(file.size, directive.maxSize || Infinity)),
Expand All @@ -58,29 +69,30 @@ Slingshot.S3Storage = {
key: _.isFunction(directive.key) ?
directive.key.call(method, file, meta) : directive.key,

AWSAccessKeyId: directive.AWSAccessKeyId,

bucket: directive.bucket,

"Content-Type": file.type,
"acl": directive.acl,

"Cache-Control": directive.cacheControl,
"Content-Disposition": directive.contentDisposition
"Content-Disposition": directive.contentDisposition || file.name &&
"inline; filename=" + quoteString(file.name, '"')
},
domain = {
protocol: "https",
host: directive.domain || directive.bucket + ".s3.amazonaws.com",
pathname: payload.key
};

payload.policy = policy.match(_.omit(payload, "AWSAccessKeyId"))
.stringify();
payload.signature = this.sign(directive.AWSSecretAccessKey, payload.policy);
bucketUrl = _.isFunction(directive.bucketUrl) ?
directive.bucketUrl(directive.bucket) : directive.bucketUrl,

download = _.extend(url.parse(directive.cdn || bucketUrl), {
pathname: payload.key
});

payload[this.accessId] = directive[this.accessId];
payload.policy = policy.match(_.omit(payload, this.accessId)).stringify();
payload.signature = this.sign(directive[this.secretKey], payload.policy);

return {
upload: url.format(_.omit(domain, "pathname")),
download: url.format(domain),
upload: bucketUrl,
download: url.format(download),
postData: [{
name: "key",
value: payload.key
Expand Down Expand Up @@ -108,3 +120,7 @@ Slingshot.S3Storage = {
.digest("base64");
}
};

function quoteString(string, quotes) {
return quotes + string.replace(quotes, '\\' + quotes) + quotes;
}
Loading

0 comments on commit 73055a7

Please sign in to comment.