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) {