Merge pull request #1 from strongloop/feature/api-cleanup
Feature/api cleanup
This commit is contained in:
commit
5db8da8c23
|
@ -0,0 +1,91 @@
|
||||||
|
var StorageService = require('../').StorageService;
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var rs = StorageService({
|
||||||
|
provider: 'rackspace',
|
||||||
|
username: 'strongloop',
|
||||||
|
apiKey: 'your-rackspace-api-key'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Container
|
||||||
|
|
||||||
|
rs.getContainers(function (err, containers) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
containers.forEach(function (c) {
|
||||||
|
console.log('rackspace: ', c.name);
|
||||||
|
c.getFiles(function (err, files) {
|
||||||
|
files.forEach(function (f) {
|
||||||
|
console.log('....', f.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
client.createContainer(options, function (err, container) { });
|
||||||
|
client.destroyContainer(containerName, function (err) { });
|
||||||
|
client.getContainer(containerName, function (err, container) { });
|
||||||
|
|
||||||
|
// File
|
||||||
|
|
||||||
|
client.upload(options, function (err) { });
|
||||||
|
client.download(options, function (err) { });
|
||||||
|
client.getFiles(container, function (err, files) { });
|
||||||
|
client.getFile(container, file, function (err, server) { });
|
||||||
|
client.removeFile(container, file, function (err) { });
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
var s3 = StorageService({
|
||||||
|
provider: 'amazon',
|
||||||
|
key: 'your-amazon-key',
|
||||||
|
keyId: 'your-amazon-key-id'
|
||||||
|
});
|
||||||
|
|
||||||
|
s3.getContainers(function (err, containers) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
containers.forEach(function (c) {
|
||||||
|
console.log('amazon: ', c.name);
|
||||||
|
c.getFiles(function (err, files) {
|
||||||
|
files.forEach(function (f) {
|
||||||
|
console.log('....', f.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var stream = s3.uploadStream('con1','test.jpg');
|
||||||
|
var input = fs.createReadStream(path.join(__dirname, 'test.jpg')).pipe(stream);
|
||||||
|
|
||||||
|
|
||||||
|
var local = StorageService({
|
||||||
|
provider: 'filesystem',
|
||||||
|
root: path.join(__dirname, 'storage')
|
||||||
|
});
|
||||||
|
|
||||||
|
// Container
|
||||||
|
|
||||||
|
local.getContainers(function (err, containers) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
containers.forEach(function (c) {
|
||||||
|
console.log('filesystem: ', c.name);
|
||||||
|
c.getFiles(function (err, files) {
|
||||||
|
files.forEach(function (f) {
|
||||||
|
console.log('....', f.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
var loopback = require('loopback')
|
|
||||||
, app = module.exports = loopback();
|
|
||||||
|
|
||||||
// var StorageService = require('../');
|
|
||||||
|
|
||||||
// expose a rest api
|
|
||||||
app.use(loopback.rest());
|
|
||||||
|
|
||||||
app.configure(function () {
|
|
||||||
app.set('port', process.env.PORT || 3000);
|
|
||||||
});
|
|
||||||
|
|
||||||
var ds = loopback.createDataSource({
|
|
||||||
connector: require('../lib/storage-connector'),
|
|
||||||
provider: 'filesystem',
|
|
||||||
root: '/tmp/storage'
|
|
||||||
});
|
|
||||||
|
|
||||||
var Container = ds.createModel('container', {name: String});
|
|
||||||
|
|
||||||
console.log(Container);
|
|
||||||
Container.getContainers(console.log);
|
|
||||||
|
|
||||||
console.log('shared', Container.getContainers.shared);
|
|
||||||
|
|
||||||
app.model(Container);
|
|
||||||
|
|
||||||
/*
|
|
||||||
var handler = new StorageService({provider: 'filesystem', root: '/tmp/storage'});
|
|
||||||
|
|
||||||
app.service('storage', handler);
|
|
||||||
|
|
||||||
app.get('/', function (req, res, next) {
|
|
||||||
res.setHeader('Content-Type', 'text/html');
|
|
||||||
var form = "<html><body><h1>Storage Service Demo</h1>" +
|
|
||||||
"<a href='/download'>List all containers</a><p>" +
|
|
||||||
"Upload to container c1: <p>" +
|
|
||||||
"<form method='POST' enctype='multipart/form-data' action='/upload/c1'>"
|
|
||||||
+ "File to upload: <input type=file name=uploadedFiles multiple=true><br>"
|
|
||||||
+ "Notes about the file: <input type=text name=note><br>"
|
|
||||||
+ "<input type=submit value=Upload></form>" +
|
|
||||||
"</body></html>";
|
|
||||||
res.send(form);
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
app.listen(app.get('port'));
|
|
||||||
console.log('http://127.0.0.1:' + app.get('port'));
|
|
109
example/app.js
109
example/app.js
|
@ -1,91 +1,40 @@
|
||||||
var StorageService = require('../');
|
var loopback = require('loopback')
|
||||||
|
, app = module.exports = loopback();
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
var rs = StorageService({
|
app.use(app.router);
|
||||||
provider: 'rackspace',
|
|
||||||
username: 'strongloop',
|
// expose a rest api
|
||||||
apiKey: 'your-rackspace-api-key'
|
app.use(loopback.rest());
|
||||||
|
|
||||||
|
app.configure(function () {
|
||||||
|
app.set('port', process.env.PORT || 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Container
|
var ds = loopback.createDataSource({
|
||||||
|
connector: require('../index'),
|
||||||
rs.getContainers(function (err, containers) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
containers.forEach(function (c) {
|
|
||||||
console.log('rackspace: ', c.name);
|
|
||||||
c.getFiles(function (err, files) {
|
|
||||||
files.forEach(function (f) {
|
|
||||||
console.log('....', f.name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
client.createContainer(options, function (err, container) { });
|
|
||||||
client.destroyContainer(containerName, function (err) { });
|
|
||||||
client.getContainer(containerName, function (err, container) { });
|
|
||||||
|
|
||||||
// File
|
|
||||||
|
|
||||||
client.upload(options, function (err) { });
|
|
||||||
client.download(options, function (err) { });
|
|
||||||
client.getFiles(container, function (err, files) { });
|
|
||||||
client.getFile(container, file, function (err, server) { });
|
|
||||||
client.removeFile(container, file, function (err) { });
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var s3 = StorageService({
|
|
||||||
provider: 'amazon',
|
|
||||||
key: 'your-amazon-key',
|
|
||||||
keyId: 'your-amazon-key-id'
|
|
||||||
});
|
|
||||||
|
|
||||||
s3.getContainers(function (err, containers) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
containers.forEach(function (c) {
|
|
||||||
console.log('amazon: ', c.name);
|
|
||||||
c.getFiles(function (err, files) {
|
|
||||||
files.forEach(function (f) {
|
|
||||||
console.log('....', f.name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var stream = s3.uploadStream('con1','test.jpg');
|
|
||||||
var input = fs.createReadStream(path.join(__dirname, 'test.jpg')).pipe(stream);
|
|
||||||
|
|
||||||
|
|
||||||
var local = StorageService({
|
|
||||||
provider: 'filesystem',
|
provider: 'filesystem',
|
||||||
root: path.join(__dirname, 'storage')
|
root: path.join(__dirname, 'storage')
|
||||||
});
|
});
|
||||||
|
|
||||||
// Container
|
var container = ds.createModel('container');
|
||||||
|
|
||||||
local.getContainers(function (err, containers) {
|
app.model(container);
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
app.get('/', function (req, res, next) {
|
||||||
return;
|
res.setHeader('Content-Type', 'text/html');
|
||||||
}
|
var form = "<html><body><h1>Storage Service Demo</h1>" +
|
||||||
containers.forEach(function (c) {
|
"<a href='/containers'>List all containers</a><p>" +
|
||||||
console.log('filesystem: ', c.name);
|
"Upload to container c1: <p>" +
|
||||||
c.getFiles(function (err, files) {
|
"<form method='POST' enctype='multipart/form-data' action='/containers/container1/upload'>"
|
||||||
files.forEach(function (f) {
|
+ "File to upload: <input type=file name=uploadedFiles multiple=true><br>"
|
||||||
console.log('....', f.name);
|
+ "Notes about the file: <input type=text name=note><br>"
|
||||||
});
|
+ "<input type=submit value=Upload></form>" +
|
||||||
});
|
"</body></html>";
|
||||||
});
|
res.send(form);
|
||||||
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.listen(app.get('port'));
|
||||||
|
console.log('http://127.0.0.1:' + app.get('port'));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var StorageService = require('../');
|
var StorageService = require('../').StorageService;
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var StorageService = require('../');
|
var StorageService = require('../').StorageService;
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
var StorageConnector = require('./lib/storage-connector');
|
||||||
|
StorageConnector.Container = require('./lib/models/container');
|
||||||
|
StorageConnector.File = require('./lib/models/file');
|
||||||
|
StorageConnector.StorageService = require('./lib/storage-service');
|
||||||
|
|
||||||
|
module.exports = StorageConnector;
|
||||||
|
|
150
lib/index.js
150
lib/index.js
|
@ -1,150 +0,0 @@
|
||||||
var factory = require('./factory');
|
|
||||||
var handler = require('./storage-handler');
|
|
||||||
|
|
||||||
var storage = require('pkgcloud').storage;
|
|
||||||
|
|
||||||
module.exports = StorageService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param options The options to create a provider
|
|
||||||
* @returns {StorageService}
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function StorageService(options) {
|
|
||||||
if (!(this instanceof StorageService)) {
|
|
||||||
return new StorageService(options);
|
|
||||||
}
|
|
||||||
this.provider = options.provider;
|
|
||||||
this.client = factory.createClient(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.getContainers = function (cb) {
|
|
||||||
return this.client.getContainers(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.createContainer = function (options, cb) {
|
|
||||||
options = options || {};
|
|
||||||
if('object' === typeof options && !(options instanceof storage.Container)) {
|
|
||||||
var Container = factory.getProvider(this.provider).Container;
|
|
||||||
options = new Container(this.client, options);
|
|
||||||
}
|
|
||||||
return this.client.createContainer(options, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.destroyContainer = function (container, cb) {
|
|
||||||
return this.client.destroyContainer(container, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.getContainer = function (container, cb) {
|
|
||||||
return this.client.getContainer(container, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// File related functions
|
|
||||||
StorageService.prototype.uploadStream = function (container, file, options, cb) {
|
|
||||||
if(!cb && typeof options === 'function') {
|
|
||||||
cb = options;
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
options = options || {};
|
|
||||||
if(container) options.container = container;
|
|
||||||
if(file) options.remote = file;
|
|
||||||
|
|
||||||
return this.client.upload(options, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.downloadStream = function (container, file, options, cb) {
|
|
||||||
if(!cb && typeof options === 'function') {
|
|
||||||
cb = options;
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
options = options || {};
|
|
||||||
if(container) options.container = container;
|
|
||||||
if(file) options.remote = file;
|
|
||||||
|
|
||||||
return this.client.download(options, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.getFiles = function (container, download, cb) {
|
|
||||||
return this.client.getFiles(container, download, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.getFile = function (container, file, cb) {
|
|
||||||
return this.client.getFile(container, file, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.removeFile = function (container, file, cb) {
|
|
||||||
return this.client.removeFile(container, file, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.upload = function (req, res, cb) {
|
|
||||||
return handler.upload(this.client, req, res, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.prototype.download = function (req, res, cb) {
|
|
||||||
return handler.download(this.client, req, res, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageService.modelName = 'storage';
|
|
||||||
|
|
||||||
StorageService.prototype.getContainers.shared = true;
|
|
||||||
StorageService.prototype.getContainers.accepts = [];
|
|
||||||
StorageService.prototype.getContainers.returns = {arg: 'containers', type: 'array'};
|
|
||||||
StorageService.prototype.getContainers.http = [
|
|
||||||
{verb: 'get', path: '/'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.getContainer.shared = true;
|
|
||||||
StorageService.prototype.getContainer.accepts = [{arg: 'container', type: 'string'}];
|
|
||||||
StorageService.prototype.getContainer.returns = {arg: 'container', type: 'object'};
|
|
||||||
StorageService.prototype.getContainer.http = [
|
|
||||||
{verb: 'get', path: '/:container'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.createContainer.shared = true;
|
|
||||||
StorageService.prototype.createContainer.accepts = [{arg: 'options', type: 'object'}];
|
|
||||||
StorageService.prototype.createContainer.returns = {arg: 'container', type: 'object'};
|
|
||||||
StorageService.prototype.createContainer.http = [
|
|
||||||
{verb: 'post', path: '/'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.destroyContainer.shared = true;
|
|
||||||
StorageService.prototype.destroyContainer.accepts = [{arg: 'container', type: 'string'}];
|
|
||||||
StorageService.prototype.destroyContainer.returns = {};
|
|
||||||
StorageService.prototype.destroyContainer.http = [
|
|
||||||
{verb: 'delete', path: '/:container'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.getFiles.shared = true;
|
|
||||||
StorageService.prototype.getFiles.accepts = [{arg: 'container', type: 'string'}];
|
|
||||||
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array'};
|
|
||||||
StorageService.prototype.getFiles.http = [
|
|
||||||
{verb: 'get', path: '/:container/files'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.getFile.shared = true;
|
|
||||||
StorageService.prototype.getFile.accepts = [{arg: 'container', type: 'string'}, {arg: 'file', type: 'string'}];
|
|
||||||
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object'};
|
|
||||||
StorageService.prototype.getFile.http = [
|
|
||||||
{verb: 'get', path: '/:container/files/:file'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.removeFile.shared = true;
|
|
||||||
StorageService.prototype.removeFile.accepts = [{arg: 'container', type: 'string'}, {arg: 'file', type: 'string'}];
|
|
||||||
StorageService.prototype.removeFile.returns = {};
|
|
||||||
StorageService.prototype.removeFile.http = [
|
|
||||||
{verb: 'delete', path: '/:container/files/:file'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.upload.shared = true;
|
|
||||||
StorageService.prototype.upload.accepts = [{arg: 'req', type: 'undefined', 'http': {source: 'req'}}];
|
|
||||||
StorageService.prototype.upload.returns = {arg: 'result', type: 'object'};
|
|
||||||
StorageService.prototype.upload.http = [
|
|
||||||
{verb: 'post', path: '/:container/upload/:file'}
|
|
||||||
];
|
|
||||||
|
|
||||||
StorageService.prototype.download.shared = true;
|
|
||||||
StorageService.prototype.download.accepts = [{arg: 'req', type: 'undefined', 'http': {source: 'req'}}];
|
|
||||||
StorageService.prototype.download.returns = {arg: 'res', type: 'stream'};
|
|
||||||
StorageService.prototype.download.http = [
|
|
||||||
{verb: 'get', path: '/:container/download/:file'}
|
|
||||||
];
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
var loopback = require('loopback');
|
||||||
|
|
||||||
|
var Container = loopback.createModel('container', {
|
||||||
|
name: String,
|
||||||
|
url: String
|
||||||
|
}, {idInjection: false, strict: false});
|
||||||
|
|
||||||
|
module.exports = Container;
|
|
@ -0,0 +1,9 @@
|
||||||
|
var loopback = require('loopback');
|
||||||
|
|
||||||
|
var File = loopback.createModel('file', {
|
||||||
|
name: String,
|
||||||
|
url: String,
|
||||||
|
container: String
|
||||||
|
}, {idInjection: false, strict: false});
|
||||||
|
|
||||||
|
module.exports = File;
|
|
@ -5,7 +5,7 @@ exports.Container = Container;
|
||||||
|
|
||||||
function Container(client, details) {
|
function Container(client, details) {
|
||||||
base.Container.call(this, client, details);
|
base.Container.call(this, client, details);
|
||||||
};
|
}
|
||||||
|
|
||||||
util.inherits(Container, base.Container);
|
util.inherits(Container, base.Container);
|
||||||
|
|
||||||
|
@ -15,4 +15,4 @@ Container.prototype._setProperties = function(details) {
|
||||||
this[k] = details[k];
|
this[k] = details[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports.File = File;
|
||||||
|
|
||||||
function File(client, details) {
|
function File(client, details) {
|
||||||
base.File.call(this, client, details);
|
base.File.call(this, client, details);
|
||||||
};
|
}
|
||||||
|
|
||||||
util.inherits(File, base.File);
|
util.inherits(File, base.File);
|
||||||
|
|
||||||
|
@ -15,4 +15,4 @@ File.prototype._setProperties = function(details) {
|
||||||
this[k] = details[k];
|
this[k] = details[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
|
@ -20,11 +20,11 @@ function FileSystemProvider(options) {
|
||||||
this.root = options.root;
|
this.root = options.root;
|
||||||
var exists = fs.existsSync(this.root);
|
var exists = fs.existsSync(this.root);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
throw new Error('Path does not exist: ' + this.root);
|
throw new Error('FileSystemProvider: Path does not exist: ' + this.root);
|
||||||
}
|
}
|
||||||
var stat = fs.statSync(this.root);
|
var stat = fs.statSync(this.root);
|
||||||
if (!stat.isDirectory()) {
|
if (!stat.isDirectory()) {
|
||||||
throw new Error('Invalid directory: ' + this.root);
|
throw new Error('FileSystemProvider: Invalid directory: ' + this.root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ function validateName(name, cb) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
|
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
|
||||||
if (!cb) {
|
if (!cb) {
|
||||||
console.error('Invalid name: ', name);
|
console.error('FileSystemProvider: Invalid name: ', name);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,10 @@ function validateName(name, cb) {
|
||||||
if (match && match.index === 0 && match[0].length === name.length) {
|
if (match && match.index === 0 && match[0].length === name.length) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
|
cb && process.nextTick(cb.bind(null,
|
||||||
|
new Error('FileSystemProvider: Invalid name: ' + name)));
|
||||||
if (!cb) {
|
if (!cb) {
|
||||||
console.error('Invalid name: ', name);
|
console.error('FileSystemProvider: Invalid name: ', name);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +58,7 @@ FileSystemProvider.prototype.getContainers = function (cb) {
|
||||||
var containers = [];
|
var containers = [];
|
||||||
var tasks = [];
|
var tasks = [];
|
||||||
files.forEach(function (f) {
|
files.forEach(function (f) {
|
||||||
tasks.push(fs.stat.bind(null, path.join(self.root, f)));
|
tasks.push(fs.stat.bind(fs, path.join(self.root, f)));
|
||||||
});
|
});
|
||||||
async.parallel(tasks, function (err, stats) {
|
async.parallel(tasks, function (err, stats) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -78,7 +79,7 @@ FileSystemProvider.prototype.getContainers = function (cb) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.createContainer = function (options, cb) {
|
FileSystemProvider.prototype.createContainer = function (options, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -86,7 +87,7 @@ FileSystemProvider.prototype.createContainer = function (options, cb) {
|
||||||
validateName(name, cb) && fs.mkdir(path.join(this.root, name), options, function (err) {
|
validateName(name, cb) && fs.mkdir(path.join(this.root, name), options, function (err) {
|
||||||
cb && cb(err, new Container(self, {name: name}));
|
cb && cb(err, new Container(self, {name: name}));
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
|
FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
|
||||||
if (!validateName(containerName, cb)) return;
|
if (!validateName(containerName, cb)) return;
|
||||||
|
@ -95,7 +96,7 @@ FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
|
||||||
fs.readdir(dir, function (err, files) {
|
fs.readdir(dir, function (err, files) {
|
||||||
var tasks = [];
|
var tasks = [];
|
||||||
files.forEach(function (f) {
|
files.forEach(function (f) {
|
||||||
tasks.push(fs.unlink.bind(null, path.join(dir, f)));
|
tasks.push(fs.unlink.bind(fs, path.join(dir, f)));
|
||||||
});
|
});
|
||||||
async.parallel(tasks, function (err) {
|
async.parallel(tasks, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -105,7 +106,7 @@ FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.getContainer = function (containerName, cb) {
|
FileSystemProvider.prototype.getContainer = function (containerName, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -122,8 +123,7 @@ FileSystemProvider.prototype.getContainer = function (containerName, cb) {
|
||||||
}
|
}
|
||||||
cb && cb(err, container);
|
cb && cb(err, container);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// File related functions
|
// File related functions
|
||||||
FileSystemProvider.prototype.upload = function (options, cb) {
|
FileSystemProvider.prototype.upload = function (options, cb) {
|
||||||
|
@ -133,12 +133,17 @@ FileSystemProvider.prototype.upload = function (options, cb) {
|
||||||
if (!validateName(file, cb)) return;
|
if (!validateName(file, cb)) return;
|
||||||
var filePath = path.join(this.root, container, file);
|
var filePath = path.join(this.root, container, file);
|
||||||
|
|
||||||
var fileOpts = {flags: 'w+',
|
var fileOpts = {flags: options.flags || 'w+',
|
||||||
encoding: null,
|
encoding: options.encoding || null,
|
||||||
mode: 0666 };
|
mode: options.mode || 0666
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
return fs.createWriteStream(filePath, fileOpts);
|
return fs.createWriteStream(filePath, fileOpts);
|
||||||
|
} catch (e) {
|
||||||
|
cb && cb(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.download = function (options, cb) {
|
FileSystemProvider.prototype.download = function (options, cb) {
|
||||||
var container = options.container;
|
var container = options.container;
|
||||||
|
@ -151,9 +156,12 @@ FileSystemProvider.prototype.download = function (options, cb) {
|
||||||
var fileOpts = {flags: 'r',
|
var fileOpts = {flags: 'r',
|
||||||
autoClose: true };
|
autoClose: true };
|
||||||
|
|
||||||
|
try {
|
||||||
return fs.createReadStream(filePath, fileOpts);
|
return fs.createReadStream(filePath, fileOpts);
|
||||||
|
} catch (e) {
|
||||||
|
cb && cb(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.getFiles = function (container, download, cb) {
|
FileSystemProvider.prototype.getFiles = function (container, download, cb) {
|
||||||
if (typeof download === 'function' && !(download instanceof RegExp)) {
|
if (typeof download === 'function' && !(download instanceof RegExp)) {
|
||||||
|
@ -167,7 +175,7 @@ FileSystemProvider.prototype.getFiles = function (container, download, cb) {
|
||||||
var files = [];
|
var files = [];
|
||||||
var tasks = [];
|
var tasks = [];
|
||||||
entries.forEach(function (f) {
|
entries.forEach(function (f) {
|
||||||
tasks.push(fs.stat.bind(null, path.join(dir, f)));
|
tasks.push(fs.stat.bind(fs, path.join(dir, f)));
|
||||||
});
|
});
|
||||||
async.parallel(tasks, function (err, stats) {
|
async.parallel(tasks, function (err, stats) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -187,8 +195,7 @@ FileSystemProvider.prototype.getFiles = function (container, download, cb) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemProvider.prototype.getFile = function (container, file, cb) {
|
FileSystemProvider.prototype.getFile = function (container, file, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -206,7 +213,13 @@ FileSystemProvider.prototype.getFile = function (container, file, cb) {
|
||||||
}
|
}
|
||||||
cb && cb(err, f);
|
cb && cb(err, f);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
FileSystemProvider.prototype.getUrl = function (options) {
|
||||||
|
options = options || {};
|
||||||
|
var filePath = path.join(this.root, options.container, options.path);
|
||||||
|
return filePath;
|
||||||
|
};
|
||||||
|
|
||||||
FileSystemProvider.prototype.removeFile = function (container, file, cb) {
|
FileSystemProvider.prototype.removeFile = function (container, file, cb) {
|
||||||
if (!validateName(container, cb)) return;
|
if (!validateName(container, cb)) return;
|
||||||
|
@ -214,4 +227,4 @@ FileSystemProvider.prototype.removeFile = function (container, file, cb) {
|
||||||
|
|
||||||
var filePath = path.join(this.root, container, file);
|
var filePath = path.join(this.root, container, file);
|
||||||
fs.unlink(filePath, cb);
|
fs.unlink(filePath, cb);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var StorageService = require('./index');
|
var StorageService = require('./storage-service');
|
||||||
/**
|
/**
|
||||||
* Export the initialize method to Loopback data
|
* Export the initialize method to Loopback data
|
||||||
* @param dataSource
|
* @param dataSource
|
||||||
|
@ -23,4 +23,4 @@ exports.initialize = function (dataSource, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
connector.define = function(model, properties, settings) {};
|
connector.define = function(model, properties, settings) {};
|
||||||
}
|
};
|
||||||
|
|
|
@ -4,13 +4,14 @@ var StringDecoder = require('string_decoder').StringDecoder;
|
||||||
/**
|
/**
|
||||||
* Handle multipart/form-data upload to the storage service
|
* Handle multipart/form-data upload to the storage service
|
||||||
* @param provider The storage service provider
|
* @param provider The storage service provider
|
||||||
* @param req The HTTP request
|
* @param {Request} req The HTTP request
|
||||||
* @param res The HTTP response
|
* @param {Response} res The HTTP response
|
||||||
* @param cb The callback
|
* @param {String} container The container name
|
||||||
|
* @param {Function} cb The callback
|
||||||
*/
|
*/
|
||||||
exports.upload = function (provider, req, res, cb) {
|
exports.upload = function (provider, req, res, container, cb) {
|
||||||
var form = new IncomingForm(this.options);
|
var form = new IncomingForm(this.options);
|
||||||
var container = req.params.container;
|
container = container || req.params.container;
|
||||||
var fields = {}, files = {};
|
var fields = {}, files = {};
|
||||||
form.handlePart = function (part) {
|
form.handlePart = function (part) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -104,21 +105,20 @@ exports.upload = function (provider, req, res, cb) {
|
||||||
/**
|
/**
|
||||||
* Handle download from a container/file
|
* Handle download from a container/file
|
||||||
* @param provider The storage service provider
|
* @param provider The storage service provider
|
||||||
* @param req The HTTP request
|
* @param {Request} req The HTTP request
|
||||||
* @param res The HTTP response
|
* @param {Response} res The HTTP response
|
||||||
* @param cb The callback
|
* @param {String} container The container name
|
||||||
|
* @param {String} file The file name
|
||||||
|
* @param {Function} cb The callback
|
||||||
*/
|
*/
|
||||||
exports.download = function(provider, req, res, cb) {
|
exports.download = function(provider, req, res, container, file, cb) {
|
||||||
var reader = provider.download({
|
var reader = provider.download({
|
||||||
container: req.params.container,
|
container: container || req && req.params.container,
|
||||||
remote: req.params.file
|
remote: file || req && req.params.file
|
||||||
});
|
});
|
||||||
reader.pipe(res);
|
reader.pipe(res);
|
||||||
reader.on('error', function(err) {
|
reader.on('error', function(err) {
|
||||||
cb && cb(err);
|
res.send(500, { error: err });
|
||||||
});
|
|
||||||
reader.on('end', function(err, result) {
|
|
||||||
cb && cb(err, result);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
var factory = require('./factory');
|
||||||
|
var handler = require('./storage-handler');
|
||||||
|
|
||||||
|
var storage = require('pkgcloud').storage;
|
||||||
|
|
||||||
|
var Container = require('./models/container');
|
||||||
|
var File = require('./models/file');
|
||||||
|
|
||||||
|
module.exports = StorageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options The options to create a provider
|
||||||
|
* @returns {StorageService}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function StorageService(options) {
|
||||||
|
if (!(this instanceof StorageService)) {
|
||||||
|
return new StorageService(options);
|
||||||
|
}
|
||||||
|
this.provider = options.provider;
|
||||||
|
this.client = factory.createClient(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function map(obj) {
|
||||||
|
return obj;
|
||||||
|
/*
|
||||||
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
var data = {};
|
||||||
|
for (var i in obj) {
|
||||||
|
if (obj.hasOwnProperty(i) && typeof obj[i] !== 'function'
|
||||||
|
&& typeof obj[i] !== 'object') {
|
||||||
|
if (i === 'newListener' || i === 'delimiter' || i === 'wildcard') {
|
||||||
|
// Skip properties from the base class
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data[i] = obj[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageService.prototype.getContainers = function (cb) {
|
||||||
|
this.client.getContainers(function (err, containers) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, containers);
|
||||||
|
} else {
|
||||||
|
cb(err, containers.map(function (c) {
|
||||||
|
return new Container(map(c));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.createContainer = function (options, cb) {
|
||||||
|
options = options || {};
|
||||||
|
if ('object' === typeof options && !(options instanceof storage.Container)) {
|
||||||
|
var Container = factory.getProvider(this.provider).Container;
|
||||||
|
options = new Container(this.client, options);
|
||||||
|
}
|
||||||
|
return this.client.createContainer(options, function (err, container) {
|
||||||
|
return cb(err, map(container));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.destroyContainer = function (container, cb) {
|
||||||
|
return this.client.destroyContainer(container, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.getContainer = function (container, cb) {
|
||||||
|
return this.client.getContainer(container, function (err, container) {
|
||||||
|
return cb(err, map(container));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// File related functions
|
||||||
|
StorageService.prototype.uploadStream = function (container, file, options, cb) {
|
||||||
|
if (!cb && typeof options === 'function') {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
if (container) options.container = container;
|
||||||
|
if (file) options.remote = file;
|
||||||
|
|
||||||
|
return this.client.upload(options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.downloadStream = function (container, file, options, cb) {
|
||||||
|
if (!cb && typeof options === 'function') {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
if (container) options.container = container;
|
||||||
|
if (file) options.remote = file;
|
||||||
|
|
||||||
|
return this.client.download(options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.getFiles = function (container, download, cb) {
|
||||||
|
return this.client.getFiles(container, download, function (err, files) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, files);
|
||||||
|
} else {
|
||||||
|
cb(err, files.map(function (f) {
|
||||||
|
return new File(map(f));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.getFile = function (container, file, cb) {
|
||||||
|
return this.client.getFile(container, file, function (err, f) {
|
||||||
|
return cb(err, map(f));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.removeFile = function (container, file, cb) {
|
||||||
|
return this.client.removeFile(container, file, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.upload = function (req, res, cb) {
|
||||||
|
return handler.upload(this.client, req, res, req.params.container, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.prototype.download = function (container, file, res, cb) {
|
||||||
|
return handler.download(this.client, null, res, container, file, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
StorageService.modelName = 'storage';
|
||||||
|
|
||||||
|
StorageService.prototype.getContainers.shared = true;
|
||||||
|
StorageService.prototype.getContainers.accepts = [];
|
||||||
|
StorageService.prototype.getContainers.returns = {arg: 'containers', type: 'array', root: true};
|
||||||
|
StorageService.prototype.getContainers.http =
|
||||||
|
{verb: 'get', path: '/'};
|
||||||
|
|
||||||
|
StorageService.prototype.getContainer.shared = true;
|
||||||
|
StorageService.prototype.getContainer.accepts = [
|
||||||
|
{arg: 'container', type: 'string'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.getContainer.returns = {arg: 'container', type: 'object', root: true};
|
||||||
|
StorageService.prototype.getContainer.http =
|
||||||
|
{verb: 'get', path: '/:container'};
|
||||||
|
|
||||||
|
StorageService.prototype.createContainer.shared = true;
|
||||||
|
StorageService.prototype.createContainer.accepts = [
|
||||||
|
{arg: 'options', type: 'object'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.createContainer.returns = {arg: 'container', type: 'object', root: true};
|
||||||
|
StorageService.prototype.createContainer.http =
|
||||||
|
{verb: 'post', path: '/'};
|
||||||
|
|
||||||
|
StorageService.prototype.destroyContainer.shared = true;
|
||||||
|
StorageService.prototype.destroyContainer.accepts = [
|
||||||
|
{arg: 'container', type: 'string'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.destroyContainer.returns = {};
|
||||||
|
StorageService.prototype.destroyContainer.http =
|
||||||
|
{verb: 'delete', path: '/:container'};
|
||||||
|
|
||||||
|
StorageService.prototype.getFiles.shared = true;
|
||||||
|
StorageService.prototype.getFiles.accepts = [
|
||||||
|
{arg: 'container', type: 'string'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true};
|
||||||
|
StorageService.prototype.getFiles.http =
|
||||||
|
{verb: 'get', path: '/:container/files'};
|
||||||
|
|
||||||
|
StorageService.prototype.getFile.shared = true;
|
||||||
|
StorageService.prototype.getFile.accepts = [
|
||||||
|
{arg: 'container', type: 'string'},
|
||||||
|
{arg: 'file', type: 'string'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true};
|
||||||
|
StorageService.prototype.getFile.http =
|
||||||
|
{verb: 'get', path: '/:container/files/:file'};
|
||||||
|
|
||||||
|
StorageService.prototype.removeFile.shared = true;
|
||||||
|
StorageService.prototype.removeFile.accepts = [
|
||||||
|
{arg: 'container', type: 'string'},
|
||||||
|
{arg: 'file', type: 'string'}
|
||||||
|
];
|
||||||
|
StorageService.prototype.removeFile.returns = {};
|
||||||
|
StorageService.prototype.removeFile.http =
|
||||||
|
{verb: 'delete', path: '/:container/files/:file'};
|
||||||
|
|
||||||
|
StorageService.prototype.upload.shared = true;
|
||||||
|
StorageService.prototype.upload.accepts = [
|
||||||
|
{arg: 'req', type: 'object', 'http': {source: 'req'}},
|
||||||
|
{arg: 'res', type: 'object', 'http': {source: 'res'}}
|
||||||
|
];
|
||||||
|
StorageService.prototype.upload.returns = {arg: 'result', type: 'object'};
|
||||||
|
StorageService.prototype.upload.http =
|
||||||
|
{verb: 'post', path: '/:container/upload'};
|
||||||
|
|
||||||
|
StorageService.prototype.download.shared = true;
|
||||||
|
StorageService.prototype.download.accepts = [
|
||||||
|
{arg: 'container', type: 'string', 'http': {source: 'path'}},
|
||||||
|
{arg: 'file', type: 'string', 'http': {source: 'path'}},
|
||||||
|
{arg: 'res', type: 'object', 'http': {source: 'res'}}
|
||||||
|
];
|
||||||
|
StorageService.prototype.download.http =
|
||||||
|
{verb: 'get', path: '/:container/download/:file'};
|
|
@ -2,12 +2,12 @@
|
||||||
"name": "loopback-storage-service",
|
"name": "loopback-storage-service",
|
||||||
"description": "Loopback Storage Service",
|
"description": "Loopback Storage Service",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "lib/index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js"
|
"test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pkgcloud": "~0.8.14",
|
"pkgcloud": "~0.8.17",
|
||||||
"async": "~0.2.9"
|
"async": "~0.2.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
test.jpg
|
|
@ -1,4 +1,4 @@
|
||||||
var StorageService = require('../lib/index.js');
|
var StorageService = require('../lib/storage-service.js');
|
||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
var request = require('supertest');
|
||||||
|
var loopback = require('loopback');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var app = loopback();
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
// expose a rest api
|
||||||
|
app.use(loopback.rest());
|
||||||
|
|
||||||
|
var ds = loopback.createDataSource({
|
||||||
|
connector: require('../lib/storage-connector'),
|
||||||
|
provider: 'filesystem',
|
||||||
|
root: path.join(__dirname, 'images')
|
||||||
|
});
|
||||||
|
|
||||||
|
var Container = ds.createModel('container');
|
||||||
|
app.model(Container);
|
||||||
|
|
||||||
|
describe('storage service', function () {
|
||||||
|
var server = null;
|
||||||
|
before(function (done) {
|
||||||
|
server = app.listen(3000, function () {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uploads files', function (done) {
|
||||||
|
|
||||||
|
request('http://localhost:3000')
|
||||||
|
.post('/containers/album1/upload')
|
||||||
|
.attach('image', path.join(__dirname, '../example/test.jpg'))
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200, function (err, res) {
|
||||||
|
assert.deepEqual(res.body, {"result": {"files": {"image": [
|
||||||
|
{"container": "album1", "name": "test.jpg", "type": "image/jpeg"}
|
||||||
|
]}, "fields": {}}});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('downloads files', function (done) {
|
||||||
|
|
||||||
|
request('http://localhost:3000')
|
||||||
|
.get('/containers/album1/download/test.jpg')
|
||||||
|
.expect(200, function (err, res) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports errors if it fails to find the file to download', function (done) {
|
||||||
|
|
||||||
|
request('http://localhost:3000')
|
||||||
|
.get('/containers/album1/download/test_not_exist.jpg')
|
||||||
|
.expect(500, function (err, res) {
|
||||||
|
assert(res.body.error);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue