brush_cli
Version:
A tool for creating and developing cmos PC Framework project.
487 lines (407 loc) • 13.3 kB
JavaScript
;
var chalk = require('chalk');
var extend = require('../utils/extend');
var fs = require('fs');
var path = require('path');
var parseUrl = require('url').parse;
var request = require('request');
var argv = require('optimist').argv;
var prompt = require('prompt');
var FormData = require('form-data');
var IonicAppLib = require('ionic-app-lib');
var IonicProject = IonicAppLib.project;
var Login = IonicAppLib.login;
var log = IonicAppLib.logging.logger;
var fail = IonicAppLib.utils.fail;
var LoginTask = require('./login');
var settings = {
title: 'push',
name: 'push',
summary: 'Upload APNS and GCM credentials to Ionic Push',
options: {
'--ios-dev-cert': 'Upload your development .p12 file to Ionic Push',
'--ios-prod-cert': 'Upload your production .p12 file to Ionic Push',
'--production-mode=y,n': 'Tell Ionic Push to use production (y) or sandbox (n) APNS servers',
'--google-api-key <your-gcm-api-key>': "Set your app's GCM API key on Ionic Push"
},
isProjectTask: true
};
function run(ionic, argv) {
var task = {
ionic: ionic,
inputValues: {},
inputFiles: {},
useCmdArgs: false
};
getCmdLineOptions();
Login.retrieveLogin()
.then(function(jar) {
if (!jar) {
log.info('No previous login existed. Attempting to log in now.');
return LoginTask.login(argv);
}
return jar;
})
.then(function(jar) {
task.jar = jar;
if (argv._[1] && argv._[1].trim() === 'webhook_url') {
if (argv._[2]) {
set_webhook_url(task, argv._[2].trim());
} else {
log.error(chalk.bold.red('You need to specify a webhook url!'));
}
} else if (argv['google-api-key']) {
set_google_api_key(task, argv['google-api-key'].trim());
} else if (argv['send'] || argv.s) {
// Dev wants to send a push notification
send_push(task);
} else if (argv['ios-dev-cert'] || argv['ios-prod-cert'] || argv.d || argv.p) {
// Dev wants to upload an ios push cert
upload_cert(task);
} else if (argv['production-mode'] || argv.l) {
// Dev wants to switch between test/live mode
// this will look like --live-mode=[y/n]
// where y will put into live and n will put into dev
set_production_mode(task);
}
})
.catch(function(ex) {
log.error('An error occurred', ex);
});
}
function set_webhook_url(task, webhook_url) { // eslint-disable-line camelcase
var project = IonicProject.load();
if (!project.get('app_id')) {
log.error(chalk.bold.red('You need to upload your app first!'));
return false;
}
var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/webhook-url';
var params = parseUrl(url);
var form = new FormData();
form.append('csrfmiddlewaretoken', getCsrfToken(task.jar));
form.append('webhook_url', webhook_url);
form.submit({
protocol: params.protocol,
hostname: params.hostname,
port: params.port,
path: params.path,
headers: form.getHeaders({
cookie: task.jar.map(function(c) {
return c.key + '=' + encodeURIComponent(c.value);
}).join('; ')
})
},
function(err, response) {
response.on('data', function() {
if (err) {
return fail('Error setting Webhook URL: ' + err);
}
if (parseInt(response.statusCode, 10) === '200') {
log.info('Webhook URL set to', webhook_url);
} else {
return fail('App not found');
}
});
});
return true;
}
function set_google_api_key(task, google_api_key) { // eslint-disable-line camelcase
var project = IonicProject.load();
if (!project.get('app_id')) {
log.error(chalk.bold.red('You need to upload your app first!'));
return false;
}
var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/google-api-key';
var params = parseUrl(url);
var form = new FormData();
form.append('csrfmiddlewaretoken', getCsrfToken(task.jar));
form.append('android_api_key', google_api_key);
form.submit({
protocol: params.protocol,
hostname: params.hostname,
port: params.port,
path: params.path,
headers: form.getHeaders({
cookie: task.jar.map(function(c) {
return c.key + '=' + encodeURIComponent(c.value);
}).join('; ')
})
},
function(err, response) {
response.on('data', function() {
if (err) {
return fail('Error setting Google API Key: ' + err);
}
if (parseInt(response.statusCode, 10) === '200') {
log.info(chalk.green('Google API Key Saved'));
} else {
return fail('App not found');
}
});
});
return true;
}
function send_push() { // eslint-disable-line camelcase
var project = IonicProject.load();
if (!project.get('app_id')) {
log.error(chalk.bold.red('You need to upload your app first!'));
return false;
}
// So, we need the push API key to send notifications.
var promptProperties = {};
promptProperties['push-api-key'] = {
name: 'push-api-key',
description: chalk.yellow.bold('Your private API key'),
required: true,
isFile: false
};
promptProperties['device-token'] = {
name: 'device-token',
description: chalk.yellow.bold('Device token'),
required: true,
isFile: false
};
promptProperties['alert'] = {
name: 'alert',
description: chalk.yellow.bold('Notification alert message'),
required: false,
isFile: false
};
promptProperties['badge'] = {
name: 'badge',
description: chalk.yellow.bold('Badge count'),
required: false,
isFile: false
};
promptProperties['sound'] = {
name: 'sound',
description: chalk.yellow.bold('Sound file name'),
required: false,
isFile: false
};
prompt.start();
prompt.get({ properties: promptProperties }, function(err, promptResult) {
if (err) {
return fail('Error: ' + err);
}
var apiKey = promptResult['push-api-key'];
var notification = {
platform: 'ios',
tokens: [promptResult['device-token']],
notification: {
alert: promptResult['alert'],
ios: {
badge: promptResult['badge'],
sound: promptResult['sound']
}
}
};
var options = {
url: 'https://push.ionic.io/api/v1/push',
method: 'POST',
json: true,
headers: {
'X-Ionic-Application-Id': project.get('app_id'),
authorization: 'Basic ' + new Buffer(apiKey + ':').toString('base64')
},
body: notification
};
request(options, function(err, response) {
if (!err && parseInt(response.statusCode, 10) === 202) {
log.info('Successfully queued push notification');
} else {
log.error('Error queueing push notification', err);
}
});
});
}
function set_production_mode(task) { // eslint-disable-line camelcase
var project = IonicProject.load();
if (!project.get('app_id')) {
log.error(chalk.bold.red('You need to upload your app first!'));
return false;
}
if (task.inputValues['production-mode'] === true) {
log.error(chalk.bold.red('You need to specify a value [y/n] to set the production mode!'));
return false;
}
task.inputValues['production-mode'] = task.inputValues['production-mode'].toLowerCase();
if (task.inputValues['production-mode'] !== 'y' && task.inputValues['production-mode'] !== 'n') {
log.error(chalk.bold.red('You need to specify a value [y/n] to set the production mode!'));
return false;
}
var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/set-production-mode';
var params = parseUrl(url);
var form = new FormData();
form.append('csrfmiddlewaretoken', getCsrfToken(task.jar));
form.append('production_mode', task.inputValues['production-mode']);
form.submit({
protocol: params.protocol,
hostname: params.hostname,
port: params.port,
path: params.path,
headers: form.getHeaders({
cookie: task.jar.map(function(c) {
return c.key + '=' + encodeURIComponent(c.value);
}).join('; ')
})
},
function(err, response) {
if (err) {
return fail('Error uploading certificate: ' + err);
}
response.setEncoding('utf8');
response.on('data', function(data) {
if (err) {
return fail('Error setting mode: ' + err);
}
try {
var d = JSON.parse(data);
if (d.errors && d.errors.length) {
for (var j = 0; j < d.errors.length; j += 1) {
log.error(chalk.bold.red(d.errors[j]));
}
return fail('Unable to set mode');
}
if (task.inputValues['production-mode'] === 'y') {
log.info('Successfully set production mode');
} else {
log.info('Successfully set development mode');
}
} catch (parseEx) {
return fail('Error response: ' + parseEx);
}
});
});
}
function upload_cert(task) { // eslint-disable-line camelcase
var project = IonicProject.load();
if (!project.get('app_id')) {
log.error(chalk.bold.red('You need to upload your app first!'));
return false;
}
var promptProperties = {};
var prodCert = '0';
if (argv['ios-dev-cert']) {
promptProperties['ios-push-cert'] = {
name: 'ios-push-cert',
description: chalk.yellow.bold('iOS Dev Push Certificate File (.p12)'),
required: true,
conform: fileExists,
isFile: true
};
} else if (argv['ios-prod-cert']) {
promptProperties['ios-push-cert'] = {
name: 'ios-push-cert',
description: chalk.yellow.bold('iOS Prod Push Certificate File (.p12)'),
required: true,
conform: fileExists,
isFile: true
};
prodCert = '1';
}
prompt.start();
prompt.get({ properties: promptProperties }, function(err, promptResult) {
if (err) {
return fail('Error: ' + err);
}
var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/upload-push-cert';
var params = parseUrl(url);
var form = new FormData();
form.append('csrfmiddlewaretoken', getCsrfToken(task.jar));
var inputFile;
try {
inputFile = promptResult['ios-push-cert'].replace(/\\ /g, ' ').trim();
form.append('ios_push_cert', fs.createReadStream(resolvePath(inputFile)));
} catch (e) {
return fail('Error loading ' + resolvePath(inputFile));
}
// Set the flag for if this is a production or dev cert
form.append('prod_cert', prodCert);
form.submit({
protocol: params.protocol,
hostname: params.hostname,
port: params.port,
path: params.path,
headers: form.getHeaders({
cookie: task.jar.map(function(c) {
return c.key + '=' + encodeURIComponent(c.value);
}).join('; ')
})
},
function(err, response) {
if (err) {
return fail('Error uploading certificate: ' + err);
}
response.setEncoding('utf8');
response.on('data', function(data) {
if (err) {
return fail('Error uploading certificate: ' + err);
}
try {
var d = JSON.parse(data);
if (d.errors && d.errors.length) {
for (var j = 0; j < d.errors.length; j += 1) {
log.error(chalk.bold.red(d.errors[j]));
}
return fail('Unable to upload certificate');
}
log.info('Successfully uploaded certificate');
} catch (parseEx) {
return fail('Error response: ' + parseEx);
}
});
});
});
}
function getCmdLineOptions(task) {
function getCmdArgValue(propertyName, shortName) {
var value = argv[propertyName] || argv[shortName];
if (value) {
task.inputValues[propertyName] = value;
task.useCmdArgs = true;
}
}
function getCmdArgFile(propertyName, shortName) {
var value = argv[propertyName] || argv[shortName];
if (value) {
if (!fileExists(value)) {
return fail('Unable to find file: ' + argv[propertyName]);
}
task.inputFiles[propertyName] = value;
task.useCmdArgs = true;
}
}
getCmdArgValue('production-mode', 'l');
getCmdArgFile('ios-push-dev-cert', 'd');
getCmdArgFile('ios-push-prod-cert', 'p');
}
function getCsrfToken(jar) {
for (var i = 0; i < jar.length; i += 1) {
if (jar[i].key === 'csrftoken') {
return jar[i].value;
}
}
return '';
}
function fileExists(filePath) {
// check if a file exists with a relative path or absolute path
filePath = filePath.replace(/\\ /g, ' ').trim();
return fs.existsSync(resolvePath(filePath));
}
function resolvePath(p) {
if (p.substr(0, 1) === '~') {
p = process.env.HOME + p.substr(1);
}
return path.resolve(p);
}
module.exports = extend(settings, {
run: run,
set_webhook_url: set_webhook_url, // eslint-disable-line camelcase
set_google_api_key: set_google_api_key, // eslint-disable-line camelcase
send_push: send_push, // eslint-disable-line camelcase
set_production_mode: set_production_mode, // eslint-disable-line camelcase
upload_cert: upload_cert, // eslint-disable-line camelcase
getCmdLineOptions: getCmdLineOptions,
getCsrfToken: getCsrfToken
});