2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
getApp : getApp ,
getApps : getApps ,
getAppIcon : getAppIcon ,
2020-03-29 17:11:10 -07:00
install : install ,
uninstall : uninstall ,
restore : restore ,
2019-10-15 09:16:29 -07:00
importApp : importApp ,
2020-03-29 17:11:10 -07:00
backup : backup ,
update : update ,
2015-07-20 00:09:47 -07:00
getLogs : getLogs ,
getLogStream : getLogStream ,
2016-01-19 13:35:28 +01:00
listBackups : listBackups ,
2020-03-29 17:11:10 -07:00
repair : repair ,
2015-07-20 00:09:47 -07:00
2019-09-08 16:57:08 -07:00
setAccessRestriction : setAccessRestriction ,
setLabel : setLabel ,
setTags : setTags ,
setIcon : setIcon ,
setMemoryLimit : setMemoryLimit ,
2020-01-28 21:30:35 -08:00
setCpuShares : setCpuShares ,
2019-09-08 16:57:08 -07:00
setAutomaticBackup : setAutomaticBackup ,
setAutomaticUpdate : setAutomaticUpdate ,
2019-10-13 18:22:03 -07:00
setReverseProxyConfig : setReverseProxyConfig ,
2019-09-08 16:57:08 -07:00
setCertificate : setCertificate ,
setDebugMode : setDebugMode ,
setEnvironment : setEnvironment ,
setMailbox : setMailbox ,
setLocation : setLocation ,
setDataDir : setDataDir ,
2020-03-29 17:11:10 -07:00
stop : stop ,
start : start ,
restart : restart ,
2016-06-17 17:12:55 -05:00
exec : exec ,
2017-08-15 20:00:52 +02:00
execWebSocket : execWebSocket ,
2016-06-17 17:12:55 -05:00
2020-03-29 17:11:10 -07:00
clone : clone ,
2017-08-18 20:45:52 -07:00
uploadFile : uploadFile ,
2020-03-29 17:11:10 -07:00
downloadFile : downloadFile ,
load : load
2015-07-20 00:09:47 -07:00
} ;
var apps = require ( '../apps.js' ) ,
assert = require ( 'assert' ) ,
2019-03-25 15:07:06 -07:00
auditSource = require ( '../auditsource.js' ) ,
2019-10-24 10:39:47 -07:00
BoxError = require ( '../boxerror.js' ) ,
2015-07-20 00:09:47 -07:00
debug = require ( 'debug' ) ( 'box:routes/apps' ) ,
HttpError = require ( 'connect-lastmile' ) . HttpError ,
HttpSuccess = require ( 'connect-lastmile' ) . HttpSuccess ,
safe = require ( 'safetydance' ) ,
2020-03-29 16:24:04 -07:00
users = require ( '../users.js' ) ,
2017-08-17 11:29:13 +02:00
util = require ( 'util' ) ,
WebSocket = require ( 'ws' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
function load ( req , res , next ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . params . id , 'string' ) ;
2020-03-29 17:11:10 -07:00
apps . get ( req . params . id , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
req . resource = result ;
next ( ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function getApp ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
next ( new HttpSuccess ( 200 , apps . removeInternalFields ( req . resource ) ) ) ;
}
2015-07-20 00:09:47 -07:00
function getApps ( req , res , next ) {
2016-02-25 12:20:11 +01:00
assert . strictEqual ( typeof req . user , 'object' ) ;
2018-08-16 19:57:22 -07:00
apps . getAllByUser ( req . user , function ( error , allApps ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-04-26 20:07:03 -07:00
2018-06-25 16:45:15 -07:00
allApps = allApps . map ( apps . removeRestrictedFields ) ;
2015-07-20 00:09:47 -07:00
next ( new HttpSuccess ( 200 , { apps : allApps } ) ) ;
} ) ;
}
function getAppIcon ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
apps . getIconPath ( req . resource , { original : req . query . original } , function ( error , iconPath ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-05-17 09:47:11 -07:00
2019-09-01 17:44:24 -07:00
res . sendFile ( iconPath ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2020-03-29 17:11:10 -07:00
function install ( req , res , next ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 16:24:04 -07:00
const data = req . body ;
2015-07-20 00:09:47 -07:00
2016-06-04 01:07:43 -07:00
// atleast one
2016-06-04 19:19:00 -07:00
if ( 'manifest' in data && typeof data . manifest !== 'object' ) return next ( new HttpError ( 400 , 'manifest must be an object' ) ) ;
if ( 'appStoreId' in data && typeof data . appStoreId !== 'string' ) return next ( new HttpError ( 400 , 'appStoreId must be a string' ) ) ;
2016-06-04 01:07:43 -07:00
if ( ! data . manifest && ! data . appStoreId ) return next ( new HttpError ( 400 , 'appStoreId or manifest is required' ) ) ;
2016-06-03 23:22:38 -07:00
// required
2015-07-20 00:09:47 -07:00
if ( typeof data . location !== 'string' ) return next ( new HttpError ( 400 , 'location is required' ) ) ;
2017-11-02 22:17:44 +01:00
if ( typeof data . domain !== 'string' ) return next ( new HttpError ( 400 , 'domain is required' ) ) ;
2015-10-16 15:11:54 +02:00
if ( typeof data . accessRestriction !== 'object' ) return next ( new HttpError ( 400 , 'accessRestriction is required' ) ) ;
2016-06-03 23:22:38 -07:00
// optional
if ( ( 'portBindings' in data ) && typeof data . portBindings !== 'object' ) return next ( new HttpError ( 400 , 'portBindings must be an object' ) ) ;
2015-07-20 00:09:47 -07:00
if ( 'icon' in data && typeof data . icon !== 'string' ) return next ( new HttpError ( 400 , 'icon is not a string' ) ) ;
2016-06-04 18:30:05 -07:00
2019-03-22 07:48:31 -07:00
if ( 'label' in data && typeof data . label !== 'string' ) return next ( new HttpError ( 400 , 'label must be a string' ) ) ;
2016-06-04 18:30:05 -07:00
// falsy values in cert and key unset the cert
if ( data . key && typeof data . cert !== 'string' ) return next ( new HttpError ( 400 , 'cert must be a string' ) ) ;
if ( data . cert && typeof data . key !== 'string' ) return next ( new HttpError ( 400 , 'key must be a string' ) ) ;
2015-10-28 22:09:19 +01:00
if ( data . cert && ! data . key ) return next ( new HttpError ( 400 , 'key must be provided' ) ) ;
if ( ! data . cert && data . key ) return next ( new HttpError ( 400 , 'cert must be provided' ) ) ;
2016-06-04 18:30:05 -07:00
2016-02-11 17:00:21 +01:00
if ( 'memoryLimit' in data && typeof data . memoryLimit !== 'number' ) return next ( new HttpError ( 400 , 'memoryLimit is not a number' ) ) ;
2016-06-04 18:30:05 -07:00
2016-11-11 10:55:44 +05:30
if ( 'sso' in data && typeof data . sso !== 'boolean' ) return next ( new HttpError ( 400 , 'sso must be a boolean' ) ) ;
2017-08-16 14:12:07 -07:00
if ( 'enableBackup' in data && typeof data . enableBackup !== 'boolean' ) return next ( new HttpError ( 400 , 'enableBackup must be a boolean' ) ) ;
2018-12-07 09:03:28 -08:00
if ( 'enableAutomaticUpdate' in data && typeof data . enableAutomaticUpdate !== 'boolean' ) return next ( new HttpError ( 400 , 'enableAutomaticUpdate must be a boolean' ) ) ;
2016-09-06 21:21:56 -07:00
2017-01-20 05:48:25 -08:00
if ( ( 'debugMode' in data ) && typeof data . debugMode !== 'object' ) return next ( new HttpError ( 400 , 'debugMode must be an object' ) ) ;
2017-01-19 11:20:24 -08:00
2018-09-04 16:21:10 -07:00
if ( 'alternateDomains' in data ) {
if ( ! Array . isArray ( data . alternateDomains ) ) return next ( new HttpError ( 400 , 'alternateDomains must be an array' ) ) ;
if ( data . alternateDomains . some ( function ( d ) { return ( typeof d . domain !== 'string' || typeof d . subdomain !== 'string' ) ; } ) ) return next ( new HttpError ( 400 , 'alternateDomains array must contain objects with domain and subdomain strings' ) ) ;
}
2018-10-11 14:07:43 -07:00
if ( 'env' in data ) {
if ( ! data . env || typeof data . env !== 'object' ) return next ( new HttpError ( 400 , 'env must be an object' ) ) ;
if ( Object . keys ( data . env ) . some ( function ( key ) { return typeof data . env [ key ] !== 'string' ; } ) ) return next ( new HttpError ( 400 , 'env must contain values as strings' ) ) ;
}
2019-09-16 09:31:34 -07:00
if ( 'overwriteDns' in req . body && typeof req . body . overwriteDns !== 'boolean' ) return next ( new HttpError ( 400 , 'overwriteDns must be boolean' ) ) ;
2020-03-29 16:24:04 -07:00
apps . downloadManifest ( data . appStoreId , data . manifest , function ( error , appStoreId , manifest ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 20:12:59 -07:00
if ( safe . query ( manifest , 'addons.docker' ) && req . user . role !== users . ROLE _OWNER ) return next ( new HttpError ( 403 , '"owner" role is required to install app with docker addon' ) ) ;
2020-03-29 16:24:04 -07:00
data . appStoreId = appStoreId ;
data . manifest = manifest ;
apps . install ( data , auditSource . fromRequest ( req ) , function ( error , result ) {
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
next ( new HttpSuccess ( 202 , { id : result . id , taskId : result . taskId } ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2019-09-08 16:57:08 -07:00
function setAccessRestriction ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( typeof req . body . accessRestriction !== 'object' ) return next ( new HttpError ( 400 , 'accessRestriction must be an object' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setAccessRestriction ( req . resource , req . body . accessRestriction , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setLabel ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( typeof req . body . label !== 'string' ) return next ( new HttpError ( 400 , 'label must be a string' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setLabel ( req . resource , req . body . label , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setTags ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( ! Array . isArray ( req . body . tags ) ) return next ( new HttpError ( 400 , 'tags must be an array' ) ) ;
if ( req . body . tags . some ( ( t ) => typeof t !== 'string' ) ) return next ( new HttpError ( 400 , 'tags array must contain strings' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setTags ( req . resource , req . body . tags , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setIcon ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( req . body . icon !== null && typeof req . body . icon !== 'string' ) return next ( new HttpError ( 400 , 'icon is null or a base-64 image string' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setIcon ( req . resource , req . body . icon , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setMemoryLimit ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( typeof req . body . memoryLimit !== 'number' ) return next ( new HttpError ( 400 , 'memoryLimit is not a number' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setMemoryLimit ( req . resource , req . body . memoryLimit , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2020-01-28 21:30:35 -08:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setCpuShares ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2020-01-28 21:30:35 -08:00
if ( typeof req . body . cpuShares !== 'number' ) return next ( new HttpError ( 400 , 'cpuShares is not a number' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setCpuShares ( req . resource , req . body . cpuShares , auditSource . fromRequest ( req ) , function ( error , result ) {
2020-01-28 21:30:35 -08:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setAutomaticBackup ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( typeof req . body . enable !== 'boolean' ) return next ( new HttpError ( 400 , 'enable must be a boolean' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setAutomaticBackup ( req . resource , req . body . enable , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setAutomaticUpdate ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( typeof req . body . enable !== 'boolean' ) return next ( new HttpError ( 400 , 'enable must be a boolean' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setAutomaticUpdate ( req . resource , req . body . enable , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
2019-10-13 18:22:03 -07:00
function setReverseProxyConfig ( req , res , next ) {
2019-09-08 16:57:08 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( req . body . robotsTxt !== null && typeof req . body . robotsTxt !== 'string' ) return next ( new HttpError ( 400 , 'robotsTxt is not a string' ) ) ;
2019-10-14 16:59:22 -07:00
if ( req . body . csp !== null && typeof req . body . csp !== 'string' ) return next ( new HttpError ( 400 , 'csp is not a string' ) ) ;
2019-10-13 18:22:03 -07:00
2020-03-29 17:11:10 -07:00
apps . setReverseProxyConfig ( req . resource , req . body , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setCertificate ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( req . body . key !== null && typeof req . body . cert !== 'string' ) return next ( new HttpError ( 400 , 'cert must be a string' ) ) ;
if ( req . body . cert !== null && typeof req . body . key !== 'string' ) return next ( new HttpError ( 400 , 'key must be a string' ) ) ;
if ( req . body . cert && ! req . body . key ) return next ( new HttpError ( 400 , 'key must be provided' ) ) ;
if ( ! req . body . cert && req . body . key ) return next ( new HttpError ( 400 , 'cert must be provided' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setCertificate ( req . resource , req . body , auditSource . fromRequest ( req ) , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
function setEnvironment ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( ! req . body . env || typeof req . body . env !== 'object' ) return next ( new HttpError ( 400 , 'env must be an object' ) ) ;
2019-09-09 15:35:02 -07:00
if ( Object . keys ( req . body . env ) . some ( ( key ) => typeof req . body . env [ key ] !== 'string' ) ) return next ( new HttpError ( 400 , 'env must contain values as strings' ) ) ;
2019-09-08 16:57:08 -07:00
2020-03-29 17:11:10 -07:00
apps . setEnvironment ( req . resource , req . body . env , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setDebugMode ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( req . body . debugMode !== null && typeof req . body . debugMode !== 'object' ) return next ( new HttpError ( 400 , 'debugMode must be an object' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setDebugMode ( req . resource , req . body . debugMode , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setMailbox ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
if ( req . body . mailboxName !== null && typeof req . body . mailboxName !== 'string' ) return next ( new HttpError ( 400 , 'mailboxName must be a string' ) ) ;
2019-11-14 21:43:14 -08:00
if ( typeof req . body . mailboxDomain !== 'string' ) return next ( new HttpError ( 400 , 'mailboxDomain must be a string' ) ) ;
2019-09-08 16:57:08 -07:00
2020-03-29 17:11:10 -07:00
apps . setMailbox ( req . resource , req . body . mailboxName , req . body . mailboxDomain , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setLocation ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
2019-10-03 16:23:01 +02:00
if ( typeof req . body . location !== 'string' ) return next ( new HttpError ( 400 , 'location must be string' ) ) ; // location may be an empty string
2019-09-08 16:57:08 -07:00
if ( ! req . body . domain ) return next ( new HttpError ( 400 , 'domain is required' ) ) ;
if ( typeof req . body . domain !== 'string' ) return next ( new HttpError ( 400 , 'domain must be string' ) ) ;
if ( 'portBindings' in req . body && typeof req . body . portBindings !== 'object' ) return next ( new HttpError ( 400 , 'portBindings must be an object' ) ) ;
if ( 'alternateDomains' in req . body ) {
if ( ! Array . isArray ( req . body . alternateDomains ) ) return next ( new HttpError ( 400 , 'alternateDomains must be an array' ) ) ;
if ( req . body . alternateDomains . some ( function ( d ) { return ( typeof d . domain !== 'string' || typeof d . subdomain !== 'string' ) ; } ) ) return next ( new HttpError ( 400 , 'alternateDomains array must contain objects with domain and subdomain strings' ) ) ;
}
2019-09-10 15:23:47 -07:00
if ( 'overwriteDns' in req . body && typeof req . body . overwriteDns !== 'boolean' ) return next ( new HttpError ( 400 , 'overwriteDns must be boolean' ) ) ;
2020-03-29 17:11:10 -07:00
apps . setLocation ( req . resource , req . body , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
function setDataDir ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-08 16:57:08 -07:00
2019-09-09 16:37:59 -07:00
if ( req . body . dataDir !== null && typeof req . body . dataDir !== 'string' ) return next ( new HttpError ( 400 , 'dataDir must be a string' ) ) ;
2019-09-08 16:57:08 -07:00
2020-03-29 17:11:10 -07:00
apps . setDataDir ( req . resource , req . body . dataDir , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
2020-03-29 17:11:10 -07:00
function repair ( req , res , next ) {
2019-09-19 17:04:11 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-09-19 17:04:11 -07:00
2019-09-21 19:45:55 -07:00
const data = req . body ;
2019-11-23 18:35:51 -08:00
if ( 'manifest' in data ) {
2020-02-11 21:05:01 -08:00
if ( ! data . manifest || typeof data . manifest !== 'object' ) return next ( new HttpError ( 400 , 'manifest must be an object' ) ) ;
2020-03-29 16:24:04 -07:00
2020-03-29 20:12:59 -07:00
if ( safe . query ( data . manifest , 'addons.docker' ) && req . user . role !== users . ROLE _OWNER ) return next ( new HttpError ( 403 , '"owner" role is required to repair app with docker addon' ) ) ;
2020-02-11 21:05:01 -08:00
}
if ( 'dockerImage' in data ) {
if ( ! data . dockerImage || typeof data . dockerImage !== 'string' ) return next ( new HttpError ( 400 , 'dockerImage must be a string' ) ) ;
2019-09-21 19:45:55 -07:00
}
2020-03-29 17:11:10 -07:00
apps . repair ( req . resource , data , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-09-19 17:04:11 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
2020-03-29 17:11:10 -07:00
function restore ( req , res , next ) {
2016-06-13 10:08:58 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2016-06-13 10:08:58 -07:00
var data = req . body ;
2019-12-05 21:15:09 -08:00
if ( ! data . backupId || typeof data . backupId !== 'string' ) return next ( new HttpError ( 400 , 'backupId must be non-empty string' ) ) ;
2016-06-13 10:13:54 -07:00
2020-03-29 17:11:10 -07:00
apps . restore ( req . resource , data . backupId , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2019-08-27 20:55:49 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2019-10-15 09:16:29 -07:00
function importApp ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-10-15 09:16:29 -07:00
var data = req . body ;
2019-12-04 18:54:25 -08:00
if ( 'backupId' in data ) { // if not provided, we import in-place
if ( typeof data . backupId !== 'string' ) return next ( new HttpError ( 400 , 'backupId must be string' ) ) ;
if ( typeof data . backupFormat !== 'string' ) return next ( new HttpError ( 400 , 'backupFormat must be string' ) ) ;
if ( 'backupConfig' in data && typeof data . backupConfig !== 'object' ) return next ( new HttpError ( 400 , 'backupConfig must be an object' ) ) ;
const backupConfig = req . body . backupConfig ;
if ( req . body . backupConfig ) {
if ( typeof backupConfig . provider !== 'string' ) return next ( new HttpError ( 400 , 'provider is required' ) ) ;
if ( 'key' in backupConfig && typeof backupConfig . key !== 'string' ) return next ( new HttpError ( 400 , 'key must be a string' ) ) ;
if ( 'acceptSelfSignedCerts' in backupConfig && typeof backupConfig . acceptSelfSignedCerts !== 'boolean' ) return next ( new HttpError ( 400 , 'format must be a boolean' ) ) ;
// testing backup config can take sometime
req . clearTimeout ( ) ;
}
}
2019-10-15 09:16:29 -07:00
2020-03-29 17:11:10 -07:00
apps . importApp ( req . resource , data , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2019-10-15 09:16:29 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
2020-03-29 17:11:10 -07:00
function clone ( req , res , next ) {
2016-06-17 17:12:55 -05:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2016-06-17 17:12:55 -05:00
var data = req . body ;
if ( typeof data . backupId !== 'string' ) return next ( new HttpError ( 400 , 'backupId must be a string' ) ) ;
if ( typeof data . location !== 'string' ) return next ( new HttpError ( 400 , 'location is required' ) ) ;
2017-11-02 22:17:44 +01:00
if ( typeof data . domain !== 'string' ) return next ( new HttpError ( 400 , 'domain is required' ) ) ;
2016-06-17 17:12:55 -05:00
if ( ( 'portBindings' in data ) && typeof data . portBindings !== 'object' ) return next ( new HttpError ( 400 , 'portBindings must be an object' ) ) ;
2019-09-16 09:31:34 -07:00
if ( 'overwriteDns' in req . body && typeof req . body . overwriteDns !== 'boolean' ) return next ( new HttpError ( 400 , 'overwriteDns must be boolean' ) ) ;
2020-03-29 17:11:10 -07:00
apps . clone ( req . resource , data , req . user , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2016-06-17 17:12:55 -05:00
2019-08-27 20:55:49 -07:00
next ( new HttpSuccess ( 201 , { id : result . id , taskId : result . taskId } ) ) ;
2016-06-17 17:12:55 -05:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function backup ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
apps . backup ( req . resource , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2019-08-27 20:55:49 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function uninstall ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
apps . uninstall ( req . resource , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2019-08-27 20:55:49 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function start ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
apps . start ( req . resource , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function stop ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 17:11:10 -07:00
apps . stop ( req . resource , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2019-09-08 16:57:08 -07:00
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2020-03-29 17:11:10 -07:00
function restart ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
2019-12-20 10:29:29 -08:00
2020-03-29 17:11:10 -07:00
apps . restart ( req . resource , auditSource . fromRequest ( req ) , function ( error , result ) {
2019-12-20 10:29:29 -08:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
}
2020-03-29 17:11:10 -07:00
function update ( req , res , next ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
var data = req . body ;
2016-06-04 19:19:00 -07:00
// atleast one
if ( 'manifest' in data && typeof data . manifest !== 'object' ) return next ( new HttpError ( 400 , 'manifest must be an object' ) ) ;
if ( 'appStoreId' in data && typeof data . appStoreId !== 'string' ) return next ( new HttpError ( 400 , 'appStoreId must be a string' ) ) ;
if ( ! data . manifest && ! data . appStoreId ) return next ( new HttpError ( 400 , 'appStoreId or manifest is required' ) ) ;
2019-09-26 13:06:15 -07:00
if ( 'skipBackup' in data && typeof data . skipBackup !== 'boolean' ) return next ( new HttpError ( 400 , 'skipBackup must be a boolean' ) ) ;
2015-07-20 00:09:47 -07:00
if ( 'force' in data && typeof data . force !== 'boolean' ) return next ( new HttpError ( 400 , 'force must be a boolean' ) ) ;
2020-03-29 16:24:04 -07:00
apps . downloadManifest ( data . appStoreId , data . manifest , function ( error , appStoreId , manifest ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2020-03-29 20:12:59 -07:00
if ( safe . query ( manifest , 'addons.docker' ) && req . user . role !== users . ROLE _OWNER ) return next ( new HttpError ( 403 , '"owner" role is required to update app with docker addon' ) ) ;
2020-03-29 16:24:04 -07:00
data . appStoreId = appStoreId ;
data . manifest = manifest ;
2020-03-30 15:05:37 -07:00
apps . update ( req . resource , data , auditSource . fromRequest ( req ) , function ( error , result ) {
2020-03-29 16:24:04 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
next ( new HttpSuccess ( 202 , { taskId : result . taskId } ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
// this route is for streaming logs
function getLogStream ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2019-01-08 12:10:53 -08:00
var lines = 'lines' in req . query ? parseInt ( req . query . lines , 10 ) : 10 ; // we ignore last-event-id
2015-11-02 11:20:50 -08:00
if ( isNaN ( lines ) ) return next ( new HttpError ( 400 , 'lines must be a valid number' ) ) ;
2015-07-20 00:09:47 -07:00
function sse ( id , data ) { return 'id: ' + id + '\ndata: ' + data + '\n\n' ; }
if ( req . headers . accept !== 'text/event-stream' ) return next ( new HttpError ( 400 , 'This API call requires EventStream' ) ) ;
2017-04-19 21:43:29 -07:00
var options = {
lines : lines ,
2019-01-08 12:10:53 -08:00
follow : true ,
format : 'json'
2017-04-19 21:43:29 -07:00
} ;
2020-03-29 17:11:10 -07:00
apps . getLogs ( req . resource , options , function ( error , logStream ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
res . writeHead ( 200 , {
'Content-Type' : 'text/event-stream' ,
'Cache-Control' : 'no-cache' ,
'Connection' : 'keep-alive' ,
'X-Accel-Buffering' : 'no' , // disable nginx buffering
'Access-Control-Allow-Origin' : '*'
} ) ;
res . write ( 'retry: 3000\n' ) ;
res . on ( 'close' , logStream . close ) ;
logStream . on ( 'data' , function ( data ) {
var obj = JSON . parse ( data ) ;
2019-04-24 16:08:30 -07:00
res . write ( sse ( obj . realtimeTimestamp , JSON . stringify ( obj ) ) ) ; // send timestamp as id
2015-07-20 00:09:47 -07:00
} ) ;
logStream . on ( 'end' , res . end . bind ( res ) ) ;
logStream . on ( 'error' , res . end . bind ( res , null ) ) ;
} ) ;
}
function getLogs ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2019-01-08 12:10:53 -08:00
var lines = 'lines' in req . query ? parseInt ( req . query . lines , 10 ) : 10 ;
2015-11-02 11:20:50 -08:00
if ( isNaN ( lines ) ) return next ( new HttpError ( 400 , 'lines must be a number' ) ) ;
2017-04-18 20:32:57 -07:00
var options = {
lines : lines ,
follow : false ,
2019-01-08 12:10:53 -08:00
format : req . query . format || 'json'
2017-04-18 20:32:57 -07:00
} ;
2020-03-29 17:11:10 -07:00
apps . getLogs ( req . resource , options , function ( error , logStream ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
res . writeHead ( 200 , {
'Content-Type' : 'application/x-logs' ,
'Content-Disposition' : 'attachment; filename="log.txt"' ,
'Cache-Control' : 'no-cache' ,
'X-Accel-Buffering' : 'no' // disable nginx buffering
} ) ;
logStream . pipe ( res ) ;
} ) ;
}
2016-05-22 21:07:05 -07:00
function demuxStream ( stream , stdin ) {
var header = null ;
stream . on ( 'readable' , function ( ) {
header = header || stream . read ( 4 ) ;
while ( header !== null ) {
var length = header . readUInt32BE ( 0 ) ;
if ( length === 0 ) {
header = null ;
return stdin . end ( ) ; // EOF
}
var payload = stream . read ( length ) ;
if ( payload === null ) break ;
stdin . write ( payload ) ;
header = stream . read ( 4 ) ;
}
} ) ;
}
2015-07-20 00:09:47 -07:00
function exec ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
var cmd = null ;
if ( req . query . cmd ) {
cmd = safe . JSON . parse ( req . query . cmd ) ;
2016-05-19 15:50:17 -07:00
if ( ! util . isArray ( cmd ) || cmd . length < 1 ) return next ( new HttpError ( 400 , 'cmd must be array with atleast size 1' ) ) ;
2015-07-20 00:09:47 -07:00
}
var columns = req . query . columns ? parseInt ( req . query . columns , 10 ) : null ;
if ( isNaN ( columns ) ) return next ( new HttpError ( 400 , 'columns must be a number' ) ) ;
var rows = req . query . rows ? parseInt ( req . query . rows , 10 ) : null ;
if ( isNaN ( rows ) ) return next ( new HttpError ( 400 , 'rows must be a number' ) ) ;
2020-03-29 18:40:49 -07:00
var tty = req . query . tty === 'true' ;
2020-03-29 20:12:59 -07:00
if ( safe . query ( req . resource , 'manifest.addons.docker' ) && req . user . role !== users . ROLE _OWNER ) return next ( new HttpError ( 403 , '"owner" role is requied to exec app with docker addon' ) ) ;
2016-01-18 11:16:06 -08:00
2020-04-08 09:18:58 -07:00
// in a badly configured reverse proxy, we might be here without an upgrade
if ( req . headers [ 'upgrade' ] !== 'tcp' ) return next ( new HttpError ( 404 , 'exec requires TCP upgrade' ) ) ;
2020-03-29 17:11:10 -07:00
apps . exec ( req . resource , { cmd : cmd , rows : rows , columns : columns , tty : tty } , function ( error , duplexStream ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
req . clearTimeout ( ) ;
res . sendUpgradeHandshake ( ) ;
2016-05-22 21:07:05 -07:00
// When tty is disabled, the duplexStream has 2 separate streams. When enabled, it has stdout/stderr merged.
duplexStream . pipe ( res . socket ) ;
if ( tty ) {
res . socket . pipe ( duplexStream ) ; // in tty mode, the client always waits for server to exit
} else {
demuxStream ( res . socket , duplexStream ) ;
res . socket . on ( 'error' , function ( ) { duplexStream . end ( ) ; } ) ;
res . socket . on ( 'end' , function ( ) { duplexStream . end ( ) ; } ) ;
}
2015-07-20 00:09:47 -07:00
} ) ;
}
2016-01-19 13:35:28 +01:00
2017-08-18 17:56:01 -07:00
function execWebSocket ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2017-08-15 20:00:52 +02:00
var cmd = null ;
if ( req . query . cmd ) {
cmd = safe . JSON . parse ( req . query . cmd ) ;
2017-08-17 09:29:15 +02:00
if ( ! util . isArray ( cmd ) || cmd . length < 1 ) return next ( new HttpError ( 400 , 'cmd must be array with atleast size 1' ) ) ;
2017-08-15 20:00:52 +02:00
}
var columns = req . query . columns ? parseInt ( req . query . columns , 10 ) : null ;
2017-08-17 09:29:15 +02:00
if ( isNaN ( columns ) ) return next ( new HttpError ( 400 , 'columns must be a number' ) ) ;
2017-08-15 20:00:52 +02:00
var rows = req . query . rows ? parseInt ( req . query . rows , 10 ) : null ;
2017-08-17 09:29:15 +02:00
if ( isNaN ( rows ) ) return next ( new HttpError ( 400 , 'rows must be a number' ) ) ;
2017-08-15 20:00:52 +02:00
var tty = req . query . tty === 'true' ? true : false ;
2020-04-08 09:18:58 -07:00
// in a badly configured reverse proxy, we might be here without an upgrade
if ( req . headers [ 'upgrade' ] !== 'websocket' ) return next ( new HttpError ( 404 , 'exec requires websocket' ) ) ;
2020-03-29 17:11:10 -07:00
apps . exec ( req . resource , { cmd : cmd , rows : rows , columns : columns , tty : tty } , function ( error , duplexStream ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2017-08-15 20:00:52 +02:00
2017-08-18 17:56:01 -07:00
req . clearTimeout ( ) ;
2017-08-15 20:00:52 +02:00
2017-08-18 17:56:01 -07:00
res . handleUpgrade ( function ( ws ) {
duplexStream . on ( 'end' , function ( ) { ws . close ( ) ; } ) ;
duplexStream . on ( 'close' , function ( ) { ws . close ( ) ; } ) ;
duplexStream . on ( 'error' , function ( error ) {
2018-11-11 21:57:45 -08:00
debug ( 'duplexStream error:' , error ) ;
2017-08-18 17:56:01 -07:00
} ) ;
duplexStream . on ( 'data' , function ( data ) {
if ( ws . readyState !== WebSocket . OPEN ) return ;
ws . send ( data . toString ( ) ) ;
} ) ;
ws . on ( 'error' , function ( error ) {
2018-11-11 21:57:45 -08:00
debug ( 'websocket error:' , error ) ;
2017-08-18 17:56:01 -07:00
} ) ;
ws . on ( 'message' , function ( msg ) {
duplexStream . write ( msg ) ;
} ) ;
ws . on ( 'close' , function ( ) {
// Clean things up, if any?
} ) ;
2017-08-15 20:00:52 +02:00
} ) ;
} ) ;
}
2016-01-19 13:35:28 +01:00
function listBackups ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2016-01-19 13:35:28 +01:00
2016-03-08 08:57:28 -08:00
var page = typeof req . query . page !== 'undefined' ? parseInt ( req . query . page ) : 1 ;
if ( ! page || page < 0 ) return next ( new HttpError ( 400 , 'page query param has to be a postive number' ) ) ;
var perPage = typeof req . query . per _page !== 'undefined' ? parseInt ( req . query . per _page ) : 25 ;
if ( ! perPage || perPage < 0 ) return next ( new HttpError ( 400 , 'per_page query param has to be a postive number' ) ) ;
2020-03-29 17:11:10 -07:00
apps . listBackups ( req . resource , page , perPage , function ( error , result ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2016-01-19 13:35:28 +01:00
next ( new HttpSuccess ( 200 , { backups : result } ) ) ;
} ) ;
}
2017-08-18 20:45:52 -07:00
function uploadFile ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2017-08-18 20:45:52 -07:00
if ( typeof req . query . file !== 'string' || ! req . query . file ) return next ( new HttpError ( 400 , 'file query argument must be provided' ) ) ;
if ( ! req . files . file ) return next ( new HttpError ( 400 , 'file must be provided as multipart' ) ) ;
2020-03-29 17:11:10 -07:00
apps . uploadFile ( req . resource , req . files . file . path , req . query . file , function ( error ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2017-08-18 20:45:52 -07:00
next ( new HttpSuccess ( 202 , { } ) ) ;
} ) ;
}
function downloadFile ( req , res , next ) {
2020-03-29 17:11:10 -07:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2017-08-18 20:45:52 -07:00
if ( typeof req . query . file !== 'string' || ! req . query . file ) return next ( new HttpError ( 400 , 'file query argument must be provided' ) ) ;
2020-03-29 17:11:10 -07:00
apps . downloadFile ( req . resource , req . query . file , function ( error , stream , info ) {
2019-10-24 18:05:14 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2017-08-18 20:45:52 -07:00
2017-08-20 18:44:26 -07:00
var headers = {
2017-08-18 20:45:52 -07:00
'Content-Type' : 'application/octet-stream' ,
2019-03-04 13:07:14 -08:00
'Content-Disposition' : ` attachment; filename*=utf-8'' ${ encodeURIComponent ( info . filename ) } ` // RFC 2184 section 4
2017-08-20 18:44:26 -07:00
} ;
if ( info . size ) headers [ 'Content-Length' ] = info . size ;
res . writeHead ( 200 , headers ) ;
2017-08-18 20:45:52 -07:00
2017-08-20 18:44:26 -07:00
stream . pipe ( res ) ;
2017-08-18 20:45:52 -07:00
} ) ;
}