├── store.json ├── contributors.txt ├── .gitignore ├── Dockerfile ├── config ├── index.js ├── environments.js └── locales │ ├── es.yml │ └── en.yml ├── bin ├── matrix-base.js ├── matrix ├── matrix-search.js ├── matrix-account.js ├── matrix-update.js ├── matrix-set.js ├── matrix-uninstall.js ├── matrix-install.js ├── matrix-unpublish.js ├── matrix-login.js ├── matrix-use.js ├── matrix-stop.js ├── matrix-restart.js ├── matrix-start.js ├── matrix-config.js ├── matrix-list.js ├── matrix-create.js ├── matrix-init.js ├── matrix-validate.js ├── matrix-remove.js ├── matrix-deploy.js ├── matrix-publish.js ├── matrix-sim.js └── matrix-register.js ├── test ├── device.test.js ├── _runner.js ├── admin.test.js ├── app.test.js └── _functions.js ├── .jshintrc ├── validate.sh ├── lib ├── localization.js ├── loader.js └── app.js ├── .travis.yml ├── README.md ├── package.json ├── HISTORY.md └── baseapp.tar /store.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | Brian Sanchez 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | *.zip 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | COPY ./ /admatrix-console 4 | 5 | WORKDIR /admatrix-console 6 | 7 | RUN npm install 8 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: { 3 | clientId: 'AdMobilizeAPIDev', 4 | clientSecret: 'AdMobilizeAPIDevSecret' 5 | } 6 | } -------------------------------------------------------------------------------- /bin/matrix-base.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./matrix-init'); 4 | var program = require('commander'); 5 | 6 | program 7 | .parse(process.argv); 8 | 9 | var pkgs = program.args; 10 | 11 | var app = pkgs[0]; 12 | 13 | if (_.isUndefined(app)) { 14 | return console.error('Must specify command'); 15 | } 16 | -------------------------------------------------------------------------------- /test/device.test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | describe.skip('has device management functions', function(){ 4 | it('`matrix list devices`') 5 | // look for sim- 6 | describe('supports a simulator', function(){ 7 | it('`matrix sim init`') 8 | it('`matrix sim restore`') 9 | it('`matrix sim start`') 10 | it('`matrix sim stop`') 11 | it('`matrix sim save`') 12 | it('`matrix sim clear`') 13 | }) 14 | 15 | it('`matrix use`') 16 | // Matrix.deviceId; 17 | it('`matrix reboot`') 18 | 19 | // it('can list all groups') 20 | }) 21 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "matrix": false, 4 | "_": false 5 | }, 6 | "node": true, 7 | "bitwise": true, 8 | "camelcase": false, 9 | "curly": false, 10 | "eqeqeq": true, 11 | "immed": true, 12 | "indent": 2, 13 | "latedef": true, 14 | "newcap": true, 15 | "maxdepth": 8, 16 | "maxparams": 4, 17 | "asi": true, 18 | "noarg": true, 19 | "quotmark": false, 20 | "regexp": true, 21 | "unused": false, 22 | "strict": false, 23 | "trailing": true, 24 | "smarttabs": true, 25 | "laxcomma": true, 26 | "mocha": true, 27 | "esversion": 6 28 | } 29 | -------------------------------------------------------------------------------- /validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -f /etc/os-release ] 4 | then 5 | if grep -qR "Raspbian" "/etc/os-release" 6 | then 7 | echo "THIS IS A RASPBERRY DON'T INSTALL MATRIX CLI IN THIS MACHINE" 8 | while true; do 9 | echo "Keep in mind that MATRIX CLI is intended to run on your host computer, not directly on the Pi running MOS." 10 | read -p "Do you wish to install this program? (y/N) " answer 11 | case $answer in 12 | [Yy]* ) echo "Resuming installation"; exit 0;; 13 | * ) echo "Aborting installation"; exit 1; 14 | esac 15 | done 16 | else 17 | exit 0 18 | fi 19 | else 20 | exit 0 21 | fi 22 | -------------------------------------------------------------------------------- /lib/localization.js: -------------------------------------------------------------------------------- 1 | var localize = require('node-yaml-localize'); 2 | 3 | var defaultPath = __dirname + '/../config/locales'; 4 | var currentPath, currentLocale; 5 | 6 | function init(path, locale, callback) { 7 | currentPath = path; 8 | currentLocale = locale; 9 | localize.init({ 10 | path: path, 11 | locale: locale 12 | }, callback); 13 | } 14 | 15 | function changeLocale(locale, cb) { 16 | init(currentPath, locale, cb); 17 | } 18 | 19 | function changePath(path, cb) { 20 | init(path, currentLocale, cb); 21 | } 22 | 23 | module.exports = { 24 | init: init, 25 | get: localize.translate, 26 | changeLocale: changeLocale, 27 | changePath: changePath 28 | }; -------------------------------------------------------------------------------- /bin/matrix: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./matrix-init')(function() { 4 | 5 | // load app 6 | require('../lib/app.js'); 7 | 8 | // exit on SIGHUP 9 | process.on('SIGHUP', function() { 10 | process.exit(0); 11 | }); 12 | 13 | // exit on SIGTERM 14 | process.on('SIGTERM', function() { 15 | process.exit(0); 16 | }); 17 | 18 | process.on('uncaughtException', function(e) { 19 | debug('Uncaught error:', e); 20 | Matrix.loader.stop(); 21 | if (e.hasOwnProperty('code') && e.code == 'ENOTFOUND') { 22 | console.log('Service unavailable, please check your connection or try again later'); 23 | } else { 24 | console.log('Matrix CLI Error:'.red, e, e.stack); 25 | } 26 | process.exit(0) 27 | }) 28 | 29 | }); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | - "8" 5 | - "6" 6 | - "6.7" 7 | - "6.5" 8 | - "6.4" 9 | - "5" 10 | - "4" 11 | sudo: required 12 | env: 13 | - CXX=g++-4.8 14 | addons: 15 | apt: 16 | sources: 17 | - ubuntu-toolchain-r-test 18 | packages: 19 | - g++-4.8 20 | - gcc-4.8 21 | - libzmq3-dev 22 | 23 | before_install: 24 | - npm install -g node-gyp 25 | 26 | before_script: 27 | - npm config get prefix 28 | - mkdir ~/.npm-global 29 | - npm config set prefix '~/.npm-global' 30 | - echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.profile 31 | - source ~/.profile 32 | - npm link 33 | - mkdir ../matrix-os 34 | - git clone https://github.com/matrix-io/matrix-os.git ../matrix-os 35 | - cd ../matrix-os/ 36 | - git submodule update --init 37 | - npm install zmq 38 | - npm install 39 | - cd ../matrix-cli 40 | 41 | after_failure: 42 | - cat ~/.matrix/store.json 43 | -------------------------------------------------------------------------------- /config/environments.js: -------------------------------------------------------------------------------- 1 | module.exports = { //export environments 2 | 3 | local: { 4 | api: 'https://dev-api.admobilize.com', 5 | mxss: 'http://localhost:3000' 6 | }, 7 | local2: { 8 | api: 'https://dev-api.admobilize.com', 9 | mxss: 'http://localhost:3001' 10 | }, 11 | dev: { 12 | api: 'https://dev-api.admobilize.com', 13 | mxss: 'https://dev-mxss.admobilize.com', 14 | appsBucket: 'dev-admobilize-matrix-apps' 15 | }, 16 | rc: { 17 | api: 'https://api.admobilize.com', 18 | mxss: 'https://mxss.admobilize.com', 19 | appsBucket: 'admobilize-matrix-apps' 20 | }, 21 | stage: { 22 | api: 'https://stage-api.admobilize.com', 23 | mxss: 'https://stage-mxss.admobilize.com' 24 | }, 25 | production: { 26 | api: 'https://api.admobilize.com', 27 | mxss: 'https://mxss.admobilize.com', 28 | appsBucket: 'admobilize-matrix-apps' 29 | }, 30 | hardcode: { 31 | api: 'https://dev-api.admobilize.com', 32 | mxss: 'https://104.197.139.81' 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > MATRIX OS (MOS) has been deprecated. We are no longer providing support for it and it may cease to function at any time. Please refer to our new library [MATRIX Lite](https://github.com/matrix-io/matrix-lite-js) to easily program your MATRIX Device with just one line of code. 2 | 3 | ## Welcome to your MatrixOS Console Line Interface 4 | 5 | [![Build Status](https://travis-ci.org/matrix-io/matrix-cli.svg?branch=master)](https://travis-ci.org/matrix-io/matrix-cli) 6 | 7 | ###### Bugs 8 | https://github.com/matrix-io/matrix-cli/issues 9 | 10 | ###### Questions 11 | http://community.matrix.one 12 | 13 | ###### Documentation 14 | http://matrix-io.github.io/matrix-documentation 15 | 16 | 17 | # Installation 18 | 19 | ``` 20 | npm install matrix-cli -g 21 | ``` 22 | 23 | ## Setup 24 | 25 | Make sure you're using our `rc` environment. 26 | 27 | ``` 28 | matrix set env rc 29 | ``` 30 | 31 | ## For Matrix-Console Developers 32 | ### Globalize Master Command 33 | Use `sudo npm link` after `git clone` to make `matrix` available globally. 34 | 35 | ### Refreshing Tarfile 36 | ``` 37 | #dont gzip, not supported 38 | tar vcf baseapp.tar baseapp 39 | ``` 40 | 41 | ### Just for you Debug command 42 | See the local configuration easy with `matrix debug` 43 | -------------------------------------------------------------------------------- /test/_runner.js: -------------------------------------------------------------------------------- 1 | var run = require('child_process').execSync; 2 | function readConfig(){ 3 | return JSON.parse( require('fs').readFileSync(require('os').homedir() + '/.matrix/store.json') ); 4 | } 5 | 6 | // process.env.DEBUG='*'; 7 | 8 | // make CLI methods available 9 | require('../bin/matrix-init') 10 | 11 | // save variables here 12 | M = {}; 13 | _ = require('lodash'); 14 | should = require('should'); 15 | 16 | var Mocha = require('mocha'); 17 | var mocha = new Mocha(); 18 | 19 | // reusable test functions 20 | fn = require('./_functions.js'); 21 | 22 | log=console.log; 23 | 24 | // Instantiate a Mocha instance. 25 | 26 | // log('Please ensure MatrixOS and Streaming Server are available.') 27 | try { 28 | run('which matrix') 29 | } catch(e) { 30 | console.log('`matrix` command is not installed. Run `npm link`.') 31 | process.exit(0); 32 | } finally { 33 | console.log('========== Running MatrixOS CLI Tests!') 34 | } 35 | 36 | // 37 | // setTimeout(function(){ 38 | // Matrix.events.on('matrix-ready', function(){ 39 | // var testDir = __dirname; 40 | // 41 | // // Add each .js file to the mocha instance 42 | // fs.readdirSync(testDir).filter(function(file) { 43 | // // Only keep the .js files 44 | // return file.substr(-7) === 'test.js'; 45 | // 46 | // }).forEach(function(file) { 47 | // console.log(file); 48 | // mocha.addFile( 49 | // path.join(testDir, file) 50 | // ); 51 | // }); 52 | // 53 | // // Run the tests. 54 | // mocha.run(function(failures) { 55 | // process.on('exit', function() { 56 | // process.exit(failures); 57 | // }); 58 | // }); 59 | // }) 60 | // }, 500 ) 61 | -------------------------------------------------------------------------------- /lib/loader.js: -------------------------------------------------------------------------------- 1 | var spinner = require('loading-spinner'); 2 | var loaderActive = false; 3 | var matrixText = '⡟⠟⡇ ⣽ ⣗ ⡏ ⢨ ⢈⢎ '.white; 4 | var loaderSpeed = 75; 5 | 6 | function speed(value) { 7 | loaderSpeed = value; 8 | } 9 | 10 | //Changes the sequence of the spinner 11 | function type(type) { 12 | switch (type) { 13 | case 'braille': 14 | brailleLoaderSequence(); 15 | break; 16 | case 'matrix': 17 | matrixLoaderSequence(); 18 | break; 19 | default: 20 | brailleLoaderSequence(); 21 | break; 22 | } 23 | } 24 | 25 | function brailleLoaderSequence() { 26 | spinner.setSequence( 27 | ['⣷ ', '⣯ ', '⣟ ', '⡿ ', '⢿ ', '⣻ ', '⣽ ', '⣾ '] 28 | ); 29 | } 30 | 31 | function matrixLoaderSequence() { 32 | spinner.setSequence( 33 | [ 34 | matrixText + '⣷'.blue + ' ', 35 | matrixText + '⣯'.yellow + ' ', 36 | matrixText + '⣟'.red + ' ', 37 | matrixText + '⡿'.cyan + ' ', 38 | matrixText + '⢿'.white + ' ', 39 | matrixText + '⣻'.magenta + ' ', 40 | matrixText + '⣽'.green + ' ', 41 | matrixText + '⣾'.grey + ' ' 42 | ] 43 | ); 44 | } 45 | 46 | function start() { 47 | if (!loaderActive && process.env['TEST_MODE'] !== true) { 48 | loaderActive = true; 49 | spinner.start(loaderSpeed, { 50 | clearChar: true, 51 | clearLine: true, 52 | doNotBlock: true, 53 | hideCursor: false 54 | }); 55 | } 56 | } 57 | 58 | function stop() { 59 | if (loaderActive && process.env['TEST_MODE'] !== true) { 60 | loaderActive = false; 61 | spinner.stop(); 62 | } 63 | } 64 | 65 | module.exports = { 66 | start: start, 67 | stop: stop, 68 | speed: speed, 69 | type: type 70 | }; -------------------------------------------------------------------------------- /bin/matrix-search.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var debug; 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('search'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | function (next) { 14 | if (!Matrix.pkgs.length || showTheHelp) { 15 | Matrix.loader.stop(); 16 | console.log('\n> matrix search ¬\n'); 17 | console.log('\t matrix search -', t('matrix.search.help').grey) 18 | console.log('\n') 19 | process.exit(1); 20 | } 21 | next(); 22 | }, 23 | Matrix.validate.userAsync, 24 | function(cb) { 25 | Matrix.firebaseInit(cb) 26 | } 27 | ], function(err) { 28 | if (err) { 29 | Matrix.loader.stop(); 30 | console.error(err.message.red); 31 | debug('Error:', err.message); 32 | return process.exit(1); 33 | } 34 | 35 | debug(Matrix.pkgs); 36 | 37 | var needle = Matrix.pkgs[0]; 38 | if (needle.length <= 2) return console.error(t('matrix.search.small_needle') + '.') 39 | 40 | Matrix.helpers.trackEvent('app-search', { aid: needle }); 41 | 42 | Matrix.firebase.app.search(needle, function(data) { 43 | Matrix.loader.stop(); 44 | if (!_.isNull(data) && !_.isUndefined(data)) { 45 | debug(data); 46 | 47 | if (!_.isArray(data)) { 48 | data = [data]; 49 | } 50 | 51 | if (_.isEmpty(data)) { 52 | console.log(t('matrix.search.no_results').green); 53 | } else { 54 | console.log(Matrix.helpers.displaySearch(data, needle)); 55 | } 56 | process.exit(); 57 | } else { 58 | console.log(t('matrix.search.no_results').green); 59 | } 60 | //Get versionId of appId with version X 61 | }); 62 | 63 | }); -------------------------------------------------------------------------------- /bin/matrix-account.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var debug; 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('account'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | Matrix.validate.userAsync, 14 | Matrix.validate.deviceAsync, 15 | // user register does fb init for login, bad if we do that 2x 16 | function(cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function(err) { 20 | if (err) { 21 | Matrix.loader.stop(); 22 | console.error(err.message.red); 23 | debug('Error:', err.message); 24 | return process.exit(1); 25 | } 26 | 27 | var target = Matrix.pkgs[0]; 28 | 29 | if (Matrix.pkgs.length > 1) { //If more than one arg, incorrect format 30 | displayHelp(); 31 | } else { 32 | if (Matrix.pkgs.length === 1 && target === 'profile') { //If asking for profile specifically 33 | async.waterfall([ 34 | Matrix.helpers.profile.get, 35 | function(profile, next) { 36 | Matrix.helpers.profile.show(profile, function(err) { next(err, profile); }); 37 | }, 38 | Matrix.helpers.profile.prompt, 39 | Matrix.helpers.profile.update 40 | ], function(err) { 41 | if (err) console.log(err); 42 | else { 43 | Matrix.loader.stop(); 44 | console.log('Update completed succesfully'); 45 | } 46 | process.exit(); 47 | }); 48 | } else { 49 | async.waterfall([ 50 | Matrix.helpers.profile.get, 51 | Matrix.helpers.profile.show 52 | ], function() { 53 | process.exit(); 54 | }); 55 | } 56 | } 57 | 58 | function displayHelp() { 59 | Matrix.loader.stop(); 60 | console.log('\n> matrix account ¬\n'); 61 | console.log('\t matrix account profile -', t('matrix.help_account_profile').grey) 62 | console.log('\n') 63 | process.exit(1); 64 | } 65 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matrix-cli", 3 | "version": "1.7.2", 4 | "description": "Matrix CLI Enables Communication with MatrixOS devices.", 5 | "preferGlobal": true, 6 | "bin": { 7 | "matrix": "./bin/matrix" 8 | }, 9 | "scripts": { 10 | "test": "mocha test", 11 | "local-setup": "cd node_modules; ln -s ../../matrix-app-config-helper ./matrix-app-config-helper;ln -s ../../matrix-firebase ./matrix-firebase; ln -s ../../matrix-node-sdk ./matrix-node-sdk; cd ..", 12 | "install-clear": "sudo rm -r node_modules/matrix-firebase node_modules/matrix-app-config-helper node_modules/matrix-node-sdk" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/matrix-io/matrix-cli.git" 17 | }, 18 | "author": "AdMobilize ", 19 | "maintainers": [ 20 | "Sean Canton ", 21 | "Diego Ribero " 22 | ], 23 | "license": "ISC", 24 | "homepage": "https://github.com/matrix-io/matrix-cli", 25 | "dependencies": { 26 | "archiver": "^0.15.1", 27 | "async": "^1.5.0", 28 | "cli-table2": "^0.2.0", 29 | "colors": "^1.1.2", 30 | "commander": "^2.12.2", 31 | "deasync": "^0.1.11", 32 | "debug": "^2.6.9", 33 | "engine.io-client": "^1.6.8", 34 | "extend": "^3.0.0", 35 | "fstream": "^1.0.7", 36 | "inquirer": "^1.1.2", 37 | "js-yaml": "^3.10.0", 38 | "jshint": "^2.8.0", 39 | "jsonwebtoken": "^7.1.9", 40 | "loading-spinner": "^1.1.4", 41 | "lodash": "^3.10.0", 42 | "matrix-app-config-helper": "git+https://github.com/matrix-io/matrix-app-config-helper.git", 43 | "matrix-firebase": "https://github.com/matrix-io/matrix-firebase/tarball/master", 44 | "matrix-node-sdk": "https://github.com/matrix-io/matrix-node-sdk/tarball/master", 45 | "mocha": "^2.5.3", 46 | "moment": "^2.19.4", 47 | "ms": "^0.7.1", 48 | "node-yaml-localize": "https://github.com/matrix-io/node-yaml-localize/tarball/bt/localization", 49 | "package-json": "^2.4.0", 50 | "prompt": "^0.2.14", 51 | "q": "^1.5.1", 52 | "request": "^2.83.0", 53 | "rx": "^4.1.0", 54 | "should": "^9.0.2", 55 | "sinon": "^1.17.5", 56 | "socket.io-client": "^1.4.5", 57 | "tar": "^2.2.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bin/matrix-update.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var debug; 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('update'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | Matrix.validate.userAsync, 14 | Matrix.validate.deviceAsync, 15 | Matrix.firebaseInit 16 | ], function (err) { 17 | Matrix.loader.stop(); 18 | if (err) { 19 | console.error(err.message.red); 20 | debug('Error:', err.message); 21 | return process.exit(1); 22 | } 23 | 24 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 25 | 26 | var appName = Matrix.pkgs[0]; 27 | console.log('____ | ' + t('matrix.update.upgrading_to') + ':' + t('matrix.update.latest_version') + ' ', appName, ' ==> '.yellow, Matrix.config.device.identifier); 28 | 29 | Matrix.loader.start(); 30 | 31 | Matrix.firebase.app.search(appName, function(result) { 32 | 33 | debug(result) 34 | 35 | if (_.isUndefined(result)) { 36 | Matrix.loader.stop(); 37 | console.log(t('matrix.update.app_undefined', { app: appName.yellow })); 38 | return process.exit(); 39 | } 40 | 41 | var versionId = result.meta.currentVersion; 42 | 43 | debug('VERSION: '.blue, versionId, 'APP: '.blue, appId); 44 | 45 | var options = { 46 | policy: result.versions[versionId].policy, 47 | name: appName, 48 | id: appId, 49 | versionId: versionId 50 | } 51 | 52 | Matrix.helpers.installApp(options, function(err) { 53 | Matrix.loader.stop(); 54 | if (err) { 55 | console.log(err); 56 | process.exit(1); 57 | } 58 | 59 | console.log(t('matrix.update.app_update_successfully').green); 60 | process.exit(0); 61 | }); 62 | 63 | }); 64 | 65 | 66 | function displayHelp() { 67 | console.log('\n> matrix update ¬ \n'); 68 | console.log('\t matrix update -', t('matrix.update.help_update').grey) 69 | console.log('\t matrix update -', t('matrix.update.help_update_app').grey) 70 | console.log('\t matrix update -', t('matrix.update.help_update_app_version').grey) 71 | console.log('\n') 72 | process.exit(); 73 | } 74 | }); -------------------------------------------------------------------------------- /bin/matrix-set.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./matrix-init'); 4 | var debug = debugLog('set'); 5 | 6 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, function() { 7 | 8 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 9 | 10 | var locales = { 11 | 'en': { name: 'English' }, 12 | 'es': { name: 'Spanish' } 13 | }; 14 | 15 | var environments = require('../config/environments.js'); 16 | 17 | if (Matrix.pkgs.indexOf('env') === 0) { 18 | 19 | var value = Matrix.pkgs[1]; 20 | if (!_.isUndefined(value) && _.keys(environments).indexOf(value) !== -1) { 21 | 22 | Matrix.config.environment = _.assign(environments[value], { name: value }); 23 | var keysToRemove = ['client', 'user', 'device', 'deviceMap']; 24 | Matrix.config = _.omit(Matrix.config, keysToRemove); 25 | 26 | Matrix.helpers.saveConfig(function() { 27 | console.log(t('matrix.set.env.env').grey + ':'.grey, Matrix.config.environment.name.green); 28 | // TODO: set-env [value] sets a environment on the Matrix 29 | process.exit(); 30 | }); 31 | } else { 32 | console.error(t('matrix.set.env.valid_environments') + ' = [ dev, rc, production ]'.yellow) 33 | } 34 | 35 | } else if (Matrix.pkgs.indexOf('locale') === 0) { 36 | 37 | var locale = Matrix.pkgs[1]; 38 | if (_.isUndefined(locale)) { 39 | console.warn(t('matrix.set.locale.locale_required') + ': `matrix set locale ` ') 40 | process.exit(0); 41 | } else { 42 | var localesRegExp = new RegExp(Object.keys(locales).join('|')); 43 | 44 | if (locale && locale.match(localesRegExp)) { 45 | Matrix.config.locale = _.assign(locale, { "name": locale }); 46 | Matrix.helpers.saveConfig(function() { 47 | console.log(t('matrix.set.locale.locale').grey + ':'.grey, Matrix.config.locale.green); 48 | process.exit(0); 49 | }); 50 | } else { 51 | var validLocales = Object.keys(locales).join(', '); 52 | console.error(t('matrix.set.locale.valid_locales') + ' = [ ' + validLocales + ' ]'); 53 | process.exit(0); 54 | } 55 | } 56 | 57 | } else { 58 | displayHelp(); 59 | } 60 | 61 | function displayHelp() { 62 | 63 | console.log('\n> matrix set ¬\n'); 64 | console.log('\t matrix set env (production|rc|dev) -', t('matrix.set.help_device').grey) 65 | console.log('\t matrix set locale (es|en) -', t('matrix.set.help_locale').grey) 66 | console.log('\n') 67 | process.exit(1); 68 | } 69 | 70 | }); -------------------------------------------------------------------------------- /bin/matrix-uninstall.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var debug; 4 | 5 | var async = require('async'); 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('uninstall'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | Matrix.validate.userAsync, 15 | Matrix.validate.deviceAsync, 16 | function(cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function (err) { 20 | Matrix.loader.stop(); 21 | if (err) { 22 | console.error(err.message.red); 23 | debug('Error:', err.message); 24 | return process.exit(1); 25 | } 26 | 27 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 28 | var target = Matrix.pkgs[0]; 29 | 30 | console.log('____ | ' + t('matrix.uninstall.uninstalling') + ' ', target.green, ' ==> '.yellow, Matrix.config.device.identifier); 31 | Matrix.loader.start(); 32 | 33 | //If the device has the app 34 | Matrix.helpers.lookupAppId(target, function(err, appId) { 35 | Matrix.loader.stop(); 36 | if (err) { 37 | console.log('Error: '.red, err.message); 38 | process.exit(); 39 | } 40 | if (!appId) { 41 | console.log("\nApp not installed in device... ") 42 | process.exit(1); 43 | } else { 44 | 45 | console.log("\nApp found in device... ") 46 | var progress; 47 | Matrix.loader.start(); 48 | 49 | Matrix.helpers.trackEvent('app-uninstall', { aid: target, did: Matrix.config.device.identifier }); 50 | 51 | Matrix.firebase.app.uninstall(Matrix.config.user.token, Matrix.config.device.identifier, appId, { 52 | error: function(err) { 53 | Matrix.loader.stop(); 54 | if (err && err.hasOwnProperty('details') && err.details.hasOwnProperty('error')) { 55 | console.error('\nUninstall Error'.red, err.details.error); 56 | } else { 57 | console.error('\nUninstall Error'.red, err); 58 | } 59 | process.exit(1); 60 | }, 61 | finished: function() { 62 | Matrix.loader.stop(); 63 | console.log('Uninstall sent to device...'.green); 64 | process.exit(0); 65 | }, 66 | start: _.once(function() { 67 | Matrix.loader.stop(); 68 | console.log('Uninstall request created'); 69 | Matrix.loader.start(); 70 | }), 71 | progress: function(msg) { 72 | if (_.isUndefined(msg)) msg = ''; 73 | else msg = ' ' + msg + ' '; 74 | if (!progress) { 75 | progress = true; 76 | Matrix.loader.stop(); 77 | process.stdout.write('Uninstall Progress:' + msg) 78 | } else { 79 | process.stdout.write('.' + msg); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | 86 | }); 87 | 88 | function displayHelp() { 89 | console.log('\n> matrix uninstall ¬\n'); 90 | console.log('\t matrix uninstall -', t('matrix.uninstall.help_app', { app: '' }).grey) 91 | console.log('\n') 92 | process.exit(1); 93 | } 94 | }); -------------------------------------------------------------------------------- /bin/matrix-install.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async') 4 | var debug; 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('install'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | Matrix.validate.userAsync, 14 | Matrix.validate.deviceAsync, 15 | // user register does fb init for login, bad if we do that 2x 16 | function (cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function (err) { 20 | Matrix.loader.stop(); 21 | if (err) { 22 | console.error(err.message.red); 23 | debug('Error:', err.message); 24 | return process.exit(1); 25 | } 26 | 27 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 28 | 29 | var appName = Matrix.pkgs[0]; 30 | var version = Matrix.pkgs[1]; 31 | 32 | // TODO lookup policy from config file, pass to function 33 | console.log('____ | ' + t('matrix.install.installing') + ' ', appName, ' ==> '.yellow, Matrix.config.device.identifier) 34 | 35 | Matrix.loader.start(); 36 | //Search the app to install 37 | Matrix.firebase.app.search(appName, function(result) { 38 | //Validate if found the app 39 | if (_.isUndefined(result)) { 40 | debug(result)  41 | Matrix.loader.stop(); 42 | console.log(t('matrix.install.app_x_not_found', { app: appName.yellow })); 43 | return process.exit(); 44 | } 45 | 46 | var versionId; 47 | //Validate if the version exist and get the version Id 48 | if (version) { 49 | versionId = _.findKey(result.versions, function(appVersion, versionId) { 50 | if (appVersion.version === version) return true; 51 | }); 52 | //If the version doesn't exist show the error and end the process 53 | if (_.isUndefined(versionId)) { 54 | Matrix.loader.stop(); 55 | console.log(t('matrix.install.app_version_x_not_found', { version: version })); 56 | return process.exit(); 57 | } 58 | } else { 59 | //If in the command doesn't set the version use a current version of app 60 | versionId = result.meta.currentVersion; 61 | } 62 | 63 | debug('VERSION: '.blue, versionId, 'APP: '.blue, appName); 64 | 65 | var options = { 66 | policy: result.versions[versionId].policy, 67 | name: appName, 68 | id: result.id, 69 | versionId: versionId 70 | } 71 | 72 | Matrix.helpers.trackEvent('app-install', { aid: appName, did: Matrix.config.device.identifier }); 73 | 74 | Matrix.helpers.installApp(options, function(err) { 75 | Matrix.loader.stop(); 76 | if (err) { 77 | console.log(err); 78 | process.exit(1); 79 | } 80 | 81 | console.log(t('matrix.install.app_install_success').green); 82 | process.exit(0); 83 | }); 84 | 85 | }); 86 | 87 | 88 | function displayHelp() { 89 | console.log('\n> matrix install ¬\n'); 90 | console.log('\t matrix install app -', t('matrix.install.help_app', { app: '' }).grey) 91 | console.log('\t matrix install sensor -', t('matrix.install.help_sensor', { sensor: '' }).grey) 92 | console.log('\n') 93 | process.exit(1); 94 | } 95 | }); -------------------------------------------------------------------------------- /bin/matrix-unpublish.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var unpublicationFinished = false; 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function(cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('unpublish'); 12 | fileUrl = 'https://storage.googlapis.com/' + Matrix.config.environment.appsBucket + '/apps'; 13 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 14 | }, 15 | Matrix.validate.userAsync, 16 | function(cb) { 17 | Matrix.firebaseInit(cb); 18 | } 19 | ], function(err) { 20 | if (err) { 21 | Matrix.loader.stop(); 22 | console.error(err.message.red); 23 | debug('Error: ', err.message); 24 | return process.exit(1); 25 | } 26 | 27 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 28 | var target = Matrix.pkgs[0]; 29 | 30 | console.log('____ | ' + 'Searching for app'); 31 | Matrix.loader.start(); 32 | 33 | //get app id 34 | Matrix.firebase.app.search(target, function(data) { 35 | 36 | Matrix.loader.stop(); 37 | if (_.isEmpty(data)) { 38 | console.log('\n App not found!'); 39 | process.exit(1); 40 | } else { 41 | console.log(data); 42 | if (data.acl.ownerId !== Matrix.config.user.id) { 43 | console.log('\n This app does not belongs to this user!'.red); 44 | process.exit(1); 45 | } else { 46 | console.log('\n App found... '); 47 | 48 | var progress; 49 | Matrix.loader.start(); 50 | 51 | Matrix.helpers.trackEvent('app-unpublish', { aid: target, did: Matrix.config.device.identifier }); 52 | 53 | console.log('____ | ' + t('matrix.unpublish.unpublishing')); 54 | Matrix.firebase.app.unpublish(Matrix.config.user.token, data.id, { 55 | error: function(err) { 56 | Matrix.loader.stop(); 57 | if (err && err.hasOwnProperty('details') && err.details.hasOwnProperty('error')) { 58 | console.error('\n Unpublish Error'.red, err.details.error); 59 | } else { 60 | console.error('\n Unpublish Error'.red, err); 61 | } 62 | process.exit(1); 63 | }, 64 | finished: function() { 65 | Matrix.loader.stop(); 66 | console.log('Unpublishing app...'.green); 67 | process.exit(0); 68 | }, 69 | start: _.once(function() { 70 | Matrix.loader.stop(); 71 | console.log('Unpublish request created'); 72 | Matrix.loader.start(); 73 | }), 74 | progress: function(msg) { 75 | if (_.isUndefined(msg)) msg = ''; 76 | else msg = ' ' + msg + ' '; 77 | if (!progress) { 78 | progress = true; 79 | Matrix.loader.stop(); 80 | process.stdout.write('Unpublish progress:' + msg); 81 | } else { 82 | process.stdout.write('.' + msg); 83 | } 84 | } 85 | }); 86 | } 87 | } 88 | }); 89 | 90 | function displayHelp() { 91 | console.log('\n> matrix unpublish ¬\n'); 92 | console.log('\t matrix unpublish -', t('matrix.unpublish.help', { app: '' }).grey); 93 | console.log('\n'); 94 | process.exit(1); 95 | } 96 | }); 97 | -------------------------------------------------------------------------------- /test/admin.test.js: -------------------------------------------------------------------------------- 1 | describe('has admin functions', function() { 2 | this.timeout(15000) 3 | 4 | describe('can switch environments', function() { 5 | 6 | it('`matrix set env local`', function(done) { 7 | fn.run('matrix set env local', { 8 | checks: ['local'], 9 | postCheck: function(done) { 10 | var env = fn.readConfig().environment.name; 11 | if (env !== 'local') { 12 | done('Invalid environment:' + env) 13 | } else { 14 | done(); 15 | } 16 | } 17 | }, done); 18 | }) 19 | 20 | it('`matrix set env production`', function(done) { 21 | fn.run('matrix set env production', { 22 | checks: ['production'], 23 | postCheck: function(done) { 24 | var env = fn.readConfig().environment.name; 25 | if (env !== 'production') { 26 | done('Invalid environment') 27 | } else { 28 | done(); 29 | } 30 | } 31 | }, done) 32 | }) 33 | 34 | it('`matrix set env dev`', function(done) { 35 | fn.run('matrix set env dev', { 36 | checks: ['dev'], 37 | postCheck: function(done) { 38 | var env = fn.readConfig().environment.name; 39 | if (env !== 'dev') { 40 | done('Invalid environment') 41 | } else { 42 | done(); 43 | } 44 | } 45 | }, done) 46 | }) 47 | }) 48 | 49 | describe('can login', function() { 50 | it('`matrix login`', fn.login); 51 | }); 52 | 53 | describe('can refresh a token', function () { 54 | //before('`matrix login`', fn.login); 55 | //it('`matrix logs in`', fn.login); 56 | it('`Refreshes an invalid token`', function (done) { 57 | var config = fn.readConfig(); 58 | if (config.hasOwnProperty('user')) { 59 | var userToken = config.user.token; 60 | userToken = userToken.substring(1, userToken.length); 61 | fn.updateConfig({ 'user': { token: userToken } }); 62 | config = fn.readConfig(); 63 | userToken = config.user.token; 64 | Matrix.config.user = config.user; //May help? 65 | Matrix.validate.userAsync(function (err) { 66 | done(err); 67 | //done('Failed to refresh the user token'); 68 | }); 69 | } else { 70 | var err = new Error('No logged in user!'); 71 | done(err); 72 | } 73 | }); 74 | }); 75 | 76 | describe('can make, list and delete devices', function() { 77 | 78 | // NOTE: This device is global for the test suite. It will be destroyed in terminal.test.app 79 | before('`matrix register device`', fn.registerDevice); 80 | 81 | it('`matrix list devices`', function(done) { 82 | fn.run( 83 | 'list devices', { 84 | checks: [ 85 | 'test-device' 86 | ] 87 | }, done); 88 | }) 89 | 90 | // doesnt work right now 91 | it.skip('`matrix remove`', function(done) { 92 | fn.run('remove test-device', { 93 | responses: [ 94 | ['test-device', 'y\n'] 95 | ], 96 | checks: [ 97 | 'Device successfully removed' 98 | ] 99 | }, done) 100 | }) 101 | 102 | 103 | }); 104 | 105 | describe('can logout', function() { 106 | it('`matrix logout`', fn.logout); 107 | }) 108 | }); -------------------------------------------------------------------------------- /bin/matrix-login.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var prompt = require('prompt'); 4 | var async = require('async'); 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | debug = debugLog('login'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | ], function(err) { 14 | if (err) { 15 | Matrix.loader.stop(); 16 | console.error(err.message.red); 17 | debug('Error:', err.message); 18 | return process.exit(1); 19 | } 20 | 21 | var schema = { 22 | properties: { 23 | username: { 24 | required: true, 25 | pattern: /\S+@\S+.\S+/ 26 | }, 27 | password: { 28 | hidden: true 29 | } 30 | } 31 | }; 32 | 33 | var user = {}; 34 | 35 | if (!_.isEmpty(Matrix.config.user)) { 36 | Matrix.loader.stop(); 37 | if (Matrix.validate.token() === false) { 38 | console.log('The token has expired. Last session started:'.yellow, Matrix.config.user.username); 39 | } else { 40 | console.log(t('matrix.already_login').yellow + ':', Matrix.config.user.username); 41 | } 42 | } 43 | 44 | prompt.delimiter = ''; 45 | prompt.message = 'Login -- '; 46 | 47 | async.waterfall([ 48 | function(callback){ 49 | prompt.start(); 50 | prompt.get(schema, function (err, result) { 51 | user.username = result.username; 52 | user.password = result.password; 53 | 54 | if (err) { 55 | Matrix.loader.stop(); 56 | if (err.toString().indexOf('canceled') > 0) callback('') 57 | else callback('Error: ', err); 58 | } 59 | callback(null, user); 60 | }); 61 | }, function(user, callback) { 62 | var oldTrack; 63 | var schemaTrack = { properties: { } }; 64 | //if user has not answered tracking question before 65 | if (!_.has(Matrix.config, 'trackOk')) { 66 | schemaTrack.properties.trackOk = { 67 | description: "Share usage information? (Y/n)", 68 | default: 'y', 69 | pattern: /y|n|yes|no|Y|N/, 70 | message: "Please answer y or n." 71 | }; 72 | } else if(_.isUndefined(Matrix.config.trackOk[user.username])) { 73 | schemaTrack.properties.trackOk = { 74 | description: "Share usage information? (Y/n)", 75 | default: 'y', 76 | pattern: /y|n|yes|no|Y|N/, 77 | message: "Please answer y or n." 78 | }; 79 | } else { 80 | oldTrack = Matrix.config.trackOk[user.username]; 81 | } 82 | callback(null, user, schemaTrack, oldTrack); 83 | }, function(user, schemaTrack, oldTrack, callback) { 84 | prompt.start(); 85 | prompt.get(schemaTrack, function (err, result) { 86 | Matrix.loader.start(); 87 | if (err) { 88 | Matrix.loader.stop(); 89 | if (err.toString().indexOf('canceled') > 0) callback('') 90 | else callback('Error: ', err); 91 | } 92 | user.trackOk = _.has(result, 'trackOk') ? result.trackOk : oldTrack; 93 | Matrix.helpers.login(user, function(err) { 94 | if (err) callback(err); 95 | callback(null); 96 | }); 97 | }); 98 | } 99 | ], function(err){ 100 | if (err) console.log(err); 101 | process.exit(); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /bin/matrix-use.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var debug; 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('use'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | Matrix.validate.userAsync 14 | ], function (err) { 15 | Matrix.loader.stop(); 16 | if (err) { 17 | console.error(err.message.red); 18 | debug('Error:', err.message); 19 | return process.exit(1); 20 | } 21 | 22 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 23 | 24 | var target = Matrix.pkgs.join(' '); 25 | var targetDeviceId = _.findKey(Matrix.config.deviceMap, { name: target }); 26 | var nameProvided = true; 27 | 28 | if (_.isEmpty(targetDeviceId)) { 29 | // not a name, must be a key? 30 | if (_.has(Matrix.config.deviceMap, target)) { 31 | targetDeviceId = target; 32 | nameProvided = false; 33 | } else { 34 | console.log(target.red, t('matrix.use.invalid_nameid')) 35 | process.exit(1); 36 | } 37 | } 38 | 39 | Matrix.loader.start(); 40 | // still API dependent, TODO: depreciate to firebase 41 | Matrix.api.device.register(targetDeviceId, function(err, state) { 42 | 43 | if (err) { 44 | Matrix.loader.stop(); 45 | console.error(err.message); 46 | debug('Error:', err); 47 | return process.exit(1); 48 | } 49 | if (state.status === 'OK') { 50 | if (!nameProvided) target = Matrix.helpers.lookupDeviceName(target); 51 | 52 | // Save the device token 53 | Matrix.config.device = {} 54 | Matrix.config.device.identifier = targetDeviceId; 55 | Matrix.config.device.token = state.results.device_token; 56 | 57 | async.parallel([ 58 | function track(cb) { 59 | // track 60 | Matrix.helpers.trackEvent('device-use', { did: targetDeviceId }, cb); 61 | }, 62 | function write(cb) { 63 | Matrix.helpers.saveConfig(cb); 64 | } 65 | ], function () { 66 | Matrix.loader.stop(); 67 | console.log('Now using device:'.grey, target, 'ID:'.grey, targetDeviceId); 68 | process.exit() 69 | }) 70 | 71 | 72 | //Create the object for keep device after session expired 73 | if (!Matrix.config.keepDevice) Matrix.config.keepDevice = {}; 74 | 75 | //Create key for the current user into the object for keep device after session expired 76 | if (!_.has(Matrix.config.keepDevice, Matrix.config.user.id)) { 77 | Matrix.config.keepDevice[Matrix.config.user.id] = {}; 78 | } 79 | //Put the data into the object for keep device after session expired 80 | Matrix.config.keepDevice[Matrix.config.user.id].identifier = Matrix.config.device.identifier; 81 | Matrix.config.keepDevice[Matrix.config.user.id].name = target; 82 | //Save config 83 | 84 | 85 | } else { 86 | debug('Matrix Use Error Object:', state); 87 | if (state.error === 'access_token not valid.') { 88 | console.log(t('matrix.use.not_authorized').red, '\n', t('matrix.use.invalid_token'), '. ', t('matrix.use.try').grey, 'matrix login') 89 | } else { 90 | console.error('Error', state.status_code.red, state.error); 91 | } 92 | } 93 | 94 | }); 95 | }); 96 | 97 | function displayHelp() { 98 | console.log('\n> matrix use ¬ \n'); 99 | console.log('\t matrix use -', t('matrix.use.command_help').grey) 100 | console.log('\n') 101 | } -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | ##[1.7.2] 7 | ### Fixed 8 | - Matrix create exits cleanly 9 | ## [1.7.1] 10 | ### Changed 11 | - New installs start on produciton environment 12 | - Updated urls to point to current servers 13 | 14 | ## [1.7] 15 | ### Fixed 16 | - Deploying app while running no longer leaves pending 17 | - List apps now displays online status correctly 18 | - Can now deploy apps in appstore 19 | - `matrix restart` now restarts apps 20 | - Registering a new user clears old device 21 | 22 | ## [1.6.1] 23 | ### Added 24 | - Unpublish application 25 | 26 | ## [1.6] 27 | ### Fixed 28 | - Device deprovisioning is handled on remove 29 | - New devices make shortName (cli-safe) 30 | 31 | ### Added 32 | - Refresh token if expired. Thanks @diegoribero 33 | 34 | ## [1.5] 35 | ### Changed 36 | - Prevented duplicate device names 37 | 38 | ### Added 39 | - `matrix upgrade` command to show how to upgrade cli 40 | 41 | ### Fixed 42 | - Make sure to update policy on deploy 43 | 44 | ## [1.4.0] 45 | ### Fixed 46 | - Remove devices when test is done 47 | ### Changes 48 | - Refactored start, stop, restart to independent services 49 | 50 | ## [1.3.9] 51 | ### Added 52 | - New application writes app name to package json 53 | 54 | ### Fixed 55 | - Missing error messages, removed stack traces. 56 | - Validate device is selected before issuing app control msg 57 | 58 | ### Changed 59 | - New API addresses 60 | - More strict docopt help text 61 | 62 | ## [1.3.8] 63 | ### Fixed 64 | - Error message from firebase init 65 | - Images deploy properly on publish 66 | - Improvements to config read for deploy/publish 67 | 68 | ### Added 69 | - Error resolution text 70 | 71 | ## [1.3.7] 72 | ### Added 73 | - ES6 passes deploy/publish linter 74 | ## [1.3.4] 75 | ### Changed 76 | Properly handles bad searches 77 | 78 | ## [1.3.2] 79 | 80 | ### Fixed 81 | No categories defaults to 'Development' on publish 82 | 83 | ## [0.2.0] 84 | 85 | - Verify Config before deploy and publish 86 | 87 | ### Fixed 88 | - Can set configuration to emails now. 89 | 90 | ### Removed 91 | - Publish no longer requires a selected device 92 | - Closed off matrix set config path 93 | 94 | ## [1.2.7] 95 | ### Added 96 | - Refactored test suite. See `test/_functions#run()` 97 | 98 | ### Test results 99 | 33 passing (2m) 100 | 14 pending 101 | 102 | ### Added 103 | - Keeping device on login 104 | - `matrix validate` command for testing config.yaml 105 | 106 | ### Changed 107 | - Fixed Login Not Exiting Bug 108 | 109 | ### Removed 110 | - Removed regex for installing sensors / apps 111 | 112 | ### Added 113 | - Timeouts for start, stop, restart in case device is off 114 | - matrix install supports versions 115 | - matrix update installs latest version 116 | - matrix remove to remove device records and data 117 | 118 | ### Changed 119 | - Acknowledgements from device for start, stop restart 120 | - Check for non-expired token before attempting to use 121 | - stop apps before installing, uninstalling or deploying 122 | - clean cancel of matrix create 123 | 124 | ## [1.2.5] 125 | ### Added 126 | - **matrix remove** command to remove devices 127 | - Check to not overwrite folders on create 128 | - Start History file 129 | - Check for token expiration 130 | - Start, stop, restart acknowledgements from firebase 131 | 132 | ### Changed 133 | - Apps cannot have only numbers as names 134 | -------------------------------------------------------------------------------- /bin/matrix-stop.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const commandTimeoutSeconds = 30; 4 | var async = require('async'); 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('stop'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | Matrix.validate.userAsync, 15 | Matrix.validate.deviceAsync, 16 | function (cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function (err) { 20 | 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | var app = Matrix.pkgs[0]; 29 | if (_.isUndefined(app) || !_.isString(app)) { 30 | Matrix.loader.stop(); 31 | console.log('\n> matrix stop - ' + t('matrix.help_stop').grey + '\n'); 32 | process.exit(1); 33 | } 34 | 35 | Matrix.helpers.trackEvent('app-stop', { aid: app, did: Matrix.config.device.identifier }); 36 | 37 | Matrix.api.device.setId(Matrix.config.device.identifier); 38 | Matrix.loader.stop(); 39 | console.log(t('matrix.stop.stopping_app') + ': ', app, Matrix.config.device.identifier); 40 | Matrix.loader.start(); 41 | 42 | //Get the app id for name 43 | Matrix.firebase.app.getIDForName(app, function (err, appId) { 44 | if (err) { 45 | Matrix.loader.stop(); 46 | console.log(t('matrix.stop.app_undefined').red); 47 | return Matrix.endIt(1, 0); 48 | } 49 | debug('appId>', appId); 50 | //Get the current status of app 51 | Matrix.firebase.app.getStatus(appId, function (status) { 52 | debug('Get current status: ' + Matrix.config.user.id + '>' + Matrix.config.device.identifier + '>' + appId + '>' + status); 53 | Matrix.loader.stop(); 54 | 55 | if (_.isUndefined(app)) { 56 | console.log('\n> matrix stop ¬\n'); 57 | console.log('\t matrix stop -', t('matrix.stop.help', { app: '' }).grey) 58 | Matrix.endIt(); 59 | 60 | //If the status of the app is different of active doesn't execute de stop command 61 | } else if (status !== 'active') { 62 | console.log(t('matrix.stop.stop_app_status_error') + ':', app); 63 | Matrix.endIt(); 64 | } else { 65 | console.log(t('matrix.stop.stopping_app') + ': ', app); 66 | var commandTimeout; 67 | 68 | Matrix.loader.start(); 69 | 70 | //Watch the app status and verify if the behavior it's right 71 | Matrix.firebase.app.watchStatus(appId, function (status) { 72 | //stop command status behavior(active -> inactive) 73 | if (status === 'inactive') { 74 | Matrix.loader.stop(); 75 | clearTimeout(commandTimeout); 76 | console.log(t('matrix.stop.stop_app_successfully') + ':', app); 77 | Matrix.endIt(); 78 | } 79 | }); 80 | 81 | //Send the stop command 82 | Matrix.api.app.stop(app, Matrix.config.device.identifier, function (err) { 83 | if (err) { 84 | Matrix.loader.stop(); 85 | console.log(t('matrix.stop.stop_app_error') + ':', app, ' (' + err.message.red + ')'); 86 | Matrix.endIt(); 87 | } 88 | 89 | //add timeout to start command 90 | commandTimeout = setTimeout(function () { 91 | Matrix.loader.stop(); 92 | console.log(t('matrix.stop.stop_timeout')); 93 | Matrix.endIt(); 94 | }, commandTimeoutSeconds * 1000); 95 | 96 | }); 97 | } 98 | }); 99 | }); 100 | 101 | }); 102 | -------------------------------------------------------------------------------- /bin/matrix-restart.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var commandTimeoutSeconds = 30; 4 | var async = require('async'); 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function(cb) { 9 | Matrix.loader.start(); 10 | 11 | debug = debugLog('restart'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | Matrix.validate.userAsync, 15 | Matrix.validate.deviceAsync, 16 | function(cb){ 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function cb(err) { 20 | 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | var app = Matrix.pkgs[0]; 29 | if (_.isUndefined(app) || !_.isString(app)) { 30 | console.log('\n> matrix restart - ' + t('matrix.help_restart').grey + '\n'); 31 | process.exit(1); 32 | } 33 | 34 | //Make sure the user has logged in 35 | Matrix.helpers.trackEvent('app-restart', { aid: app, did: Matrix.config.device.identifier }); 36 | 37 | Matrix.api.device.setId(Matrix.config.device.identifier); 38 | 39 | 40 | Matrix.firebase.app.getIDForName(app, function(err, appId) { 41 | if (err) { 42 | Matrix.loader.stop(); 43 | console.error(err.message); 44 | return Matrix.endIt(1, 0); 45 | } 46 | 47 | debug('appId>', appId); 48 | // Get the current status of app 49 | Matrix.firebase.app.getStatus(appId, function(status) { 50 | debug("Get current status: " + Matrix.config.user.id + '>' + Matrix.config.device.identifier + '>' + appId + '>' + status); 51 | 52 | if (_.isUndefined(app)) { 53 | console.log('\n> matrix restart ¬\n'); 54 | console.log('\t matrix restart -', t('matrix.restart.help', { app: '' }).grey) 55 | Matrix.endIt(); 56 | //If the status of the app is different of active doesn't execute de restart command 57 | } else if (status !== 'active') { 58 | 59 | console.log(t('matrix.restart.restart_app_status_error') + ':', app); 60 | Matrix.endIt(); 61 | 62 | } else { 63 | 64 | var commandTimeout; 65 | 66 | Matrix.loader.stop(); 67 | console.log(t('matrix.restart.restarting_app') + ': ', app, Matrix.config.device.identifier); 68 | Matrix.loader.start(); 69 | // Keep track of previous app status 70 | var wasInactive; 71 | 72 | // Watch the app status and verify if the behavior it's right 73 | Matrix.firebase.app.watchStatus(appId, function(status) { 74 | 75 | // Restart command status behavior(active -> inactive -> active) 76 | if (status === 'active' && wasInactive == true) { 77 | 78 | clearTimeout(commandTimeout); 79 | Matrix.loader.stop(); 80 | console.log(t('matrix.restart.restart_app_successfully') + ':', app); 81 | wasInactive = false; 82 | Matrix.endIt(); 83 | 84 | } else if (status === 'inactive') { 85 | 86 | Matrix.loader.stop(); 87 | console.log(t('matrix.stop.stop_app_successfully') + ':', app); 88 | wasInactive = true; 89 | Matrix.loader.start(); 90 | } 91 | 92 | }); 93 | 94 | //Send the restart command 95 | Matrix.api.app.restart(app, function(err, res) { 96 | 97 | if (err) { 98 | Matrix.loader.stop(); 99 | console.log(t('matrix.restart.restart_app_error') + ':', app, ' (' + err.message.red + ')'); 100 | Matrix.endIt(); 101 | } 102 | 103 | //add timeout to restart command 104 | commandTimeout = setTimeout(function() { 105 | console.log(t('matrix.restart.restart_timeout')); 106 | Matrix.endIt(); 107 | }, commandTimeoutSeconds * 1000); 108 | 109 | }); 110 | } 111 | }); 112 | }); 113 | }); 114 | 115 | -------------------------------------------------------------------------------- /bin/matrix-start.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const commandTimeoutSeconds = 30; 4 | var async = require('async'); 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('start'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | Matrix.validate.userAsync, 15 | Matrix.validate.deviceAsync, 16 | function (cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function (err) { 20 | 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | var app = Matrix.pkgs[0]; 29 | if (_.isUndefined(app) || !_.isString(app)) { 30 | Matrix.loader.stop(); 31 | console.log('\n> matrix start - ' + t('matrix.help_start').grey + '\n'); 32 | process.exit(1); 33 | } 34 | 35 | Matrix.helpers.trackEvent('app-start', { aid: app, did: Matrix.config.device.identifier }); 36 | 37 | Matrix.api.device.setId(Matrix.config.device.identifier); 38 | Matrix.loader.stop(); 39 | console.log(t('matrix.start.starting_app') + ': ', app, Matrix.config.device.identifier); 40 | Matrix.loader.start(); 41 | 42 | //Get the app id for name 43 | Matrix.firebase.app.getIDForName(app, function(err, appId) { 44 | if (err) { 45 | Matrix.loader.stop(); 46 | console.error(t('matrix.start.app_undefined').red); 47 | return Matrix.endIt(1, 0); 48 | } 49 | debug('appId>', appId); 50 | //Get the current status of app 51 | Matrix.firebase.app.getStatus(appId, function(status) { 52 | debug('Get current status: ' + Matrix.config.user.id + '>' + Matrix.config.device.identifier + '>' + appId + '>' + status); 53 | Matrix.loader.stop(); 54 | 55 | if (_.isUndefined(app)) { 56 | console.log('\n> matrix start ¬\n'); 57 | console.log('\t matrix start -', t('matrix.start.help', { app: '' }).grey) 58 | Matrix.endIt(); 59 | 60 | //If the status of the app is different of active or error doesn't execute de start command 61 | } else if (status === 'active' || status === 'pending') { 62 | console.log(t('matrix.start.start_app_status_error') + ':', app, status.green); 63 | Matrix.endIt(); 64 | } else { 65 | console.log(t('matrix.start.starting_app') + ': ', app); 66 | var commandTimeout; 67 | 68 | Matrix.loader.start(); 69 | 70 | //Watch the app status and verify if the behavior it's right 71 | Matrix.firebase.app.watchStatus(appId, function(status) { 72 | //stop command status behavior(inactive or error -> active) 73 | if (status === 'active') { 74 | Matrix.loader.stop(); 75 | clearTimeout(commandTimeout); 76 | console.log(t('matrix.start.start_app_successfully') + ': ', app); 77 | Matrix.endIt(); 78 | 79 | } else if (status === 'error') { 80 | Matrix.loader.stop(); 81 | clearTimeout(commandTimeout); 82 | console.error('The application failed to start, please update it and try again. \nIf it keeps failing you may want to contact the developer.'.yellow); 83 | Matrix.endIt(); 84 | } 85 | }); 86 | 87 | //Send the start command 88 | Matrix.api.app.start(app, Matrix.config.device.identifier, function(err, res) { 89 | if (err) { 90 | Matrix.loader.stop(); 91 | console.log(t('matrix.start.start_app_error') + ':', app, ' (' + err.message.red + ')'); 92 | Matrix.endIt(); 93 | } 94 | 95 | //add timeout to start command 96 | commandTimeout = setTimeout(function () { 97 | Matrix.loader.stop(); 98 | console.log(t('matrix.start.start_timeout')); 99 | Matrix.endIt(); 100 | }, commandTimeoutSeconds * 1000); 101 | 102 | }); 103 | } //else 104 | }); 105 | }); 106 | 107 | }); 108 | -------------------------------------------------------------------------------- /bin/matrix-config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var debug; 5 | 6 | //TODO matrix config won't exit 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('config'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | Matrix.validate.userAsync, 15 | Matrix.validate.deviceAsync, 16 | // user register does fb init for login, bad if we do that 2x 17 | function(cb) { 18 | Matrix.firebaseInit(cb) 19 | } 20 | ], function(err) { 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | var options = {}; 29 | var target = Matrix.pkgs[0]; // app name 30 | var key = Matrix.pkgs[1]; //target 31 | var value = Matrix.pkgs[2]; // in case they don't use = 32 | 33 | // split on = if exists 34 | if (!_.isUndefined(key) && key.indexOf('=') > -1) { 35 | value = key.split('=')[1]; 36 | key = key.split('=')[0]; 37 | } 38 | 39 | // normalize a.b.c for firebase a/b/c 40 | if (!_.isUndefined(key)) { 41 | key = key.replace(/\./g, '/'); 42 | } 43 | 44 | //split up commas 45 | if (!_.isUndefined(value) && value.indexOf(',') > -1) { 46 | // is an array 47 | value = value.split(','); 48 | } 49 | 50 | //support multiword 51 | if (!_.isUndefined(value) && Matrix.pkgs.length > 3) { 52 | value += ' ' + _.tail(_.tail(Matrix.pkgs)).join(' '); 53 | } 54 | 55 | Matrix.loader.stop(); 56 | console.log('App Path:'.blue, _.compact([target, key, value]).join('/').grey) 57 | 58 | if (Matrix.pkgs.indexOf('--watch') > -1 || Matrix.pkgs.indexOf('-w') > -1) { 59 | options.watch = true; 60 | } 61 | 62 | if (Matrix.pkgs.indexOf('--help') > -1 || Matrix.pkgs.indexOf('-h') > -1) { 63 | showHelp(); 64 | } else if (_.isUndefined(target)) { 65 | // get form 66 | 67 | console.log(t('matrix.config.device_config') + ' ', Matrix.config.device.identifier); 68 | Matrix.firebase.device.getConfig(handleResponse); 69 | 70 | // matrix config base 71 | } else if (_.isUndefined(key)) { 72 | 73 | Matrix.firebase.app.getIDForName(target, function(err, appId) { 74 | if (err) return console.error(err); 75 | debug('appId>', appId); 76 | Matrix.firebase.app.getConfig(appId, handleResponse); 77 | }) 78 | 79 | } else if (_.isUndefined(value)) { 80 | // get deep 81 | // 82 | key = '/' + key.replace(/\./g, '/'); 83 | debug('Firebase: '.blue, target) 84 | Matrix.firebase.app.getIDForName(target, function(err, appId) { 85 | if (err) return console.error(err); 86 | debug('appId>', appId); 87 | Matrix.firebase.app.getConfigKey(appId, key, handleResponse); 88 | }) 89 | 90 | // matrix config base keywords=test,keyword 91 | } else { 92 | 93 | debug('>>>', target, value); 94 | 95 | Matrix.firebase.app.getIDForName(target, function(err, appId) { 96 | if (err) return console.error(err); 97 | debug('appId>', appId); 98 | console.log('Application', target, '\nconfig value:', key, '\nupdate:', value) 99 | Matrix.firebase.app.setConfigKey(appId, key, value, function() { 100 | Matrix.helpers.trackEvent('app-config-change', { aid: target, did: Matrix.config.device.identifier }, process.exit); 101 | }); 102 | }) 103 | 104 | } 105 | 106 | 107 | function handleResponse(err, app) { 108 | if (err) return console.error(t('matrix.helpers.config_error'), err); 109 | if (_.isNull(app)) { console.error(t('matrix.helpers.config_error'), app) } 110 | console.log(require('util').inspect(app, { depth: 3, colors: true })); 111 | process.exit(); 112 | } 113 | 114 | 115 | function showHelp() { 116 | 117 | console.log('\n> matrix config ¬\n'); 118 | console.log('\t matrix config -', t('matrix.config.help').grey) 119 | console.log('\tmatrix config -', t('matrix.config.help_app').grey) 120 | console.log('\tmatrix config -', t('matrix.config.help_app_key').grey) 121 | console.log('\tmatrix config -', t('matrix.config.help_app_key_value').grey) 122 | console.log('\n') 123 | process.exit(1); 124 | } 125 | }); -------------------------------------------------------------------------------- /bin/matrix-list.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var debug, target; 4 | var async = require('async') 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function (cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('list'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | function (cb) { 14 | if (!Matrix.pkgs.length || showTheHelp) { Matrix.loader.stop(); return displayHelp(); } 15 | else { 16 | target = Matrix.pkgs[0]; 17 | cb(); 18 | } 19 | }, 20 | Matrix.validate.userAsync, 21 | function (cb) { Matrix.firebaseInit(cb); }, 22 | listAll, 23 | listDevices, 24 | Matrix.validate.deviceAsync, 25 | listApps, 26 | // user register does fb init for login, bad if we do that 2x 27 | ], function (err) { 28 | Matrix.loader.stop(); 29 | if (err) { 30 | console.error(err.message.red); 31 | debug('Error:', err.message); 32 | return process.exit(1); 33 | } 34 | 35 | console.log('Unknown parameter'.yellow + ' ' + target); 36 | displayHelp(); 37 | }); 38 | 39 | 40 | function displayHelp() { 41 | console.log('\n> matrix list ¬\n'); 42 | console.log('\t matrix list devices -', t('matrix.list.help_devices').grey) 43 | // console.log('\t matrix list groups -', t('matrix.list.help_groups').grey) 44 | console.log('\t matrix list apps -', t('matrix.list.help_apps').grey) 45 | console.log('\t matrix list all -', t('matrix.list.help_all').grey) 46 | console.log('\n') 47 | process.exit(1); 48 | } 49 | 50 | function listDevices(cb) { 51 | if (target.match(/device/)) { 52 | 53 | Matrix.firebase.device.list(function(devices) { 54 | debug('Device list found: ', devices) 55 | 56 | var deviceMap = {}; 57 | Matrix.loader.stop(); 58 | async.eachOf(devices, function(userDevice, deviceId, cb) { 59 | Matrix.firebase.device.lookup(deviceId, function(err, device) { 60 | debug(device) 61 | if (err) { 62 | if (err.code == 'PERMISSION_DENIED') { 63 | console.error('Permission denied, skipping device: ', deviceId); 64 | return cb(); 65 | } else { 66 | return cb(err) 67 | } 68 | } else { 69 | if (!_.isEmpty(device)) { 70 | //TODO temporary defaults until device creation includes this 71 | if (!device.hasOwnProperty('runtime')) device.runtime = { online: false, lastConnectionEvent: 0 }; 72 | if (!device.hasOwnProperty('config')) device.config = { init: [] }; 73 | deviceMap[deviceId] = { 74 | name: device.meta.name, 75 | online: device.runtime.online, 76 | description: device.meta.description, 77 | lastSeen: device.runtime.lastConnectionEvent, 78 | defaultApps: device.config.init 79 | } 80 | } 81 | cb(); 82 | } 83 | }) 84 | }, function(err) { 85 | if (err) { 86 | console.error(err.red); 87 | process.exit(1); 88 | } 89 | console.log(Matrix.helpers.displayDevices(deviceMap)); 90 | Matrix.config.deviceMap = deviceMap; 91 | Matrix.helpers.saveConfig(function() { 92 | process.exit(); 93 | }); 94 | }) 95 | }); 96 | } else { cb(); } 97 | } 98 | 99 | function listApps(cb) { 100 | if (target.match(/app/)) { 101 | Matrix.firebase.app.list(function(err, apps) { 102 | Matrix.loader.stop(); 103 | if (err) { 104 | console.error('- ', t('matrix.list.app_list_error') + ':', err); 105 | process.exit(1); 106 | } 107 | if (_.isUndefined(apps) || _.isNull(apps)) apps = {}; 108 | 109 | //Retrieve status for each app 110 | async.forEach(Object.keys(apps), function(appId, done) { 111 | Matrix.firebase.app.getStatus(appId, function(status) { 112 | if (_.isUndefined(status)) status = "inactive"; //Set default status to inactive 113 | debug("Status Watch: " + Matrix.config.user.id + '>' + Matrix.config.device.identifier + '>' + appId + '>' + status); 114 | apps[appId].status = status; 115 | done(); 116 | }); 117 | }, function(err) { 118 | //Once the status or each app has been collected, print table 119 | console.log(Matrix.helpers.displayApps(apps)); 120 | process.exit(); 121 | }); 122 | }); 123 | } else { cb(); } 124 | } 125 | 126 | function listAll(cb) { 127 | if (target.match(/all/)) { 128 | Matrix.firebase.user.getAllApps(function(err, resp) { 129 | Matrix.loader.stop(); 130 | if (_.isEmpty(resp)) { 131 | console.error(t('matrix.list.no_results')); 132 | process.exit(1); 133 | } 134 | debug('Device List>', resp); 135 | console.log(Matrix.helpers.displayDeviceApps(resp)); 136 | 137 | // save for later 138 | Matrix.config.deviceMap = resp; 139 | Matrix.helpers.saveConfig(function() { 140 | process.exit(); 141 | }) 142 | }); 143 | } else { cb(); } 144 | } 145 | -------------------------------------------------------------------------------- /bin/matrix-create.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | var tar = require('tar'); 5 | var prompt = require('prompt'); 6 | var yaml = require('js-yaml'); 7 | var pwd = process.cwd(); 8 | var async = require('async') 9 | 10 | var debug; 11 | 12 | async.series([ 13 | require('./matrix-init'), 14 | function (cb) { 15 | Matrix.loader.start(); 16 | debug = debugLog('create'); 17 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 18 | }, 19 | 20 | ], function(err) { 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | var app = Matrix.pkgs[0]; 29 | 30 | if (parseInt(app) === app) { 31 | Matrix.loader.stop(); 32 | console.error(t('matrix.create.bad_numbers')) 33 | process.exit(1); 34 | } 35 | 36 | function onError(err) { 37 | Matrix.loader.stop(); 38 | console.error(t('matrix.create.error_creating') + ':', err); 39 | process.exit(1); 40 | } 41 | 42 | // to write to disk after prompt 43 | var configString; 44 | 45 | // check if path already exists, refuse if so 46 | fs.access(process.cwd() + "/" + app, fs.F_OK, function(err) { 47 | if (!err) { 48 | Matrix.loader.stop(); 49 | console.error(t('matrix.create.error_creating') + ':', t('matrix.create.folder_exist')); 50 | process.exit(1); 51 | } else { 52 | var nameP = { 53 | name: 'name', 54 | description: 'App Name', 55 | pattern: /\w|\n|-/, 56 | message: 'App name must be a single word. Use - for multi word app names', 57 | required: true 58 | }; 59 | 60 | var descP = { 61 | name: 'description', 62 | description: 'Description', 63 | required: true 64 | } 65 | 66 | var keyP = { 67 | name: 'keywords', 68 | description: 'Keywords', 69 | } 70 | 71 | prompt.delimiter = ''; 72 | prompt.message = 'Create new application -- '; 73 | 74 | var ps = [descP, keyP]; 75 | 76 | if (_.isUndefined(app)) { 77 | // nop app mentioned 78 | ps.unshift(nameP) 79 | } else { 80 | prompt.message += ' ( ' + app + ' ) '; 81 | } 82 | 83 | Matrix.loader.stop(); 84 | prompt.start(); 85 | prompt.get(ps, function(err, results) { 86 | 87 | if (err) { 88 | if (err.toString().indexOf('canceled') > 0) { 89 | console.log(''); 90 | process.exit(); 91 | } else { 92 | console.log("Error: ", err); 93 | process.exit(); 94 | } 95 | } 96 | 97 | debug(results); 98 | 99 | if (_.isUndefined(app)) { 100 | // no app name defined 101 | app = results.name; 102 | } else { 103 | // app name defined 104 | results.name = app; 105 | } 106 | results.shortName = results.name.replace(/\s/g, '-'); 107 | //Add a display name 108 | results.displayName = _.startCase(results.name); 109 | 110 | // write the config yaml 111 | configString = yaml.safeDump(results); 112 | 113 | debug('Writing config...', configString); 114 | 115 | Matrix.loader.start(); 116 | 117 | var extractor = tar.Extract({ 118 | path: pwd + "/" + app, 119 | strip: 1 120 | }) 121 | .on('error', onError) 122 | .on('end', function onFinishedExtract() { 123 | 124 | Matrix.helpers.trackEvent('app-create', { aid: app }); 125 | 126 | Matrix.loader.stop(); 127 | 128 | fs.writeFileSync(app + '/config.yaml', '\n' + configString, { flag: 'a' }); 129 | 130 | changeNameP = require(pwd + "/" + app + '/package.json'); 131 | changeNameP.name = app; 132 | Matrix.helpers.changeName(changeNameP, pwd + "/" + app + '/package.json', function(err) { 133 | if (err) { 134 | console.error('Error updating package.json file: ' + err.message.red); 135 | process.exit(1); 136 | } 137 | }); 138 | 139 | console.log(t('matrix.create.new_folder') + ':>'.grey, app.green + '/'.grey); 140 | console.log(' app.js'.grey, '-', t('matrix.create.description_app')) 141 | console.log(' config.yaml'.grey, '-', t('matrix.create.description_config')) 142 | console.log(' README.MD'.grey, '-', t('matrix.create.description_developer')) 143 | console.log(' index.js'.grey, '-', t('matrix.create.description_index')) 144 | console.log(' package.json'.grey, '-', t('matrix.create.description_package')) 145 | process.exit(1); 146 | }); 147 | 148 | fs.createReadStream(__dirname + "/../baseapp.tar") 149 | .on('error', onError) 150 | .pipe(extractor); 151 | // unzip baseApp.zip to named folder 152 | }); 153 | } 154 | }); 155 | 156 | function displayHelp() { 157 | console.log('\n> matrix create ¬\n'); 158 | console.log('\t matrix create -', t('matrix.create.help', { app: '' }).grey) 159 | console.log('\n') 160 | process.exit(1); 161 | } 162 | }); -------------------------------------------------------------------------------- /bin/matrix-init.js: -------------------------------------------------------------------------------- 1 | require('colors'); 2 | 3 | debugLog = require('debug'); 4 | var debug = debugLog('cli'); 5 | var async = require('async'); 6 | 7 | showTheHelp = (process.argv.indexOf('--help') > -1); 8 | 9 | Matrix = {}; 10 | Matrix.version = require('../package.json').version; 11 | Matrix.config = require('../config/index'); 12 | Matrix.api = require('matrix-node-sdk'); 13 | _ = require('lodash'); 14 | 15 | var program = require('commander'); 16 | program.parse(process.argv); 17 | Matrix.pkgs = program.args; 18 | Matrix.localization = require('../lib/localization'); 19 | Matrix.localesFolder = __dirname + '/../config/locales'; 20 | Matrix.config.locale = 'en'; // set default locale 21 | t = Matrix.localization.get; // international translator 22 | 23 | 24 | Matrix.helpers = require('../lib/helpers'); 25 | //sets Matrix.config with local variables 26 | Matrix.config = _.assign(Matrix.config, Matrix.helpers.getConfig()); 27 | //set default locale 28 | //Use this to validate for user and display messages accordingly 29 | Matrix.validate = require('./matrix-validate'); 30 | 31 | // do user monitoring 32 | if (_.has(Matrix.config, 'trackOk')) { 33 | process.env.TRACKOK = 'true'; 34 | } else { 35 | Matrix.config.trackOk = {}; 36 | } 37 | 38 | // These are used to override if there is no environment set in config 39 | var options = { 40 | clientId: 'AdMobilizeAPIDev', 41 | clientSecret: 'AdMobilizeAPIDevSecret', 42 | apiUrl: process.env['MATRIX_API_SERVER'] || 'https://api.admobilize.com', 43 | mxssUrl: process.env['MATRIX_STREAMING_SERVER'] || 'https://mxss.admobilize.com', 44 | appsBucket: process.env['MATRIX_APPS_BUCKET'] || 'admobilize-matrix-apps' 45 | }; 46 | 47 | Matrix.endIt = function (code, seconds) { 48 | if (_.isUndefined(code)) code = 0; 49 | if (_.isUndefined(seconds)) seconds = 1; 50 | 51 | if (seconds != 0) { 52 | setTimeout(function () { 53 | process.nextTick(function () { process.exit(code); }); 54 | }, seconds * 1000) 55 | } else { 56 | process.nextTick(function () { process.exit(code); }); 57 | } 58 | } 59 | 60 | // since we async we can't rely on this to load fully by the time we're rolling 61 | function init(finished) { 62 | 63 | // neeed the async to ensure config file is saved before continuing 64 | async.series([ 65 | function (cb) { 66 | if (_.has(Matrix.config.environment, 'name')) { 67 | debug('Env: ', Matrix.config.environment.name); 68 | options.apiUrl = Matrix.config.environment.api; 69 | options.mxssUrl = Matrix.config.environment.mxss; 70 | options.appsBucket = Matrix.config.environment.appsBucket; 71 | } 72 | cb(); 73 | }, 74 | function (cb) { 75 | if (!_.has(Matrix.config.environment, 'name')) { 76 | // temp for hackathon 77 | debug('No env set, using dev default. Go VISA-MATRIX Hackathon 2017 participants!'); 78 | Matrix.config.environment = { 79 | name: process.env.NODE_ENV || 'production', 80 | api: options.apiUrl, 81 | mxss: options.mxssUrl, 82 | appsBucket: options.appsBucket 83 | }; 84 | Matrix.helpers.saveConfig(cb); 85 | } else { 86 | cb(); 87 | } 88 | } 89 | ], function continueInit(err) { 90 | if (err) console.error(err); 91 | 92 | 93 | 94 | if (Matrix.config.environment.name === 'rc' || Matrix.config.environment.name === 'production') { 95 | options.clientId = 'AdMobilizeClientID' 96 | options.clientSecret = 'AdMobilizeClientSecret' 97 | } 98 | 99 | // strip out 100 | Matrix.options = options; 101 | 102 | Matrix.api.makeUrls(options.apiUrl, options.mxssUrl); 103 | 104 | // to make user / device / etc available to sdk 105 | Matrix.api.setConfig(Matrix.config); 106 | 107 | //Loader, currently using the default braille spinner 108 | Matrix.loader = require('../lib/loader'); 109 | Matrix.loader.type('braille'); //Types: braille, matrix 110 | 111 | Matrix.firebase = require('matrix-firebase'); 112 | Matrix.firebaseInit = function initFirebase(cb) { 113 | var currentDevice = (!_.isEmpty(Matrix.config.device) && !_.isEmpty(Matrix.config.device.identifier)) ? Matrix.config.device.identifier : ''; 114 | debug('Firebase Init', Matrix.config.user.id, currentDevice); 115 | Matrix.firebase.init( 116 | Matrix.config.user.id, 117 | currentDevice, 118 | Matrix.config.user.token, 119 | Matrix.config.environment.name, 120 | function (err) { 121 | if (err) { debug('firebase error', err) } 122 | var errorCode = Matrix.validate.firebaseError(err); 123 | if (errorCode !== 0) { 124 | if (errorCode === 1) { 125 | //TODO try to refresh token before failing 126 | Matrix.loader.stop(); 127 | console.log('Invalid user, log in again'.yellow); 128 | Matrix.helpers.logout(function () { 129 | process.exit(); 130 | }); 131 | 132 | } else if (errorCode == 4) { 133 | console.log('Network timeout, please check your connection and try again'.yellow); 134 | } else { 135 | console.error('Error initializing Firebase: '.yellow, err.message.red); 136 | 137 | // specific info on how to resolve 138 | if (err.code === 'auth/custom-token-mismatch') { 139 | console.error('Server environments may be incongruent.', 140 | 'Rerun `%s` and login again to reset your configuration', 'matrix set env '.yellow 141 | ); 142 | } 143 | } 144 | process.exit(); 145 | } 146 | return cb(); 147 | }); 148 | //## firebase.init 149 | } 150 | //# firebaseInit 151 | 152 | // all done with setup 153 | finished(); 154 | }) 155 | 156 | } 157 | 158 | module.exports = init; 159 | -------------------------------------------------------------------------------- /bin/matrix-validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * user - Checks if a valid user is set along with a valid user token, if invalid it refreshes it 3 | * @param {bool} exit If the process should exit on failed user validation. Defaults to true 4 | * @returns {bool} 5 | */ 6 | function userAsync(cb) { 7 | if (_.isEmpty(Matrix.config.user)) { 8 | return cb(new Error(t('matrix.please_login'))); 9 | } else { 10 | if (!token()) { 11 | if (!_.isEmpty(Matrix.config.user.refreshToken)) { 12 | Matrix.helpers.refreshToken(Matrix.config.user.refreshToken, function (err, userToken) { 13 | debug('Err:', err, ' Token:', userToken); 14 | if (err || _.isEmpty(userToken)) { 15 | return cb(new Error('Token refresh failed!')); 16 | } else { 17 | Matrix.config.user.token = userToken; 18 | Matrix.helpers.saveConfig(function (err) { 19 | if (!err) return cb(); 20 | else return cb(new Error('Unable to save new user token')); 21 | }); 22 | } 23 | }); 24 | } else { 25 | return cb(new Error('Unable to refesh token!')); 26 | } 27 | } else { 28 | return cb(); 29 | } 30 | } 31 | } 32 | 33 | 34 | /** 35 | * user - Checks if a valid user is set along with a valid user token, if invalid it refreshes it 36 | * @param {bool} exit If the process should exit on failed user validation. Defaults to true 37 | * @returns {bool} 38 | */ 39 | function user(exit) { 40 | var result = false; 41 | if (_.isEmpty(exit)) exit = true; 42 | if (_.isEmpty(Matrix.config.user)) { 43 | debug('No user found'); 44 | } else { 45 | if (!token()) { 46 | if (!_.isEmpty(Matrix.config.user.refreshToken)) { 47 | var tokenData = Matrix.helpers.syncRefreshToken(Matrix.config.user.refreshToken); 48 | if (!_.isUndefined(tokenData.err) || _.isEmpty(tokenData.token)) { 49 | console.log('Token refresh failed!'); 50 | } else { 51 | debug('Token refreshed!'); 52 | Matrix.config.user.token = tokenData.token; 53 | var err = Matrix.helpers.syncSaveConfig(); 54 | if (!_.isEmpty(err)) console.error('Unable to save new user token!'.red, err); 55 | else result = true; 56 | } 57 | } else { 58 | console.log('Unable to refesh token!'); 59 | } 60 | } else { 61 | result = true; 62 | } 63 | } 64 | 65 | if (!result) { 66 | debug('Invalid token and unable to refresh it'); 67 | console.log(t('matrix.please_login').yellow); 68 | if (exit) process.exit(1); 69 | } 70 | return result; 71 | } 72 | 73 | /** 74 | * device - Checks if a device is properly set 75 | * @param {bool} exit If the process should exit on failed user validation. Defaults to true 76 | * @returns {bool} 77 | */ 78 | function deviceAsync(cb) { 79 | var err; 80 | if (_.isEmpty(Matrix.config.device) || _.isUndefined(Matrix.config.device.token)) { 81 | Matrix.loader.stop(); 82 | console.error('matrix list devices'.grey, ' - > '.yellow + t('matrix.validate.select_device_id').yellow, '\nmatrix use\n'.grey) 83 | err = new Error(t('matrix.validate.no_device')); 84 | } 85 | cb(err); 86 | } 87 | 88 | /** 89 | * device - Checks if a device is properly set 90 | * @param {bool} exit If the process should exit on failed user validation. Defaults to true 91 | * @returns {bool} 92 | */ 93 | function device(exit) { 94 | var result = true; 95 | if (_.isEmpty(exit)) exit = true; 96 | if (_.isEmpty(Matrix.config.device) || _.isUndefined(Matrix.config.device.token)) { 97 | Matrix.loader.stop(); 98 | console.error(t('matrix.validate.no_device') + '\n', '\nmatrix list devices'.grey, ' - > '.yellow + t('matrix.validate.select_device_id').yellow, '\nmatrix use\n'.grey) 99 | result = false; 100 | } 101 | if (!result && exit) process.exit(); 102 | return result; 103 | } 104 | 105 | /** 106 | * token - Verifies token integrity and expiration 107 | * returns {bool} Wether the token is valid or not 108 | */ 109 | function token(refresh) { 110 | if (_.isEmpty(refresh)) refresh = true; 111 | var jwt = require('jsonwebtoken'); 112 | var token = Matrix.config.user.token; 113 | var result = false; 114 | if (!_.isUndefined(token)) { 115 | var decode = jwt.decode(token, { complete: true }); 116 | 117 | if (_.isEmpty(decode)) debug('Incorrect token format'); 118 | else { 119 | if (decode.payload.exp < Math.round(new Date().getTime() / 1000)) 120 | debug('Token Expired.'); 121 | else 122 | result = true; 123 | } 124 | } 125 | if (!result) debug('Invalid token!'); 126 | else debug('Token ok!'.green); 127 | 128 | return result; 129 | } 130 | 131 | function isCurrentDevice(deviceId) { 132 | return (!_.isEmpty(Matrix.config.device) && Matrix.config.device.identifier === deviceId); 133 | } 134 | 135 | // 1 Invalid token 136 | // 2 Unlisted error 137 | // 3 Unknown error 138 | // 4 Network timeout 139 | //Returns a specific code for each case 140 | function firebaseError(err) { 141 | if (err) { 142 | if (err.hasOwnProperty('code')) { 143 | if (err.code == 'auth/invalid-custom-token') { 144 | return 1; 145 | } else if (err.code == 'auth/network-request-failed') { 146 | return 4; 147 | } else { 148 | Matrix.loader.stop(); 149 | console.log('Authentication error (' + err.code + '): ', err.message); 150 | return 2; 151 | } 152 | } else { 153 | Matrix.loader.stop(); 154 | console.log('Authentication error: ', err); 155 | return 3; 156 | } 157 | } else { 158 | return 0; 159 | } 160 | } 161 | 162 | module.exports = { 163 | device: device, 164 | user: user, 165 | token: token, 166 | config: function(config) { 167 | var configHelper = require('matrix-app-config-helper') 168 | return configHelper.validate(config); 169 | }, 170 | isCurrentDevice: isCurrentDevice, 171 | firebaseError: firebaseError, 172 | deviceAsync: deviceAsync, 173 | userAsync: userAsync, 174 | }; -------------------------------------------------------------------------------- /bin/matrix-remove.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var debug; 4 | var async = require('async'); 5 | 6 | async.series([ 7 | require('./matrix-init'), 8 | function(cb) { 9 | Matrix.loader.start(); 10 | debug = debugLog('remove'); 11 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 12 | }, 13 | Matrix.validate.userAsync, 14 | function(cb) { 15 | Matrix.firebaseInit(cb) 16 | } 17 | ], function(err) { 18 | if (err) { 19 | Matrix.loader.stop(); 20 | console.error(err.message.red); 21 | debug('Error:', err.message); 22 | return process.exit(1); 23 | } 24 | 25 | if (showTheHelp) return displayHelp(); 26 | 27 | var progress, target; 28 | if (Matrix.pkgs.length < 1) { // No id specified 29 | Matrix.validate.device(); // Require a selected device 30 | target = Matrix.config.device.identifier; 31 | } else if (Matrix.pkgs.length > 1) { //Id specified 32 | target = Matrix.pkgs.join(' '); 33 | } else if (Matrix.pkgs.length === 1) { //Id specified 34 | target = Matrix.pkgs[0]; 35 | } else { 36 | return displayHelp(); 37 | } 38 | 39 | var targetValue = _.findKey(Matrix.config.deviceMap, { name: target }); 40 | 41 | if (_.isEmpty(targetValue)) { // not a name, must be a key? 42 | if (_.has(Matrix.config.deviceMap, target)) { 43 | targetValue = target; 44 | } else { //Doesn't exist 45 | Matrix.loader.stop(); 46 | console.log('Device ' + target.yellow + ' doesn\'t exist'); 47 | return process.exit(2); 48 | } 49 | } 50 | 51 | Matrix.firebase.device.lookup(targetValue, function(error, device) { //Check if device exists in firebase 52 | if (error) { //Device doesn't exist in Firebase 53 | delete Matrix.config.deviceMap[targetValue]; //Remove from local cache 54 | Matrix.helpers.saveConfig(function() { 55 | Matrix.loader.stop(); 56 | console.log('Device ' + targetValue.yellow + ' was already removed'); 57 | return process.exit(3); 58 | }); 59 | } else { // Device exists 60 | 61 | Matrix.loader.stop(); 62 | var deleteDevice = false; 63 | var Rx = require('rx'); 64 | var prompts = new Rx.Subject(); // need to inject results of answers into questions for decision trees 65 | require('inquirer').prompt(prompts).ui.process.subscribe(function(ans) { 66 | 67 | //Confirm 68 | if (ans.name === 'confirm') { 69 | if (ans.answer === true) deleteDevice = true; 70 | prompts.onCompleted(); 71 | } 72 | 73 | }, function(e) { console.error(e) }, function() { 74 | 75 | if (!deleteDevice) { 76 | process.exit(0); 77 | } else { //Proceed to delete 78 | //Create a timeout in case workers don't respond' 79 | var deleteTimeoutSeconds = 20; 80 | var deleteTimeout = setTimeout(function() { //If deleteTimeoutSeconds is passed, fail 81 | Matrix.loader.stop(); 82 | console.log('Unable to delete device right now, please try again later'); 83 | process.exit(3) 84 | }, deleteTimeoutSeconds * 1000); 85 | 86 | Matrix.helpers.trackEvent('device-remove', { did: targetValue }); 87 | 88 | Matrix.firebase.device.delete(targetValue, { //Send removal task to Firebase queue 89 | error: function(err) { 90 | Matrix.loader.stop(); 91 | clearTimeout(deleteTimeout); //Remove timeout 92 | if (err) { 93 | if (err.hasOwnProperty('state') && err.state === 'device-deprovisioning-in-progress') { 94 | debug('Deprovisioning device step... ignore this'); 95 | } else if (err.hasOwnProperty('details') && err.details.hasOwnProperty('error')) { //Report error 96 | console.error('\n' + t('matrix.remove.error').red + ': ', err.details.error); 97 | process.exit(1); //Stop exectuion 98 | } else { 99 | console.error('\n' + t('matrix.remove.error').red + ': ', err); 100 | process.exit(1); //Stop exectuion 101 | } 102 | } 103 | }, 104 | finished: function() { 105 | clearTimeout(deleteTimeout); //Remove timeout 106 | Matrix.loader.stop(); 107 | delete Matrix.config.deviceMap[targetValue]; //Remove from local cache 108 | Matrix.helpers.saveConfig(function() { 109 | Matrix.loader.stop(); 110 | console.log('\n' + t('matrix.remove.finish').green + '...'.green); 111 | process.exit(0); //Stop exectuion 112 | }); 113 | }, 114 | start: function() { 115 | _.once(function() { 116 | Matrix.loader.stop(); 117 | console.log(t('matrix.remove.start') + '...') 118 | Matrix.loader.start(); 119 | }) 120 | }, 121 | progress: function(msg) { 122 | if (_.isUndefined(msg)) msg = ''; 123 | else msg = ' ' + msg + ' '; 124 | if (!progress) { 125 | progress = true; 126 | Matrix.loader.stop(); 127 | process.stdout.write(t('matrix.remove.progress') + msg); //Report progress 128 | } else { 129 | process.stdout.write('.' + msg); 130 | } 131 | } 132 | }); 133 | } 134 | }); 135 | 136 | prompts.onNext({ 137 | type: 'confirm', 138 | name: 'confirm', 139 | message: 'This removes the device ' + targetValue.yellow + ' (' + device.meta.name.yellow + ') and all the data associated with it, are you sure?'.white, 140 | default: false //Default to NO 141 | }); 142 | 143 | } 144 | }); 145 | 146 | function displayHelp() { 147 | Matrix.loader.stop(); 148 | console.log('\n> matrix remove ¬\n'); 149 | console.log('\t matrix remove -', t('matrix.remove.help_selected').grey); 150 | console.log('\t matrix remove -', t('matrix.remove.help_id', { id: '' }).grey); 151 | console.log('\n'); 152 | process.exit(1); 153 | } 154 | }); -------------------------------------------------------------------------------- /baseapp.tar: -------------------------------------------------------------------------------- 1 | baseapp/000755 000765 000024 00000000000 12775543651 012424 5ustar00godstaff000000 000000 baseapp/app.js000644 000765 000024 00000000072 12775544123 013534 0ustar00godstaff000000 000000 // app code goes here 2 | // matrix.init().... 3 | // 4 | // have fun 5 | baseapp/config.yaml000644 000765 000024 00000000052 12775543563 014554 0ustar00godstaff000000 000000 # do not touch this line 6 | configVersion: 2 7 | baseapp/index.js000644 000765 000024 00000000226 12626076260 014061 0ustar00godstaff000000 000000 var appName = require('path').basename(__dirname).split('.')[0]; 8 | 9 | matrix = require('./../matrix.js') 10 | 11 | matrix.startApp(appName); 12 | 13 | require('./app.js'); 14 | baseapp/package.json000644 000765 000024 00000000405 12704231665 014700 0ustar00godstaff000000 000000 { 15 | "name": "baseapp.matrix", 16 | "version": "1.0.0", 17 | "description": "", 18 | "main": "index.js", 19 | "scripts": { 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "dependencies": { 25 | "cpu-stats": "^1.0.0" 26 | } 27 | } 28 | baseapp/README.MD000644 000765 000024 00000000422 12775537124 013577 0ustar00godstaff000000 000000 This is your app folder. Please only change app.js and config.yaml. 29 | 30 | Do not modify index.js. 31 | 32 | You may add packages via `npm`, they will be installed when the application is deployed to a device. 33 | 34 | ## Developer Documentation 35 | 36 | http://github.io/matrix-io/matrix-documentation/ 37 | -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | var run = require('child_process').spawn; 2 | var exec = require('child_process').exec; 3 | var assert = require('assert'); 4 | var Matrix; 5 | 6 | 7 | describe('can manage apps', function() { 8 | this.timeout(120000); 9 | before(function(done) { 10 | // fire up a matrix device -- fn.registerDevice must run before this 11 | var fs = require('fs'); 12 | var admobModules = fs.readdirSync('../..'); 13 | if (admobModules.indexOf('matrix-os') < -1) { 14 | done('No MATRIX OS found'); 15 | } else { 16 | Matrix = require('child_process').fork('../matrix-os/index.js', { 17 | cwd: '../matrix-os/', 18 | env: { 19 | MATRIX_DEVICE_ID: M.DEVICE_ID || process.env.MATRIX_DEVICE_ID, 20 | MATRIX_DEVICE_SECRET: M.DEVICE_SECRET || process.env.MATRIX_DEVICE_SECRET, 21 | NO_UPGRADE: true, 22 | NODE_ENV: 'dev', 23 | TEST_MODE: true, 24 | DEBUG: (process.env.hasOwnProperty('DEBUG')) ? '*' : '' 25 | }, 26 | stdio: 'ignore' 27 | }) 28 | Matrix.on('message', function(msg) { 29 | if (msg['matrix-ready'] === true) { 30 | done(); 31 | console.log('MatrixOS ready') 32 | } 33 | }) 34 | } 35 | }) 36 | 37 | before(fn.login); 38 | 39 | before(fn.useDevice); 40 | 41 | it('`matrix install`', function(done) { 42 | fn.run('matrix install sensorTest', { 43 | responses: [ 44 | ['access to the above', 'y\n\n'] 45 | ], 46 | checks: ['Installing'] 47 | }, done) 48 | }) 49 | 50 | it('`matrix list apps`', function(done) { 51 | fn.run('matrix list apps', { 52 | checks: 'sensorTest' 53 | }, done) 54 | }) 55 | 56 | it('`matrix start`', function(done) { 57 | fn.run('matrix start sensorTest', { 58 | checks: ['Starting: sensorTest'] 59 | }, done) 60 | }) 61 | 62 | // Tired of it 63 | it.skip('`matrix restart`', function(done) { 64 | fn.run('matrix restart sensorTest', { 65 | checks: ['Restarted'] 66 | }, done) 67 | }) 68 | 69 | it('`matrix stop`', function(done) { 70 | fn.run('matrix stop sensorTest', { 71 | checks: ['Stopped: sensorTest'] 72 | }, done) 73 | }) 74 | 75 | it('`matrix uninstall`', function(done) { 76 | fn.run('matrix uninstall sensorTest', { 77 | checks: 'Uninstall sent to device' 78 | }, done) 79 | }) 80 | 81 | 82 | describe('appdev lifecycle', function() { 83 | this.timeout(15000) 84 | before(function(done) { 85 | require('child_process').exec('rm -rf matrix-test-app matrix-test-app2') 86 | done() 87 | }) 88 | 89 | it('`matrix create`', function(done) { 90 | fn.run('matrix create', { 91 | responses: [ 92 | ['App Name', 'matrix-test-app\n'], 93 | ['Description', 'description\n'], 94 | ['Keywords', 'foo,bar\n'], 95 | ], 96 | checks: ['matrix-test-app'], 97 | postCheck: function(done) { 98 | var config = require('js-yaml').safeLoad(require('fs').readFileSync('./matrix-test-app/config.yaml')); 99 | assert((config.name === 'matrix-test-app')); 100 | assert((config.description === 'description')); 101 | assert((config.keywords === 'foo,bar')); 102 | done(); 103 | } 104 | }, done) 105 | }); 106 | 107 | it('`matrix create app-name`', function(done) { 108 | fn.run('matrix create matrix-test-app2', { 109 | responses: [ 110 | ['Description', 'description\n'], 111 | ['Keywords', 'foo,bar\n'], 112 | ], 113 | checks: 'New Folder', 114 | postCheck: function(done) { 115 | var config = require('js-yaml').safeLoad(require('fs').readFileSync('./matrix-test-app2/config.yaml')); 116 | assert((config.name === 'matrix-test-app2')); 117 | assert((config.description === 'description')); 118 | assert((config.keywords === 'foo,bar')); 119 | done(); 120 | } 121 | }, done) 122 | }) 123 | this.timeout(45000) 124 | it('`matrix deploy`', function(done) { 125 | fn.run('matrix deploy matrix-test-app', { 126 | checks: 'Application installation SUCCESS' 127 | }, done) 128 | }) 129 | it('`matrix list apps`', function(done) { 130 | fn.run('matrix list apps', { 131 | checks: 'matrix-test-app' 132 | }, done) 133 | }) 134 | 135 | after(function(done) { 136 | require('child_process').exec('rm -rf matrix-test-app matrix-test-app2') 137 | done(); 138 | }) 139 | }) 140 | 141 | 142 | 143 | after(function(done) { 144 | Matrix.kill(); 145 | done(); 146 | }) 147 | 148 | // after(fn.logout) 149 | // 150 | // it('`matrix list apps`') 151 | // it('`matrix search`') 152 | // // it('`matrix search -s`') 153 | // it('`matrix install`') 154 | // // it('`matrix install sensor`') 155 | // it('`matrix uninstall`') 156 | // // it('`matrix update`') 157 | // it('`matrix start`', function(done){ 158 | // var proc = run('matrix', ['start','monitor']) 159 | // 160 | // proc.stdout.on('data', function(out){ 161 | // if (out.toString().indexOf('error') > -1 ){ 162 | // done(new Error('error: ' + out.toString() )) 163 | // } else if (out.toString().indexOf('Started: monitor') > -1 ){ 164 | // done() 165 | // } 166 | // }) 167 | // }) 168 | // 169 | // it('`matrix stop`', function(done){ 170 | // 171 | // var proc = run('matrix', ['stop','monitor']) 172 | // proc.stdout.on('data', function(out){ 173 | // console.log(out.toString()) 174 | // if (out.toString().indexOf('error') > -1){ 175 | // done(new Error('error: '+ out.toString() )) 176 | // } else if (out.toString().indexOf('Stopped: monitor') > -1 ){ 177 | // done() 178 | // } 179 | // }) 180 | // }) 181 | // it('`matrix set config`') 182 | // it('`matrix set config app`') 183 | // it('`matrix set config app key`') 184 | // it('`matrix set config app key=value`') 185 | }) 186 | 187 | // describe('has application development functions', function(){ 188 | // it('`matrix create`', function (done) { 189 | // var name = 'matrixtestapp-' + _.map(Array(8), function(){ 190 | // return Math.round(Math.random()*16).toString(16) 191 | // }).join(''); 192 | // 193 | // require('child_process').spawnSync('matrix', ['create', name ]) 194 | // var fs = require('fs'); 195 | // if ( fs.statSync( name ).isDirectory() ) { 196 | // exec('rm -r '+ name); 197 | // done(); 198 | // } else { 199 | // done('Matrix Create Error') 200 | // } 201 | // }) 202 | // it('`matrix deploy`') 203 | // it('`matrix trigger`') 204 | // it('`matrix log`') 205 | // }) -------------------------------------------------------------------------------- /test/_functions.js: -------------------------------------------------------------------------------- 1 | // fires off the cmd line easily 2 | /** 3 | * run - run a command and watch the results 4 | * cmd '' - matrix command to run 5 | * options.responses [] - nested array of call and responses 6 | * options.checks [] - make sure all these strings exist in the output 7 | * options.postCheck f() - ( done, output=['','',...]) run a final check 8 | */ 9 | 10 | var showLogs = false; 11 | 12 | var run = function(cmd, options, done) { 13 | if (!_.isFunction(done)) { 14 | throw new Error('Run needs a done()'); 15 | } 16 | var args = cmd.split(' '); 17 | var isM = cmd.split(' ').shift(); 18 | if (isM === 'matrix') { 19 | // matrix included, remove 20 | args.shift(); 21 | } 22 | 23 | if (_.isString(options.checks)) { 24 | options.checks = [options.checks] 25 | } 26 | // console.log(args) 27 | var proc = require('child_process').spawn('matrix', args); 28 | 29 | var responseCount = 0; //options.responses.length; 30 | var checkCount = 0; //options.checks.length; 31 | 32 | var respondPrompts = _.map(options.responses, _.first); 33 | // return first for regex map 34 | // => /name|password/ 35 | var respondRegex = new RegExp(_.map(options.responses, _.first).join('|')); 36 | 37 | var targetChecks = (options.hasOwnProperty('checks')) ? options.checks.length : 0; 38 | var targetResps = (options.hasOwnProperty('responses')) ? options.responses.length : 0; 39 | 40 | // global to match multis 41 | var checkRegex = new RegExp(options.checks.join('|'), 'g'); 42 | 43 | // console.log(respondRegex, checkRegex) 44 | // 45 | 46 | var output = []; 47 | var finished = false; 48 | 49 | var handleOutput = function(out) { 50 | out = out.toString(); 51 | output.push(out.split('\n')) 52 | if (process.env.hasOwnProperty('DEBUG')) { 53 | console.log(out); 54 | } 55 | // called for each line of out 56 | var respMatch = out.match(respondRegex); 57 | // console.log(responseCount, '<', targetResps); 58 | // console.log(respMatch, out, '[]=>', respondRegex, targetResps) 59 | if (responseCount < targetResps && options.hasOwnProperty('responses') && !_.isNull(respMatch)) { 60 | var index = respondPrompts.indexOf(respMatch[0]); 61 | console.log(respMatch[0], index, options.responses[index][1]) 62 | proc.stdin.write(options.responses[index][1]); 63 | responseCount += 1; 64 | } 65 | 66 | if (options.hasOwnProperty('checks') && !_.isNull(out.match(checkRegex))) { 67 | checkCount += out.match(checkRegex).length; 68 | } 69 | 70 | // console.log(responseCount, checkCount) 71 | if (!finished && responseCount >= targetResps && checkCount >= targetChecks) { 72 | finished = true; 73 | 74 | if (options.hasOwnProperty('postCheck')) { 75 | // make sure command has time to finish 76 | setTimeout(function() { 77 | // console.log('>>>Function POSTCHECK') 78 | options.postCheck(done, output); 79 | }, 100) 80 | } else { 81 | done(); 82 | } 83 | } 84 | } 85 | // TODO: Debug uses stderr 86 | proc.stdout.on('data', handleOutput ); 87 | 88 | // forward errors 89 | proc.stderr.on('data', handleOutput ); 90 | 91 | proc.on('close', function(code) { 92 | console.log('finished'.green, cmd, code) 93 | }) 94 | } 95 | 96 | 97 | module.exports = { 98 | showLogs: showLogs, 99 | run: run, 100 | readConfig: function readConfig() { 101 | return JSON.parse(require('fs').readFileSync(require('os').homedir() + '/.matrix/store.json')); 102 | }, 103 | updateConfig: function updateConfig(valuesObject) { 104 | var fileContent = JSON.parse(require('fs').readFileSync(require('os').homedir() + '/.matrix/store.json')); 105 | fileContent = _.merge(valuesObject, fileContent); 106 | return require('fs').writeFileSync(require('os').homedir() + '/.matrix/store.json', JSON.stringify(fileContent)); 107 | }, 108 | login: function(done) { 109 | run('matrix login', { 110 | responses: [ 111 | ['username', 'testuser@testing.admobilize.com\n'], 112 | ['password', 'test1234\n'], 113 | ['Share usage information', 'n\n'] 114 | ], 115 | checks: [ 116 | 'Login Successful' 117 | ], 118 | postCheck: function(done) { 119 | if (fn.readConfig().user.hasOwnProperty('token')) { 120 | done(); 121 | } else { 122 | done('No Token Saved to Local Config') 123 | } 124 | } 125 | }, done); 126 | }, 127 | registerDevice: function(done) { 128 | var seed = Math.round(Math.random() * 1000000); 129 | 130 | run('matrix register device', { 131 | responses: [ 132 | ['device name', 'test-device-' + seed + '\n'], 133 | ['device description', 'test-description\n'] 134 | ], 135 | checks: [ 136 | 'MATRIX_DEVICE_ID', 137 | 'MATRIX_DEVICE_SECRET', 138 | 'matrix use test-device-' + seed 139 | ], 140 | postCheck: function(done, output) { 141 | output = _.flatten(output); 142 | var exports = _.filter(output, function(o) { 143 | return (o.indexOf('export') > -1) 144 | }); 145 | // console.log(exports); 146 | // make these available 147 | M.DEVICE_ID = exports[0].split('=')[1].trim(); 148 | M.DEVICE_SECRET = exports[1].split('=')[1].trim(); 149 | done(); 150 | } 151 | }, done) 152 | }, 153 | useDevice: function(done) { 154 | // if we haven't done the whole test, get deviceid from the config 155 | if (!M.hasOwnProperty('DEVICE_ID')) { 156 | console.log('No new device made. Using first entry from deviceMap') 157 | var c = fn.readConfig(); 158 | 159 | M.DEVICE_ID = (c.device.hasOwnProperty('identifier')) ? 160 | c.device.identifier : 161 | Object.keys(c.deviceMap)[0]; 162 | } 163 | 164 | console.log('Use Device', M.DEVICE_ID); 165 | 166 | run('matrix use ' + M.DEVICE_ID, { 167 | checks: ['test-device'], 168 | postCheck: function(done) { 169 | var config = fn.readConfig(); 170 | if (!config.hasOwnProperty('device')) { 171 | console.log(config); 172 | console.log(require('os').homedir() + '/.matrix/store.json') 173 | return done(new Error('No Config File Found')); 174 | } 175 | var did = config.device.identifier; 176 | var name = config.deviceMap[did].name; 177 | if (name.indexOf('test-device') > -1) { 178 | done(); 179 | } else { 180 | done('Finished, but bad device map') 181 | } 182 | } 183 | }, done); 184 | }, 185 | removeDevice: function(done) { 186 | // if we haven't done the whole test, get deviceid from the config 187 | if (!M.hasOwnProperty('DEVICE_ID')) { 188 | console.log('No new device made. Using first entry from deviceMap') 189 | var c = fn.readConfig(); 190 | 191 | M.DEVICE_ID = (c.device.hasOwnProperty('identifier')) ? 192 | c.device.identifier : 193 | Object.keys(c.deviceMap)[0]; 194 | } 195 | 196 | console.log('Remove Device', M.DEVICE_ID); 197 | 198 | run('matrix remove ' + M.DEVICE_ID, { 199 | responses: [ 200 | ['test-device', 'y\n'] 201 | ], 202 | checks: [ 203 | 'Device successfully removed' 204 | ] 205 | }, done); 206 | }, 207 | logout: function(done) { 208 | run('matrix logout', { 209 | checks: ['Logged Out Successfully'], 210 | postCheck: function(done) { 211 | var config = fn.readConfig(); 212 | if (_.has(config, 'user')) { 213 | done('User Not Deleted on Logout') 214 | } else { 215 | done(); 216 | } 217 | } 218 | }, done) 219 | } 220 | } -------------------------------------------------------------------------------- /bin/matrix-deploy.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var deploymentFinished = false; 5 | var workerTimeoutSeconds = 30; 6 | var deviceTimeoutSeconds = 30; 7 | 8 | var debug, fileUrl; 9 | 10 | async.series([ 11 | require('./matrix-init'), 12 | function (cb) { 13 | Matrix.loader.start(); 14 | debug = debugLog('deploy'); 15 | fileUrl = 'https://storage.googleapis.com/' + Matrix.config.environment.appsBucket + '/apps'; // //.zip 16 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 17 | }, 18 | Matrix.validate.userAsync, 19 | Matrix.validate.deviceAsync, 20 | // user register does fb init for login, bad if we do that 2x 21 | function (cb) { 22 | Matrix.firebaseInit(cb) 23 | } 24 | ], function (err) { 25 | if (err) { 26 | Matrix.loader.stop(); 27 | console.error(err.message.red); 28 | debug('Error:', err.message); 29 | return process.exit(1); 30 | } 31 | 32 | if (showTheHelp) return displayHelp(); 33 | 34 | var appName = Matrix.pkgs[0]; 35 | var pwd = process.cwd(); 36 | 37 | if (_.isUndefined(appName)) { 38 | // infer name from current directory + strip out suffix jic 39 | appName = require('path').basename(pwd).replace('.matrix', ''); 40 | // step out one level so we can target this dir 41 | pwd += '/'; 42 | } else { 43 | pwd += '/' + appName + '/'; 44 | } 45 | 46 | var destinationFilePath = require('os').homedir() + '/.matrix/' + appName + '.zip'; 47 | 48 | async.parallel({ 49 | folder: async.apply(Matrix.helpers.checkAppFolder, pwd), 50 | code: async.apply(Matrix.helpers.checkAppCode, pwd), 51 | data: async.apply(Matrix.helpers.collectAppData, appName, pwd) 52 | }, 53 | function (err, results) { 54 | if (!err && !_.isUndefined(results.data)) { 55 | var appDetails = results.data; 56 | debug('Using app details: ' + JSON.stringify(appDetails)); 57 | 58 | Matrix.helpers.zipAppFolder(pwd, destinationFilePath, function (err) { 59 | if (err) { 60 | Matrix.loader.stop(); 61 | console.error('Error zipping app folder: ' + err.message.red); 62 | process.exit(); 63 | } else { 64 | onEnd(appDetails); 65 | } 66 | }); 67 | 68 | } else { 69 | Matrix.loader.stop(); 70 | console.error(err.message.red); 71 | } 72 | }); 73 | 74 | function onEnd(details) { 75 | 76 | Matrix.helpers.trackEvent('app-deploy', { aid: appName, did: Matrix.config.device.identifier }); 77 | 78 | debug('Finished packaging ', appName); 79 | var downloadFileName = Matrix.config.user.id + '/' + appName.toLowerCase() + '-' + Math.round(Math.random() * Math.pow(10, 8)) + '.zip'; 80 | details.file = fileUrl + '/' + appName + '/' + downloadFileName; 81 | var configOk = true; 82 | Matrix.loader.stop(); 83 | console.log('Validating configuration file...'); 84 | try { 85 | configOk = Matrix.validate.config(details.config); 86 | } catch (e) { 87 | console.error(e); 88 | console.log('Configuration Invalid. Please make sure the config.yaml file is properly formatted and try again'.yellow); 89 | process.exit(); 90 | } 91 | 92 | if (!configOk) { 93 | console.log('Configuration Invalid. Please make sure the config.yaml is properly formatted and try again'.red); 94 | process.exit(); 95 | } 96 | console.log('Successfully validated configuration file'); 97 | Matrix.loader.start(); 98 | 99 | Matrix.helpers.getUploadUrl(downloadFileName, appName, 'zip', function (err, uploadUrl) { 100 | if (!err) { 101 | Matrix.helpers.uploadPackage(destinationFilePath, uploadUrl, function (err) { 102 | 103 | var appData = Matrix.helpers.formAppData(details); 104 | appData.override = true; //If true the appstore won't check for uniqueness 105 | 106 | var deployedAppId, workerTimeout, deviceTimeout; 107 | var nowInstalling = false; 108 | //Listen for the app installation in device (appId from users>devices>apps) 109 | Matrix.firebase.app.watchNamedUserApp(appName, function (app, appId) { 110 | debug('App install ' + appId + ' activity'); 111 | if (!_.isUndefined(appId) && _.isUndefined(deployedAppId)) { 112 | debug('App id ' + appId + ' identified'); 113 | deployedAppId = appId; 114 | //Listen for the status change (deviceapps) 115 | Matrix.firebase.app.watchStatus(deployedAppId, function (status) { 116 | debug('App deployed with status > ' + status); 117 | Matrix.loader.stop(); 118 | if (status === 'error') { 119 | console.error(t('matrix.install.app_install_error'), ' ', app); 120 | process.exit(1); 121 | //It must first go through the pending state (nowInstalling) and then back to inactive 122 | } else if (nowInstalling && status === 'inactive') { 123 | clearTimeout(deviceTimeout); 124 | var deploymentTimer = setInterval(function () { 125 | if (deploymentFinished) { 126 | clearTimeout(deploymentTimer); 127 | console.log('Application ' + appName.green + ' was successfully installed!'); 128 | console.log(t('matrix.install.app_install_success').green); 129 | // clear out zip file 130 | // require('child_process').execSync('rm ' + destinationFilePath); 131 | // debug( destinationFilePath, 'removed'); 132 | endIt(); 133 | } 134 | }, 400); 135 | } else if (status === 'active') { 136 | console.log('App running already, will attempt restart.') 137 | process.exit(1); 138 | } else if (status === 'pending') { 139 | nowInstalling = true 140 | console.log('Installing ' + appName + ' on device...'); 141 | Matrix.loader.start(); 142 | } 143 | }); 144 | } 145 | }); 146 | 147 | //Start timeout in case the workers aren't up' 148 | workerTimeout = setTimeout(function () { 149 | console.log('Server response timeout, please try again later'.yellow); 150 | process.exit(1); 151 | }, workerTimeoutSeconds * 1000); 152 | 153 | //Send the app deployment request 154 | var options = { 155 | deviceId: Matrix.config.device.identifier, 156 | appData: appData, 157 | userId: Matrix.config.user.id 158 | }; 159 | 160 | Matrix.firebase.app.deploy(options, { 161 | error: function (err) { 162 | clearTimeout(workerTimeout); 163 | if (err.hasOwnProperty('details')) { 164 | console.log('App deployment failed: '.red, err.details.error); 165 | } else { 166 | console.log('App deployment failed: '.red, err.message); 167 | } 168 | process.exit(); 169 | }, 170 | finished: function () { 171 | clearTimeout(workerTimeout); 172 | Matrix.loader.stop(); 173 | console.log('Deploying to device...'); 174 | //Start timeout in case the workers aren't up' 175 | deviceTimeout = setTimeout(function () { 176 | console.log(t('matrix.install.device_install_timeout').yellow); 177 | process.exit(1); 178 | }, deviceTimeoutSeconds * 1000); 179 | Matrix.loader.start(); 180 | deploymentFinished = true; 181 | }, 182 | start: function () { 183 | Matrix.loader.stop(); 184 | console.log('Requesting deploy...'); 185 | Matrix.loader.start(); 186 | }, 187 | progress: function () { 188 | Matrix.loader.stop(); 189 | console.log('Processing deployment parameters...'); 190 | Matrix.loader.start(); 191 | } 192 | }); 193 | 194 | }); 195 | } else { 196 | console.error(err); 197 | return process.exit(1); 198 | } 199 | }); 200 | 201 | } 202 | 203 | function endIt() { 204 | setTimeout(function () { 205 | process.nextTick(function () { 206 | process.exit(0); 207 | }) 208 | }, 1000) 209 | } 210 | 211 | function displayHelp() { 212 | console.log('\n> matrix deploy ¬\n'); 213 | console.log('\t matrix deploy -', t('matrix.deploy.help', { app: '' }).grey) 214 | console.log('\n') 215 | process.exit(1); 216 | } 217 | 218 | }); -------------------------------------------------------------------------------- /bin/matrix-publish.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var async = require('async'); 4 | var publicationFinished = false; 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('publish'); 12 | fileUrl = 'https://storage.googleapis.com/' + Matrix.config.environment.appsBucket + '/apps'; // //.zip 13 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 14 | }, 15 | Matrix.validate.userAsync, 16 | function(cb) { 17 | Matrix.firebaseInit(cb) 18 | } 19 | ], function(err) { 20 | if (err) { 21 | Matrix.loader.stop(); 22 | console.error(err.message.red); 23 | debug('Error:', err.message); 24 | return process.exit(1); 25 | } 26 | 27 | if (showTheHelp) return displayHelp(); 28 | 29 | var appName = Matrix.pkgs[0]; 30 | var pwd = process.cwd(); 31 | 32 | if (_.isUndefined(appName)) { 33 | // infer name from current directory + strip out suffix jic 34 | appName = require('path').basename(pwd).replace('.matrix', ''); 35 | // step out one level so we can target this dir 36 | pwd += '/'; 37 | } else { 38 | pwd += '/' + appName + '/'; 39 | } 40 | 41 | var destinationFilePath = require('os').homedir() + '/.matrix/' + appName + '.zip'; 42 | var packageContent; 43 | var hasReadme = false; 44 | 45 | async.parallel({ 46 | folder: async.apply(Matrix.helpers.checkAppFolder, pwd), 47 | code: async.apply(Matrix.helpers.checkAppCode, pwd), 48 | data: async.apply(Matrix.helpers.collectAppData, appName, pwd) 49 | }, 50 | function(err, results) { 51 | if (!err && !_.isUndefined(results.data)) { 52 | if (!_.isUndefined(results.folder) && results.folder.hasOwnProperty('readme')) { //If it has a readme file 53 | hasReadme = results.folder.readme; 54 | } 55 | var appDetails = results.data; 56 | debug('Using app details: ' + JSON.stringify(appDetails)); 57 | var configOk = true; 58 | Matrix.loader.stop(); 59 | console.log('Validating configuration file...'); 60 | try { 61 | configOk = Matrix.validate.config(appDetails.config); 62 | } catch (e) { 63 | console.error(e); 64 | console.log('Publication interrupted. Please make sure the config.yaml file is properly formatted and try again'.yellow); 65 | process.exit(); 66 | } 67 | 68 | if (!configOk) { 69 | console.log('Publication interrupted. Please adjust the config.yaml file and try again'.yellow); 70 | process.exit(); 71 | } 72 | console.log('Successful config file validation'); 73 | Matrix.loader.start(); 74 | 75 | var newVersion = Matrix.helpers.patchVersion(appDetails.version); 76 | 77 | //ASK 78 | var Rx = require('rx'); 79 | var promptHandler = new Rx.Subject(); 80 | 81 | Matrix.loader.stop(); 82 | require('inquirer').prompt(promptHandler).ui.process.subscribe(function(answer) { 83 | if (answer.name === 'current' && answer.answer === true) { 84 | promptHandler.onCompleted(); 85 | } 86 | 87 | if (answer.name === 'version') { 88 | appDetails.version = answer.answer; 89 | try { 90 | packageContent = require(pwd + '/' + 'package.json'); 91 | } catch (err) { 92 | console.error('Error reading package.json file:' + err.message); 93 | process.exit(1); 94 | } 95 | packageContent.version = appDetails.version; 96 | Matrix.helpers.updateFile(packageContent, pwd + '/package.json', function(err) { 97 | if (err) { 98 | console.error('Error updating package.json file: ' + err.message.red); 99 | process.exit(1); 100 | } 101 | promptHandler.onCompleted(); 102 | }); 103 | } 104 | 105 | }, function (e) { console.error(e) }, function () { 106 | Matrix.loader.start(); 107 | Matrix.helpers.zipAppFolder(pwd, destinationFilePath, function(err) { 108 | if (err) { 109 | Matrix.loader.stop(); 110 | console.error('Error zipping app folder: ' + err.message.red); 111 | process.exit(); 112 | } else { 113 | onEnd(appDetails); 114 | } 115 | }); 116 | }); 117 | 118 | //Confirm deployment to current version 119 | promptHandler.onNext({ 120 | type: 'confirm', 121 | name: 'current', 122 | message: 'Publish ' + appName.yellow + ' version '.white + appDetails.version.yellow + '?'.white, 123 | default: true 124 | }); 125 | 126 | //Ask for new version to deploy 127 | promptHandler.onNext({ 128 | type: 'input', 129 | name: 'version', 130 | message: 'Select new version to use:'.white, 131 | default: newVersion, 132 | validate: function(versionInput) { 133 | if (versionInput.match('^(?:(\\d+)\.)(?:(\\d+)\.)(\\d+)$')) { 134 | return true; 135 | } else { 136 | return versionInput + ' is not a valid version'; 137 | } 138 | }, 139 | when: function(answers) { 140 | return !answers.current; 141 | } 142 | }); 143 | 144 | } else { 145 | Matrix.loader.stop(); 146 | console.log('App configuration error, please adjust it and try again'.yellow); 147 | console.error(err.message.red); 148 | } 149 | }); 150 | 151 | var localReadmeFileName = 'README.MD'; 152 | var remoteReadmeFileName = 'readme.md'; 153 | 154 | function onEnd(details) { 155 | debug('Finished packaging ', appName); 156 | var downloadFileName = details.version + '.zip'; 157 | details.file = fileUrl + '/' + appName + '/' + downloadFileName; 158 | async.parallel([ 159 | function(next) { 160 | if (hasReadme) { 161 | Matrix.helpers.getUploadUrl(remoteReadmeFileName, appName, 'text', function(err, readmeUploadUrl) { 162 | if (err) { 163 | next(err); 164 | } else { 165 | Matrix.helpers.uploadPackage(pwd + '/' + localReadmeFileName, readmeUploadUrl, function(err) { 166 | next(err); 167 | }); 168 | } 169 | }); 170 | } else { 171 | next(); 172 | } 173 | }, 174 | function(next) { 175 | Matrix.helpers.getUploadUrl(downloadFileName, appName, 'zip', function(err, uploadUrl) { 176 | if (!err) { 177 | Matrix.helpers.uploadPackage(destinationFilePath, uploadUrl, function(err) { 178 | next(err); 179 | }); 180 | } else { 181 | Matrix.loader.stop(); 182 | console.error(err); 183 | return process.exit(1); 184 | } 185 | }); 186 | } 187 | ], function(err) { 188 | if (err) { 189 | Matrix.loader.stop(); 190 | console.error(err.message); 191 | debug('Error:', err); 192 | process.exit(1); 193 | } 194 | 195 | var appData = Matrix.helpers.formAppData(details); 196 | if (details.config.hasOwnProperty('galleryUrl')) iconUrl = details.config.galleryUrl; 197 | 198 | if (hasReadme) { 199 | appData.meta.readme = fileUrl + '/' + appName + '/' + remoteReadmeFileName; 200 | debug('README URL: ' + appData.meta.readme); 201 | } 202 | 203 | Matrix.helpers.trackEvent('app-publish', { aid: appName }); 204 | //Listen for the app creation in appStore 205 | Matrix.firebase.appstore.watchForAppCreated(appName, function(app) { 206 | debug('app published>', app); 207 | var publicationTimer = setInterval(function() { 208 | if (publicationFinished) { 209 | clearTimeout(publicationTimer); 210 | console.log('Application ' + appName.green + ' published!'); 211 | endIt(); 212 | } else { 213 | debug('Publication not finished') 214 | } 215 | }, 400); 216 | }); 217 | 218 | //Send the app creation request 219 | var events = { 220 | error: function(err) { //error 221 | if (err.hasOwnProperty('details')) { 222 | console.log('App registration failed: '.red, err.details.error); 223 | } else { 224 | console.log('App registration failed: '.red, err.message); 225 | } 226 | debug(err); 227 | process.exit(); 228 | }, 229 | finished: function() { //finished 230 | Matrix.loader.stop(); 231 | console.log('App registered in appstore'); 232 | publicationFinished = true; 233 | }, 234 | start: function() { //start 235 | Matrix.loader.stop(); 236 | console.log('App registration formed...'); 237 | Matrix.loader.start(); 238 | }, 239 | progress: function() { //progress 240 | Matrix.loader.stop(); 241 | console.log('App registration in progress...'); 242 | Matrix.loader.start(); 243 | } 244 | }; 245 | 246 | Matrix.firebase.app.publish(Matrix.config.user.token, '', Matrix.config.user.id, appData, events); 247 | }); 248 | 249 | } 250 | 251 | function endIt() { 252 | setTimeout(function() { 253 | process.nextTick(function() { 254 | process.exit(0); 255 | }) 256 | }, 1000) 257 | } 258 | 259 | function displayHelp() { 260 | Matrix.loader.stop(); 261 | console.log('\n> matrix publish ¬\n'); 262 | console.log('\t matrix publish -', t('matrix.publish.help').grey, { app: '' }) 263 | console.log('\n') 264 | process.exit(1); 265 | } 266 | 267 | }); -------------------------------------------------------------------------------- /bin/matrix-sim.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var prompt = require('prompt'); 4 | var p = require('child_process'); 5 | var async = require('async'); 6 | var debug; 7 | 8 | async.series([ 9 | require('./matrix-init'), 10 | function (cb) { 11 | Matrix.loader.start(); 12 | debug = debugLog('sim'); 13 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 14 | }, 15 | Matrix.validate.userAsync 16 | ], function (err) { 17 | 18 | if (err) { 19 | Matrix.loader.stop(); 20 | console.error(err.message.red); 21 | debug('Error:', err.message); 22 | return process.exit(1); 23 | } 24 | 25 | if (!Matrix.pkgs.length || showTheHelp) return displayHelp(); 26 | var cmd = Matrix.pkgs[0]; 27 | 28 | // matrix sim init 29 | if (cmd === 'init') { 30 | 31 | if (_.has(Matrix.config, 'sim.id')) { 32 | Matrix.loader.stop(); 33 | console.log('\n' + t('matrix.sim.init.device_already_initialized') + '.'); 34 | console.log('\n' + t('matrix.sim.init.target_device') + ':\n'); 35 | console.log('matrix use %s'.grey, Matrix.config.sim.id, '\n'); 36 | process.exit(); 37 | } 38 | 39 | // make sure device name, device id and userId are available 40 | var deviceId = 'sim-' + _.times(24, function() { 41 | return Math.round(Math.random() * 16).toString(16) 42 | }).join(''); 43 | 44 | debug(deviceId); 45 | 46 | prompt.delimiter = ''; 47 | prompt.message = [t('matrix.sim.init.specify_data_for_init') + '\n']; 48 | Matrix.loader.stop(); 49 | prompt.start(); 50 | prompt.get(['name', 'description'], function(err, inputs) { 51 | if (err) { 52 | if (err.toString().indexOf('canceled') > 0) { 53 | console.log(''); 54 | process.exit(); 55 | } else { 56 | console.log("Error: ", err); 57 | process.exit(); 58 | } 59 | } 60 | //TODO Probably need to adjust this later 61 | // check for dupe name, note, this requires matrix list devices to have run 62 | 63 | _.each(Matrix.config.deviceMap, function(d) { 64 | if (inputs.name === d.name) { 65 | console.error(d.name, ' ' + t('matrix.sim.init.device_is_already_used')); 66 | process.exit(); 67 | } 68 | }); 69 | 70 | console.log(t('matrix.sim.init.creating_device') + ' ', inputs, '[' + deviceId + ']') 71 | Matrix.loader.start(); 72 | var deviceObj = { 73 | type: 'matrix', 74 | osVersion: 'sim', 75 | version: require(__dirname + '/../package.json').version, 76 | name: inputs.name, 77 | description: inputs.description, 78 | hardwareId: deviceId 79 | }; 80 | 81 | Matrix.firebaseInit(function() { 82 | debug('Firebase init passed'); 83 | 84 | var events = { 85 | error: function(err) { 86 | Matrix.loader.stop(); 87 | console.log('Error creating device '.red + deviceObj.name.yellow + ': '.red, err); 88 | process.exit(); 89 | }, 90 | finished: function() { 91 | Matrix.loader.stop(); 92 | console.log('Device registered succesfuly'); 93 | 94 | Matrix.config.sim = { 95 | token: Matrix.config.user.token, 96 | id: deviceId 97 | } 98 | 99 | Matrix.helpers.saveConfig(function() { 100 | console.log(t('matrix.sim.init.success').green) 101 | console.log('\n' + t('matrix.sim.init.to_target_device') + ':\n'); 102 | console.log('matrix use %s'.grey, Matrix.config.sim.id, '\n'); 103 | process.exit(); 104 | }); 105 | }, 106 | start: function() { 107 | Matrix.loader.stop(); 108 | console.log('Device registration request formed...'); 109 | Matrix.loader.start(); 110 | }, 111 | progress: function() { 112 | Matrix.loader.stop(); 113 | console.log('Registering device...'); 114 | Matrix.loader.start(); 115 | } 116 | }; 117 | 118 | Matrix.firebase.device.add(deviceObj, events); 119 | }); 120 | 121 | /* 122 | Matrix.api.device.create(deviceObj, function (err, device) { 123 | if (err) return console.error(t('matrix.sim.init.error_creating_device'), err); 124 | debug('Create Device:', device); 125 | Matrix.api.device.register(deviceObj.deviceId, function (err, results) { 126 | if (err) { 127 | if (err.status_code === 401) { 128 | return console.error(t('matrix.sim.init.invalid_session') + '. ' + t('matrix.sim.init.please') + ', ' + 'matrix login'.grey, ' ' + t('matrix.sim.init.and') + ' ', 'matrix use', deviceId); 129 | } 130 | return console.error(t('matrix.sim.init.register_error'), err); 131 | } 132 | debug(results); 133 | // local config 134 | Matrix.config.sim = { 135 | token: results, 136 | id: deviceId 137 | } 138 | Matrix.helpers.saveConfig(); 139 | 140 | console.log(t('matrix.sim.init.success').green) 141 | console.log('\n' + t('matrix.sim.init.to_target_device') + ':\n'); 142 | console.log('matrix use %s'.grey, Matrix.config.sim.id, '\n'); 143 | }); 144 | });*/ 145 | }); 146 | 147 | } else if (cmd === 'start') { 148 | var option = Matrix.pkgs[1]; 149 | 150 | if (!Matrix.config.hasOwnProperty('sim')) { 151 | return console.log('matrix sim init'.grey + ' ' + t('matrix.sim.start.before_message')) 152 | } 153 | if (Matrix.config.device.identifier.indexOf('sim-') !== 0) { 154 | return console.log(t('matrix.sim.start.device') + ' ', Matrix.config.device.identifier, ' ' + t('matrix.sim.start.not_a_virtual_device') + ' ' + '`matrix sim init`'.grey + t('matrix.sim.start.and') + '`matrix use`'.grey + '.') 155 | } 156 | 157 | if (Matrix.config.sim.custom === true) { 158 | console.log(t('matrix.sim.start.custom_image_detected').grey + '...'.grey) 159 | } 160 | 161 | if (option === 'debug') { 162 | console.log(t('matrix.sim.start.debug_on')); 163 | } 164 | // TODO: abstract away docker machine? 165 | 166 | //test for docker 167 | checkDocker(); 168 | 169 | var cmd = 'docker run ' + [ 170 | // show debug if `matrix sim start debug` 171 | (option === 'debug') ? '-e DEBUG=*' : '', 172 | // TODO: add NODE_ENV and options for local, dev, stage and prod 173 | '-e NODE_ENV=dev', 174 | '-e MATRIX_DEVICE_ID="' + Matrix.config.device.identifier + '"', 175 | '-e MATRIX_USERNAME="' + Matrix.config.user.username + '"', 176 | 'admobilize/matrix-os' + 177 | ((_.get(Matrix.config, 'sim.custom') === true) ? ':custom' : ':latest') 178 | ].join(' '); 179 | 180 | debug(cmd); 181 | var proc = require('child_process').exec(cmd, {}, function(err, out, stderr) { 182 | // if (err) console.error('ERROR'.red, err); 183 | if (stderr) { 184 | console.error(t('matrix.sim.start.error').red, stderr); 185 | if (stderr.indexOf('Cannot connect to the Docker daemon.') > -1) { 186 | console.log(t('matrix.sim.please') + ' `docker-machine start matrix`. ' + t('matrix.sim.start.then') + ' `eval $(docker-machine env matrix)`. ' + t('matrix.sim.start.if_different_name') + '.') 187 | } 188 | } 189 | console.log(out); 190 | }); 191 | 192 | console.log(t('matrix.sim.start.starting_sim') + ' [', Matrix.config.device.identifier, ']', '\n ' + t('matrix.sim.start.stop_with') + ' matrix sim stop'.grey); 193 | 194 | proc.stdout.on('data', function(data) { 195 | console.log(data); 196 | }); 197 | 198 | proc.stderr.on('data', function(data) { 199 | console.log('DEBUG'.green, data); 200 | }); 201 | 202 | 203 | } else if (cmd.match(/restore|upgrade/)) { 204 | checkDocker(); 205 | 206 | console.log('Downloading latest MatrixOS image.') 207 | 208 | var cmd = 'docker pull admobilize/matrix-os:latest'; 209 | 210 | var proc = require('child_process').exec(cmd, {}, function(err, out, stderr) { 211 | if (stderr) console.error(t('matrix.sim.start.error').red, stderr); 212 | }) 213 | 214 | proc.stdout.on('data', function(data) { 215 | console.log('stdout', data); 216 | }) 217 | 218 | if (Matrix.config.hasOwnProperty('sim')) { 219 | Matrix.config.sim.custom = false; 220 | } 221 | 222 | Matrix.helpers.saveConfig(); 223 | 224 | //matrix sim save 225 | } else if (cmd === 'save') { 226 | checkDocker(); 227 | 228 | 229 | runDockerCmd('commit ' + getContainerId() + ' admobilize/matrix-os:custom'); 230 | 231 | Matrix.config.sim.custom = true; 232 | Matrix.helpers.saveConfig(); 233 | console.log(t('matrix.sim.save.state_saved').blue) 234 | 235 | } else if (cmd === 'clear' || cmd === 'reset') { 236 | Matrix.config.sim = {}; 237 | Matrix.helpers.saveConfig(); 238 | console.log(t('matrix.sim.clear.simulation_cleared').blue) 239 | 240 | } else if (cmd === 'ssh') { 241 | 242 | var lastDockerId = p.execSync('docker ps -q | head -n1 | tr "\n" " "') 243 | var ssh = p.spawn('docker exec -it ' + lastDockerId + ' bash', { shell: true }); 244 | ssh.stdout.on('data', console.log) 245 | ssh.stderr.on('data', console.log) 246 | } else if (cmd === 'stop') { 247 | // find processes by Name 248 | var stopList = p.execSync('docker ps | grep admobilize/matrix-os').toString().substr(0, 12); 249 | console.log(stopList); 250 | p.exec('docker stop ' + stopList, function(err) { 251 | if (err) console.error(err); 252 | console.log(t('matrix.sim.stop.sim_stopped')) 253 | }) 254 | } else { 255 | showHelp(); 256 | } 257 | 258 | 259 | function getContainerId() { 260 | var id = require('child_process').execSync('docker ps -q --filter "ancestor=admobilize/matrix-os"'); 261 | return id.toString().trim(); 262 | } 263 | 264 | function runDockerCmd(cmd) { 265 | log(cmd); 266 | var proc = require('child_process').exec('docker ' + cmd, {}, function(err, out, stderr) { 267 | if (stderr) console.error('ERROR'.red, stderr); 268 | }) 269 | 270 | proc.stdout.on('data', function(data) { 271 | console.log(data); 272 | }) 273 | } 274 | 275 | function checkDocker() { 276 | try { 277 | var proc = require('child_process').execSync('which docker', { 278 | stdio: [null, null, null] 279 | }); 280 | } catch (e) { 281 | if (e.toString().indexOf('Command failed') > -1) { 282 | console.error(t('matrix.sim.docker.docker_not_found') + '.'.red, '\n' + t('matrix.sim.docker.install_from') + ' https://docs.docker.com/engine/installation/') 283 | 284 | console.error(t('matrix.sim.docker.then') + ' `docker-machine create --driver virtualbox matrix`'.grey) 285 | process.exit(1); 286 | } 287 | } 288 | } 289 | 290 | function displayHelp() { 291 | Matrix.loader.stop(); 292 | console.log('\n> matrix sim ¬\n'); 293 | console.log('\t matrix sim upgrade -', t('matrix.sim.help_upgrade').grey) 294 | console.log('\t matrix sim restore -', t('matrix.sim.help_restore').grey) 295 | console.log('\t matrix sim init -', t('matrix.sim.help_init').grey) 296 | console.log('\t matrix sim start -', t('matrix.sim.help_start').grey) 297 | console.log('\t matrix sim save -', t('matrix.sim.help_save').grey) 298 | console.log('\t matrix sim clear -', t('matrix.sim.help_clear').grey) 299 | console.log('\n') 300 | process.exit(1); 301 | } 302 | }); -------------------------------------------------------------------------------- /config/locales/es.yml: -------------------------------------------------------------------------------- 1 | es: 2 | matrix: 3 | already_login: 'Sesión ya iniciada como:' 4 | api_title: API 5 | streaming_title: Streaming 6 | user_title: Usuario 7 | device_title: Dispositivo 8 | locale_title: Localización 9 | help_login: Iniciar sesión en la plataforma del MatrixOS 10 | help_register: Crea un usuario en la plataforma MatrixOS 11 | help_register_device: registrar nuevo dispositivo 12 | help_account: Maneja los detalles de la cuenta de usuario 13 | help_account_profile: actualizar la información del perfil de usuario 14 | help_logout: Cerrar la sesión en la plataforma MatrixOS 15 | help_use: Indica el dispositivo activo 16 | help_set: 'Establece la configuración de ambiente, actualiza la configuración de una aplicación, cambia la localización del cli' 17 | help_sim: Maneja el simulador de MatrixOS local usando Docker 18 | help_list: 'Información acerca de los dispositivos, aplicaciónes e instalaciones' 19 | help_reboot: Reinicia el dispositivo actual MatrixOS 20 | help_search: Busca aplicaciones 21 | help_install: Instala una aplicación o sensor en el dispositivo MatrixOS activo 22 | help_install_default: app por defecto 23 | help_config: Ve y modifica la configuración de una aplicación 24 | help_uninstall: Desisntala una aplicación 25 | help_update: Actualiza a una versión específica de una aplicación de MatrixOS 26 | help_start: Inicia una aplicación en el dispositivo MatrixOS 27 | help_stop: Detiene una aplicación corriendo en el MatrixOS activo 28 | help_restart: Reinicia una aplicación corriendo en el MatrixOS 29 | help_create: Crea un nuevo andamiaje para una aplicación de MatrixOS 30 | help_deploy: Sube y despliega una aplicación al dispositivo MatrixOS activo 31 | help_trigger: Ejecuta una prueba de disparador 32 | help_log: Loguea la salida del dispositivo MatrixOS y las aplicaciones seleccionadas 33 | help_ping: Luces de activación en el dispositivo seleccionado 34 | help_remove: Eliminar un dispositivo de su cuenta y eliminar todos los datos 35 | help_upgrade: Actualizar CLI en su computadora local 36 | please_login: 'No se ha iniciado sesión con un usuario, Por favor' 37 | set_device: 'No se ha seleccionado un dispositivo. Usa' # `matrix use` 38 | validate: 39 | no_device: No se ha selecionado un dispositivo 40 | select_device_id: selecciona el id de un dispositivo' 41 | #SETUP: 42 | login: 43 | already_login_warning: 'Ya hay una cuenta registrada' # 44 | user_auth_error: 'Error en la autenticación' 45 | login_success: 'Sesión iniciada correctamente' 46 | logout: 47 | logout_success: 'Sesión cerrada correctamente' 48 | use: 49 | using_device_by_name: 'Ahora usando el dispositivo' 50 | using_device_by_id: 'Ahora usando el dispositivo con id' 51 | not_authorized: 'No autorizado' 52 | device_not_found: El dispositivo no existe' 53 | invalid_token: 'Tu token es invalido' 54 | invalid_nameid: 'No es un nombre de dispositivo ni un id de dispositivo' 55 | try: 'Intenta' 56 | command_help: 'establece como dispositivo activo el dispositivo con id' 57 | set: 58 | help_device: 'configurar el ambiente del dispositivo' 59 | help_config: 'configurar las variables de aplicacion' 60 | help_locale: 'establecer la localización actual de cli' 61 | env: 62 | env: 'Ambiente' 63 | valid_environments: 'Ambientes válidos' 64 | config: 65 | use: Usa 66 | no_app: 'Nombre de la app requerido' 67 | invalid_key_value: Llave y valor invalido # 68 | no_key_value: 'Debe ingresar llave y valor' 69 | locale: 70 | locale_required: 'Localización requerida' 71 | locale: 'Localización' 72 | valid_locales: 'Localizaciónes válidas' 73 | #MANAGEMENT: 74 | sim: 75 | unknowm_parameter: Parametro no existe # 76 | command_help_sim: matrix sim # 77 | please: 'Por favor' 78 | before_using: 'antes de que intente utilizar el simulador' 79 | init: 80 | specify_data_for_init: Por favor ingrese la información para este dispositivo virtual # 81 | warning_sim_init: please use ´matrix sim init´ # 82 | device_already_initialized: Ya se inicio un dispositivo virtual 83 | target_device: Referencia a este dispositivo 84 | device_is_already_used: Ya esta en uso 85 | creating_device: Creando 86 | error_creating_device: Error al crear 87 | invalid_session: Sesión invalida 88 | please: Por favor 89 | and: 'y' 90 | register_error: Error de registro 91 | success: exito 92 | to_target_device: Referencia a este dispositivo 93 | start: 94 | before_message: Primero por favor 95 | device: Dispositivo 96 | not_a_virtual_device: No es un MatrixOS virtual. Por favor 97 | and: 'y' 98 | custom_image_detected: Simulador de imagen personalizada detecta 99 | debug_on: Modo de depuracion 100 | error: ERROR 101 | please: Por favor 102 | then: Entonces 103 | if_different_name: "Si usted tiene un nombre de maquina diferente, use" 104 | starting_sim: MatrixOS Simulador iniciado 105 | stop_with: Detenga con Ctrl + C 106 | restore: 107 | downloading_image: Descargando ultima imagen de MatrixOS 108 | save: 109 | state_saved: Estado de simulacion guardados 110 | clear: 111 | simulation_cleared: Simulador limpio 112 | stop: 113 | sim_stopped: Simulador matrix detenido 114 | docker: 115 | docker_not_found: Docker no encontrado 116 | install_from: Por favor instale docker de 117 | then: Entonces 118 | help_upgrade: Actualizando la imagen de su simulador 119 | help_restore: Reinciando su simulador 120 | help_init: Iniciar su simulador 121 | help_start: Ambiente Virtual MatrixOS 122 | help_save: "Estado guardado de MatrixOS, use despues deploy / install" 123 | help_clear: Eliminar la simulacion de datos local 124 | list: 125 | list_devices: Lista de dispositivos # 126 | list_groups: Lista de grupos # 127 | list_apps: Lista de aplicaciones # 128 | list_all: Lista de todo # 129 | app_list_error: 'Error en la lista de apps' 130 | no_results: 'No se encontraron resultados' 131 | help_devices: 'mostrar dispositivos disponibles' 132 | help_groups: 'mostrar grupos de dispositivos' 133 | help_apps: 'mostrar aplicaciones en el dispositivo actual' 134 | help_all: 'mostrar todos los dispositivos con aplicaciones instaladas' 135 | reboot: 136 | device_offline: Dispositivo no esta online #  137 | rebooting: 'Reinciando dispositivo' 138 | rebooted: 'Dispositivo reinciando' # 139 | 140 | #APPS: 141 | search: 142 | help: busca aplicaciones de MATRIX 143 | small_needle: Tu aguja es muy pequeña para buscarla en nuestro pajar 144 | search_successfully: Resultados de búsqueda # 145 | no_results: No se encontraron coincidencias # 146 | install: 147 | installing: Instalando 148 | app_installed: Se instaló la aplicación 149 | sensor_installed: sensor instalado 150 | command_help: instala # [ app / sensor ] # 151 | app_install_error: La instalación de la aplicación falló # 152 | sensor_install_error: La instalación del sensor falló # 153 | app_not_found: La aplicación no existe # 154 | sensor_not_found: El sensor no existe # 155 | config: 156 | config_usage_command: 'advertencias' # 157 | device_config: 'Configuración para el dispositivo' 158 | show_config_app1: 'configuration the apliccation' # 159 | help: traer las configuraciones del dispositivo 160 | help_app: traer las configuraciones de la aplicación 161 | help_app_key: traer la llave de la configuración de la aplicación 162 | help_app_key_value: configurar el valor y la llave a la aplicación 163 | show_config_app: 'configuracion de aplicacion ' # 164 | specified_application_does_not_exist: 'la aplicacion especificada no existe' # 165 | specify_key: Especifique una llave # 166 | key_doesnt_exist: La llave especificada no existe # 167 | key_value: 'Valor:' # 168 | uninstall: 169 | uninstalled: 'Se desinstaló' 170 | application_unspecified: 'especifique [ aplicacion ] ' # 171 | app_undefined: 'esta aplicacion no existe' # 172 | device_offline: Dispositivo desconectado, Por favor reconecte el dispositivo # 173 | update: 174 | upgrading_to: 'Actualizando a' 175 | latest_version: 'versión mas reciente' 176 | of: 'de' 177 | help_update: 'actualizar el software en el dispositivo activo' 178 | help_update_app: 'actualizar una aplicación a la versión mas reciente' 179 | help_update_app_version: 'actualizar una aplicación a una versión específica' 180 | app_undefined: 'La aplicación no existe' # 181 | app_update_successfully: 'Aplicación actualizada a la versión mas reciente' # 182 | version_undefined: 'Versión de aplicación no soportada' # 183 | version_doesnt_exist: Esta version no existe # 184 | version_update_successfully: 'Aplicación actualizada con éxito a' # 185 | error_updating: Error actualizando # 186 | start: 187 | application_unspecified: 'especifique [ app(name) ] ' # 188 | app_undefined: 'Esta aplicación no existe' # 189 | starting_app: 'Iniciando' 190 | start_app_successfully: 'Iniciada' 191 | app_failed_to_start: 'Fallo al iniciar la aplicación' # 192 | stop: 193 | application_unspecified: 'especifique [ aplicacion(nombre) ] ' # 194 | app_undefined: 'Esta aplicacion no existe' # 195 | stopping_app: 'Deteniendo' 196 | stop_app_successfully: 'Detenida' 197 | restart: 198 | application_unspecified: 'especifique [ aplicacion(nombre) ] ' # 199 | app_undefined: 'este dispositivo no existe' # 200 | restarting_app: 'Reiniciando' 201 | restart_app_successfully: 'Reiniciada' 202 | restart_app_error: 'Fallo a reiniciar la aplicación' 203 | #DEVELOPMENT 204 | create: 205 | name_undefined: 'Debe especificar un nombre de aplicación' 206 | new_folder: Nueva carpeta 207 | error_creating: Ha ocurrido un error 208 | description_app: esta es la logica de tu aplicación 209 | description_config: 'cambia variables, indica sensores, configura el dashboard' 210 | description_developer: información acerca de desarrollar aplicaciones MATRIX 211 | description_index: 'punto de entrada de la aplicación, no modificar' 212 | description_package: 'archivo de información de node.js, no modificarlo sin conocimiento' 213 | deploy: 214 | # command_usage: deploy use # 215 | app_not_found: 'No se encontró el archivo %{detect_file}. Estás seguro que una aplicación MatrixOS reside en %{pwd}?' 216 | folders_not_supported: 'Culpa nuestra. Añun no se puede hacer despliegue de directorios' 217 | cancel_deploy: 'SE cancela el despliegue debido a errores y advertencias' 218 | reading: Leyendo 219 | writing: Escribiendo 220 | error: Ha ocurrido un error 221 | deploy_error: Error de despliegue 222 | deply_started: Se inició el despliegue 223 | stream_error: Error en el flujo de despliegue 224 | deploy_complete: Despliegué finalizado 225 | bad_payload: Información corrupta en el contenido del despliegue 226 | app_install_failed: Fallo en la instalación de la aplicación 227 | app_installed: Aplicación instalada 228 | application_unspecified: 'especifique [ aplicacion ]' #? 229 | deploy_app_successfully: 'despliegue de la aplicacion satisfactoria' #? 230 | app_undefined: 'aplicacion indefinida' #? 231 | publish: 232 | help: Publica la aplicacion aprobada %{app} en la tienda de aplicaciones 233 | unpublish: 234 | unpublishing: Cancelar la publicacion # 235 | help_app: Anula la publicacion de la aplicacion %{app} de la tienda de aplicaciones # 236 | trigger: 237 | no_specified_test: 'prueba indefinida' #? 238 | defined_test_doesn`t_exist: 'la prueba especificada no existe' #? 239 | run_trigger_successfully: 'corriendo prueba' #? 240 | log: 241 | app_not_select: 'seleccione una aplicacion `matrix log app-nombre` ' #? 242 | app_undefined: 'la aplicacion especificada no existe' #? 243 | logs_show: 'logs' #? 244 | helpers: 245 | adding: Agregando 246 | config_error: Error de configuración 247 | config_save_error: Error guardando la configuración 248 | config_parse_error: 'Configuration parsing Error' 249 | apps: 250 | name: Nombre 251 | description: Descripción 252 | version: v 253 | shortname: Nombre corto 254 | devices: 255 | device_id: ID de dispositivo 256 | name: Nombre 257 | ok: ok 258 | description: Descripción 259 | last_online: Última sesión 260 | groups: 261 | name: Nombre 262 | devices: "# Dispositivos" 263 | group_id: ID de grupo 264 | search: 265 | installs: +1s 266 | name: Nombre 267 | description: Descriptción 268 | short_name: Nombre Corto 269 | latest: Mas reciente 270 | device_apps: 271 | device: Dispositivo 272 | app_name: Nombre de aplicación 273 | description: Descripción 274 | -------------------------------------------------------------------------------- /lib/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lib/app.js 3 | */ 4 | var program = require('commander'); 5 | var _ = require('lodash'); 6 | var fs = require('fs'); 7 | var async = require('async'); 8 | var debug = debugLog('sdk') 9 | var npmVersion = require('package-json'); 10 | //Matrix.latestVersion; //Latest npm matrix-cli version is stored here 11 | 12 | async.series([ 13 | function (next) { //Fetch latest npm version 14 | npmVersion('matrix-cli', 'latest').then(function (package) { 15 | Matrix.latestVersion = package.version; 16 | next(); 17 | }).catch(next); 18 | }, 19 | async.apply(Matrix.localization.init, Matrix.localesFolder, Matrix.config.locale), //i18n initialization 20 | function () { //Main flow 21 | var t = Matrix.localization.get; 22 | 23 | // base cmd 24 | // 25 | // program 26 | // .version(Matrix.version) 27 | // .description(colors.yellow( 28 | // '\t Anyway this cake is great\n\t It\'s so delicious and moist' + 29 | // '\n\n' + 30 | // '\t ,:/+/-\n' + 31 | // '\t /M/ .,-=;//;-\n' + 32 | // '\t .:/= ;MH/, ,=/+%$XH@MM#@:\n' + 33 | // '\t -$##@+$###@H@MMM#######H:. -/H#\n' + 34 | // '\t .,H@H@ X######@ -H#####@+- -+H###@x\n' + 35 | // '\t .,@##H; +XM##M/, =%@###@X;-\n' + 36 | // '\tX%- :M##########$. .:%M###@%:\n' + 37 | // '\tM##H, +H@@@$/-. ,;$M###@%, -\n' + 38 | // '\tM###M=,,---,.-%%H####M$: ,+@##\n' + 39 | // '\t@##################@/. :%##@$-\n' + 40 | // '\tM################H, ;HM##M$=\n' + 41 | // '\t##################. .=$M##M$=\n' + 42 | // '\t#################H..;XM##M$= .:+\n' + 43 | // '\tM####################@%= =+@MH%\n' + 44 | // '\t@#################M/. =+H#X%=\n' + 45 | // '\t=+M###############M, -/X#X+;.\n' + 46 | // '\t .;XM###########H= ,/X#H+:,\n' + 47 | // '\t .=+HM#######M+/+HM@+=.\n' + 48 | // '\t ,:/%XM####H/.\n' + 49 | // '\t ,.:=-.' 50 | // )); 51 | // 52 | 53 | program.command('Setup') 54 | program.command('register', t('matrix.help_register').grey + '\n[device] - ' + t('matrix.help_register_device')); 55 | program.command('account', t('matrix.help_account').grey + '\n[profile] - ' + t('matrix.help_account_profile')); 56 | program.command('remove', t('matrix.help_remove').grey); 57 | program.command('login', t('matrix.help_login').grey); 58 | program.command('logout') 59 | .description(t('matrix.help_logout')) 60 | .action(function () { 61 | Matrix.validate.userAsync(function (err) { 62 | if (err) { 63 | debug('Error:', err.message); 64 | return process.exit(1); 65 | } 66 | 67 | Matrix.helpers.trackEvent('user-logout'); 68 | Matrix.helpers.logout(function () { 69 | console.log(t('matrix.logout.logout_success').green); 70 | process.exit(0); 71 | }); 72 | }); //Make sure the user has logged in 73 | }) 74 | 75 | program.command('upgrade') 76 | .description(t('matrix.help_upgrade')) 77 | .action(function () { 78 | console.log('To Upgrade MATRIX CLI, please run\n\n', 'npm update -g matrix-cli\n'.grey); 79 | }) 80 | 81 | //TODO : Add CREATE ACCOUNT command; 82 | 83 | 84 | // indicate subcommands or usage information using \n 85 | program.command('use', t('matrix.help_use').grey); 86 | program.command('set [target]', t('matrix.help_set') + '\n( env (production|dev), locale (es|en) )'.grey) 87 | program.command('Management') 88 | // program.command('sim [target]', t('matrix.help_sim') + '\n[ init, restore, start, stop, save, clear ]'); 89 | program.command('list [target]', t('matrix.help_list') + '\n( apps, devices, all ) '.grey) 90 | 91 | /** Update the App or the Matrix **/ 92 | // program 93 | // .command('reboot') 94 | // .description(t('matrix.help_reboot')) 95 | // .action(function() { 96 | // Matrix.validate.user(); //Make sure the user has logged in 97 | // Matrix.validate.device(); //Make sure the user has logged in 98 | 99 | // Matrix.helpers.trackEvent('device-reboot', { did: Matrix.config.device.identifier }); 100 | 101 | // Matrix.api.device.reboot(Matrix.config.device.identifier, function(err) { 102 | // console.log(t('matrix.reboot.rebooting') + '...'); 103 | // process.exit(); 104 | // }); 105 | // }); 106 | 107 | 108 | // HEY DEVELOPER - THIS ONE IS USEFUL 109 | // Hidden in help output 110 | program.command('debug').description('list out configuration object') 111 | .action(function () { 112 | console.log(Matrix.config) 113 | }); 114 | 115 | program.command('Apps'); 116 | program.command('search', t('matrix.help_search').grey); 117 | program.command('install', t('matrix.help_install')) 118 | program.command('uninstall', t('matrix.help_uninstall'), ' ') 119 | program.command('config', t('matrix.help_config')) 120 | program.command('update', t('matrix.help_update')) 121 | program.command('start', t('matrix.help_start')); 122 | program.command('stop', t('matrix.help_stop')); 123 | program.command('restart', t('matrix.help_restart')); 124 | 125 | program.command('development'); 126 | 127 | // 128 | /** App management **/ 129 | program.command('create [app]', t('matrix.help_create')) 130 | 131 | // // Run from current directory 132 | program.command('deploy [dir]', t('matrix.help_deploy')) 133 | program.command('publish [version]', '(name) Publishes a version of the App to the store.') 134 | program.command('unpublish ', '(name) unpublish an App from the store') 135 | program 136 | .command('trigger [string]') 137 | .description(t('matrix.help_trigger')) 138 | .action(function (data) { 139 | 140 | async.series([ 141 | Matrix.validate.userAsync, 142 | Matrix.validate.deviceAsync, 143 | async.apply(Matrix.api.app.trigger, Matrix.config.device.identifier, data) 144 | ], function (err) { 145 | if (err) { 146 | console.error(err.message.red); 147 | debug('Error:', err); 148 | } 149 | endIt(); 150 | }); 151 | }) 152 | 153 | program 154 | .command('ping') 155 | .description(t('matrix.help_ping')) 156 | .action(function () { 157 | 158 | async.series([ 159 | Matrix.validate.userAsync, 160 | Matrix.validate.deviceAsync, 161 | async.apply(Matrix.api.app.trigger, Matrix.config.device.identifier, 'amazing-matrix-ping') 162 | ], function (err) { 163 | // bail on null, null has no string coloring 164 | if (!_.isNull(err) && err) { 165 | console.error(err.message.red); 166 | debug('Error:', err); 167 | } 168 | return endIt(); 169 | }); 170 | 171 | }) 172 | 173 | // /** Matrix - In App - Uninstall **/ 174 | program 175 | .command('log') 176 | .option('-f, --follow', 'Follow the log') 177 | .description(t('matrix.help_log')) 178 | .action(function (option) { 179 | 180 | async.series([ 181 | Matrix.validate.userAsync, 182 | Matrix.validate.deviceAsync 183 | ], function (err) { 184 | if (err) { 185 | console.error(err.message.red); 186 | debug('Error:', err); 187 | return endIt(); 188 | } 189 | 190 | var options = {}; 191 | options.deviceId = Matrix.config.device.identifier; 192 | 193 | var eq = '='; 194 | for (var i = 1; i < process.stdout.columns; i++) { 195 | eq += '='; 196 | } 197 | 198 | console.log(eq.grey); 199 | 200 | Matrix.api.app.log(options, function (err, log) { 201 | if (err) console.error(err); 202 | if (_.has(log, 'payload')) console.log(log.payload); 203 | if (!option.follow) { 204 | // process.exit(); 205 | } 206 | }); 207 | }); 208 | }); 209 | 210 | program 211 | .command('validate') 212 | .action(function (appName) { 213 | var yaml = require('js-yaml'); 214 | try { 215 | process.env.DEBUG = '*'; 216 | var configRaw = fs.readFileSync(process.cwd() + '/' + appName + '/config.yaml'); 217 | var config = yaml.safeLoad(configRaw); 218 | Matrix.validate.config(config); 219 | } catch (e) { 220 | console.error(e); 221 | } 222 | }); 223 | // 224 | program.command('track').action(function (event) { 225 | Matrix.helpers.trackEvent('test-event', { aid: event }, function () { 226 | console.log(arguments); 227 | }); 228 | }); 229 | 230 | // 231 | // debug(process, '================', program); 232 | 233 | 234 | 235 | // Main Programs 236 | var commandList = _.map(program.commands, function (p) { 237 | return p._name; 238 | }); 239 | 240 | 241 | // strip out -h / --help 242 | _.pull(process.argv, ['-h', '--h', '--help']); 243 | 244 | // nonsensically complicated way to override the help text 245 | if (process.argv.length === 2 || commandList.indexOf(process.argv[2]) === -1) { 246 | 247 | var updateMessage = ''; 248 | if (!_.isUndefined(Matrix.latestVersion) && Matrix.latestVersion != Matrix.version) { 249 | updateMessage = 'v' + Matrix.latestVersion + ' available!'; 250 | updateMessage = ' (' + updateMessage.yellow + ')'; 251 | } 252 | if (_.has(Matrix.config, 'environment.name')) { 253 | var env = ' - '.grey + Matrix.config.environment.name.grey; 254 | } 255 | 256 | console.log('\n') 257 | console.log(_.repeat(' ', 10), '_ _ ____ ___ ____ _ _ _') 258 | console.log(_.repeat(' ', 10), '|\\/| |__| | |__/ | \\/ _ |', '[o]'.grey) 259 | console.log(_.repeat(' ', 10), '| | | | | | \\ | _/\\_ |_ |_ |', 260 | 'v'.grey + Matrix.version.grey + updateMessage, (env || '')); 261 | 262 | 263 | var log = []; 264 | 265 | // TODO: Should we start with API? only if not logged in 266 | log.push(t('matrix.api_title').grey + ': '.grey + Matrix.options.apiUrl, t('matrix.streaming_title').grey + ': '.grey + Matrix.options.mxssUrl); 267 | 268 | 269 | if (!_.isEmpty(Matrix.config.user)) { 270 | log.push('\n' + t('matrix.user_title').grey + ':'.grey, Matrix.config.user.username); 271 | } else { 272 | log.push('\n' + t('matrix.user_title').grey + ':'.grey, t('matrix.no_user_message').yellow); 273 | } 274 | if (!_.isEmpty(Matrix.config.device)) { 275 | log.push(t('matrix.device_title').grey + ':'.grey, Matrix.config.device.identifier); 276 | } else { 277 | log.push(t('matrix.device_title').grey + ':'.grey, 'No device selected'.yellow); 278 | } 279 | 280 | if (!_.isEmpty(Matrix.config.locale)) { 281 | log.push(t('matrix.locale_title').grey + ':'.grey, Matrix.config.locale); 282 | } 283 | 284 | if (!_.isUndefined(Matrix.config.environment.name)) { 285 | log.push(t('matrix.env_title').grey + ':'.grey, Matrix.config.environment.name); 286 | } 287 | 288 | // if (!_.isUndefined(Matrix.version)) { 289 | // log.push(t('matrix.v_title').grey + ':'.grey, Matrix.version); 290 | // } 291 | 292 | 293 | // TODO: add support for active group 294 | 295 | _.each(program.commands, function (c) { 296 | var subcommands; 297 | var command = c._name; 298 | var desc = c._description || ''; 299 | 300 | if (command.match(/debug|validate|track/)) return; 301 | 302 | var pad = 14 - command.length; 303 | 304 | // break off sub commands 305 | desc = desc.split('\n'); 306 | if (desc.length > 1) subcommands = desc[1]; 307 | 308 | // restore original string 309 | desc = desc[0]; 310 | 311 | if (_.isEmpty(desc)) { 312 | // section markers 313 | var l = command.length; 314 | var r = Math.floor((14 - l) / 2); 315 | console.log('\n', _.repeat('-', r).grey, command.toUpperCase(), _.repeat('-', r).grey) 316 | } else { 317 | console.log(_.repeat(' ', pad), command, '-', desc.grey); 318 | if (!_.isUndefined(subcommands)) console.log(_.repeat(' ', pad + command.length), ' ↳', subcommands.grey); 319 | } 320 | }); 321 | 322 | console.log('\n\n', log.join(' '.blue)); 323 | 324 | if (process.argv.length === 3) { 325 | var snark = [ 326 | 'You\'re not very good at this.', 327 | 'Try again. Little train right?', 328 | 'You can DO THIS.', 329 | 'Above is some helpful reference material.', 330 | 'Have you been sleeping at your desk?', 331 | 'Your finger must have slipped.', 332 | 'There ARE alot of words to remember.', 333 | 'Have you had your coffee yet?', 334 | 'Don\'t let your dreams be dreams!', 335 | // jeje matrix snark 336 | 'Difference between knowing and walking the path.' 337 | ] 338 | console.log('\n', process.argv[2].toString().yellow, 'is'.grey, 'NOT'.red, 'a valid command.'.grey, _.sample(snark).grey); 339 | } 340 | 341 | console.log(); 342 | 343 | // override the built in help 344 | process.argv.push('donthelp') 345 | 346 | } 347 | 348 | program.parse(process.argv); 349 | 350 | function endIt() { 351 | setTimeout(function () { 352 | process.nextTick(function () { 353 | process.exit(0); 354 | }) 355 | }, 1000) 356 | } 357 | } 358 | 359 | ]); 360 | -------------------------------------------------------------------------------- /bin/matrix-register.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var prompt = require('prompt'); 4 | var async = require('async'); 5 | var debug; 6 | 7 | async.series([ 8 | require('./matrix-init'), 9 | function (cb) { 10 | Matrix.loader.start(); 11 | debug = debugLog('register'); 12 | Matrix.localization.init(Matrix.localesFolder, Matrix.config.locale, cb); 13 | }, 14 | // basic register doesn't use firebase, and will login automatically 15 | handleBasicRegister, 16 | // user register does fb init for login, bad if we do that 2x 17 | function (cb) { 18 | Matrix.firebaseInit(cb); 19 | } 20 | ], function(err) { 21 | if (err) { 22 | Matrix.loader.stop(); 23 | console.error(err.message.red); 24 | debug('Error:', err.message); 25 | return process.exit(1); 26 | } 27 | 28 | if (Matrix.pkgs.length > 0) { 29 | var command = Matrix.pkgs[0]; 30 | if (command !== 'device') { 31 | Matrix.loader.stop(); 32 | console.warn('matrix register', command, 'is not a valid command'); 33 | process.exit(1); 34 | } else { 35 | Matrix.validate.userAsync(function (err) { 36 | if (err) { 37 | Matrix.loader.stop(); 38 | console.error(err.message.red); 39 | debug('Error:', err.message); 40 | return process.exit(1); 41 | } 42 | Matrix.loader.stop(); 43 | console.log(t('matrix.register.creating_device')); 44 | Matrix.loader.start(); 45 | // do device Registration 46 | var deviceSchema = { 47 | properties: { 48 | name: { 49 | required: true, 50 | description: 'device name' 51 | }, 52 | description: { 53 | description: 'device description' 54 | } 55 | // for when this is ready 56 | // serial: { 57 | // required: true 58 | // description: 'serial number' 59 | // } 60 | } 61 | }; 62 | Matrix.loader.stop(); 63 | prompt.delimiter = ''; 64 | prompt.message = 'Device Registration -- '; 65 | prompt.start(); 66 | 67 | //TODO: Async this cascade 68 | prompt.get(deviceSchema, function (err, result) { 69 | Matrix.loader.start(); 70 | // all of the below is untested - next line = matrix use 71 | // Matrix.config.device.identifier = result.deviceId; 72 | 73 | Matrix.helpers.saveConfig(function () { 74 | 75 | Matrix.firebase.user.getAllDevices(function (devices) { 76 | 77 | var deviceIds = _.keys(devices); 78 | debug('Existing Device Ids', deviceIds) 79 | 80 | var deviceObj = { 81 | type: 'matrix', 82 | osVersion: '0', 83 | version: require(__dirname + '/../package.json').version, 84 | name: result.name, 85 | description: result.description 86 | }; 87 | 88 | var events = { 89 | error: function (err) { 90 | if (err.hasOwnProperty('state') && err.state == 'device-provisioning-in-progress') { 91 | debug('Provisioning device step... ignore this') 92 | } else { 93 | Matrix.loader.stop(); 94 | console.log('Error creating device '.red + deviceObj.name.yellow + ': '.red, err); 95 | process.exit(); 96 | } 97 | }, 98 | finished: function () { 99 | Matrix.loader.stop(); 100 | console.log('Device registered successfully'); 101 | }, 102 | start: function () { 103 | Matrix.loader.stop(); 104 | console.log('Device registration request formed...'); 105 | Matrix.loader.start(); 106 | }, 107 | progress: function () { 108 | Matrix.loader.stop(); 109 | console.log('Registering device...'); 110 | Matrix.loader.start(); 111 | } 112 | }; 113 | 114 | var deviceObj = { 115 | type: 'matrix', 116 | osVersion: '0', 117 | version: require(__dirname + '/../package.json').version, 118 | name: result.name, 119 | description: result.description, 120 | }; 121 | 122 | var duplicateDevices = _.values(Matrix.config.deviceMap).filter(function(d) { 123 | return d.name === result.name; 124 | }); 125 | 126 | if (duplicateDevices.length != 0){ 127 | console.error('Device name should be unique!'); 128 | process.exit(1); 129 | } 130 | 131 | // fire off worker 132 | Matrix.firebase.device.add(deviceObj, events); 133 | 134 | // wrap this up 135 | Matrix.firebase.user.watchForDeviceAdd(function (d) { 136 | var deviceId = d.key; 137 | if (!_.isEmpty(deviceId) && deviceIds.indexOf(deviceId) === -1) { 138 | debug('new device on user record!'); 139 | Matrix.loader.stop(); 140 | console.log('New Device'.green, deviceId); 141 | Matrix.helpers.trackEvent('device-register', { did: deviceId }); 142 | 143 | // // add to local ref 144 | // Matrix.config.device.deviceMap = _.merge({}, Matrix.config.device.appMap, d.val() ); 145 | // Matrix.helpers.saveConfig(); 146 | 147 | 148 | // fetch secret 149 | // this part will be automated in the future. idk how. 150 | Matrix.loader.start(); 151 | Matrix.api.device.getSecret(deviceId, function (err, secret) { 152 | Matrix.loader.stop(); 153 | if (err) { 154 | console.error('Secret Error: ', err); 155 | process.exit(1); 156 | } else if (_.isUndefined(secret)) { 157 | console.error('No secret found: ', secret); 158 | process.exit(1); 159 | } 160 | 161 | // return the secret 162 | console.log('\nSave your *device id* and *device secret*'.green) 163 | console.log('You will not be able to see the secret for this device again'.grey) 164 | 165 | console.log('\nSave the following to ~/.envrc on your Pi\n'.grey) 166 | console.log('export MATRIX_DEVICE_ID=' + deviceId); 167 | console.log('export MATRIX_DEVICE_SECRET=' + secret.results.deviceSecret) 168 | 169 | console.log(); 170 | console.log('Make these available by running `source ~/.envrc` before running MATRIX OS'.grey); 171 | console.log('\nSet up `matrix` CLI to target this device\n'.grey); 172 | console.log('matrix use', deviceId); 173 | console.log('or'.grey) 174 | console.log('matrix use', result.name); 175 | console.log(); 176 | Matrix.helpers.refreshDeviceMap(process.exit) 177 | }) 178 | } 179 | }) 180 | // #watchDeviceAdd 181 | // 182 | }); 183 | // #getAllDevices 184 | // 185 | }) 186 | // ##firebaseInit 187 | 188 | }); 189 | 190 | 191 | }) 192 | // # prompt 193 | } 194 | } else { 195 | 196 | processPromptData(function (err, userData) { 197 | if (err) { 198 | console.log('Error: ', err); 199 | process.exit(); 200 | } 201 | if (userData.password !== userData.confirmPassword) { 202 | return console.error('Passwords didn\'t match'); 203 | } 204 | /** set the creds **/ 205 | Matrix.config.user = { 206 | username: userData.username, 207 | password: userData.password 208 | }; 209 | 210 | Matrix.config.user.jwt_token = true; 211 | 212 | Matrix.config.client = {}; 213 | debug('Client', Matrix.options); 214 | 215 | Matrix.loader.start(); 216 | Matrix.api.register.user(userData.username, userData.password, Matrix.options.clientId, function (err, out) { 217 | /*500 server died 218 | 400 bad request 219 | user exists 220 | missing parameter 221 | 401*/ 222 | if (err) { 223 | Matrix.loader.stop(); 224 | if (err.hasOwnProperty('status_code')) { 225 | if (err.status_code === 500) { 226 | console.log('Server unavailable, please try again later'); 227 | } else if (err.status_code === 400) { 228 | console.log('Unable to create user ' + userData.username + ', user already exists'); 229 | } else { 230 | console.log('Unknown error (' + err.status_code + '): ', err); 231 | } 232 | } else { 233 | if (err.hasOwnProperty('code') && err.code == 'ENOTFOUND') { 234 | console.error('Unable to reach server, please try again later'); 235 | } else { 236 | console.error('Unknown error: ', err); 237 | } 238 | } 239 | process.exit(); 240 | } else { 241 | var userOptions = { 242 | username: userData.username, 243 | password: userData.password, 244 | trackOk: userData.profile.trackOk 245 | } 246 | 247 | login(userData, userOptions); 248 | } 249 | }); 250 | }); 251 | } 252 | }); 253 | 254 | function processPromptData(cb) { 255 | Matrix.helpers.profile.prompt(function(err, profile) { 256 | var schema = { 257 | properties: { 258 | username: { 259 | required: true, 260 | pattern: /\S+@\S+\.\S+/, 261 | message: 'Username must be a valid email', 262 | description: 'Username: ' 263 | }, 264 | password: { 265 | required: true, 266 | pattern: /^(?=.*?[a-zA-Z])(?=.*?[0-9]).{6,}/, 267 | message: 'Password must be at least 6 characters long and should contain a lower case letter and a number', 268 | hidden: true, 269 | description: 'Password: ' 270 | }, 271 | confirmPassword: { 272 | hidden: true, 273 | description: 'Confirm Password: ' 274 | } 275 | } 276 | }; 277 | 278 | prompt.delimiter = ''; 279 | prompt.message = 'User -- '; 280 | prompt.start(); 281 | prompt.get(schema, function(err, result) { 282 | /*if (err && err.toString().indexOf('canceled') > 0) { 283 | err = new Error('User registration cancelled'); 284 | } */ 285 | result.profile = profile; 286 | cb(err, result); 287 | }); 288 | }); 289 | } 290 | 291 | function login(userData, userOptions){ 292 | async.series([ 293 | function(cb){ 294 | Matrix.helpers.logout(function(){ 295 | debug('Cleaning old data...'); 296 | }); 297 | cb(null); 298 | }, function(cb) { 299 | Matrix.helpers.login(userOptions, function (err) { 300 | if (err) { 301 | Matrix.loader.stop(); 302 | cb('Unable to login, your account was created but the profile info couldn\'t be updated'.red); 303 | } 304 | 305 | Matrix.helpers.profile.update(userData.profile, function (err) { 306 | Matrix.loader.stop(); 307 | debug('User', Matrix.config.user, out); 308 | if (err) { 309 | cb('Unable to update profile, your account was created but the profile information couldn\'t be updated'.yellow); 310 | } 311 | cb('User ' + userData.username + ' successfully created'); 312 | }); 313 | cb(null); 314 | }); 315 | } 316 | ], function(err){ 317 | if (err) console.log(err); 318 | process.exit(1); 319 | }); 320 | } 321 | 322 | function handleBasicRegister(cb) { 323 | // continue flow 324 | if (Matrix.pkgs.length !== 0) return cb(); 325 | 326 | processPromptData(function(err, userData) { 327 | if (err) { 328 | console.log('Error: ', err); 329 | process.exit(); 330 | } 331 | 332 | if (userData.password !== userData.confirmPassword) { 333 | return console.error('Passwords didn\'t match'); 334 | } 335 | /** set the creds **/ 336 | Matrix.config.user = { 337 | username: userData.username, 338 | password: userData.password 339 | }; 340 | 341 | Matrix.config.user.jwt_token = true; 342 | 343 | Matrix.config.client = {}; 344 | debug('Client', Matrix.options); 345 | 346 | Matrix.loader.start(); 347 | Matrix.api.register.user(userData.username, userData.password, Matrix.options.clientId, function(err, out) { 348 | /*500 server died 349 | 400 bad request 350 | user exists 351 | missing parameter 352 | 401*/ 353 | if (err) { 354 | Matrix.loader.stop(); 355 | if (err.hasOwnProperty('status_code')) { 356 | if (err.status_code === 500) { 357 | console.log('Server unavailable, please try again later'); 358 | } else if (err.status_code === 400) { 359 | console.log('Unable to create user ' + userData.username + ', user already exists'); 360 | } else { 361 | console.log('Unknown error (' + err.status_code + '): ', err); 362 | } 363 | } else { 364 | if (err.hasOwnProperty('code') && err.code == 'ENOTFOUND') { 365 | console.error('Unable to reach server, please try again later'); 366 | } else { 367 | console.error('Unknown error: ', err); 368 | } 369 | } 370 | process.exit(); 371 | } else { 372 | var userOptions = { 373 | username: userData.username, 374 | password: userData.password, 375 | trackOk: userData.profile.trackOk 376 | } 377 | 378 | //login does fb init :( 379 | login(userData, userOptions); 380 | } 381 | }); 382 | }); 383 | } 384 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | matrix: 3 | already_login: 'Already logged in as:' 4 | api_title: API 5 | streaming_title: Streaming 6 | user_title: User 7 | no_user_message: No user selected 8 | device_title: Device 9 | locale_title: Locale 10 | env_title: Env 11 | v_title: V 12 | help_login: Login to the MATRIX platform 13 | help_register: Create a MATRIX platform user 14 | help_register_device: register new device 15 | help_account: Manage user account details 16 | help_account_profile: update user profile info 17 | help_logout: Log out of the MATRIX platform 18 | help_config: (key=value) View and set application configuration 19 | help_create: (name) Creates a new scaffolding for an application 20 | help_deploy: (name) Deploy application to the active device 21 | help_install_default: Install application 22 | help_install: Install an app to selected MATRIX device 23 | help_list: 'Information about your devices, applications and installs' 24 | help_log: Logs output from selected device and applications 25 | help_ping: Flash lights on selected Matrix device 26 | help_reboot: Reboots the active device 27 | help_remove: Remove a device from your account 28 | help_restart: Restarts an app running on the device 29 | help_search: Search for apps 30 | help_set: 'Set environment/locale' 31 | help_sim: Manage local device simulator with Docker 32 | help_start: Starts an app running on the active device 33 | help_stop: Stops an app running on the active device 34 | help_trigger: Runs a CrossTalk event test 35 | help_uninstall: Uninstall the app 36 | help_update: Install a specific application version 37 | help_upgrade: Updates CLI on your local computer 38 | help_use: Indicate active device 39 | please_login: No user logged in 40 | expired: Expired session > `matrix login` 41 | set_device: No Device Set. Use # `matrix use` 42 | validate: 43 | no_device: No Device Selected 44 | select_device_id: select a device id' 45 | #SETUP: 46 | account: 47 | no_profile: No profile data available 48 | login: 49 | already_login_warning: 'there is already a registered account' # 50 | user_auth_error: 'User Authentication Error' 51 | login_success: 'Login Successful' 52 | logout: 53 | logout_success: 'Logged Out Successfully' 54 | use: 55 | using_device_by_name: 'Now using device' 56 | using_device_by_id: 'Now using device id' 57 | not_authorized: 'Not Authorized' 58 | device_not_found: Device doesn`t exist # 59 | invalid_token: 'Your token is invalid' 60 | invalid_nameid: 'is not a device name or a device id' 61 | try: 'Try' 62 | command_help: 'set active device to device id' 63 | register: 64 | creating_device: 'Registering a new device.' 65 | remove: 66 | start: Preparing to remove device 67 | progress: Removing device 68 | finish: Device successfully removed 69 | error: Unable to remove device 70 | help_selected: Remove the selected device 71 | help_id: Remove the device with id %{id} 72 | set: 73 | help_device: 'set device environment' 74 | help_config: 'configures application variables' 75 | help_locale: 'set current locale for the cli' 76 | env: 77 | env: 'Env' 78 | valid_environments: 'Valid Environments' 79 | config: 80 | use: Use 81 | no_app: 'App Name Required' 82 | invalid_key_value: key and value invalid # 83 | no_key_value: 'Must supply key and value invalid`' 84 | locale: 85 | locale_required: 'Locale Required' 86 | locale: 'Locale' 87 | valid_locales: 'Valid locales' 88 | #MANAGEMENT: 89 | sim: 90 | unknowm_parameter: parameter doesn´t exist # 91 | command_help_sim: matrix sim # 92 | please: 'Please' 93 | before_using_sim: 'before attempting to use the simulator' 94 | init: 95 | specify_data_for_init: Please enter information for this virtual device # 96 | warning_sim_init: please use ´matrix sim init´ # 97 | device_already_initialized: You already have a virtual device initialized 98 | target_device: To target this device 99 | device_is_already_used: is already used 100 | creating_device: Creating 101 | error_creating_device: Create Error 102 | invalid_session: Invalid Session 103 | please: Please 104 | and: and 105 | register_error: Register Error 106 | success: Success 107 | to_target_device: To target this device 108 | start: 109 | before_message: first please 110 | device: Device 111 | not_a_virtual_device: is not a virtual MatrixOS. Please 112 | and: and 113 | custom_image_detected: Custom Simulator Image detected 114 | debug_on: Debug mode on 115 | error: ERROR 116 | please: Please 117 | then: Then 118 | if_different_name: "If you have a different machine name, use that" 119 | starting_sim: MatrixOS Simulator Starting 120 | stop_with: Stop with Ctrl + C or 121 | restore: 122 | downloading_image: Downloading latest MatrixOS image 123 | save: 124 | state_saved: Simulator state saved 125 | clear: 126 | simulation_cleared: Simulation Cleared 127 | stop: 128 | sim_stopped: Matrix Simulator Stopped 129 | docker: 130 | docker_not_found: Docker not found 131 | install_from: Please install docker from 132 | then: Then 133 | help_upgrade: upgrade your simulator image 134 | help_restore: reset your simulator 135 | help_init: initialize your simulator 136 | help_start: start MatrixOS virtual environment 137 | help_save: "save MatrixOS state, use after deploy / install" 138 | help_clear: remove simulation local data 139 | list: 140 | list_devices: devices list # 141 | list_groups: groups list # 142 | list_apps: apps list # 143 | list_all: all # 144 | app_list_error: 'App List Error' 145 | no_results: 'No Results Found' 146 | help_devices: 'display available devices' 147 | help_groups: 'display groups of devices' 148 | help_apps: 'display apps on current device' 149 | help_all: 'display all devices with installed apps' 150 | reboot: 151 | device_offline: device is not online #  152 | rebooting: 'Device rebooting' 153 | rebooted: 'Device rebooted' # 154 | #APPS: 155 | search: 156 | help: find MATRIX apps 157 | small_needle: Your needle is too small to find in our haystack 158 | search_successfully: Search results # 159 | no_results: No results found # 160 | install: 161 | help_app: Install the app %{app} to active MatrixOS device 162 | help_sensor: Install the sensor %{sensor} to active MatrixOS device 163 | installing: Installing 164 | app_installed: Installed app 165 | sensor_installed: sensor installed 166 | command_help: install # [ app / sensor ] # 167 | app_install_success: Application installation SUCCESS # 168 | app_install_error: Application installation failed # 169 | sensor_install_error: Sensor installation failed # 170 | app_not_found: Application doesn't exist # 171 | sensor_not_found: Sensor doesn't exist # 172 | app_x_not_found: Application %{app} not found 173 | app_version_x_not_found: Application version %{version} not found 174 | installing_x_with_policy: Installing %{app} with policy 175 | invalid_app_status: Invalid application status 176 | waiting_for_device_install: Awaiting installation on Device 177 | device_install_timeout: Unable to reach device, the application will be installed as soon as your device is available 178 | start: Preparing installation 179 | progress: Installation progress 180 | config: 181 | config_usage_command: 'warnings' # 182 | device_config: Config for device 183 | show_config_app1: 'configuration the apliccation' # 184 | help: get device configurations 185 | help_app: get application configurations 186 | help_app_key: 'get application configuration key' 187 | help_app_key_value: set application configuration key value 188 | show_config_app: 'app settings ' # 189 | specified_application_does_not_exist: 'app specified doesn`t exist' # 190 | specify_key: specify a key # 191 | key_doesnt_exist: this key specified doesn´t exist # 192 | key_value: 'value:' 193 | uninstall: 194 | help_app: Uninstall the app %{app} from active MatrixOS device 195 | uninstalled: 'Uninstalled' 196 | uninstalling: Uninstalling 197 | application_unspecified: 'error: missing required argument' # 198 | app_undefined: 'this app doesn`t exist' # 199 | device_offline: Device offline, reconect the device please # 200 | update: 201 | upgrading_to: 'Upgrading to' 202 | latest_version: 'latest version' 203 | of: 'of' 204 | help_update: 'software on active device' 205 | help_update_app: 'update application to latest' 206 | help_update_app_version: 'update application to specified version' 207 | app_undefined: 'Application doesn`t exist' # 208 | app_update_successfully: 'Upgrading to latest version' # 209 | version_undefined: 'Unsupported app version' # 210 | version_doesnt_exist: this version doesn´t exist # 211 | version_update_successfully: 'Upgrading to' # 212 | error_updating: Error updating # 213 | start: 214 | help: start the application %{app} 215 | application_unspecified: 'specify [ app(name) ] ' # 216 | app_undefined: 'This app doesn`t exist' # 217 | starting_app: 'Starting' 218 | start_app_successfully: 'Started' 219 | start_app_error: 'Unable to start' 220 | start_app_status_error: 'The application can`t start because it`s currently active or pending installation' 221 | start_timeout: 'Could not start application. Unable to reach device' 222 | stop: 223 | help: stop the application %{app} 224 | application_unspecified: 'specify [ app(name) ] ' # 225 | app_undefined: 'This app doesn`t exist' # 226 | stopping_app: 'Stopping' 227 | stop_app_successfully: 'Stopped' 228 | stop_app_error: 'Unable to stop' 229 | stop_app_status_error: 'The application can`t be stopped because it isn`t currently active' 230 | stop_timeout: 'Could not stop application. Unable to reach device' 231 | restart: 232 | help: restart the application %{app} 233 | application_unspecified: 'specify [ app(name) ] ' # 234 | app_undefined: 'this app doesn`t exist' # 235 | restarting_app: 'Restarting' 236 | restart_app_successfully: 'Restarted' 237 | restart_app_error: 'Unable to restart' 238 | restart_app_status_error: 'The application can`t be restarted because it isn`t currently active' 239 | restart_timeout: 'Could not restart application. Unable to reach device' 240 | #DEVELOPMENT 241 | create: 242 | help: Creates a scaffolding for the app %{app} 243 | name_undefined: 'Must specify app name' 244 | new_folder: New Folder 245 | error_creating: An error occurred 246 | bad_numbers: Application names must not only be numbers 247 | description_app: this is your application logic 248 | description_config: 'change variables, indicate sensors, configure dashboard' 249 | description_developer: information about developing MATRIX apps 250 | description_index: 'app entry point, do not modify' 251 | description_package: 'node.js information file, do not modify without knowledge' 252 | folder_exist: 'the app folder already exists' 253 | deploy: 254 | help: Uploads and deploys an app to the active MatrixOS %{app} 255 | app_not_found: 'No %{detect_file} found. Are you sure a MatrixOS app resides in %{pwd}?' 256 | folders_not_supported: 'Our fault. Directories can not be deployed yet' 257 | cancel_deploy: 'Cancel deploy due to errors & warnings' 258 | reading: Reading 259 | writing: Writing 260 | error: An error occurred 261 | deploy_error: Deploy Error 262 | deply_started: Deploy Started 263 | stream_error: Deploy Stream Error 264 | deploy_complete: Deploy Complete 265 | bad_payload: Bad Deployment Info Payload 266 | app_install_failed: App Install Fail 267 | app_installed: App Installed 268 | application_unspecified: 'specify [ app ]' # 269 | deploy_app_successfully: 'deploying app Successfully' # 270 | app_undefined: 'could not do deploy' # 271 | publish: 272 | help: Publishes the approved app %{app} on the app store 273 | unpublish: 274 | unpublishing: Unpublishing # 275 | help: Unpublish the app %{app} from the app store # 276 | trigger: 277 | no_specified_test: 'error: missing required argument' # 278 | defined_test_doesn`t_exist: 'test doesn`t exist' # 279 | run_trigger_successfully: 'running test' # 280 | log: 281 | app_not_select: 'select an app `matrix log app-name` ' # 282 | app_undefined: 'app specified doesn´t exits ' # 283 | logs_show: 'logs' # 284 | helpers: 285 | adding: Adding 286 | config_error: Configuration error 287 | config_save_error: Configuration save error 288 | config_parse_error: 'Configuration parsing Error' 289 | apps: 290 | name: Name 291 | description: Description 292 | version: v 293 | shortname: Shortname 294 | devices: 295 | device_id: Device ID 296 | name: Name 297 | ok: ok 298 | description: Description 299 | last_online: Last Online 300 | groups: 301 | name: Name 302 | devices: "# Devices" 303 | group_id: Group ID 304 | search: 305 | installs: +1s 306 | name: Name 307 | description: Description 308 | short_name: Short Name 309 | latest: Latest 310 | device_apps: 311 | device: Device 312 | app_name: App Name 313 | description: Description 314 | --------------------------------------------------------------------------------