diff --git a/lib/cli.js b/lib/cli.js index b49c806a..6e40c8db 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -427,10 +427,15 @@ program , 'public ip for global access' , String , ip()) + .option('--save-dir ' + , 'where to save files' + , String + , os.tmpdir()) .action(function(options) { require('./roles/storage/temp')({ port: options.port , publicIp: options.publicIp + , saveDir: options.saveDir }) }) diff --git a/lib/roles/app.js b/lib/roles/app.js index c5ddd47e..ff5027d7 100644 --- a/lib/roles/app.js +++ b/lib/roles/app.js @@ -64,6 +64,20 @@ module.exports = function(options) { secret: options.secret , authUrl: options.authUrl })) + + // Proxied requests must come before any body parsers + app.post('/api/v1/resources', function(req, res) { + proxy.web(req, res, { + target: options.storageUrl + }) + }) + + app.get('/api/v1/resources/:id', function(req, res) { + proxy.web(req, res, { + target: options.storageUrl + }) + }) + app.use(express.json()) app.use(express.urlencoded()) app.use(express.csrf()) @@ -187,18 +201,6 @@ module.exports = function(options) { }) }) - app.post('/api/v1/resources', function(req, res) { - proxy.web(req, res, { - target: options.storageUrl - }) - }) - - app.get('/api/v1/resources/:id', function(req, res) { - proxy.web(req, res, { - target: options.storageUrl - }) - }) - io.set('authorization', (function() { var parse = Promise.promisify(express.cookieParser(options.secret)) return function(handshake, accept) { diff --git a/lib/roles/storage/temp.js b/lib/roles/storage/temp.js index cfece85b..a44c748d 100644 --- a/lib/roles/storage/temp.js +++ b/lib/roles/storage/temp.js @@ -1,12 +1,16 @@ var http = require('http') var util = require('util') +var fs = require('fs') var express = require('express') var formidable = require('formidable') var Promise = require('bluebird') var ApkReader = require('adbkit-apkreader') +var request = require('request') +var temp = require('temp') var logger = require('../../util/logger') +var requtil = require('../../util/requtil') var Storage = require('../../util/storage') module.exports = function(options) { @@ -19,46 +23,86 @@ module.exports = function(options) { app.set('case sensitive routing', true) app.set('trust proxy', true) - app.use(express.json()) - app.use(express.urlencoded()) - storage.on('timeout', function(id) { log.info('Cleaning up inactive resource "%s"', id) }) + function process(file) { + log.info('Processing "%s"', file.path) + + var reader = ApkReader.readFile(file.path) + var manifest = reader.readManifestSync() + var id = storage.store(file) + + return { + url: util.format( + 'http://%s:%s/api/v1/resources/%s' + , options.publicIp + , options.port + , id + ) + , manifest: manifest + } + } + + function download(url) { + var resolver = Promise.defer() + var path = temp.path({ + dir: options.saveDir + }) + + log.info('Downloading "%s" to "%s"', url, path) + + function errorListener(err) { + resolver.reject(err) + } + + function closeListener() { + resolver.resolve({ + path: path + }) + } + + try { + var dl = request(url) + .pipe(fs.createWriteStream(path)) + .on('error', errorListener) + .on('close', closeListener) + } + catch (err) { + resolver.reject(err) + } + + return resolver.promise.finally(function() { + dl.removeListener('error', errorListener) + dl.removeListener('end', closeListener) + }) + } + app.post('/api/v1/resources', function(req, res) { var form = Promise.promisifyAll(new formidable.IncomingForm()) form.parseAsync(req) .spread(function(fields, files) { if (files.file) { - try { - var reader = ApkReader.readFile(files.file.path) - var manifest = reader.readManifestSync() - var id = storage.store(files.file) - res.json(201, { - success: true - , url: util.format( - 'http://%s:%s/api/v1/resources/%s' - , options.publicIp - , options.port - , id - ) - , manifest: manifest - }) - } - catch (err) { - log.error('ApkReader had an error', err.stack) - res.json(500, { - success: false - }) - } + return process(files.file) + } + else if (fields.url) { + return download(fields.url).then(process) } else { - res.json(400, { - success: false - }) + throw new requtil.ValidationError('"file" or "url" is required') } }) + .then(function(data) { + data.success = true + res.json(201, data) + }) + .catch(requtil.ValidationError, function() { + res.json(400, { + success: false + , error: 'ValidationError' + }) + }) .catch(function(err) { log.error('Failed to save resource: ', err.stack) res.json(500, { diff --git a/res/app/components/stf/control/control-service.js b/res/app/components/stf/control/control-service.js index 0789519f..6b968ce2 100644 --- a/res/app/components/stf/control/control-service.js +++ b/res/app/components/stf/control/control-service.js @@ -1,6 +1,7 @@ module.exports = function ControlServiceFactory( $rootScope , $upload +, $http , socket , TransactionService ) { @@ -108,34 +109,49 @@ module.exports = function ControlServiceFactory( return tx } + function install(options) { + var app = options.manifest.application + var tx = TransactionService.create(target) + var params = { + url: options.url + } + if (app.launcherActivities.length) { + var activity = app.launcherActivities[0] + params.launchActivity = { + action: 'android.intent.action.MAIN' + , component: options.manifest.package + '/' + activity.name + , category: ['android.intent.category.LAUNCHER'] + , flags: 0x10200000 + } + } + socket.emit('device.install', channel, tx.channel, params) + tx.manifest = options.manifest + return tx + } + this.install = function(files) { - return $upload.upload({ - url: '/api/v1/resources' - , method: 'POST' - , file: files[0] - }) - .then(function(response) { - var manifest = response.data.manifest - var app = manifest.application - var tx = TransactionService.create(target) - console.log('resp',response) - console.log(manifest) - var params = { - url: response.data.url - } - if (app.launcherActivities.length) { - var activity = app.launcherActivities[0] - params.launchActivity = { - action: 'android.intent.action.MAIN' - , component: manifest.package + '/' + activity.name - , category: ['android.intent.category.LAUNCHER'] - , flags: 0x10200000 + if (typeof files === 'string') { + return $http({ + url: '/api/v1/resources' + , method: 'POST' + , data: { + url: files } - } - socket.emit('device.install', channel, tx.channel, params) - tx.manifest = manifest - return tx - }) + }) + .then(function(response) { + return install(response.data) + }) + } + else { + return $upload.upload({ + url: '/api/v1/resources' + , method: 'POST' + , file: files[0] + }) + .then(function(response) { + return install(response.data) + }) + } } this.uninstall = function(pkg) {