Coverage

85%
246
210
36

conf.js

100%
9
9
0
LineHitsSource
1// ------------------------------------------------------------------------------------------
2
31exports.ACCESS_KEY = '<Please apply your access key>';
41exports.SECRET_KEY = '<Dont send your secret key to anyone>';
5
61exports.REDIRECT_URI = '<RedirectURL>';
71exports.AUTHORIZATION_ENDPOINT = '<AuthURL>';
81exports.TOKEN_ENDPOINT = 'https://acc.qbox.me/oauth2/token';
9
101exports.PUT_TIMEOUT = 300000; // 300s = 5m
11
121exports.IO_HOST = 'http://iovip.qbox.me';
131exports.FS_HOST = 'https://fs.qbox.me';
141exports.RS_HOST = 'http://rs.qbox.me:10100';
15
16// ------------------------------------------------------------------------------------------
17

digestauth.js

88%
75
66
9
LineHitsSource
11var crypto = require('crypto');
21var http = require('http');
31var https = require('https');
41var uri = require('url');
51var querystring = require('querystring');
61var conf = require('./conf.js');
71var util = require('./util.js');
8
9// ------------------------------------------------------------------------------------------
10// func checksum
11
121function checksum(opt, body) {
1339 var hmac = crypto.createHmac('sha1', conf.SECRET_KEY);
1439 hmac.update(opt.path + "\n");
1539 if (body) {
160 hmac.update(body);
17 }
1839 var digest = hmac.digest('base64');
1939 return util.base64ToUrlsafe(digest);
20}
21
22// ------------------------------------------------------------------------------------------
23// type Client
24
251function Client() {
26}
27
281Client.prototype.auth = function(opt, params) {
2939 opt.headers['Authorization'] = 'QBox ' + conf.ACCESS_KEY + ':' + checksum(opt, params);
30};
31
321Client.prototype.execute = function(url, params, onresp, onerror) {
33
3439 var u = uri.parse(url);
3539 var opt = {
36 headers: {'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate'},
37 host: u.hostname,
38 port: u.port,
39 path: u.path,
40 method: 'POST'
41 };
42
4339 var proto;
4439 if (u.protocol === 'https:') {
450 proto = https;
46 } else {
4739 proto = http;
48 }
49
5039 var body;
5139 var isStream = false;
5239 var contentLength = 0;
5339 var contentType = 'application/x-www-form-urlencoded';
5439 if (params) {
559 if (params instanceof util.Binary) {
565 contentType = 'application/octet-stream';
575 contentLength = params.bytes;
585 isStream = true;
594 } else if (params instanceof util.Form) {
604 contentType = params.contentType;
614 contentLength = null;
624 isStream = true;
63 } else {
640 if (typeof params === 'string') {
650 body = params;
66 } else {
670 body = querystring.stringify(params);
68 }
690 contentLength = body.length;
70 }
71 }
7239 opt.headers['Content-Type'] = contentType;
7339 if (contentLength !== null) {
7435 opt.headers['Content-Length'] = contentLength;
75 }
76
7739 this.auth(opt, body);
78
7939 var req = proto.request(opt, onresp);
8039 req.on('error', onerror);
81
8239 if (params) {
839 if (isStream) {
849 params.stream.pipe(req);
85 } else {
860 req.end(params);
87 }
88 } else {
8930 req.end();
90 }
9139 return req;
92};
93
941Client.prototype.callWith = function(url, params, onret) {
95
9639 var onresp = function(res) {
9737 util.readAll(res, function(data) {
9837 var ret;
9937 if (data.length === 0) {
1006 ret = {code: res.statusCode};
1016 if (res.statusCode !== 200) {
1020 ret.error = 'E' + res.statusCode;
103 }
1046 onret(ret);
1056 return;
106 }
10731 try {
10831 ret = JSON.parse(data);
10931 if (res.statusCode === 200) {
11022 ret = {code: 200, data: ret};
111 } else {
1129 ret.code = res.statusCode;
113 }
114 } catch (e) {
1150 ret = {code: -2, error: e.toString(), detail: e};
116 }
11731 onret(ret);
118 });
119 };
120
12139 var onerror = function(e) {
1222 var ret = {
123 code: -1,
124 error: e.message,
125 detail: e
126 };
1272 onret(ret);
128 };
129
13039 return this.execute(url, params, onresp, onerror);
131};
132
1331exports.Client = Client;
134
135// ------------------------------------------------------------------------------------------
136

util.js

81%
33
27
6
LineHitsSource
1// ------------------------------------------------------------------------------------------
2// func encode
3
41exports.base64ToUrlsafe = function(v) {
590 return v.replace(/\//g, '_').replace(/\+/g, '-');
6};
7
81exports.encode = function(v) {
951 var encoded = new Buffer(v || '').toString('base64');
1051 return exports.base64ToUrlsafe(encoded);
11};
12
13// ------------------------------------------------------------------------------------------
14// func readAll
15
161exports.readAll = function(strm, ondata) {
1737 var out = [];
1837 var total = 0;
1937 strm.on('data', function(chunk) {
2031 out.push(chunk);
2131 total += chunk.length;
22 });
2337 strm.on('end', function() {
2437 var data;
2537 switch (out.length) {
26 case 0:
276 data = new Buffer(0);
286 break;
29 case 1:
3031 data = out[0];
3131 break;
32 default:
330 data = new Buffer(total);
340 var pos = 0;
350 for (var i = 0; i < out.length; i++) {
360 var chunk = out[i];
370 chunk.copy(data, pos);
380 pos += chunk.length;
39 }
40 }
4137 ondata(data);
42 });
43};
44
45// ------------------------------------------------------------------------------------------
46// type Binary
47
481function Binary(stream, bytes) {
495 this.stream = stream;
505 this.bytes = bytes;
51}
52
531exports.Binary = Binary;
54
55// type Form
56
571function Form(stream, contentType) {
584 this.stream = stream;
594 this.contentType = contentType;
60}
61
621exports.Form = Form;
63// ------------------------------------------------------------------------------------------
64

rs.js

82%
116
96
20
LineHitsSource
11var fs = require('fs');
21var path = require('path');
31var mime = require('mime');
41var formstream = require('formstream');
51var config = require('./conf.js');
61var util = require('./util.js');
71var img = require('./img.js');
8
9// ------------------------------------------------------------------------------------------
10// type Service
11
121function Service(conn, bucket) {
131 this.conn = conn;
141 this.bucket = bucket;
15}
16
171Service.prototype.mkbucket = function(bucketname, onret) {
180 var url = config.RS_HOST + '/mkbucket/' + bucketname;
190 this.conn.callWith(url, null, onret);
20};
21
221Service.prototype.buckets = function(onret) {
230 var url = config.RS_HOST + '/buckets';
240 this.conn.callWith(url, null, onret);
25};
26
271Service.prototype.putAuth = function(onret) {
28 /*
29 * func PutAuth() => PutAuthRet
30 * 上传授权(生成一个短期有效的可匿名上传URL)
31 **/
326 var url = config.IO_HOST + '/put-auth/';
336 this.conn.callWith(url, null, onret);
34};
35
361Service.prototype.putAuthEx = function(expires, callbackUrl, onret) {
37 /*
38 * func PutAuthEx(expires, callbackUrl) => PutAuthRet
39 * 上传授权(生成一个短期有效的可匿名上传URL)
40 **/
411 var url = config.IO_HOST + '/put-auth/' + expires + '/callback/' + util.encode(callbackUrl);
421 this.conn.callWith(url, null, onret);
43};
44
451Service.prototype.put = function(key, mimeType, fp, bytes, onret) {
46 /*
47 * func Put(key string, mimeType string, fp File, bytes int64) => (data PutRet, code int, err Error)
48 * 上传一个流
49 **/
505 if (!mimeType) {
511 mimeType = 'application/octet-stream';
52 }
535 var entryURI = this.bucket + ':' + key;
545 var url = config.IO_HOST + '/rs-put/' + util.encode(entryURI) + '/mimeType/' + util.encode(mimeType);
555 var binary = new util.Binary(fp, bytes);
565 return this.conn.callWith(url, binary, onret);
57};
58
591Service.prototype.putFile = function(key, mimeType, localFile, onret) {
60 /*
61 * func PutFile(key string, mimeType string, localFile string) => (data PutRet, code int, err Error)
62 * 上传文件
63 **/
645 var self = this;
655 if (!mimeType) {
665 mimeType = mime.lookup(localFile);
67 }
685 fs.stat(localFile, function(err, fi) {
695 if (err) {
701 onret({code: -1, error: err.toString(), detail: err});
711 return;
72 }
734 var fp = fs.createReadStream(localFile);
744 self.put(key, mimeType, fp, fi.size, onret);
75 });
76};
77
781Service.prototype.upload = function(upToken, key, mimeType, filename, stream, onret) {
79 /*
80 * func Upload(upToken string, key string, mimeType string, filename string, stream ReadStream) => (data PutRet, code int, err Error)
81 * 以multipart/form-data形式上传ReadStream
82 **/
834 var self = this;
844 if (!mimeType) {
853 mimeType = mime.lookup(filename);
86 }
874 var entryURI = this.bucket + ':' + key;
884 entryURI = '/rs-put/' + util.encode(entryURI) + '/mimeType/' + util.encode(mimeType);
89
904 var form = formstream();
914 form.field('action', entryURI);
924 form.stream('file', stream, filename, mimeType);
93
944 form = new util.Form(form, form.headers()['Content-Type']);
954 return this.conn.callWith(upToken, form, onret);
96};
97
981Service.prototype.uploadFile = function(upToken, key, mimeType, localFile, onret) {
99 /*
100 * func UploadFile(upToken string, key string, mimeType string, localFile string) => (data PutRet, code int, err Error)
101 * 以multipart/form-data形式上传文件
102 **/
1032 var self = this;
1042 if (!mimeType) {
1052 mimeType = mime.lookup(localFile);
106 }
1072 fs.stat(localFile, function(err, fi) {
1082 if (err) {
1091 onret({code: -1, error: err.toString(), detail: err});
1101 return;
111 }
1121 var filename = path.basename(localFile);
1131 var stream = fs.createReadStream(localFile);
1141 self.upload(upToken, key, mimeType, filename, stream, onret);
115 });
116};
117
1181Service.prototype.get = function(key, attName, onret) {
119 /*
120 * func Get(key string, attName string) => GetRet
121 * 下载授权(生成一个短期有效的可匿名下载URL)
122 **/
1238 var entryURI = this.bucket + ':' + key;
1248 var url = config.RS_HOST + '/get/' + util.encode(entryURI) + '/attName/' + util.encode(attName);
1258 this.conn.callWith(url, null, onret);
126};
127
1281Service.prototype.getIfNotModified = function(key, attName, base, onret) {
129 /*
130 * func GetIfNotModified(key string, attName string, base string) => GetRet
131 * 下载授权(生成一个短期有效的可匿名下载URL),如果服务端文件没被人修改的话(用于断点续传)
132 **/
1333 var entryURI = this.bucket + ':' + key;
1343 var url = config.RS_HOST + '/get/' + util.encode(entryURI) + '/attName/' + util.encode(attName) + '/base/' + base;
1353 this.conn.callWith(url, null, onret);
136};
137
1381Service.prototype.stat = function(key, onret) {
139 /*
140 * func Stat(key string) => Entry
141 * 取资源属性
142 */
1432 var entryURI = this.bucket + ':' + key;
1442 var url = config.RS_HOST + '/stat/' + util.encode(entryURI);
1452 this.conn.callWith(url, null, onret);
146};
147
1481Service.prototype.publish = function(domain, onret) {
149 /*
150 * func Publish(domain string) => Bool
151 * 将本 Table 的内容作为静态资源发布。静态资源的url为:http://domain/key
152 **/
1532 var url = config.RS_HOST + '/publish/' + util.encode(domain) + '/from/' + this.bucket;
1542 this.conn.callWith(url, null, onret);
155};
156
1571Service.prototype.unpublish = function(domain, onret) {
158 /*
159 * func Unpublish(domain string) => Bool
160 * 取消发布
161 */
1622 var url = config.RS_HOST + '/unpublish/' + util.encode(domain);
1632 this.conn.callWith(url, null, onret);
164};
165
1661Service.prototype.remove = function(key, onret) {
167 /*
168 * func Delete(key string) => Bool
169 * 删除资源
170 **/
1713 var entryURI = this.bucket + ':' + key;
1723 var url = config.RS_HOST + '/delete/' + util.encode(entryURI);
1733 this.conn.callWith(url, null, onret);
174};
175
1761Service.prototype.drop = function(onret) {
177 /*
178 * func Drop() => Bool
179 * 删除整个表(慎用!)
180 **/
1812 var url = config.RS_HOST + '/drop/' + this.bucket;
1822 this.conn.callWith(url, null, onret);
183};
184
185/*
186 * 持久化存储一个经过云端服务处理过后的资源
187 */
1881Service.prototype.saveAs = function(key, source_url, opWithParams, onret) {
1891 var destEntryURI = this.bucket + ':' + key;
1901 var saveAsEntryURI = util.encode(destEntryURI);
1911 var saveAsParam = "/save-as/" + saveAsEntryURI;
1921 var newurl = source_url + '?' + opWithParams + saveAsParam;
1931 this.conn.callWith(newurl, null, onret);
194};
195
196/*
197 * 图像处理接口(可持久化存储缩略图)
198 * func imageMogrifyAs(<DestKey>, <SourceImageDownloadURL>, <opts>, <callbackFunc>) => Entry
199 * opts = {
200 * "thumbnail": <ImageSizeGeometry>,
201 * "gravity": <GravityType>, =NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
202 * "crop": <ImageSizeAndOffsetGeometry>,
203 * "quality": <ImageQuality>,
204 * "rotate": <RotateDegree>,
205 * "format": <DestinationImageFormat>, =jpg, gif, png, tif, etc.
206 * "auto_orient": <TrueOrFalse>
207 * }
208 */
2091Service.prototype.imageMogrifyAs = function(key, source_img_url, opts, onret) {
2101 var mogrifyParams = img.mkMogrifyParams(opts);
2111 this.saveAs(key, source_img_url, mogrifyParams, onret);
212};
213
214/*
215 * 水印设置接口
216 * setProtected() - 设置原图保护
217 * setSeparator() - 设置分隔符
218 * setStyle() - 设置图片预览风格别名
219 * unsetStyle() - 取消设置图片预览风格别名
220*/
2211Service.prototype.setProtected = function(protectedMode, onret){
2220 var url = config.PUB_HOST + "/accessMode/" + this.bucket + "/mode/" + protectedMode;
2230 this.conn.callWith(url, null, onret);
224};
225
2261Service.prototype.setSeparator = function(sep, onret){
2270 var sepBuffer = new Buffer(sep);
2280 sep = sepBuffer.toString('base64');
2290 var url = config.PUB_HOST + "/separator/" + this.bucket + "/sep/" + sep;
2300 this.conn.callWith(url, null, onret);
231};
232
2331Service.prototype.setStyle = function(name, style, onret){
2340 var nameBuffer = new Buffer(name);
2350 name = nameBuffer.toString('base64');
236
2370 var styleBuffer = new Buffer(style);
2380 style = styleBuffer.toString('base64');
239
2400 var url = config.PUB_HOST + "/style/" + this.bucket + "/name/" + name + "/style/" + style;
2410 this.conn.callWith(url, null, onret);
242};
243
2441Service.prototype.unsetStyle = function(name, onret){
2450 var nameBuffer = new Buffer(name);
2460 name = nameBuffer.toString('base64');
247
2480 var url = config.PUB_HOST + "/unstyle/" + this.bucket + "/name/" + name;
2490 this.conn.callWith(url, null, onret);
250};
251
2521exports.Service = Service;
253
254// ------------------------------------------------------------------------------------------
255

img.js

92%
13
12
1
LineHitsSource
1/*
2 * 图像处理接口,生成图像处理的参数
3 * func mkMogrifyParams() => string
4 * opts = {
5 * "thumbnail": <ImageSizeGeometry>,
6 * "gravity": <GravityType>, =NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
7 * "crop": <ImageSizeAndOffsetGeometry>,
8 * "quality": <ImageQuality>,
9 * "rotate": <RotateDegree>,
10 * "format": <DestinationImageFormat>, =jpg, gif, png, tif, etc.
11 * "auto_orient": <TrueOrFalse>
12 * }
13 */
141exports.mkMogrifyParams = function(opts){
151 opts = opts || {};
161 var keys = ["thumbnail", "gravity", "crop", "quality", "rotate", "format"];
171 var params_string = "", key = null, val = null;
181 for (var i=0; i < keys.length; i++) {
196 key = keys[i];
206 if (undefined !== opts[key]) {
212 params_string += '/' + key + '/' + opts[key];
22 }
23 }
241 if(undefined !== opts.auto_orient && opts.auto_orient === true){
251 params_string += "/auto-orient";
26 }
271 return 'imageMogr' + params_string;
28};
29
30/*
31 * 图像处理接口,生成最终的缩略图预览URL
32 */
331exports.mogrify = function(source_img_url, opts){
340 return source_img_url + '?' + this.mkMogrifyParams(opts);
35};