2013-12-23 04:22:54 +00:00
|
|
|
Promise = require 'bluebird'
|
|
|
|
utils = require './utils'
|
2014-12-22 20:12:38 +00:00
|
|
|
knex = require './db'
|
2015-03-03 13:55:14 +00:00
|
|
|
express = require 'express'
|
|
|
|
bodyParser = require 'body-parser'
|
2016-05-23 23:59:45 -03:00
|
|
|
bufferEq = require 'buffer-equal-constant-time'
|
2015-07-24 14:26:33 -03:00
|
|
|
request = require 'request'
|
|
|
|
config = require './config'
|
2015-10-06 19:55:01 +00:00
|
|
|
device = require './device'
|
2016-05-11 14:44:05 +00:00
|
|
|
dockerUtils = require './docker-utils'
|
2016-04-18 19:04:06 +03:00
|
|
|
_ = require 'lodash'
|
2016-06-17 06:06:42 +00:00
|
|
|
compose = require './compose'
|
2013-12-14 05:18:20 +00:00
|
|
|
|
2015-10-06 19:55:01 +00:00
|
|
|
module.exports = (application) ->
|
2015-01-28 15:13:26 +00:00
|
|
|
api = express()
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter = express.Router()
|
|
|
|
parsedRouter = express.Router()
|
|
|
|
parsedRouter.use(bodyParser())
|
|
|
|
|
2015-01-28 15:13:26 +00:00
|
|
|
api.use (req, res, next) ->
|
2015-11-02 13:16:43 -03:00
|
|
|
utils.getOrGenerateSecret('api')
|
2015-10-06 19:55:01 +00:00
|
|
|
.then (secret) ->
|
2016-05-23 23:59:45 -03:00
|
|
|
if bufferEq(new Buffer(req.query.apikey), new Buffer(secret))
|
2015-10-06 19:55:01 +00:00
|
|
|
next()
|
|
|
|
else
|
|
|
|
res.sendStatus(401)
|
|
|
|
.catch (err) ->
|
|
|
|
# This should never happen...
|
|
|
|
res.status(503).send('Invalid API key in supervisor')
|
2013-12-14 05:18:20 +00:00
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.get '/ping', (req, res) ->
|
2015-07-24 20:59:58 +00:00
|
|
|
res.send('OK')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/blink', (req, res) ->
|
2015-01-28 15:13:26 +00:00
|
|
|
utils.mixpanelTrack('Device blink')
|
|
|
|
utils.blink.pattern.start()
|
|
|
|
setTimeout(utils.blink.pattern.stop, 15000)
|
2015-03-09 12:06:24 +00:00
|
|
|
res.sendStatus(200)
|
2013-12-14 05:18:20 +00:00
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
parsedRouter.post '/v1/update', (req, res) ->
|
2015-01-28 15:13:26 +00:00
|
|
|
utils.mixpanelTrack('Update notification')
|
2015-08-12 20:24:18 +00:00
|
|
|
application.update(req.body.force)
|
2015-03-09 12:06:24 +00:00
|
|
|
res.sendStatus(204)
|
2013-12-14 05:18:20 +00:00
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/reboot', (req, res) ->
|
2015-08-21 00:21:41 +00:00
|
|
|
utils.mixpanelTrack('Reboot')
|
2015-08-25 19:40:54 +00:00
|
|
|
request.post(config.gosuperAddress + '/v1/reboot')
|
|
|
|
.pipe(res)
|
2015-08-21 00:21:41 +00:00
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/shutdown', (req, res) ->
|
2015-08-21 00:21:41 +00:00
|
|
|
utils.mixpanelTrack('Shutdown')
|
2015-08-25 19:40:54 +00:00
|
|
|
request.post(config.gosuperAddress + '/v1/shutdown')
|
|
|
|
.pipe(res)
|
2015-08-21 00:21:41 +00:00
|
|
|
|
2016-06-16 18:47:12 +03:00
|
|
|
parsedRouter.post '/v1/purge', (req, res) ->
|
2015-07-24 20:59:58 +00:00
|
|
|
appId = req.body.appId
|
|
|
|
utils.mixpanelTrack('Purge /data', appId)
|
|
|
|
if !appId?
|
|
|
|
return res.status(400).send('Missing app id')
|
2015-08-24 18:42:21 +00:00
|
|
|
Promise.using application.lockUpdates(appId, true), ->
|
2015-08-21 19:48:07 +00:00
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
if !app?
|
|
|
|
throw new Error('App not found')
|
2015-08-17 22:08:52 +00:00
|
|
|
application.kill(app)
|
2015-08-24 18:42:21 +00:00
|
|
|
.then ->
|
|
|
|
new Promise (resolve, reject) ->
|
2015-08-26 22:42:10 +00:00
|
|
|
request.post(config.gosuperAddress + '/v1/purge', { json: true, body: applicationId: appId })
|
2015-08-24 18:42:21 +00:00
|
|
|
.on 'error', reject
|
|
|
|
.on 'response', -> resolve()
|
|
|
|
.pipe(res)
|
|
|
|
.finally ->
|
|
|
|
application.start(app)
|
2015-07-31 17:17:57 +00:00
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
2015-07-24 14:26:33 -03:00
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/tcp-ping', (req, res) ->
|
2015-09-02 17:42:11 +05:30
|
|
|
utils.disableCheck(false)
|
|
|
|
res.sendStatus(204)
|
2015-08-26 19:10:22 +05:30
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.delete '/v1/tcp-ping', (req, res) ->
|
2015-09-02 17:42:11 +05:30
|
|
|
utils.disableCheck(true)
|
|
|
|
res.sendStatus(204)
|
2015-08-26 19:10:22 +05:30
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
parsedRouter.post '/v1/restart', (req, res) ->
|
2015-08-20 22:42:13 +00:00
|
|
|
appId = req.body.appId
|
2015-09-08 15:38:38 +00:00
|
|
|
force = req.body.force
|
2015-08-20 22:42:13 +00:00
|
|
|
utils.mixpanelTrack('Restart container', appId)
|
|
|
|
if !appId?
|
|
|
|
return res.status(400).send('Missing app id')
|
2015-09-08 15:38:38 +00:00
|
|
|
Promise.using application.lockUpdates(appId, force), ->
|
2015-08-20 22:42:13 +00:00
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
if !app?
|
|
|
|
throw new Error('App not found')
|
|
|
|
application.kill(app)
|
|
|
|
.then ->
|
|
|
|
application.start(app)
|
|
|
|
.then ->
|
|
|
|
res.status(200).send('OK')
|
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
parsedRouter.post '/v1/apps/:appId/stop', (req, res) ->
|
2016-04-20 19:08:35 +03:00
|
|
|
{ appId } = req.params
|
|
|
|
{ force } = req.body
|
2016-04-08 16:51:39 -03:00
|
|
|
utils.mixpanelTrack('Stop container', appId)
|
|
|
|
if !appId?
|
|
|
|
return res.status(400).send('Missing app id')
|
|
|
|
Promise.using application.lockUpdates(appId, force), ->
|
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
if !app?
|
|
|
|
throw new Error('App not found')
|
|
|
|
application.kill(app, true, false)
|
2016-04-20 15:49:22 +03:00
|
|
|
.then ->
|
2016-04-21 09:51:17 +03:00
|
|
|
res.json(_.pick(app, 'containerId'))
|
2016-04-08 16:51:39 -03:00
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/apps/:appId/start', (req, res) ->
|
2016-04-20 19:08:35 +03:00
|
|
|
{ appId } = req.params
|
2016-04-08 16:51:39 -03:00
|
|
|
utils.mixpanelTrack('Start container', appId)
|
|
|
|
if !appId?
|
|
|
|
return res.status(400).send('Missing app id')
|
2016-04-16 13:52:38 -03:00
|
|
|
Promise.using application.lockUpdates(appId), ->
|
2016-04-08 16:51:39 -03:00
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
if !app?
|
|
|
|
throw new Error('App not found')
|
|
|
|
application.start(app)
|
2016-04-20 15:49:22 +03:00
|
|
|
.then ->
|
2016-04-21 09:51:17 +03:00
|
|
|
res.json(_.pick(app, 'containerId'))
|
2016-04-08 16:51:39 -03:00
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.get '/v1/apps/:appId', (req, res) ->
|
2016-04-20 19:08:35 +03:00
|
|
|
{ appId } = req.params
|
2016-04-16 13:52:38 -03:00
|
|
|
utils.mixpanelTrack('GET app', appId)
|
|
|
|
if !appId?
|
|
|
|
return res.status(400).send('Missing app id')
|
|
|
|
Promise.using application.lockUpdates(appId, true), ->
|
2016-04-21 09:51:17 +03:00
|
|
|
columns = [ 'appId', 'containerId', 'commit', 'imageId', 'env' ]
|
|
|
|
knex('app').select(columns).where({ appId })
|
2016-04-16 13:52:38 -03:00
|
|
|
.then ([ app ]) ->
|
|
|
|
if !app?
|
|
|
|
throw new Error('App not found')
|
|
|
|
# Don't return keys on the endpoint
|
2016-05-28 22:12:08 +03:00
|
|
|
app.env = _.omit(JSON.parse(app.env), config.privateAppEnvVars)
|
2016-04-20 19:43:16 +03:00
|
|
|
# Don't return data that will be of no use to the user
|
2016-04-21 09:51:17 +03:00
|
|
|
res.json(app)
|
2016-04-16 13:52:38 -03:00
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
|
|
|
|
2015-10-06 19:55:01 +00:00
|
|
|
# Expires the supervisor's API key and generates a new one.
|
|
|
|
# It also communicates the new key to the Resin API.
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/regenerate-api-key', (req, res) ->
|
2015-11-02 13:16:43 -03:00
|
|
|
utils.newSecret('api')
|
2015-10-06 19:55:01 +00:00
|
|
|
.then (secret) ->
|
2015-10-28 20:02:00 -03:00
|
|
|
device.updateState(api_secret: secret)
|
2015-10-06 19:55:01 +00:00
|
|
|
res.status(200).send(secret)
|
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.get '/v1/device', (req, res) ->
|
2016-02-15 20:34:11 +00:00
|
|
|
res.json(device.getState())
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.post '/v1/images/create', dockerUtils.createImage
|
2016-05-18 13:47:54 -03:00
|
|
|
unparsedRouter.post '/v1/images/load', dockerUtils.loadImage
|
2016-05-17 19:18:39 +00:00
|
|
|
unparsedRouter.delete '/v1/images/*', dockerUtils.deleteImage
|
2016-05-11 21:47:55 +00:00
|
|
|
unparsedRouter.get '/v1/images', dockerUtils.listImages
|
|
|
|
parsedRouter.post '/v1/containers/create', dockerUtils.createContainer
|
|
|
|
parsedRouter.post '/v1/containers/:id/start', dockerUtils.startContainer
|
|
|
|
unparsedRouter.post '/v1/containers/:id/stop', dockerUtils.stopContainer
|
|
|
|
unparsedRouter.delete '/v1/containers/:id', dockerUtils.deleteContainer
|
|
|
|
unparsedRouter.get '/v1/containers', dockerUtils.listContainers
|
|
|
|
|
2016-06-17 06:06:42 +00:00
|
|
|
unparsedRouter.post '/v1/apps/:appId/compose/up', (req, res) ->
|
|
|
|
appId = req.params.appId
|
|
|
|
onStatus = (status) ->
|
|
|
|
status = JSON.stringify(status) if _.isObject(status)
|
|
|
|
res.write(status)
|
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
return res.status(400).send('App not found') if !app?
|
|
|
|
res.status(200)
|
|
|
|
compose.up(application.composePath(appId), onStatus)
|
|
|
|
.catch (err) ->
|
|
|
|
console.log('Error on compose up:', err, err.stack)
|
|
|
|
.finally ->
|
|
|
|
res.end()
|
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
|
|
|
unparsedRouter.post '/v1/apps/:appId/compose/down', (req, res) ->
|
|
|
|
appId = req.params.appId
|
|
|
|
onStatus = (status) ->
|
|
|
|
status = JSON.stringify(status) if _.isObject(status)
|
|
|
|
res.write(status)
|
|
|
|
knex('app').select().where({ appId })
|
|
|
|
.then ([ app ]) ->
|
|
|
|
return res.status(400).send('App not found') if !app?
|
|
|
|
res.status(200)
|
|
|
|
compose.down(application.composePath(appId), onStatus)
|
|
|
|
.catch (err) ->
|
|
|
|
console.log('Error on compose down:', err, err.stack)
|
|
|
|
.finally ->
|
|
|
|
res.end()
|
|
|
|
.catch (err) ->
|
|
|
|
res.status(503).send(err?.message or err or 'Unknown error')
|
|
|
|
|
2016-05-11 21:47:55 +00:00
|
|
|
api.use(unparsedRouter)
|
|
|
|
api.use(parsedRouter)
|
2016-05-11 14:44:05 +00:00
|
|
|
|
2015-01-28 15:13:26 +00:00
|
|
|
return api
|