'use strict'; var common = require('./common.js'), debug = require('debug')('e2e:appstore'), request = require('superagent-sync'), sleep = require('./shell.js').sleep, stripe = require('stripe'); exports = module.exports = AppStore; function AppStore(origin) { this._origin = origin || process.env.APPSTORE_API_ORIGIN; // credentials for api calls this._credentials = { password: null, accessToken: null }; this._adminCredentials = { password: null, accessToken: null }; } AppStore.prototype.getAccessToken = function (user) { var res = request.post(this._origin + '/api/v1/login').send({ email: user.email, password: user.password, persistent: true }).end(); common.verifyResponse2xx(res, 'Could not login as user:' + user.email + ' password:' + user.password); return res.body.accessToken; }; AppStore.prototype.getProfile = function () { var res = request.get(this._origin + '/api/v1/profile').query({ accessToken: this._credentials.accessToken }).end(); return res.body.profile; }; AppStore.prototype.setCredentials = function (password, accessToken) { this._credentials = { password: password, accessToken: accessToken }; }; AppStore.prototype.setAdminCredentials = function (password, accessToken) { this._adminCredentials = { password: password, accessToken: accessToken }; }; AppStore.prototype.getCloudrons = function () { var res = request.get(this._origin + '/api/v1/cloudrons').query({ accessToken: this._credentials.accessToken, page: 1, per_page: 50 }).end(); return res.body.boxes; }; AppStore.prototype.getCloudron = function (boxId) { var res = request.get(this._origin + '/api/v1/cloudrons/' + boxId).query({ accessToken: this._credentials.accessToken }).retry(0).end(); if (res.statusCode === 404) return null; common.verifyResponse2xx(res, 'Could not query cloudron status'); return res.body.box; }; AppStore.prototype.waitForIP = function (boxId) { process.stdout.write('Waiting for cloudron to get an IP.'); var res; for (var i = 0; i < 60; i++) { sleep(20); process.stdout.write('.'); res = request.get(this._origin + '/api/v1/cloudrons/' + boxId).query({ accessToken: this._credentials.accessToken }).end(); common.verifyResponse2xx(res, 'Could not query cloudron status'); if (res.body.box.ip) { debug(); break; } } if (!res.body || !res.body.box.ip) throw new Error('waitForIP timeout'); debug('Box IP:%s'.green, res.body.box.ip); return res.body.box.ip; }; AppStore.prototype.waitForCloudron = function (boxId) { var creationTime = new Date(); process.stdout.write('Waiting for cloudron to come up.'); var boxInfo = null, res; for (var i = 0; i < 120; i++) { sleep(30); process.stdout.write('.'); res = request.get(this._origin + '/api/v1/cloudrons/' + boxId).query({ accessToken: this._credentials.accessToken }).end(); common.verifyResponse2xx(res, 'Could not query cloudron status'); boxInfo = res.body.box; if (boxInfo.status === 'ready') { debug(); break; } } if (!boxInfo) throw new Error('waitForCloudron: could not get cloudron information'); // check for ready state if (boxInfo.status !== 'ready') throw new Error('waitForCloudron: could not get cloudron status'); debug('Box created in %s minutes with IP:%s'.green, (new Date() - creationTime) / 60000, res.body.box.ip); // even if the cloudron sent heartbeat to appstore, doesn't mean we can contact the cloudron thanks to DO networking insanity process.stdout.write('Waiting for Cloudron to be reachable.'); for (i = 0; i < 60; i++) { sleep(20); process.stdout.write('.'); res = request.get('https://' + boxInfo.ip).end(); if (!res.error) break; debug(res.error); } if (res.error) throw new Error('waitForCloudron: could not reach cloudron'); return boxInfo; }; AppStore.prototype.createCloudron = function (box) { var accessToken = this._credentials.accessToken; var res = request.post(this._origin + '/api/v1/cloudrons').send(box).query({ accessToken: accessToken }).end(); common.verifyResponse2xx(res, 'Could not create cloudron %j', box); debug('Cloudron %s created'.green, box.domain); return res.body.box; }; // Only allowed by admins AppStore.prototype.deleteCloudron = function (box) { var res = request.post(this._origin + '/api/v1/cloudrons/' + box.id) .query({ accessToken: this._adminCredentials.accessToken }) .set('X-HTTP-Method-Override', 'DELETE') .send({ password: this._adminCredentials.password }) .end(); common.verifyResponse2xx(res, 'Could not delete cloudron'); process.stdout.write('Waiting for Cloudron to disappear.'); for (var i = 0; i < 60; i++) { if (this.getCloudron(box.id) === null) return; sleep(20); process.stdout.write('.'); } throw new Error('deleteCloudron: timedout waiting for cloudron to disappear'); }; AppStore.prototype.getManifest = function (appId, version) { var res = request.get(this._origin + '/api/v1/apps/' + appId + '/versions/' + version).end(); common.verifyResponse2xx(res, 'Could not get get app manifest'); return res.body.manifest; }; AppStore.prototype.restore = function (boxId, backupId) { var res = request.post(this._origin + '/api/v1/cloudrons/' + boxId + '/restore/' + encodeURIComponent(backupId)).query({ accessToken: this._credentials.accessToken }).end(); common.verifyResponse2xx(res, 'Could not restore cloudron'); }; AppStore.prototype.setupBilling = function (user, callback) { var stripeApi = stripe(common.stripeSecret()); var that = this; stripeApi.tokens.create({ card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' } }, function (error, token) { if (error) return callback(error); debug('Got stripe token', token.id); var data = { firstName: 'Max', lastName: 'Mustermann', company: 'Muster Inc', street: '200 Muster Blvd.', city: 'Muenster', zip: '12312', state: 'Munster', country: 'DE', billingToken: token.id }; var res = request.put(that._origin + '/api/v1/users/' + user.id).send(data).query({ accessToken: that._credentials.accessToken }).end(); common.verifyResponse2xx(res, 'Could not setup billing'); callback(null); }); };