attache_client 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/app/assets/javascripts/attache/bootstrap3.js +6 -5
- data/app/assets/javascripts/attache/bootstrap3.js.jsx +6 -5
- data/app/assets/javascripts/attache/cors_upload.js +15 -5
- data/lib/attache_client.rb +11 -1
- data/lib/attache_client/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85797f28c584f9b228fa2109cd30bd897f1fb722
|
4
|
+
data.tar.gz: 9d8bcc930c642e7b53ff2103bd847b7d6e3590fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b62f1989a4eac6e82ca9b155694535f73c6eb88f6a9f4f3bc70898ab4172dbb3504f94806912a9b15c49b573d143d01012b5c7443e8b1415af3be06c753c3474
|
7
|
+
data.tar.gz: 7d5f934642057ba36dd4ce6c9a79d29fc8899d03e156805012885652f1e487e4a9e9ff69b7b21cac4c1ab3034ba0ee953530523d75c5641a3c4d670219af54f1
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Ruby on Rails integration for [attache server](https://github.com/choonkeat/attache)
|
4
4
|
|
5
|
+
NOTE: You can learn how to use this gem by looking at the [changes made to our example app](https://github.com/choonkeat/attache-railsapp/commit/16cb1274dcce5be01b6c9d42ad60c30c106ad7f9) or follow the step by step instructions in this `README`
|
6
|
+
|
5
7
|
## Dependencies
|
6
8
|
|
7
9
|
[React](https://github.com/reactjs/react-rails), jQuery, Bootstrap 3
|
@@ -123,6 +125,20 @@ Alternatively, you can get the list of urls and manipulate it however you want
|
|
123
125
|
= image_tag attache_urls(@user.photo_path, '128x128#').sample
|
124
126
|
```
|
125
127
|
|
128
|
+
### Environment configs
|
129
|
+
|
130
|
+
`ATTACHE_UPLOAD_URL` points to the attache server upload url. e.g. `http://localhost:9292/upload`
|
131
|
+
|
132
|
+
`ATTACHE_DOWNLOAD_URL` points to url prefix for downloading the resized images, e.g. `http://cdn.lvh.me:9292/view`
|
133
|
+
|
134
|
+
`ATTACHE_UPLOAD_DURATION` refers to the number of seconds before a signed upload request is considered expired, e.g. `600`
|
135
|
+
|
136
|
+
`ATTACHE_SECRET_KEY` is the shared secret with the `attache` server. e.g. `t0psecr3t`
|
137
|
+
|
138
|
+
* If this variable is not set, then upload requests will not be signed & `ATTACHE_UPLOAD_DURATION` will be ignored
|
139
|
+
* If this variable is set, it must be the same value as `SECRET_KEY` is set on the `attache` server
|
140
|
+
|
141
|
+
|
126
142
|
# License
|
127
143
|
|
128
144
|
MIT
|
@@ -23,9 +23,10 @@ var AttacheFileInput = React.createClass({displayName: "AttacheFileInput",
|
|
23
23
|
this.state.files = {};
|
24
24
|
this.setState(this.state);
|
25
25
|
// upload the file via CORS
|
26
|
+
var that = this;
|
26
27
|
new CORSUpload({
|
27
28
|
file_element: file_element, onComplete: this.setFileValue, onProgress: this.setFileValue,
|
28
|
-
onError: function(uid, status) {
|
29
|
+
onError: function(uid, status) { that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' }); }
|
29
30
|
});
|
30
31
|
// we don't want the file binary to be uploaded in the main form
|
31
32
|
file_element.value = '';
|
@@ -73,13 +74,13 @@ var AttacheFilePreview = React.createClass({displayName: "AttacheFilePreview",
|
|
73
74
|
},
|
74
75
|
|
75
76
|
render: function() {
|
76
|
-
var className = "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
|
77
|
-
var pctString = (this.props.src ? 100 : this.props.percentLoaded) + "%";
|
78
|
-
var pctDesc = (this.props.src ? 'Loading...' : pctString);
|
77
|
+
var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
|
78
|
+
var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
|
79
|
+
var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
|
79
80
|
var img = (this.props.src ? (React.createElement("img", {src: this.props.src, onLoad: this.removeProgressBar})) : '');
|
80
81
|
var pctStyle = { width: pctString, minWidth: '3em' };
|
81
82
|
var cptStyle = { textOverflow: "ellipsis" };
|
82
|
-
var caption = React.createElement("div", {className: "pull-left", style: cptStyle}, this.props.filename || this.props.path.split('/').pop());
|
83
|
+
var caption = React.createElement("div", {className: "pull-left", style: cptStyle}, this.props.filename || this.props.path && this.props.path.split('/').pop());
|
83
84
|
|
84
85
|
if (this.state.srcWas != this.props.src) {
|
85
86
|
var progress = (
|
@@ -23,9 +23,10 @@ var AttacheFileInput = React.createClass({
|
|
23
23
|
this.state.files = {};
|
24
24
|
this.setState(this.state);
|
25
25
|
// upload the file via CORS
|
26
|
+
var that = this;
|
26
27
|
new CORSUpload({
|
27
28
|
file_element: file_element, onComplete: this.setFileValue, onProgress: this.setFileValue,
|
28
|
-
onError: function(uid, status) {
|
29
|
+
onError: function(uid, status) { that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' }); }
|
29
30
|
});
|
30
31
|
// we don't want the file binary to be uploaded in the main form
|
31
32
|
file_element.value = '';
|
@@ -73,13 +74,13 @@ var AttacheFilePreview = React.createClass({
|
|
73
74
|
},
|
74
75
|
|
75
76
|
render: function() {
|
76
|
-
var className = "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
|
77
|
-
var pctString = (this.props.src ? 100 : this.props.percentLoaded) + "%";
|
78
|
-
var pctDesc = (this.props.src ? 'Loading...' : pctString);
|
77
|
+
var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
|
78
|
+
var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
|
79
|
+
var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
|
79
80
|
var img = (this.props.src ? (<img src={this.props.src} onLoad={this.removeProgressBar} />) : '');
|
80
81
|
var pctStyle = { width: pctString, minWidth: '3em' };
|
81
82
|
var cptStyle = { textOverflow: "ellipsis" };
|
82
|
-
var caption = <div className="pull-left" style={cptStyle}>{this.props.filename || this.props.path.split('/').pop()}</div>;
|
83
|
+
var caption = <div className="pull-left" style={cptStyle}>{this.props.filename || this.props.path && this.props.path.split('/').pop()}</div>;
|
83
84
|
|
84
85
|
if (this.state.srcWas != this.props.src) {
|
85
86
|
var progress = (
|
@@ -14,8 +14,17 @@ var CORSUpload = (function() {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
CORSUpload.prototype.handleFileSelect = function(file_element) {
|
17
|
-
var f, files, output, _i, _len, _results, url;
|
18
|
-
|
17
|
+
var f, files, output, _i, _len, _results, url, $ele;
|
18
|
+
$ele = $(file_element);
|
19
|
+
url = $ele.data('uploadurl');
|
20
|
+
if ($ele.data('hmac')) {
|
21
|
+
url = url +
|
22
|
+
"?hmac=" + encodeURIComponent($ele.data('hmac')) +
|
23
|
+
"&uuid=" + encodeURIComponent($ele.data('uuid')) +
|
24
|
+
"&expiration=" + encodeURIComponent($ele.data('expiration')) +
|
25
|
+
""
|
26
|
+
}
|
27
|
+
|
19
28
|
files = file_element.files;
|
20
29
|
output = [];
|
21
30
|
_results = [];
|
@@ -45,7 +54,8 @@ var CORSUpload = (function() {
|
|
45
54
|
CORSUpload.prototype.performUpload = function(file, url) {
|
46
55
|
var this_s3upload, xhr;
|
47
56
|
this_s3upload = this;
|
48
|
-
|
57
|
+
url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'file=' + encodeURIComponent(file.name);
|
58
|
+
xhr = this.createCORSRequest('PUT', url);
|
49
59
|
if (!xhr) {
|
50
60
|
this.onError(file.uid, 'CORS not supported');
|
51
61
|
} else {
|
@@ -53,11 +63,11 @@ var CORSUpload = (function() {
|
|
53
63
|
if (xhr.status === 200) {
|
54
64
|
this_s3upload.onComplete(file.uid, JSON.parse(e.target.responseText));
|
55
65
|
} else {
|
56
|
-
return this_s3upload.onError(file.uid,
|
66
|
+
return this_s3upload.onError(file.uid, xhr.status + ' ' + xhr.statusText);
|
57
67
|
}
|
58
68
|
};
|
59
69
|
xhr.onerror = function() {
|
60
|
-
return this_s3upload.onError(file.uid, '
|
70
|
+
return this_s3upload.onError(file.uid, 'Unable to reach server');
|
61
71
|
};
|
62
72
|
xhr.upload.onprogress = function(e) {
|
63
73
|
var percentLoaded;
|
data/lib/attache_client.rb
CHANGED
@@ -4,6 +4,7 @@ module AttacheClient
|
|
4
4
|
module ViewHelper
|
5
5
|
ATTACHE_UPLOAD_URL = ENV.fetch('ATTACHE_UPLOAD_URL') { 'http://localhost:9292/upload' }
|
6
6
|
ATTACHE_DOWNLOAD_URL = ENV.fetch('ATTACHE_DOWNLOAD_URL') { 'http://localhost:9292/view' }
|
7
|
+
ATTACHE_UPLOAD_DURATION = ENV.fetch('ATTACHE_UPLOAD_DURATION') { '600' }.to_i # 10 minutes
|
7
8
|
|
8
9
|
def attache_urls(json, geometry)
|
9
10
|
array = json.kind_of?(Array) ? json : [*json]
|
@@ -17,6 +18,15 @@ module AttacheClient
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def attache_options(geometry, current_value)
|
21
|
+
auth = if ENV['ATTACHE_SECRET_KEY']
|
22
|
+
uuid = SecureRandom.uuid
|
23
|
+
expiration = (Time.now + ATTACHE_UPLOAD_DURATION).to_i
|
24
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['ATTACHE_SECRET_KEY'], "#{uuid}#{expiration}")
|
25
|
+
{ uuid: uuid, expiration: expiration, hmac: hmac }
|
26
|
+
else
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
20
30
|
{
|
21
31
|
class: 'enable-attache',
|
22
32
|
data: {
|
@@ -24,7 +34,7 @@ module AttacheClient
|
|
24
34
|
value: [*current_value],
|
25
35
|
uploadurl: ATTACHE_UPLOAD_URL,
|
26
36
|
downloadurl: ATTACHE_DOWNLOAD_URL,
|
27
|
-
},
|
37
|
+
}.merge(auth),
|
28
38
|
}
|
29
39
|
end
|
30
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attache_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- choonkeat
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|