├── .gitignore ├── platziverse-deploy ├── roles │ ├── platziverse-db │ │ ├── meta │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── deps.yml │ │ │ └── main.yml │ │ └── files │ │ │ └── platziverse-db │ │ │ ├── diagram.png │ │ │ ├── README.md │ │ │ ├── lib │ │ │ ├── db.js │ │ │ ├── metric.js │ │ │ └── agent.js │ │ │ ├── models │ │ │ ├── metric.js │ │ │ └── agent.js │ │ │ ├── tests │ │ │ ├── fixtures │ │ │ │ └── agent.js │ │ │ └── agent-tests.js │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ ├── setup.js │ │ │ └── examples │ │ │ └── index.js │ ├── platziverse-agent │ │ ├── meta │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── deps.yml │ │ │ └── main.yml │ │ └── files │ │ │ └── platziverse-agent │ │ │ ├── utils.js │ │ │ ├── package.json │ │ │ ├── examples │ │ │ └── index.js │ │ │ ├── README.md │ │ │ └── index.js │ ├── platziverse-mqtt │ │ ├── meta │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── deps.yml │ │ │ └── main.yml │ │ └── files │ │ │ ├── platziverse-mqtt.service │ │ │ └── platziverse-mqtt │ │ │ ├── utils.js │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ └── server.js │ ├── database │ │ ├── meta │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ ├── platziverse-api │ │ ├── tasks │ │ │ ├── deps.yml │ │ │ └── main.yml │ │ ├── meta │ │ │ └── main.yml │ │ └── files │ │ │ ├── platziverse-api.service │ │ │ ├── platziverse-api │ │ │ ├── auth.js │ │ │ ├── config.js │ │ │ ├── tests │ │ │ │ ├── fixtures │ │ │ │ │ └── agent.js │ │ │ │ └── api-tests.js │ │ │ ├── server.js │ │ │ ├── package.json │ │ │ └── api.js │ │ │ └── platziverse-api.conf │ ├── platziverse-web │ │ ├── tasks │ │ │ ├── deps.yml │ │ │ └── main.yml │ │ ├── files │ │ │ ├── platziverse-web │ │ │ │ ├── .babelrc │ │ │ │ ├── public │ │ │ │ │ ├── images │ │ │ │ │ │ └── platziverse.png │ │ │ │ │ └── index.html │ │ │ │ ├── client │ │ │ │ │ ├── line-chart.js │ │ │ │ │ ├── app.js │ │ │ │ │ ├── app.vue │ │ │ │ │ ├── metric.vue │ │ │ │ │ └── agent.vue │ │ │ │ ├── utils.js │ │ │ │ ├── config.js │ │ │ │ ├── server.js │ │ │ │ ├── package.json │ │ │ │ └── proxy.js │ │ │ ├── platziverse-web.service │ │ │ └── platziverse-web.conf │ │ └── meta │ │ │ └── main.yml │ └── node │ │ └── tasks │ │ └── main.yml ├── inventory.ini ├── frontend.yml ├── backend.yml └── Vagrantfile ├── platziverse-db ├── diagram.png ├── README.md ├── lib │ ├── db.js │ ├── metric.js │ └── agent.js ├── models │ ├── metric.js │ └── agent.js ├── tests │ ├── fixtures │ │ └── agent.js │ └── agent-tests.js ├── index.js ├── package.json ├── setup.js └── examples │ └── index.js ├── platziverse-web ├── .babelrc ├── public │ ├── images │ │ └── platziverse.png │ └── index.html ├── client │ ├── line-chart.js │ ├── app.js │ ├── app.vue │ ├── metric.vue │ └── agent.vue ├── utils.js ├── config.js ├── server.js ├── package.json └── proxy.js ├── platziverse-api ├── auth.js ├── config.js ├── tests │ ├── fixtures │ │ └── agent.js │ └── api-tests.js ├── server.js ├── package.json └── api.js ├── platziverse-agent ├── utils.js ├── package.json ├── examples │ └── index.js ├── README.md └── index.js ├── platziverse-mqtt ├── utils.js ├── README.md ├── package.json └── server.js └── platziverse-cli ├── package.json └── platziverse.js /.gitignore: -------------------------------------------------------------------------------- 1 | ssh 2 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: node } 4 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: node } 4 | -------------------------------------------------------------------------------- /platziverse-db/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julianduque/platziverse/HEAD/platziverse-db/diagram.png -------------------------------------------------------------------------------- /platziverse-web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-3"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: node } 4 | - { role: platziverse-db } 5 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/database/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: ANXS.postgresql } 4 | - { role: geerlingguy.redis } 5 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/tasks/deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt: name=git state=present 3 | - apt: name=build-essential state=present 4 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/tasks/deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt: name=git state=present 3 | - apt: name=build-essential state=present 4 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/tasks/deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt: name=git state=present 3 | - apt: name=build-essential state=present 4 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/tasks/deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt: name=git state=present 3 | - apt: name=build-essential state=present 4 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/tasks/deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt: name=git state=present 3 | - apt: name=build-essential state=present 4 | -------------------------------------------------------------------------------- /platziverse-web/public/images/platziverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julianduque/platziverse/HEAD/platziverse-web/public/images/platziverse.png -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-3"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: node } 4 | - { role: jdauphant.nginx } 5 | - { role: platziverse-db } 6 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: node } 4 | - { role: jdauphant.nginx } 5 | - { role: platziverse-agent } 6 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julianduque/platziverse/HEAD/platziverse-deploy/roles/platziverse-db/files/platziverse-db/diagram.png -------------------------------------------------------------------------------- /platziverse-deploy/inventory.ini: -------------------------------------------------------------------------------- 1 | backend-production ansible_ssh_user=root ansible_ssh_host=165.227.65.215 ansible_ssh_port=22 2 | frontend-production ansible_ssh_user=root ansible_ssh_host=45.55.128.174 ansible_ssh_port=22 3 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/public/images/platziverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julianduque/platziverse/HEAD/platziverse-deploy/roles/platziverse-web/files/platziverse-web/public/images/platziverse.png -------------------------------------------------------------------------------- /platziverse-deploy/roles/database/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restarting PostgreSQL 3 | service: 4 | name=postgresql 5 | state=reloaded 6 | - name: Restarting Redis 7 | service: 8 | name=redis 9 | state=restarted 10 | -------------------------------------------------------------------------------- /platziverse-db/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-db 2 | 3 | ## Usage 4 | 5 | ``` js 6 | const setupDatabase = require('platziverse-db') 7 | 8 | setupDabase(config).then(db => { 9 | const { Agent, Metric } = db 10 | 11 | }).catch(err => console.error(err)) 12 | ``` 13 | -------------------------------------------------------------------------------- /platziverse-db/lib/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | let sequelize = null 5 | 6 | module.exports = function setupDatabase (config) { 7 | if (!sequelize) { 8 | sequelize = new Sequelize(config) 9 | } 10 | return sequelize 11 | } 12 | -------------------------------------------------------------------------------- /platziverse-deploy/frontend.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: frontend-* 3 | gather_facts: False 4 | pre_tasks: 5 | - name: Install Python 2 6 | raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) 7 | - setup: 8 | roles: 9 | - platziverse-web 10 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-db 2 | 3 | ## Usage 4 | 5 | ``` js 6 | const setupDatabase = require('platziverse-db') 7 | 8 | setupDabase(config).then(db => { 9 | const { Agent, Metric } = db 10 | 11 | }).catch(err => console.error(err)) 12 | ``` 13 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/database/vars/main.yml: -------------------------------------------------------------------------------- 1 | postgresql_databases: 2 | - name: platziverse 3 | owner: platzi 4 | hstore: yes 5 | postgresql_users: 6 | - name: platzi 7 | pass: platzi 8 | encrypted: no 9 | postgresql_user_privileges: 10 | - name: platzi 11 | db: platziverse 12 | priv: "ALL" 13 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/lib/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | let sequelize = null 5 | 6 | module.exports = function setupDatabase (config) { 7 | if (!sequelize) { 8 | sequelize = new Sequelize(config) 9 | } 10 | return sequelize 11 | } 12 | -------------------------------------------------------------------------------- /platziverse-deploy/backend.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: backend-* 3 | gather_facts: False 4 | pre_tasks: 5 | - name: Install Python 2 6 | raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) 7 | - setup: 8 | roles: 9 | - database 10 | - platziverse-mqtt 11 | - platziverse-api 12 | -------------------------------------------------------------------------------- /platziverse-web/client/line-chart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { Line, mixins } = require('vue-chartjs') 4 | const { reactiveProp } = mixins 5 | 6 | module.exports = Line.extend({ 7 | mixins: [ reactiveProp ], 8 | props: [ 'options' ], 9 | mounted () { 10 | this.renderChart(this.chartData, this.options) 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /platziverse-api/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const jwt = require('jsonwebtoken') 4 | 5 | function sign (payload, secret, callback) { 6 | jwt.sign(payload, secret, callback) 7 | } 8 | 9 | function verify (token, secret, callback) { 10 | jwt.verify(token, secret, callback) 11 | } 12 | 13 | module.exports = { 14 | sign, 15 | verify 16 | } 17 | -------------------------------------------------------------------------------- /platziverse-agent/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function parsePayload (payload) { 4 | if (payload instanceof Buffer) { 5 | payload = payload.toString('utf8') 6 | } 7 | 8 | try { 9 | payload = JSON.parse(payload) 10 | } catch (e) { 11 | payload = null 12 | } 13 | 14 | return payload 15 | } 16 | 17 | module.exports = { 18 | parsePayload 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-mqtt/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function parsePayload (payload) { 4 | if (payload instanceof Buffer) { 5 | payload = payload.toString('utf8') 6 | } 7 | 8 | try { 9 | payload = JSON.parse(payload) 10 | } catch (e) { 11 | payload = null 12 | } 13 | 14 | return payload 15 | } 16 | 17 | module.exports = { 18 | parsePayload 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Plativerse API 3 | After=network.target 4 | 5 | [Service] 6 | Environment=NODE_ENV=production 7 | Type=simple 8 | User=root 9 | WorkingDirectory=/usr/local/src/platziverse-api 10 | ExecStart=/usr/bin/npm start 11 | Restart=on-failure 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/client/line-chart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { Line, mixins } = require('vue-chartjs') 4 | const { reactiveProp } = mixins 5 | 6 | module.exports = Line.extend({ 7 | mixins: [ reactiveProp ], 8 | props: [ 'options' ], 9 | mounted () { 10 | this.renderChart(this.chartData, this.options) 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: deps.yml 3 | - name: Copy platziverse-agent 4 | copy: 5 | src=platziverse-agent 6 | dest=/usr/local/src 7 | mode=u=rwx,g=rwx,o=rx 8 | - name: Run npm install 9 | command: npm install 10 | chdir=/usr/local/src/platziverse-agent 11 | creates=/usr/local/src/platziverse-agent/node_modules 12 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/files/platziverse-mqtt.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Platziverse MQTT 3 | After=network.target 4 | 5 | [Service] 6 | Environment=NODE_ENV=production 7 | Type=simple 8 | User=root 9 | WorkingDirectory=/usr/local/src/platziverse-mqtt 10 | ExecStart=/usr/bin/npm start 11 | Restart=on-failure 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const jwt = require('jsonwebtoken') 4 | 5 | function sign (payload, secret, callback) { 6 | jwt.sign(payload, secret, callback) 7 | } 8 | 9 | function verify (token, secret, callback) { 10 | jwt.verify(token, secret, callback) 11 | } 12 | 13 | module.exports = { 14 | sign, 15 | verify 16 | } 17 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/files/platziverse-agent/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function parsePayload (payload) { 4 | if (payload instanceof Buffer) { 5 | payload = payload.toString('utf8') 6 | } 7 | 8 | try { 9 | payload = JSON.parse(payload) 10 | } catch (e) { 11 | payload = null 12 | } 13 | 14 | return payload 15 | } 16 | 17 | module.exports = { 18 | parsePayload 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/files/platziverse-mqtt/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function parsePayload (payload) { 4 | if (payload instanceof Buffer) { 5 | payload = payload.toString('utf8') 6 | } 7 | 8 | try { 9 | payload = JSON.parse(payload) 10 | } catch (e) { 11 | payload = null 12 | } 13 | 14 | return payload 15 | } 16 | 17 | module.exports = { 18 | parsePayload 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/node/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Downloading Setup Script 3 | get_url: 4 | url=https://deb.nodesource.com/setup_8.x 5 | dest=/tmp/install_node_8.sh 6 | mode=u=rx,g=rx,o=rx 7 | - name: Running Setup Script 8 | command: /tmp/install_node_8.sh 9 | creates=/etc/apt/sources.list.d/nodesource_8.list 10 | - name: Installing Node.js 11 | apt: 12 | update_cache=yes 13 | state=latest 14 | name=nodejs 15 | -------------------------------------------------------------------------------- /platziverse-web/client/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Vue = require('vue') 4 | const App = require('./app.vue') 5 | const Agent = require('./agent.vue') 6 | const Metric = require('./metric.vue') 7 | 8 | Vue.component('agent', Agent) 9 | Vue.component('metric', Metric) 10 | 11 | // eslint-disable-next-line no-unused-vars 12 | const vm = new Vue({ 13 | el: '#app', 14 | render (createElement) { 15 | return createElement(App) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /platziverse-web/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function pipe (source, target) { 4 | if (!source.emit || !target.emit) { 5 | throw TypeError(`Please pass EventEmitter's as argument`) 6 | } 7 | 8 | const emit = source._emit = source.emit 9 | 10 | source.emit = function () { 11 | emit.apply(source, arguments) 12 | target.emit.apply(target, arguments) 13 | return source 14 | } 15 | } 16 | 17 | module.exports = { 18 | pipe 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: deps.yml 3 | - name: Copy platziverse-db 4 | copy: 5 | src=platziverse-db 6 | dest=/usr/local/src 7 | mode=u=rwx,g=rwx,o=rx 8 | - name: Run npm install 9 | command: npm install 10 | chdir=/usr/local/src/platziverse-db 11 | creates=/usr/local/src/platziverse-db/node_modules 12 | - name: Setup Database 13 | command: npm run setup -- --yes 14 | chdir=/usr/local/src/platziverse-db 15 | -------------------------------------------------------------------------------- /platziverse-db/models/metric.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | const setupDatabase = require('../lib/db') 5 | 6 | module.exports = function setupMetricModel (config) { 7 | const sequelize = setupDatabase(config) 8 | 9 | return sequelize.define('metric', { 10 | type: { 11 | type: Sequelize.STRING, 12 | allowNull: false 13 | }, 14 | value: { 15 | type: Sequelize.TEXT, 16 | allowNull: false 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-web/config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | endpoint: process.env.API_ENDPOINT || 'http://localhost:3000', 5 | serverHost: process.env.SERVER_HOST || 'http://localhost:8080', 6 | mqttHost: process.env.MQTT_HOST || 'mqtt://localhost', 7 | apiToken: process.env.API_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InBsYXR6aSIsImFkbWluIjp0cnVlLCJwZXJtaXNzaW9ucyI6WyJtZXRyaWNzOnJlYWQiXSwiaWF0IjoxNTAyMzkzNDExfQ.XMKKy9sgqA0TDKjCcgA4_784H2wP7RVQocttSTE-RTU' 8 | } 9 | -------------------------------------------------------------------------------- /platziverse-api/config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api:db') 4 | 5 | module.exports = { 6 | db: { 7 | database: process.env.DB_NAME || 'platziverse', 8 | username: process.env.DB_USER || 'platzi', 9 | password: process.env.DB_PASS || 'platzi', 10 | host: process.env.DB_HOST || 'localhost', 11 | dialect: 'postgres', 12 | logging: s => debug(s) 13 | }, 14 | auth: { 15 | secret: process.env.SECRET || 'platzi' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/client/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Vue = require('vue') 4 | const App = require('./app.vue') 5 | const Agent = require('./agent.vue') 6 | const Metric = require('./metric.vue') 7 | 8 | Vue.component('agent', Agent) 9 | Vue.component('metric', Metric) 10 | 11 | // eslint-disable-next-line no-unused-vars 12 | const vm = new Vue({ 13 | el: '#app', 14 | render (createElement) { 15 | return createElement(App) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function pipe (source, target) { 4 | if (!source.emit || !target.emit) { 5 | throw TypeError(`Please pass EventEmitter's as argument`) 6 | } 7 | 8 | const emit = source._emit = source.emit 9 | 10 | source.emit = function () { 11 | emit.apply(source, arguments) 12 | target.emit.apply(target, arguments) 13 | return source 14 | } 15 | } 16 | 17 | module.exports = { 18 | pipe 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name api.platziverse.space; 4 | 5 | location / { 6 | proxy_set_header Host $host; 7 | proxy_set_header X-Real-IP $remote_addr; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | proxy_set_header X-Forwarded-Proto $scheme; 10 | proxy_http_version 1.1; 11 | 12 | proxy_pass http://127.0.0.1:3000; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/models/metric.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | const setupDatabase = require('../lib/db') 5 | 6 | module.exports = function setupMetricModel (config) { 7 | const sequelize = setupDatabase(config) 8 | 9 | return sequelize.define('metric', { 10 | type: { 11 | type: Sequelize.STRING, 12 | allowNull: false 13 | }, 14 | value: { 15 | type: Sequelize.TEXT, 16 | allowNull: false 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | endpoint: process.env.API_ENDPOINT || 'http://localhost:3000', 5 | serverHost: process.env.SERVER_HOST || 'http://localhost:8080', 6 | mqttHost: process.env.MQTT_HOST || 'mqtt://localhost', 7 | apiToken: process.env.API_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InBsYXR6aSIsImFkbWluIjp0cnVlLCJwZXJtaXNzaW9ucyI6WyJtZXRyaWNzOnJlYWQiXSwiaWF0IjoxNTAyMzkzNDExfQ.XMKKy9sgqA0TDKjCcgA4_784H2wP7RVQocttSTE-RTU' 8 | } 9 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api:db') 4 | 5 | module.exports = { 6 | db: { 7 | database: process.env.DB_NAME || 'platziverse', 8 | username: process.env.DB_USER || 'platzi', 9 | password: process.env.DB_PASS || 'platzi', 10 | host: process.env.DB_HOST || 'localhost', 11 | dialect: 'postgres', 12 | logging: s => debug(s) 13 | }, 14 | auth: { 15 | secret: process.env.SECRET || 'platzi' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Plativerse Web 3 | After=network.target 4 | 5 | [Service] 6 | Environment=NODE_ENV=production 7 | Environment=API_ENDPOINT=http://api.platziverse.space 8 | Environment=SERVER_HOST=http://platziverse.space 9 | Environment=MQTT_HOST=mqtt://api.platziverse.space 10 | Type=simple 11 | User=root 12 | WorkingDirectory=/usr/local/src/platziverse-web 13 | ExecStart=/usr/bin/npm start 14 | Restart=on-failure 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: deps.yml 3 | - name: Copying platziverse-mqtt 4 | copy: 5 | src=platziverse-mqtt 6 | dest=/usr/local/src 7 | mode=u=rwx,g=rwx,o=rx 8 | - name: Running npm install 9 | command: npm install 10 | chdir=/usr/local/src/platziverse-mqtt 11 | creates=/usr/local/src/platziverse-mqtt/node_modules 12 | - name: Install systemd script 13 | copy: 14 | src=platziverse-mqtt.service 15 | dest=/lib/systemd/system 16 | - name: Start platziverse-mqtt 17 | service: 18 | name=platziverse-mqtt 19 | state=restarted 20 | enabled=yes 21 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name platziverse.space; 4 | 5 | location / { 6 | proxy_set_header Host $host; 7 | proxy_set_header X-Real-IP $remote_addr; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | proxy_set_header X-Forwarded-Proto $scheme; 10 | proxy_set_header Upgrade $http_upgrade; 11 | proxy_set_header Connection "upgrade"; 12 | 13 | proxy_http_version 1.1; 14 | 15 | proxy_pass http://127.0.0.1:8080; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platziverse-mqtt/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-mqtt 2 | 3 | ## `agent/connected` 4 | 5 | ``` js 6 | { 7 | agent: { 8 | uuid, // auto generar 9 | username, // definir por configuración 10 | name, // definir por configuración 11 | hostname, // obtener del sistema operativo 12 | pid // obtener del proceso 13 | } 14 | } 15 | ``` 16 | 17 | ## `agent/disconnected` 18 | 19 | ``` js 20 | { 21 | agent: { 22 | uuid 23 | } 24 | } 25 | ``` 26 | 27 | ## `agent/message` 28 | 29 | ``` js 30 | { 31 | agent, 32 | metrics: [ 33 | { 34 | type, 35 | value 36 | } 37 | ], 38 | timestamp // generar cuando creamos el mensaje 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/files/platziverse-mqtt/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-mqtt 2 | 3 | ## `agent/connected` 4 | 5 | ``` js 6 | { 7 | agent: { 8 | uuid, // auto generar 9 | username, // definir por configuración 10 | name, // definir por configuración 11 | hostname, // obtener del sistema operativo 12 | pid // obtener del proceso 13 | } 14 | } 15 | ``` 16 | 17 | ## `agent/disconnected` 18 | 19 | ``` js 20 | { 21 | agent: { 22 | uuid 23 | } 24 | } 25 | ``` 26 | 27 | ## `agent/message` 28 | 29 | ``` js 30 | { 31 | agent, 32 | metrics: [ 33 | { 34 | type, 35 | value 36 | } 37 | ], 38 | timestamp // generar cuando creamos el mensaje 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /platziverse-agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-agent", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - Agent", 6 | "main": "index.js", 7 | "scripts": { 8 | "lint": "standard" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/platzi/platziverse.git" 13 | }, 14 | "author": "Julian Duque ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/platzi/platziverse/issues" 18 | }, 19 | "homepage": "https://github.com/platzi/platziverse#readme", 20 | "devDependencies": { 21 | "standard": "^10.0.3" 22 | }, 23 | "dependencies": { 24 | "debug": "^3.0.0", 25 | "defaults": "^1.0.3", 26 | "mqtt": "^2.11.0", 27 | "uuid": "^3.1.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: deps.yml 3 | - name: Copying platziverse-api 4 | copy: 5 | src=platziverse-api 6 | dest=/usr/local/src 7 | mode=u=rwx,g=rwx,o=rx 8 | - name: Running npm install 9 | command: npm install 10 | chdir=/usr/local/src/platziverse-api 11 | creates=/usr/local/src/platziverse-api/node_modules 12 | - name: Install systemd script 13 | copy: 14 | src=platziverse-api.service 15 | dest=/lib/systemd/system 16 | - name: Install nginx config 17 | copy: 18 | src=platziverse-api.conf 19 | dest=/etc/nginx/sites-enabled 20 | - name: Start platziverse-api 21 | service: 22 | name=platziverse-api 23 | state=restarted 24 | enabled=yes 25 | - name: Restart nginx 26 | service: 27 | name=nginx 28 | state=reloaded 29 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: deps.yml 3 | - name: Copying platziverse-web 4 | copy: 5 | src=platziverse-web 6 | dest=/usr/local/src 7 | mode=u=rwx,g=rwx,o=rx 8 | - name: Running npm install 9 | command: npm install 10 | chdir=/usr/local/src/platziverse-web 11 | creates=/usr/local/src/platziverse-web/node_modules 12 | - name: Install systemd script 13 | copy: 14 | src=platziverse-web.service 15 | dest=/lib/systemd/system 16 | - name: Install nginx config 17 | copy: 18 | src=platziverse-web.conf 19 | dest=/etc/nginx/sites-enabled 20 | - name: Start platziverse-web 21 | service: 22 | name=platziverse-web 23 | state=restarted 24 | enabled=yes 25 | - name: Restart nginx 26 | service: 27 | name=nginx 28 | state=reloaded 29 | -------------------------------------------------------------------------------- /platziverse-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-cli", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - CLI", 6 | "bin": "platziverse.js", 7 | "scripts": { 8 | "lint": "standard" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/platzi/platziverse.git" 13 | }, 14 | "author": "Julian Duque ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/platzi/platziverse/issues" 18 | }, 19 | "homepage": "https://github.com/platzi/platziverse#readme", 20 | "devDependencies": { 21 | "standard": "^10.0.3" 22 | }, 23 | "dependencies": { 24 | "blessed": "^0.1.81", 25 | "blessed-contrib": "^4.7.5", 26 | "moment": "^2.18.1", 27 | "platziverse-agent": "file:../platziverse-agent" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/files/platziverse-agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-agent", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - Agent", 6 | "main": "index.js", 7 | "scripts": { 8 | "lint": "standard" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/platzi/platziverse.git" 13 | }, 14 | "author": "Julian Duque ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/platzi/platziverse/issues" 18 | }, 19 | "homepage": "https://github.com/platzi/platziverse#readme", 20 | "devDependencies": { 21 | "standard": "^10.0.3" 22 | }, 23 | "dependencies": { 24 | "debug": "^3.0.0", 25 | "defaults": "^1.0.3", 26 | "mqtt": "^2.11.0", 27 | "uuid": "^3.1.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /platziverse-db/models/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | const setupDatabase = require('../lib/db') 5 | 6 | module.exports = function setupAgentModel (config) { 7 | const sequelize = setupDatabase(config) 8 | 9 | return sequelize.define('agent', { 10 | uuid: { 11 | type: Sequelize.STRING, 12 | allowNull: false 13 | }, 14 | username: { 15 | type: Sequelize.STRING, 16 | allowNull: false 17 | }, 18 | name: { 19 | type: Sequelize.STRING, 20 | allowNull: false 21 | }, 22 | hostname: { 23 | type: Sequelize.STRING, 24 | allowNull: false 25 | }, 26 | pid: { 27 | type: Sequelize.INTEGER, 28 | allowNull: false 29 | }, 30 | connected: { 31 | type: Sequelize.BOOLEAN, 32 | allowNull: false, 33 | defaultValue: false 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/models/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Sequelize = require('sequelize') 4 | const setupDatabase = require('../lib/db') 5 | 6 | module.exports = function setupAgentModel (config) { 7 | const sequelize = setupDatabase(config) 8 | 9 | return sequelize.define('agent', { 10 | uuid: { 11 | type: Sequelize.STRING, 12 | allowNull: false 13 | }, 14 | username: { 15 | type: Sequelize.STRING, 16 | allowNull: false 17 | }, 18 | name: { 19 | type: Sequelize.STRING, 20 | allowNull: false 21 | }, 22 | hostname: { 23 | type: Sequelize.STRING, 24 | allowNull: false 25 | }, 26 | pid: { 27 | type: Sequelize.INTEGER, 28 | allowNull: false 29 | }, 30 | connected: { 31 | type: Sequelize.BOOLEAN, 32 | allowNull: false, 33 | defaultValue: false 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /platziverse-api/tests/fixtures/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const agent = { 4 | id: 1, 5 | uuid: 'yyy-yyy-yyy', 6 | name: 'fixture', 7 | username: 'platzi', 8 | hostname: 'test-host', 9 | pid: 0, 10 | connected: true, 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | 15 | const agents = [ 16 | agent, 17 | extend(agent, { id: 2, uuid: 'yyy-yyy-yyw', connected: false, username: 'test' }), 18 | extend(agent, { id: 3, uuid: 'yyy-yyy-yyx' }), 19 | extend(agent, { id: 4, uuid: 'yyy-yyy-yyz', username: 'test' }) 20 | ] 21 | 22 | function extend (obj, values) { 23 | const clone = Object.assign({}, obj) 24 | return Object.assign(clone, values) 25 | } 26 | 27 | module.exports = { 28 | single: agent, 29 | all: agents, 30 | connected: agents.filter(a => a.connected), 31 | platzi: agents.filter(a => a.username === 'platzi'), 32 | byUuid: id => agents.filter(a => a.uuid === id).shift(), 33 | byId: id => agents.filter(a => a.id === id).shift() 34 | } 35 | -------------------------------------------------------------------------------- /platziverse-db/tests/fixtures/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const agent = { 4 | id: 1, 5 | uuid: 'yyy-yyy-yyy', 6 | name: 'fixture', 7 | username: 'platzi', 8 | hostname: 'test-host', 9 | pid: 0, 10 | connected: true, 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | 15 | const agents = [ 16 | agent, 17 | extend(agent, { id: 2, uuid: 'yyy-yyy-yyw', connected: false, username: 'test' }), 18 | extend(agent, { id: 3, uuid: 'yyy-yyy-yyx' }), 19 | extend(agent, { id: 4, uuid: 'yyy-yyy-yyz', username: 'test' }) 20 | ] 21 | 22 | function extend (obj, values) { 23 | const clone = Object.assign({}, obj) 24 | return Object.assign(clone, values) 25 | } 26 | 27 | module.exports = { 28 | single: agent, 29 | all: agents, 30 | connected: agents.filter(a => a.connected), 31 | platzi: agents.filter(a => a.username === 'platzi'), 32 | byUuid: id => agents.filter(a => a.uuid === id).shift(), 33 | byId: id => agents.filter(a => a.id === id).shift() 34 | } 35 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/files/platziverse-agent/examples/index.js: -------------------------------------------------------------------------------- 1 | const PlatziverseAgent = require('../') 2 | 3 | const agent = new PlatziverseAgent({ 4 | name: 'myapp', 5 | username: 'admin', 6 | interval: 2000 7 | }) 8 | 9 | agent.addMetric('rss', function getRss () { 10 | return process.memoryUsage().rss 11 | }) 12 | 13 | agent.addMetric('promiseMetric', function getRandomPromise () { 14 | return Promise.resolve(Math.random()) 15 | }) 16 | 17 | agent.addMetric('callbackMetric', function getRandomCallback (callback) { 18 | setTimeout(() => { 19 | callback(null, Math.random()) 20 | }, 1000) 21 | }) 22 | 23 | agent.connect() 24 | 25 | // This agent only 26 | agent.on('connected', handler) 27 | agent.on('disconnected', handler) 28 | agent.on('message', handler) 29 | 30 | // Other Agents 31 | agent.on('agent/connected', handler) 32 | agent.on('agent/disconnected', handler) 33 | agent.on('agent/message', handler) 34 | 35 | function handler (payload) { 36 | console.log(payload) 37 | } 38 | -------------------------------------------------------------------------------- /platziverse-mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-mqtt", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - MQTT Server", 6 | "main": "server.js", 7 | "scripts": { 8 | "start": "NODE_ENV=production node server.js", 9 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 10 | "lint": "standard" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/platzi/platziverse.git" 15 | }, 16 | "author": "Julian Duque ", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/platzi/platziverse/issues" 20 | }, 21 | "homepage": "https://github.com/platzi/platziverse#readme", 22 | "devDependencies": { 23 | "nodemon": "^1.11.0", 24 | "standard": "^10.0.3" 25 | }, 26 | "dependencies": { 27 | "chalk": "^2.1.0", 28 | "debug": "^3.0.0", 29 | "mosca": "^2.5.2", 30 | "platziverse-db": "file:../platziverse-db", 31 | "redis": "^2.8.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/tests/fixtures/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const agent = { 4 | id: 1, 5 | uuid: 'yyy-yyy-yyy', 6 | name: 'fixture', 7 | username: 'platzi', 8 | hostname: 'test-host', 9 | pid: 0, 10 | connected: true, 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | 15 | const agents = [ 16 | agent, 17 | extend(agent, { id: 2, uuid: 'yyy-yyy-yyw', connected: false, username: 'test' }), 18 | extend(agent, { id: 3, uuid: 'yyy-yyy-yyx' }), 19 | extend(agent, { id: 4, uuid: 'yyy-yyy-yyz', username: 'test' }) 20 | ] 21 | 22 | function extend (obj, values) { 23 | const clone = Object.assign({}, obj) 24 | return Object.assign(clone, values) 25 | } 26 | 27 | module.exports = { 28 | single: agent, 29 | all: agents, 30 | connected: agents.filter(a => a.connected), 31 | platzi: agents.filter(a => a.username === 'platzi'), 32 | byUuid: id => agents.filter(a => a.uuid === id).shift(), 33 | byId: id => agents.filter(a => a.id === id).shift() 34 | } 35 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/tests/fixtures/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const agent = { 4 | id: 1, 5 | uuid: 'yyy-yyy-yyy', 6 | name: 'fixture', 7 | username: 'platzi', 8 | hostname: 'test-host', 9 | pid: 0, 10 | connected: true, 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | 15 | const agents = [ 16 | agent, 17 | extend(agent, { id: 2, uuid: 'yyy-yyy-yyw', connected: false, username: 'test' }), 18 | extend(agent, { id: 3, uuid: 'yyy-yyy-yyx' }), 19 | extend(agent, { id: 4, uuid: 'yyy-yyy-yyz', username: 'test' }) 20 | ] 21 | 22 | function extend (obj, values) { 23 | const clone = Object.assign({}, obj) 24 | return Object.assign(clone, values) 25 | } 26 | 27 | module.exports = { 28 | single: agent, 29 | all: agents, 30 | connected: agents.filter(a => a.connected), 31 | platzi: agents.filter(a => a.username === 'platzi'), 32 | byUuid: id => agents.filter(a => a.uuid === id).shift(), 33 | byId: id => agents.filter(a => a.id === id).shift() 34 | } 35 | -------------------------------------------------------------------------------- /platziverse-agent/examples/index.js: -------------------------------------------------------------------------------- 1 | const PlatziverseAgent = require('../') 2 | 3 | const agent = new PlatziverseAgent({ 4 | name: 'Node.js', 5 | username: 'admin', 6 | interval: 100, 7 | mqtt: { 8 | host: 'mqtt://api.platziverse.space' 9 | } 10 | }) 11 | 12 | agent.addMetric('rss', function getRss () { 13 | return process.memoryUsage().rss 14 | }) 15 | 16 | agent.addMetric('promiseMetric', function getRandomPromise () { 17 | return Promise.resolve(Math.random()) 18 | }) 19 | 20 | agent.addMetric('callbackMetric', function getRandomCallback (callback) { 21 | setTimeout(() => { 22 | callback(null, Math.random()) 23 | }, 1000) 24 | }) 25 | 26 | agent.connect() 27 | 28 | // This agent only 29 | agent.on('connected', handler) 30 | agent.on('disconnected', handler) 31 | agent.on('message', handler) 32 | 33 | // Other Agents 34 | agent.on('agent/connected', handler) 35 | agent.on('agent/disconnected', handler) 36 | agent.on('agent/message', handler) 37 | 38 | function handler (payload) { 39 | console.log(payload) 40 | } 41 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/files/platziverse-mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-mqtt", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - MQTT Server", 6 | "main": "server.js", 7 | "scripts": { 8 | "start": "NODE_ENV=production node server.js", 9 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 10 | "lint": "standard" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/platzi/platziverse.git" 15 | }, 16 | "author": "Julian Duque ", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/platzi/platziverse/issues" 20 | }, 21 | "homepage": "https://github.com/platzi/platziverse#readme", 22 | "devDependencies": { 23 | "nodemon": "^1.11.0", 24 | "standard": "^10.0.3" 25 | }, 26 | "dependencies": { 27 | "chalk": "^2.1.0", 28 | "debug": "^3.0.0", 29 | "mosca": "^2.5.2", 30 | "platziverse-db": "file:../platziverse-db", 31 | "redis": "^2.8.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /platziverse-agent/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-agent 2 | 3 | ## Usage 4 | 5 | ``` js 6 | const PlatziverseAgent = require('platziverse-agent') 7 | 8 | const agent = new PlatziverseAgent({ 9 | name: 'myapp', 10 | username: 'admin', 11 | interval: 2000 12 | }) 13 | 14 | agent.addMetric('rss', function getRss () { 15 | return process.memoryUsage().rss 16 | }) 17 | 18 | agent.addMetric('promiseMetric', function getRandomPromise () { 19 | return Promise.resolve(Math.random()) 20 | }) 21 | 22 | agent.addMetric('callbackMetric', function getRandomCallback (callback) { 23 | setTimeout(() => { 24 | callback(null, Math.random()) 25 | }, 1000) 26 | }) 27 | 28 | agent.connect() 29 | 30 | // This agent only 31 | agent.on('connected', handler) 32 | agent.on('disconnected', handler) 33 | agent.on('message', handler) 34 | 35 | // Other Agents 36 | agent.on('agent/connected', handler) 37 | agent.on('agent/disconnected', handler) 38 | agent.on('agent/message', handler) 39 | 40 | function handler (payload) { 41 | console.log(payload) 42 | } 43 | 44 | setTimeout(() => agent.disconnect(), 20000) 45 | ``` 46 | -------------------------------------------------------------------------------- /platziverse-db/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const setupDatabase = require('./lib/db') 4 | const setupAgentModel = require('./models/agent') 5 | const setupMetricModel = require('./models/metric') 6 | const setupAgent = require('./lib/agent') 7 | const setupMetric = require('./lib/metric') 8 | const defaults = require('defaults') 9 | 10 | module.exports = async function (config) { 11 | config = defaults(config, { 12 | dialect: 'sqlite', 13 | pool: { 14 | max: 10, 15 | min: 0, 16 | idle: 10000 17 | }, 18 | query: { 19 | raw: true 20 | } 21 | }) 22 | 23 | const sequelize = setupDatabase(config) 24 | const AgentModel = setupAgentModel(config) 25 | const MetricModel = setupMetricModel(config) 26 | 27 | AgentModel.hasMany(MetricModel) 28 | MetricModel.belongsTo(AgentModel) 29 | 30 | await sequelize.authenticate() 31 | 32 | if (config.setup) { 33 | await sequelize.sync({ force: true }) 34 | } 35 | 36 | const Agent = setupAgent(AgentModel) 37 | const Metric = setupMetric(MetricModel, AgentModel) 38 | 39 | return { 40 | Agent, 41 | Metric 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/files/platziverse-agent/README.md: -------------------------------------------------------------------------------- 1 | # platziverse-agent 2 | 3 | ## Usage 4 | 5 | ``` js 6 | const PlatziverseAgent = require('platziverse-agent') 7 | 8 | const agent = new PlatziverseAgent({ 9 | name: 'myapp', 10 | username: 'admin', 11 | interval: 2000 12 | }) 13 | 14 | agent.addMetric('rss', function getRss () { 15 | return process.memoryUsage().rss 16 | }) 17 | 18 | agent.addMetric('promiseMetric', function getRandomPromise () { 19 | return Promise.resolve(Math.random()) 20 | }) 21 | 22 | agent.addMetric('callbackMetric', function getRandomCallback (callback) { 23 | setTimeout(() => { 24 | callback(null, Math.random()) 25 | }, 1000) 26 | }) 27 | 28 | agent.connect() 29 | 30 | // This agent only 31 | agent.on('connected', handler) 32 | agent.on('disconnected', handler) 33 | agent.on('message', handler) 34 | 35 | // Other Agents 36 | agent.on('agent/connected', handler) 37 | agent.on('agent/disconnected', handler) 38 | agent.on('agent/message', handler) 39 | 40 | function handler (payload) { 41 | console.log(payload) 42 | } 43 | 44 | setTimeout(() => agent.disconnect(), 20000) 45 | ``` 46 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const setupDatabase = require('./lib/db') 4 | const setupAgentModel = require('./models/agent') 5 | const setupMetricModel = require('./models/metric') 6 | const setupAgent = require('./lib/agent') 7 | const setupMetric = require('./lib/metric') 8 | const defaults = require('defaults') 9 | 10 | module.exports = async function (config) { 11 | config = defaults(config, { 12 | dialect: 'sqlite', 13 | pool: { 14 | max: 10, 15 | min: 0, 16 | idle: 10000 17 | }, 18 | query: { 19 | raw: true 20 | } 21 | }) 22 | 23 | const sequelize = setupDatabase(config) 24 | const AgentModel = setupAgentModel(config) 25 | const MetricModel = setupMetricModel(config) 26 | 27 | AgentModel.hasMany(MetricModel) 28 | MetricModel.belongsTo(AgentModel) 29 | 30 | await sequelize.authenticate() 31 | 32 | if (config.setup) { 33 | await sequelize.sync({ force: true }) 34 | } 35 | 36 | const Agent = setupAgent(AgentModel) 37 | const Metric = setupMetric(MetricModel, AgentModel) 38 | 39 | return { 40 | Agent, 41 | Metric 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /platziverse-web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Platziverse - Simple IoT Platform 8 | 9 | 28 | 29 | 30 |
31 |
32 | PlatziVerse 33 |
34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /platziverse-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-db", 3 | "version": "1.0.0", 4 | "description": "A Simple IoT Platform - DB Module", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "DEBUG=platziverse:* nyc --reporter=lcov ava tests/ --verbose", 8 | "setup": "DEBUG=platziverse:* node setup.js", 9 | "lint": "standard" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/platzi/platziverse.git" 14 | }, 15 | "author": "Julian Duque ", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/platzi/platziverse/issues" 19 | }, 20 | "homepage": "https://github.com/platzi/platziverse#readme", 21 | "devDependencies": { 22 | "ava": "^0.21.0", 23 | "nyc": "^11.1.0", 24 | "proxyquire": "^1.8.0", 25 | "sinon": "^3.1.0", 26 | "sqlite3": "^3.1.9", 27 | "standard": "^10.0.3" 28 | }, 29 | "dependencies": { 30 | "chalk": "^2.1.0", 31 | "debug": "^2.6.8", 32 | "defaults": "^1.0.3", 33 | "inquirer": "^3.2.1", 34 | "minimist": "^1.2.0", 35 | "pg": "^7.1.0", 36 | "pg-hstore": "^2.3.2", 37 | "sequelize": "^4.4.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Platziverse - Simple IoT Platform 8 | 9 | 28 | 29 | 30 |
31 |
32 | PlatziVerse 33 |
34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /platziverse-api/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api') 4 | const http = require('http') 5 | const chalk = require('chalk') 6 | const express = require('express') 7 | const asyncify = require('express-asyncify') 8 | 9 | const api = require('./api') 10 | 11 | const port = process.env.PORT || 3000 12 | const app = asyncify(express()) 13 | const server = http.createServer(app) 14 | 15 | app.use('/api', api) 16 | 17 | // Express Error Handler 18 | app.use((err, req, res, next) => { 19 | debug(`Error: ${err.message}`) 20 | 21 | if (err.message.match(/not found/)) { 22 | return res.status(404).send({ error: err.message }) 23 | } 24 | 25 | res.status(500).send({ error: err.message }) 26 | }) 27 | 28 | function handleFatalError (err) { 29 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 30 | console.error(err.stack) 31 | process.exit(1) 32 | } 33 | 34 | if (!module.parent) { 35 | process.on('uncaughtException', handleFatalError) 36 | process.on('unhandledRejection', handleFatalError) 37 | 38 | server.listen(port, () => { 39 | console.log(`${chalk.green('[platziverse-api]')} server listening on port ${port}`) 40 | }) 41 | } 42 | 43 | module.exports = server 44 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-db", 3 | "version": "1.0.0", 4 | "description": "A Simple IoT Platform - DB Module", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "DEBUG=platziverse:* nyc --reporter=lcov ava tests/ --verbose", 8 | "setup": "DEBUG=platziverse:* node setup.js", 9 | "lint": "standard" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/platzi/platziverse.git" 14 | }, 15 | "author": "Julian Duque ", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/platzi/platziverse/issues" 19 | }, 20 | "homepage": "https://github.com/platzi/platziverse#readme", 21 | "devDependencies": { 22 | "ava": "^0.21.0", 23 | "nyc": "^11.1.0", 24 | "proxyquire": "^1.8.0", 25 | "sinon": "^3.1.0", 26 | "sqlite3": "^3.1.9", 27 | "standard": "^10.0.3" 28 | }, 29 | "dependencies": { 30 | "chalk": "^2.1.0", 31 | "debug": "^2.6.8", 32 | "defaults": "^1.0.3", 33 | "inquirer": "^3.2.1", 34 | "minimist": "^1.2.0", 35 | "pg": "^7.1.0", 36 | "pg-hstore": "^2.3.2", 37 | "sequelize": "^4.4.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api') 4 | const http = require('http') 5 | const chalk = require('chalk') 6 | const express = require('express') 7 | const asyncify = require('express-asyncify') 8 | 9 | const api = require('./api') 10 | 11 | const port = process.env.PORT || 3000 12 | const app = asyncify(express()) 13 | const server = http.createServer(app) 14 | 15 | app.use('/api', api) 16 | 17 | // Express Error Handler 18 | app.use((err, req, res, next) => { 19 | debug(`Error: ${err.message}`) 20 | 21 | if (err.message.match(/not found/)) { 22 | return res.status(404).send({ error: err.message }) 23 | } 24 | 25 | res.status(500).send({ error: err.message }) 26 | }) 27 | 28 | function handleFatalError (err) { 29 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 30 | console.error(err.stack) 31 | process.exit(1) 32 | } 33 | 34 | if (!module.parent) { 35 | process.on('uncaughtException', handleFatalError) 36 | process.on('unhandledRejection', handleFatalError) 37 | 38 | server.listen(port, () => { 39 | console.log(`${chalk.green('[platziverse-api]')} server listening on port ${port}`) 40 | }) 41 | } 42 | 43 | module.exports = server 44 | -------------------------------------------------------------------------------- /platziverse-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-api", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - API", 6 | "main": "server.js", 7 | "scripts": { 8 | "test": "DEBUG=platziverse:* ava tests/ --verbose", 9 | "start": "NODE_ENV=production node server.js", 10 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 11 | "lint": "standard" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/platzi/platziverse.git" 16 | }, 17 | "author": "Julian Duque ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/platzi/platziverse/issues" 21 | }, 22 | "homepage": "https://github.com/platzi/platziverse#readme", 23 | "devDependencies": { 24 | "ava": "^0.21.0", 25 | "nodemon": "^1.11.0", 26 | "proxyquire": "^1.8.0", 27 | "sinon": "^3.1.0", 28 | "standard": "^10.0.3", 29 | "supertest": "^3.0.0" 30 | }, 31 | "dependencies": { 32 | "chalk": "^2.1.0", 33 | "debug": "^3.0.0", 34 | "express": "^4.15.4", 35 | "express-asyncify": "^1.0.0", 36 | "express-jwt": "^5.3.0", 37 | "express-jwt-permissions": "^1.0.0", 38 | "jsonwebtoken": "^7.4.2", 39 | "platziverse-db": "file:../platziverse-db" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /platziverse-db/setup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:db:setup') 4 | const inquirer = require('inquirer') 5 | const chalk = require('chalk') 6 | const minimist = require('minimist') 7 | const db = require('./') 8 | 9 | const args = minimist(process.argv) 10 | const prompt = inquirer.createPromptModule() 11 | 12 | async function setup () { 13 | if (!args.yes) { 14 | const answer = await prompt([ 15 | { 16 | type: 'confirm', 17 | name: 'setup', 18 | message: 'This will destroy your database, are you sure?' 19 | } 20 | ]) 21 | 22 | if (!answer.setup) { 23 | return console.log('Nothing happened :)') 24 | } 25 | } 26 | 27 | const config = { 28 | database: process.env.DB_NAME || 'platziverse', 29 | username: process.env.DB_USER || 'platzi', 30 | password: process.env.DB_PASS || 'platzi', 31 | host: process.env.DB_HOST || 'localhost', 32 | dialect: 'postgres', 33 | logging: s => debug(s), 34 | setup: true 35 | } 36 | 37 | await db(config).catch(handleFatalError) 38 | 39 | console.log('Success!') 40 | process.exit(0) 41 | } 42 | 43 | function handleFatalError (err) { 44 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 45 | console.error(err.stack) 46 | process.exit(1) 47 | } 48 | 49 | setup() 50 | -------------------------------------------------------------------------------- /platziverse-db/lib/metric.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function setupMetric (MetricModel, AgentModel) { 4 | async function findByAgentUuid (uuid) { 5 | return MetricModel.findAll({ 6 | attributes: [ 'type' ], 7 | group: [ 'type' ], 8 | include: [{ 9 | attributes: [], 10 | model: AgentModel, 11 | where: { 12 | uuid 13 | } 14 | }], 15 | raw: true 16 | }) 17 | } 18 | 19 | async function findByTypeAgentUuid (type, uuid) { 20 | return MetricModel.findAll({ 21 | attributes: [ 'id', 'type', 'value', 'createdAt' ], 22 | where: { 23 | type 24 | }, 25 | limit: 20, 26 | order: [[ 'createdAt', 'DESC' ]], 27 | include: [{ 28 | attributes: [], 29 | model: AgentModel, 30 | where: { 31 | uuid 32 | } 33 | }], 34 | raw: true 35 | }) 36 | } 37 | 38 | async function create (uuid, metric) { 39 | const agent = await AgentModel.findOne({ 40 | where: { uuid } 41 | }) 42 | 43 | if (agent) { 44 | Object.assign(metric, { agentId: agent.id }) 45 | const result = await MetricModel.create(metric) 46 | return result.toJSON() 47 | } 48 | } 49 | 50 | return { 51 | create, 52 | findByAgentUuid, 53 | findByTypeAgentUuid 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-api", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Simple IoT Platform - API", 6 | "main": "server.js", 7 | "scripts": { 8 | "test": "DEBUG=platziverse:* ava tests/ --verbose", 9 | "start": "NODE_ENV=production node server.js", 10 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 11 | "lint": "standard" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/platzi/platziverse.git" 16 | }, 17 | "author": "Julian Duque ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/platzi/platziverse/issues" 21 | }, 22 | "homepage": "https://github.com/platzi/platziverse#readme", 23 | "devDependencies": { 24 | "ava": "^0.21.0", 25 | "nodemon": "^1.11.0", 26 | "proxyquire": "^1.8.0", 27 | "sinon": "^3.1.0", 28 | "standard": "^10.0.3", 29 | "supertest": "^3.0.0" 30 | }, 31 | "dependencies": { 32 | "chalk": "^2.1.0", 33 | "debug": "^3.0.0", 34 | "express": "^4.15.4", 35 | "express-asyncify": "^1.0.0", 36 | "express-jwt": "^5.3.0", 37 | "express-jwt-permissions": "^1.0.0", 38 | "jsonwebtoken": "^7.4.2", 39 | "platziverse-db": "file:../platziverse-db" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/setup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:db:setup') 4 | const inquirer = require('inquirer') 5 | const chalk = require('chalk') 6 | const minimist = require('minimist') 7 | const db = require('./') 8 | 9 | const args = minimist(process.argv) 10 | const prompt = inquirer.createPromptModule() 11 | 12 | async function setup () { 13 | if (!args.yes) { 14 | const answer = await prompt([ 15 | { 16 | type: 'confirm', 17 | name: 'setup', 18 | message: 'This will destroy your database, are you sure?' 19 | } 20 | ]) 21 | 22 | if (!answer.setup) { 23 | return console.log('Nothing happened :)') 24 | } 25 | } 26 | 27 | const config = { 28 | database: process.env.DB_NAME || 'platziverse', 29 | username: process.env.DB_USER || 'platzi', 30 | password: process.env.DB_PASS || 'platzi', 31 | host: process.env.DB_HOST || 'localhost', 32 | dialect: 'postgres', 33 | logging: s => debug(s), 34 | setup: true 35 | } 36 | 37 | await db(config).catch(handleFatalError) 38 | 39 | console.log('Success!') 40 | process.exit(0) 41 | } 42 | 43 | function handleFatalError (err) { 44 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 45 | console.error(err.stack) 46 | process.exit(1) 47 | } 48 | 49 | setup() 50 | -------------------------------------------------------------------------------- /platziverse-db/lib/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function setupAgent (AgentModel) { 4 | async function createOrUpdate (agent) { 5 | const cond = { 6 | where: { 7 | uuid: agent.uuid 8 | } 9 | } 10 | 11 | const existingAgent = await AgentModel.findOne(cond) 12 | 13 | if (existingAgent) { 14 | const updated = await AgentModel.update(agent, cond) 15 | return updated ? AgentModel.findOne(cond) : existingAgent 16 | } 17 | 18 | const result = await AgentModel.create(agent) 19 | return result.toJSON() 20 | } 21 | 22 | function findById (id) { 23 | return AgentModel.findById(id) 24 | } 25 | 26 | function findByUuid (uuid) { 27 | return AgentModel.findOne({ 28 | where: { 29 | uuid 30 | } 31 | }) 32 | } 33 | 34 | function findAll () { 35 | return AgentModel.findAll() 36 | } 37 | 38 | function findConnected () { 39 | return AgentModel.findAll({ 40 | where: { 41 | connected: true 42 | } 43 | }) 44 | } 45 | 46 | function findByUsername (username) { 47 | return AgentModel.findAll({ 48 | where: { 49 | username, 50 | connected: true 51 | } 52 | }) 53 | } 54 | 55 | return { 56 | createOrUpdate, 57 | findById, 58 | findByUuid, 59 | findAll, 60 | findConnected, 61 | findByUsername 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/lib/metric.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function setupMetric (MetricModel, AgentModel) { 4 | async function findByAgentUuid (uuid) { 5 | return MetricModel.findAll({ 6 | attributes: [ 'type' ], 7 | group: [ 'type' ], 8 | include: [{ 9 | attributes: [], 10 | model: AgentModel, 11 | where: { 12 | uuid 13 | } 14 | }], 15 | raw: true 16 | }) 17 | } 18 | 19 | async function findByTypeAgentUuid (type, uuid) { 20 | return MetricModel.findAll({ 21 | attributes: [ 'id', 'type', 'value', 'createdAt' ], 22 | where: { 23 | type 24 | }, 25 | limit: 20, 26 | order: [[ 'createdAt', 'DESC' ]], 27 | include: [{ 28 | attributes: [], 29 | model: AgentModel, 30 | where: { 31 | uuid 32 | } 33 | }], 34 | raw: true 35 | }) 36 | } 37 | 38 | async function create (uuid, metric) { 39 | const agent = await AgentModel.findOne({ 40 | where: { uuid } 41 | }) 42 | 43 | if (agent) { 44 | Object.assign(metric, { agentId: agent.id }) 45 | const result = await MetricModel.create(metric) 46 | return result.toJSON() 47 | } 48 | } 49 | 50 | return { 51 | create, 52 | findByAgentUuid, 53 | findByTypeAgentUuid 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/lib/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function setupAgent (AgentModel) { 4 | async function createOrUpdate (agent) { 5 | const cond = { 6 | where: { 7 | uuid: agent.uuid 8 | } 9 | } 10 | 11 | const existingAgent = await AgentModel.findOne(cond) 12 | 13 | if (existingAgent) { 14 | const updated = await AgentModel.update(agent, cond) 15 | return updated ? AgentModel.findOne(cond) : existingAgent 16 | } 17 | 18 | const result = await AgentModel.create(agent) 19 | return result.toJSON() 20 | } 21 | 22 | function findById (id) { 23 | return AgentModel.findById(id) 24 | } 25 | 26 | function findByUuid (uuid) { 27 | return AgentModel.findOne({ 28 | where: { 29 | uuid 30 | } 31 | }) 32 | } 33 | 34 | function findAll () { 35 | return AgentModel.findAll() 36 | } 37 | 38 | function findConnected () { 39 | return AgentModel.findAll({ 40 | where: { 41 | connected: true 42 | } 43 | }) 44 | } 45 | 46 | function findByUsername (username) { 47 | return AgentModel.findAll({ 48 | where: { 49 | username, 50 | connected: true 51 | } 52 | }) 53 | } 54 | 55 | return { 56 | createOrUpdate, 57 | findById, 58 | findByUuid, 59 | findAll, 60 | findConnected, 61 | findByUsername 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /platziverse-web/client/app.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 69 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/client/app.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 69 | -------------------------------------------------------------------------------- /platziverse-db/examples/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../') 4 | 5 | async function run () { 6 | const config = { 7 | database: process.env.DB_NAME || 'platziverse', 8 | username: process.env.DB_USER || 'platzi', 9 | password: process.env.DB_PASS || 'platzi', 10 | host: process.env.DB_HOST || 'localhost', 11 | dialect: 'postgres' 12 | } 13 | 14 | const { Agent, Metric } = await db(config).catch(handleFatalError) 15 | 16 | const agent = await Agent.createOrUpdate({ 17 | uuid: 'yyy', 18 | name: 'test', 19 | username: 'test', 20 | hostname: 'test', 21 | pid: 1, 22 | connected: true 23 | }).catch(handleFatalError) 24 | 25 | console.log('--agent--') 26 | console.log(agent) 27 | 28 | const agents = await Agent.findAll().catch(handleFatalError) 29 | console.log('--agents--') 30 | console.log(agents) 31 | 32 | const metrics = await Metric.findByAgentUuid(agent.uuid).catch(handleFatalError) 33 | console.log('--metrics--') 34 | console.log(metrics) 35 | 36 | const metric = await Metric.create(agent.uuid, { 37 | type: 'memory', 38 | value: '300' 39 | }).catch(handleFatalError) 40 | 41 | console.log('--metric--') 42 | console.log(metric) 43 | 44 | const metricsByType = await Metric.findByTypeAgentUuid('memory', agent.uuid).catch(handleFatalError) 45 | console.log('--metrics--') 46 | console.log(metricsByType) 47 | } 48 | 49 | function handleFatalError (err) { 50 | console.error(err.message) 51 | console.error(err.stack) 52 | process.exit(1) 53 | } 54 | 55 | run() 56 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/examples/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../') 4 | 5 | async function run () { 6 | const config = { 7 | database: process.env.DB_NAME || 'platziverse', 8 | username: process.env.DB_USER || 'platzi', 9 | password: process.env.DB_PASS || 'platzi', 10 | host: process.env.DB_HOST || 'localhost', 11 | dialect: 'postgres' 12 | } 13 | 14 | const { Agent, Metric } = await db(config).catch(handleFatalError) 15 | 16 | const agent = await Agent.createOrUpdate({ 17 | uuid: 'yyy', 18 | name: 'test', 19 | username: 'test', 20 | hostname: 'test', 21 | pid: 1, 22 | connected: true 23 | }).catch(handleFatalError) 24 | 25 | console.log('--agent--') 26 | console.log(agent) 27 | 28 | const agents = await Agent.findAll().catch(handleFatalError) 29 | console.log('--agents--') 30 | console.log(agents) 31 | 32 | const metrics = await Metric.findByAgentUuid(agent.uuid).catch(handleFatalError) 33 | console.log('--metrics--') 34 | console.log(metrics) 35 | 36 | const metric = await Metric.create(agent.uuid, { 37 | type: 'memory', 38 | value: '300' 39 | }).catch(handleFatalError) 40 | 41 | console.log('--metric--') 42 | console.log(metric) 43 | 44 | const metricsByType = await Metric.findByTypeAgentUuid('memory', agent.uuid).catch(handleFatalError) 45 | console.log('--metrics--') 46 | console.log(metricsByType) 47 | } 48 | 49 | function handleFatalError (err) { 50 | console.error(err.message) 51 | console.error(err.stack) 52 | process.exit(1) 53 | } 54 | 55 | run() 56 | -------------------------------------------------------------------------------- /platziverse-web/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:web') 4 | const http = require('http') 5 | const path = require('path') 6 | const express = require('express') 7 | const asyncify = require('express-asyncify') 8 | const socketio = require('socket.io') 9 | const chalk = require('chalk') 10 | const PlatziverseAgent = require('platziverse-agent') 11 | 12 | const proxy = require('./proxy') 13 | const { pipe } = require('./utils') 14 | const { mqttHost } = require('./config') 15 | 16 | const port = process.env.PORT || 8080 17 | const app = asyncify(express()) 18 | const server = http.createServer(app) 19 | const io = socketio(server) 20 | const agent = new PlatziverseAgent({ 21 | mqtt: { 22 | host: mqttHost 23 | } 24 | }) 25 | 26 | app.use(express.static(path.join(__dirname, 'public'))) 27 | app.use('/', proxy) 28 | 29 | // Socket.io / WebSockets 30 | io.on('connect', socket => { 31 | debug(`Connected ${socket.id}`) 32 | 33 | pipe(agent, socket) 34 | }) 35 | 36 | // Express Error Handler 37 | app.use((err, req, res, next) => { 38 | debug(`Error: ${err.message}`) 39 | 40 | if (err.message.match(/not found/)) { 41 | return res.status(404).send({ error: err.message }) 42 | } 43 | 44 | res.status(500).send({ error: err.message }) 45 | }) 46 | 47 | function handleFatalError (err) { 48 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 49 | console.error(err.stack) 50 | process.exit(1) 51 | } 52 | 53 | process.on('uncaughtException', handleFatalError) 54 | process.on('unhandledRejection', handleFatalError) 55 | 56 | server.listen(port, () => { 57 | console.log(`${chalk.green('[platziverse-web]')} server listening on port ${port}`) 58 | agent.connect() 59 | }) 60 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:web') 4 | const http = require('http') 5 | const path = require('path') 6 | const express = require('express') 7 | const asyncify = require('express-asyncify') 8 | const socketio = require('socket.io') 9 | const chalk = require('chalk') 10 | const PlatziverseAgent = require('platziverse-agent') 11 | 12 | const proxy = require('./proxy') 13 | const { pipe } = require('./utils') 14 | const { mqttHost } = require('./config') 15 | 16 | const port = process.env.PORT || 8080 17 | const app = asyncify(express()) 18 | const server = http.createServer(app) 19 | const io = socketio(server) 20 | const agent = new PlatziverseAgent({ 21 | mqtt: { 22 | host: mqttHost 23 | } 24 | }) 25 | 26 | app.use(express.static(path.join(__dirname, 'public'))) 27 | app.use('/', proxy) 28 | 29 | // Socket.io / WebSockets 30 | io.on('connect', socket => { 31 | debug(`Connected ${socket.id}`) 32 | 33 | pipe(agent, socket) 34 | }) 35 | 36 | // Express Error Handler 37 | app.use((err, req, res, next) => { 38 | debug(`Error: ${err.message}`) 39 | 40 | if (err.message.match(/not found/)) { 41 | return res.status(404).send({ error: err.message }) 42 | } 43 | 44 | res.status(500).send({ error: err.message }) 45 | }) 46 | 47 | function handleFatalError (err) { 48 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 49 | console.error(err.stack) 50 | process.exit(1) 51 | } 52 | 53 | process.on('uncaughtException', handleFatalError) 54 | process.on('unhandledRejection', handleFatalError) 55 | 56 | server.listen(port, () => { 57 | console.log(`${chalk.green('[platziverse-web]')} server listening on port ${port}`) 58 | agent.connect() 59 | }) 60 | -------------------------------------------------------------------------------- /platziverse-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-web", 3 | "version": "1.0.0", 4 | "description": "A Simple IoT Platform - Web Dashboard", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "NODE_ENV=production node server.js", 8 | "prestart": "npm run build", 9 | "build": "NODE_ENV=production browserify -g envify -t babelify -t vueify client/app.js | uglifyjs -c -m -o public/bundle.js", 10 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 11 | "prestart-dev": "npm run build-dev", 12 | "build-dev": "browserify -g envify -t babelify -t vueify client/app.js -o public/bundle.js", 13 | "lint": "standard" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/platzi/platziverse.git" 18 | }, 19 | "author": "Julian Duque ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/platzi/platziverse/issues" 23 | }, 24 | "homepage": "https://github.com/platzi/platziverse#readme", 25 | "devDependencies": { 26 | "babel-plugin-transform-runtime": "^6.23.0", 27 | "babel-preset-es2015": "^6.24.1", 28 | "babel-preset-stage-3": "^6.24.1", 29 | "babelify": "^7.3.0", 30 | "browserify": "^14.4.0", 31 | "envify": "^4.1.0", 32 | "nodemon": "^1.11.0", 33 | "standard": "^10.0.3", 34 | "uglify-js": "^3.0.27", 35 | "vueify": "^9.4.1" 36 | }, 37 | "dependencies": { 38 | "chalk": "^2.1.0", 39 | "chart.js": "^2.6.0", 40 | "debug": "^3.0.0", 41 | "express": "^4.15.4", 42 | "express-asyncify": "^1.0.0", 43 | "moment": "^2.18.1", 44 | "platziverse-agent": "file:../platziverse-agent", 45 | "random-material-color": "^1.0.3", 46 | "request": "^2.81.0", 47 | "request-promise-native": "^1.0.4", 48 | "socket.io": "^2.0.3", 49 | "socket.io-client": "^2.0.3", 50 | "vue": "^2.4.2", 51 | "vue-chartjs": "^2.8.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platziverse-web", 3 | "version": "1.0.0", 4 | "description": "A Simple IoT Platform - Web Dashboard", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "NODE_ENV=production node server.js", 8 | "prestart": "npm run build", 9 | "build": "NODE_ENV=production browserify -g envify -t babelify -t vueify client/app.js | uglifyjs -c -m -o public/bundle.js", 10 | "start-dev": "DEBUG=platziverse:* nodemon server.js", 11 | "prestart-dev": "npm run build-dev", 12 | "build-dev": "browserify -g envify -t babelify -t vueify client/app.js -o public/bundle.js", 13 | "lint": "standard" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/platzi/platziverse.git" 18 | }, 19 | "author": "Julian Duque ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/platzi/platziverse/issues" 23 | }, 24 | "homepage": "https://github.com/platzi/platziverse#readme", 25 | "devDependencies": { 26 | "babel-plugin-transform-runtime": "^6.23.0", 27 | "babel-preset-es2015": "^6.24.1", 28 | "babel-preset-stage-3": "^6.24.1", 29 | "babelify": "^7.3.0", 30 | "browserify": "^14.4.0", 31 | "envify": "^4.1.0", 32 | "nodemon": "^1.11.0", 33 | "standard": "^10.0.3", 34 | "uglify-js": "^3.0.27", 35 | "vueify": "^9.4.1" 36 | }, 37 | "dependencies": { 38 | "chalk": "^2.1.0", 39 | "chart.js": "^2.6.0", 40 | "debug": "^3.0.0", 41 | "express": "^4.15.4", 42 | "express-asyncify": "^1.0.0", 43 | "moment": "^2.18.1", 44 | "platziverse-agent": "file:../platziverse-agent", 45 | "random-material-color": "^1.0.3", 46 | "request": "^2.81.0", 47 | "request-promise-native": "^1.0.4", 48 | "socket.io": "^2.0.3", 49 | "socket.io-client": "^2.0.3", 50 | "vue": "^2.4.2", 51 | "vue-chartjs": "^2.8.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /platziverse-api/tests/api-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('ava') 4 | const util = require('util') 5 | const request = require('supertest') 6 | const sinon = require('sinon') 7 | const proxyquire = require('proxyquire') 8 | 9 | const agentFixtures = require('./fixtures/agent') 10 | const config = require('../config') 11 | const auth = require('../auth') 12 | const sign = util.promisify(auth.sign) 13 | 14 | let sandbox = null 15 | let server = null 16 | let dbStub = null 17 | let token = null 18 | let AgentStub = {} 19 | let MetricStub = {} 20 | 21 | test.beforeEach(async () => { 22 | sandbox = sinon.sandbox.create() 23 | 24 | dbStub = sandbox.stub() 25 | dbStub.returns(Promise.resolve({ 26 | Agent: AgentStub, 27 | Metric: MetricStub 28 | })) 29 | 30 | AgentStub.findConnected = sandbox.stub() 31 | AgentStub.findConnected.returns(Promise.resolve(agentFixtures.connected)) 32 | 33 | token = await sign({ admin: true, username: 'platzi' }, config.auth.secret) 34 | 35 | const api = proxyquire('../api', { 36 | 'platziverse-db': dbStub 37 | }) 38 | 39 | server = proxyquire('../server', { 40 | './api': api 41 | }) 42 | }) 43 | 44 | test.afterEach(() => { 45 | sandbox && sinon.sandbox.restore() 46 | }) 47 | 48 | test.serial.cb('/api/agents', t => { 49 | request(server) 50 | .get('/api/agents') 51 | .set('Authorization', `Bearer ${token}`) 52 | .expect(200) 53 | .expect('Content-Type', /json/) 54 | .end((err, res) => { 55 | t.falsy(err, 'should not return an error') 56 | let body = JSON.stringify(res.body) 57 | let expected = JSON.stringify(agentFixtures.connected) 58 | t.deepEqual(body, expected, 'response body should be the expected') 59 | t.end() 60 | }) 61 | }) 62 | 63 | test.serial.todo('/api/agents - not authorized') 64 | test.serial.todo('/api/agent/:uuid') 65 | test.serial.todo('/api/agent/:uuid - not found') 66 | 67 | test.serial.todo('/api/metrics/:uuid') 68 | test.serial.todo('/api/metrics/:uuid - not found') 69 | 70 | test.serial.todo('/api/metrics/:uuid/:type') 71 | test.serial.todo('/api/metrics/:uuid/:type - not found') 72 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/tests/api-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('ava') 4 | const util = require('util') 5 | const request = require('supertest') 6 | const sinon = require('sinon') 7 | const proxyquire = require('proxyquire') 8 | 9 | const agentFixtures = require('./fixtures/agent') 10 | const config = require('../config') 11 | const auth = require('../auth') 12 | const sign = util.promisify(auth.sign) 13 | 14 | let sandbox = null 15 | let server = null 16 | let dbStub = null 17 | let token = null 18 | let AgentStub = {} 19 | let MetricStub = {} 20 | 21 | test.beforeEach(async () => { 22 | sandbox = sinon.sandbox.create() 23 | 24 | dbStub = sandbox.stub() 25 | dbStub.returns(Promise.resolve({ 26 | Agent: AgentStub, 27 | Metric: MetricStub 28 | })) 29 | 30 | AgentStub.findConnected = sandbox.stub() 31 | AgentStub.findConnected.returns(Promise.resolve(agentFixtures.connected)) 32 | 33 | token = await sign({ admin: true, username: 'platzi' }, config.auth.secret) 34 | 35 | const api = proxyquire('../api', { 36 | 'platziverse-db': dbStub 37 | }) 38 | 39 | server = proxyquire('../server', { 40 | './api': api 41 | }) 42 | }) 43 | 44 | test.afterEach(() => { 45 | sandbox && sinon.sandbox.restore() 46 | }) 47 | 48 | test.serial.cb('/api/agents', t => { 49 | request(server) 50 | .get('/api/agents') 51 | .set('Authorization', `Bearer ${token}`) 52 | .expect(200) 53 | .expect('Content-Type', /json/) 54 | .end((err, res) => { 55 | t.falsy(err, 'should not return an error') 56 | let body = JSON.stringify(res.body) 57 | let expected = JSON.stringify(agentFixtures.connected) 58 | t.deepEqual(body, expected, 'response body should be the expected') 59 | t.end() 60 | }) 61 | }) 62 | 63 | test.serial.todo('/api/agents - not authorized') 64 | test.serial.todo('/api/agent/:uuid') 65 | test.serial.todo('/api/agent/:uuid - not found') 66 | 67 | test.serial.todo('/api/metrics/:uuid') 68 | test.serial.todo('/api/metrics/:uuid - not found') 69 | 70 | test.serial.todo('/api/metrics/:uuid/:type') 71 | test.serial.todo('/api/metrics/:uuid/:type - not found') 72 | -------------------------------------------------------------------------------- /platziverse-web/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const express = require('express') 4 | const asyncify = require('express-asyncify') 5 | const request = require('request-promise-native') 6 | 7 | const { endpoint, apiToken } = require('./config') 8 | 9 | const api = asyncify(express.Router()) 10 | 11 | api.get('/agents', async (req, res, next) => { 12 | const options = { 13 | method: 'GET', 14 | url: `${endpoint}/api/agents`, 15 | headers: { 16 | 'Authorization': `Bearer ${apiToken}` 17 | }, 18 | json: true 19 | } 20 | 21 | let result 22 | try { 23 | result = await request(options) 24 | } catch (e) { 25 | return next(new Error(e.error.error)) 26 | } 27 | 28 | res.send(result) 29 | }) 30 | 31 | api.get('/agent/:uuid', async (req, res, next) => { 32 | const { uuid } = req.params 33 | const options = { 34 | method: 'GET', 35 | url: `${endpoint}/api/agent/${uuid}`, 36 | headers: { 37 | 'Authorization': `Bearer ${apiToken}` 38 | }, 39 | json: true 40 | } 41 | 42 | let result 43 | try { 44 | result = await request(options) 45 | } catch (e) { 46 | return next(new Error(e.error.error)) 47 | } 48 | 49 | res.send(result) 50 | }) 51 | 52 | api.get('/metrics/:uuid', async (req, res, next) => { 53 | const { uuid } = req.params 54 | const options = { 55 | method: 'GET', 56 | url: `${endpoint}/api/metrics/${uuid}`, 57 | headers: { 58 | 'Authorization': `Bearer ${apiToken}` 59 | }, 60 | json: true 61 | } 62 | 63 | let result 64 | try { 65 | result = await request(options) 66 | } catch (e) { 67 | return next(new Error(e.error.error)) 68 | } 69 | 70 | res.send(result) 71 | }) 72 | 73 | api.get('/metrics/:uuid/:type', async (req, res, next) => { 74 | const { uuid, type } = req.params 75 | const options = { 76 | method: 'GET', 77 | url: `${endpoint}/api/metrics/${uuid}/${type}`, 78 | headers: { 79 | 'Authorization': `Bearer ${apiToken}` 80 | }, 81 | json: true 82 | } 83 | 84 | let result 85 | try { 86 | result = await request(options) 87 | } catch (e) { 88 | return next(new Error(e.error.error)) 89 | } 90 | 91 | res.send(result) 92 | }) 93 | 94 | module.exports = api 95 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const express = require('express') 4 | const asyncify = require('express-asyncify') 5 | const request = require('request-promise-native') 6 | 7 | const { endpoint, apiToken } = require('./config') 8 | 9 | const api = asyncify(express.Router()) 10 | 11 | api.get('/agents', async (req, res, next) => { 12 | const options = { 13 | method: 'GET', 14 | url: `${endpoint}/api/agents`, 15 | headers: { 16 | 'Authorization': `Bearer ${apiToken}` 17 | }, 18 | json: true 19 | } 20 | 21 | let result 22 | try { 23 | result = await request(options) 24 | } catch (e) { 25 | return next(new Error(e.error.error)) 26 | } 27 | 28 | res.send(result) 29 | }) 30 | 31 | api.get('/agent/:uuid', async (req, res, next) => { 32 | const { uuid } = req.params 33 | const options = { 34 | method: 'GET', 35 | url: `${endpoint}/api/agent/${uuid}`, 36 | headers: { 37 | 'Authorization': `Bearer ${apiToken}` 38 | }, 39 | json: true 40 | } 41 | 42 | let result 43 | try { 44 | result = await request(options) 45 | } catch (e) { 46 | return next(new Error(e.error.error)) 47 | } 48 | 49 | res.send(result) 50 | }) 51 | 52 | api.get('/metrics/:uuid', async (req, res, next) => { 53 | const { uuid } = req.params 54 | const options = { 55 | method: 'GET', 56 | url: `${endpoint}/api/metrics/${uuid}`, 57 | headers: { 58 | 'Authorization': `Bearer ${apiToken}` 59 | }, 60 | json: true 61 | } 62 | 63 | let result 64 | try { 65 | result = await request(options) 66 | } catch (e) { 67 | return next(new Error(e.error.error)) 68 | } 69 | 70 | res.send(result) 71 | }) 72 | 73 | api.get('/metrics/:uuid/:type', async (req, res, next) => { 74 | const { uuid, type } = req.params 75 | const options = { 76 | method: 'GET', 77 | url: `${endpoint}/api/metrics/${uuid}/${type}`, 78 | headers: { 79 | 'Authorization': `Bearer ${apiToken}` 80 | }, 81 | json: true 82 | } 83 | 84 | let result 85 | try { 86 | result = await request(options) 87 | } catch (e) { 88 | return next(new Error(e.error.error)) 89 | } 90 | 91 | res.send(result) 92 | }) 93 | 94 | module.exports = api 95 | -------------------------------------------------------------------------------- /platziverse-api/api.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api:routes') 4 | const express = require('express') 5 | const asyncify = require('express-asyncify') 6 | const auth = require('express-jwt') 7 | const guard = require('express-jwt-permissions')() 8 | const db = require('platziverse-db') 9 | 10 | const config = require('./config') 11 | 12 | const api = asyncify(express.Router()) 13 | 14 | let services, Agent, Metric 15 | 16 | api.use('*', async (req, res, next) => { 17 | if (!services) { 18 | debug('Connecting to database') 19 | try { 20 | services = await db(config.db) 21 | } catch (e) { 22 | return next(e) 23 | } 24 | 25 | Agent = services.Agent 26 | Metric = services.Metric 27 | } 28 | next() 29 | }) 30 | 31 | api.get('/agents', auth(config.auth), async (req, res, next) => { 32 | debug('A request has come to /agents') 33 | 34 | const { user } = req 35 | 36 | if (!user || !user.username) { 37 | return next(new Error('Not authorized')) 38 | } 39 | 40 | let agents = [] 41 | try { 42 | if (user.admin) { 43 | agents = await Agent.findConnected() 44 | } else { 45 | agents = await Agent.findByUsername(user.username) 46 | } 47 | } catch (e) { 48 | return next(e) 49 | } 50 | 51 | res.send(agents) 52 | }) 53 | 54 | api.get('/agent/:uuid', async (req, res, next) => { 55 | const { uuid } = req.params 56 | 57 | debug(`request to /agent/${uuid}`) 58 | 59 | let agent 60 | try { 61 | agent = await Agent.findByUuid(uuid) 62 | } catch (e) { 63 | return next(e) 64 | } 65 | 66 | if (!agent) { 67 | return next(new Error(`Agent not found with uuid ${uuid}`)) 68 | } 69 | 70 | res.send(agent) 71 | }) 72 | 73 | api.get('/metrics/:uuid', auth(config.auth), guard.check(['metrics:read']), async (req, res, next) => { 74 | const { uuid } = req.params 75 | 76 | debug(`request to /metrics/${uuid}`) 77 | 78 | let metrics = [] 79 | try { 80 | metrics = await Metric.findByAgentUuid(uuid) 81 | } catch (e) { 82 | return next(e) 83 | } 84 | 85 | if (!metrics || metrics.length === 0) { 86 | return next(new Error(`Metrics not found for agent with uuid ${uuid}`)) 87 | } 88 | 89 | res.send(metrics) 90 | }) 91 | 92 | api.get('/metrics/:uuid/:type', async (req, res, next) => { 93 | const { uuid, type } = req.params 94 | 95 | debug(`request to /metrics/${uuid}/${type}`) 96 | 97 | let metrics = [] 98 | try { 99 | metrics = await Metric.findByTypeAgentUuid(type, uuid) 100 | } catch (e) { 101 | return next(e) 102 | } 103 | 104 | if (!metrics || metrics.length === 0) { 105 | return next(new Error(`Metrics (${type}) not found for agent with uuid ${uuid}`)) 106 | } 107 | 108 | res.send(metrics) 109 | }) 110 | 111 | module.exports = api 112 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-api/files/platziverse-api/api.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:api:routes') 4 | const express = require('express') 5 | const asyncify = require('express-asyncify') 6 | const auth = require('express-jwt') 7 | const guard = require('express-jwt-permissions')() 8 | const db = require('platziverse-db') 9 | 10 | const config = require('./config') 11 | 12 | const api = asyncify(express.Router()) 13 | 14 | let services, Agent, Metric 15 | 16 | api.use('*', async (req, res, next) => { 17 | if (!services) { 18 | debug('Connecting to database') 19 | try { 20 | services = await db(config.db) 21 | } catch (e) { 22 | return next(e) 23 | } 24 | 25 | Agent = services.Agent 26 | Metric = services.Metric 27 | } 28 | next() 29 | }) 30 | 31 | api.get('/agents', auth(config.auth), async (req, res, next) => { 32 | debug('A request has come to /agents') 33 | 34 | const { user } = req 35 | 36 | if (!user || !user.username) { 37 | return next(new Error('Not authorized')) 38 | } 39 | 40 | let agents = [] 41 | try { 42 | if (user.admin) { 43 | agents = await Agent.findConnected() 44 | } else { 45 | agents = await Agent.findByUsername(user.username) 46 | } 47 | } catch (e) { 48 | return next(e) 49 | } 50 | 51 | res.send(agents) 52 | }) 53 | 54 | api.get('/agent/:uuid', async (req, res, next) => { 55 | const { uuid } = req.params 56 | 57 | debug(`request to /agent/${uuid}`) 58 | 59 | let agent 60 | try { 61 | agent = await Agent.findByUuid(uuid) 62 | } catch (e) { 63 | return next(e) 64 | } 65 | 66 | if (!agent) { 67 | return next(new Error(`Agent not found with uuid ${uuid}`)) 68 | } 69 | 70 | res.send(agent) 71 | }) 72 | 73 | api.get('/metrics/:uuid', auth(config.auth), guard.check(['metrics:read']), async (req, res, next) => { 74 | const { uuid } = req.params 75 | 76 | debug(`request to /metrics/${uuid}`) 77 | 78 | let metrics = [] 79 | try { 80 | metrics = await Metric.findByAgentUuid(uuid) 81 | } catch (e) { 82 | return next(e) 83 | } 84 | 85 | if (!metrics || metrics.length === 0) { 86 | return next(new Error(`Metrics not found for agent with uuid ${uuid}`)) 87 | } 88 | 89 | res.send(metrics) 90 | }) 91 | 92 | api.get('/metrics/:uuid/:type', async (req, res, next) => { 93 | const { uuid, type } = req.params 94 | 95 | debug(`request to /metrics/${uuid}/${type}`) 96 | 97 | let metrics = [] 98 | try { 99 | metrics = await Metric.findByTypeAgentUuid(type, uuid) 100 | } catch (e) { 101 | return next(e) 102 | } 103 | 104 | if (!metrics || metrics.length === 0) { 105 | return next(new Error(`Metrics (${type}) not found for agent with uuid ${uuid}`)) 106 | } 107 | 108 | res.send(metrics) 109 | }) 110 | 111 | module.exports = api 112 | -------------------------------------------------------------------------------- /platziverse-deploy/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | config.vm.box = "ubuntu/xenial64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # NOTE: This will enable public access to the opened port 26 | # config.vm.network "forwarded_port", guest: 80, host: 8080 27 | 28 | # Create a forwarded port mapping which allows access to a specific port 29 | # within the machine from a port on the host machine and only allow access 30 | # via 127.0.0.1 to disable public access 31 | # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 32 | 33 | # Create a private network, which allows host-only access to the machine 34 | # using a specific IP. 35 | # config.vm.network "private_network", ip: "192.168.33.10" 36 | 37 | # Create a public network, which generally matched to bridged network. 38 | # Bridged networks make the machine appear as another physical device on 39 | # your network. 40 | # config.vm.network "public_network" 41 | 42 | # Share an additional folder to the guest VM. The first argument is 43 | # the path on the host to the actual folder. The second argument is 44 | # the path on the guest to mount the folder. And the optional third 45 | # argument is a set of non-required options. 46 | # config.vm.synced_folder "../data", "/vagrant_data" 47 | 48 | # Provider-specific configuration so you can fine-tune various 49 | # backing providers for Vagrant. These expose provider-specific options. 50 | # Example for VirtualBox: 51 | # 52 | # config.vm.provider "virtualbox" do |vb| 53 | # # Display the VirtualBox GUI when booting the machine 54 | # vb.gui = true 55 | # 56 | # # Customize the amount of memory on the VM: 57 | # vb.memory = "1024" 58 | # end 59 | # 60 | # View the documentation for the provider you are using for more 61 | # information on available options. 62 | 63 | # Enable provisioning with a shell script. Additional provisioners such as 64 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 65 | # documentation for more information about their specific syntax and use. 66 | # config.vm.provision "shell", inline: <<-SHELL 67 | # apt-get update 68 | # apt-get install -y apache2 69 | # SHELL 70 | end 71 | -------------------------------------------------------------------------------- /platziverse-web/client/metric.vue: -------------------------------------------------------------------------------- 1 | 12 | 26 | 136 | -------------------------------------------------------------------------------- /platziverse-agent/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:agent') 4 | const os = require('os') 5 | const util = require('util') 6 | const mqtt = require('mqtt') 7 | const defaults = require('defaults') 8 | const uuid = require('uuid') 9 | const EventEmitter = require('events') 10 | 11 | const { parsePayload } = require('./utils') 12 | 13 | const options = { 14 | name: 'untitled', 15 | username: 'platzi', 16 | interval: 5000, 17 | mqtt: { 18 | host: 'mqtt://localhost' 19 | } 20 | } 21 | 22 | class PlatziverseAgent extends EventEmitter { 23 | constructor (opts) { 24 | super() 25 | 26 | this._options = defaults(opts, options) 27 | this._started = false 28 | this._timer = null 29 | this._client = null 30 | this._agentId = null 31 | this._metrics = new Map() 32 | } 33 | 34 | addMetric (type, fn) { 35 | this._metrics.set(type, fn) 36 | } 37 | 38 | removeMetric (type) { 39 | this._metrics.delete(type) 40 | } 41 | 42 | connect () { 43 | if (!this._started) { 44 | const opts = this._options 45 | this._client = mqtt.connect(opts.mqtt.host) 46 | this._started = true 47 | 48 | this._client.subscribe('agent/message') 49 | this._client.subscribe('agent/connected') 50 | this._client.subscribe('agent/disconnected') 51 | 52 | this._client.on('connect', () => { 53 | this._agentId = uuid.v4() 54 | 55 | this.emit('connected', this._agentId) 56 | 57 | this._timer = setInterval(async () => { 58 | if (this._metrics.size > 0) { 59 | let message = { 60 | agent: { 61 | uuid: this._agentId, 62 | username: opts.username, 63 | name: opts.name, 64 | hostname: os.hostname() || 'localhost', 65 | pid: process.pid 66 | }, 67 | metrics: [], 68 | timestamp: new Date().getTime() 69 | } 70 | 71 | for (let [ metric, fn ] of this._metrics) { 72 | if (fn.length === 1) { 73 | fn = util.promisify(fn) 74 | } 75 | 76 | message.metrics.push({ 77 | type: metric, 78 | value: await Promise.resolve(fn()) 79 | }) 80 | } 81 | 82 | debug('Sending', message) 83 | 84 | this._client.publish('agent/message', JSON.stringify(message)) 85 | this.emit('message', message) 86 | } 87 | }, opts.interval) 88 | }) 89 | 90 | this._client.on('message', (topic, payload) => { 91 | payload = parsePayload(payload) 92 | 93 | let broadcast = false 94 | switch (topic) { 95 | case 'agent/connected': 96 | case 'agent/disconnected': 97 | case 'agent/message': 98 | broadcast = payload && payload.agent && payload.agent.uuid !== this._agentId 99 | break 100 | } 101 | 102 | if (broadcast) { 103 | this.emit(topic, payload) 104 | } 105 | }) 106 | 107 | this._client.on('error', () => this.disconnect()) 108 | } 109 | } 110 | 111 | disconnect () { 112 | if (this._started) { 113 | clearInterval(this._timer) 114 | this._started = false 115 | this.emit('disconnected', this._agentId) 116 | this._client.end() 117 | } 118 | } 119 | } 120 | 121 | module.exports = PlatziverseAgent 122 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/client/metric.vue: -------------------------------------------------------------------------------- 1 | 12 | 26 | 136 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-agent/files/platziverse-agent/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:agent') 4 | const os = require('os') 5 | const util = require('util') 6 | const mqtt = require('mqtt') 7 | const defaults = require('defaults') 8 | const uuid = require('uuid') 9 | const EventEmitter = require('events') 10 | 11 | const { parsePayload } = require('./utils') 12 | 13 | const options = { 14 | name: 'untitled', 15 | username: 'platzi', 16 | interval: 5000, 17 | mqtt: { 18 | host: 'mqtt://localhost' 19 | } 20 | } 21 | 22 | class PlatziverseAgent extends EventEmitter { 23 | constructor (opts) { 24 | super() 25 | 26 | this._options = defaults(opts, options) 27 | this._started = false 28 | this._timer = null 29 | this._client = null 30 | this._agentId = null 31 | this._metrics = new Map() 32 | } 33 | 34 | addMetric (type, fn) { 35 | this._metrics.set(type, fn) 36 | } 37 | 38 | removeMetric (type) { 39 | this._metrics.delete(type) 40 | } 41 | 42 | connect () { 43 | if (!this._started) { 44 | const opts = this._options 45 | this._client = mqtt.connect(opts.mqtt.host) 46 | this._started = true 47 | 48 | this._client.subscribe('agent/message') 49 | this._client.subscribe('agent/connected') 50 | this._client.subscribe('agent/disconnected') 51 | 52 | this._client.on('connect', () => { 53 | this._agentId = uuid.v4() 54 | 55 | this.emit('connected', this._agentId) 56 | 57 | this._timer = setInterval(async () => { 58 | if (this._metrics.size > 0) { 59 | let message = { 60 | agent: { 61 | uuid: this._agentId, 62 | username: opts.username, 63 | name: opts.name, 64 | hostname: os.hostname() || 'localhost', 65 | pid: process.pid 66 | }, 67 | metrics: [], 68 | timestamp: new Date().getTime() 69 | } 70 | 71 | for (let [ metric, fn ] of this._metrics) { 72 | if (fn.length === 1) { 73 | fn = util.promisify(fn) 74 | } 75 | 76 | message.metrics.push({ 77 | type: metric, 78 | value: await Promise.resolve(fn()) 79 | }) 80 | } 81 | 82 | debug('Sending', message) 83 | 84 | this._client.publish('agent/message', JSON.stringify(message)) 85 | this.emit('message', message) 86 | } 87 | }, opts.interval) 88 | }) 89 | 90 | this._client.on('message', (topic, payload) => { 91 | payload = parsePayload(payload) 92 | 93 | let broadcast = false 94 | switch (topic) { 95 | case 'agent/connected': 96 | case 'agent/disconnected': 97 | case 'agent/message': 98 | broadcast = payload && payload.agent && payload.agent.uuid !== this._agentId 99 | break 100 | } 101 | 102 | if (broadcast) { 103 | this.emit(topic, payload) 104 | } 105 | }) 106 | 107 | this._client.on('error', () => this.disconnect()) 108 | } 109 | } 110 | 111 | disconnect () { 112 | if (this._started) { 113 | clearInterval(this._timer) 114 | this._started = false 115 | this.emit('disconnected', this._agentId) 116 | this._client.end() 117 | } 118 | } 119 | } 120 | 121 | module.exports = PlatziverseAgent 122 | -------------------------------------------------------------------------------- /platziverse-cli/platziverse.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | /* eslint new-cap: "off" */ 6 | 7 | const blessed = require('blessed') 8 | const contrib = require('blessed-contrib') 9 | const moment = require('moment') 10 | const PlatziverseAgent = require('platziverse-agent') 11 | 12 | const agent = new PlatziverseAgent({ 13 | mqtt: { 14 | host: 'mqtt://api.platziverse.space' 15 | } 16 | }) 17 | const screen = blessed.screen() 18 | 19 | const agents = new Map() 20 | const agentMetrics = new Map() 21 | let extended = [] 22 | let selected = { 23 | uuid: null, 24 | type: null 25 | } 26 | 27 | const grid = new contrib.grid({ 28 | rows: 1, 29 | cols: 4, 30 | screen 31 | }) 32 | 33 | const tree = grid.set(0, 0, 1, 1, contrib.tree, { 34 | label: 'Connected Agents' 35 | }) 36 | 37 | const line = grid.set(0, 1, 1, 3, contrib.line, { 38 | label: 'Metric', 39 | showLegend: true, 40 | minY: 0, 41 | xPadding: 5 42 | }) 43 | 44 | agent.on('agent/connected', payload => { 45 | const { uuid } = payload.agent 46 | 47 | if (!agents.has(uuid)) { 48 | agents.set(uuid, payload.agent) 49 | agentMetrics.set(uuid, {}) 50 | } 51 | 52 | renderData() 53 | }) 54 | 55 | agent.on('agent/disconnected', payload => { 56 | const { uuid } = payload.agent 57 | 58 | if (agents.has(uuid)) { 59 | agents.delete(uuid) 60 | agentMetrics.delete(uuid) 61 | } 62 | 63 | renderData() 64 | }) 65 | 66 | agent.on('agent/message', payload => { 67 | const { uuid } = payload.agent 68 | const { timestamp } = payload 69 | 70 | if (!agents.has(uuid)) { 71 | agents.set(uuid, payload.agent) 72 | agentMetrics.set(uuid, {}) 73 | } 74 | 75 | const metrics = agentMetrics.get(uuid) 76 | 77 | payload.metrics.forEach(m => { 78 | const { type, value } = m 79 | 80 | if (!Array.isArray(metrics[type])) { 81 | metrics[type] = [] 82 | } 83 | 84 | const length = metrics[type].length 85 | if (length >= 20) { 86 | metrics[type].shift() 87 | } 88 | 89 | metrics[type].push({ 90 | value, 91 | timestamp: moment(timestamp).format('HH:mm:ss') 92 | }) 93 | }) 94 | 95 | renderData() 96 | }) 97 | 98 | tree.on('select', node => { 99 | const { uuid } = node 100 | 101 | if (node.agent) { 102 | node.extended ? extended.push(uuid) : extended = extended.filter(e => e !== uuid) 103 | selected.uuid = null 104 | selected.type = null 105 | return 106 | } 107 | 108 | selected.uuid = uuid 109 | selected.type = node.type 110 | 111 | renderMetric() 112 | }) 113 | 114 | function renderData () { 115 | const treeData = {} 116 | let idx = 0 117 | for (let [ uuid, val ] of agents) { 118 | const title = ` ${val.name} - (${val.pid})` 119 | treeData[title] = { 120 | uuid, 121 | agent: true, 122 | extended: extended.includes(uuid), 123 | children: {} 124 | } 125 | 126 | const metrics = agentMetrics.get(uuid) 127 | Object.keys(metrics).forEach(type => { 128 | const metric = { 129 | uuid, 130 | type, 131 | metric: true 132 | } 133 | 134 | const metricName = ` ${type} ${' '.repeat(1000)} ${idx++}` 135 | treeData[title].children[metricName] = metric 136 | }) 137 | } 138 | 139 | tree.setData({ 140 | extended: true, 141 | children: treeData 142 | }) 143 | 144 | renderMetric() 145 | } 146 | 147 | function renderMetric () { 148 | if (!selected.uuid && !selected.type) { 149 | line.setData([{ x: [], y: [], title: '' }]) 150 | screen.render() 151 | return 152 | } 153 | 154 | const metrics = agentMetrics.get(selected.uuid) 155 | const values = metrics[selected.type] 156 | const series = [{ 157 | title: selected.type, 158 | x: values.map(v => v.timestamp).slice(-10), 159 | y: values.map(v => v.value).slice(-10) 160 | }] 161 | 162 | line.setData(series) 163 | screen.render() 164 | } 165 | 166 | screen.key([ 'escape', 'q', 'C-c' ], (ch, key) => { 167 | process.exit(0) 168 | }) 169 | 170 | agent.connect() 171 | tree.focus() 172 | screen.render() 173 | -------------------------------------------------------------------------------- /platziverse-web/client/agent.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 75 | 76 | 163 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-web/files/platziverse-web/client/agent.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 75 | 76 | 163 | -------------------------------------------------------------------------------- /platziverse-mqtt/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:mqtt') 4 | const mosca = require('mosca') 5 | const redis = require('redis') 6 | const chalk = require('chalk') 7 | const db = require('platziverse-db') 8 | 9 | const { parsePayload } = require('./utils') 10 | 11 | const backend = { 12 | type: 'redis', 13 | redis, 14 | return_buffers: true 15 | } 16 | 17 | const settings = { 18 | port: 1883, 19 | backend 20 | } 21 | 22 | const config = { 23 | database: process.env.DB_NAME || 'platziverse', 24 | username: process.env.DB_USER || 'platzi', 25 | password: process.env.DB_PASS || 'platzi', 26 | host: process.env.DB_HOST || 'localhost', 27 | dialect: 'postgres', 28 | logging: s => debug(s) 29 | } 30 | 31 | const server = new mosca.Server(settings) 32 | const clients = new Map() 33 | 34 | let Agent, Metric 35 | 36 | server.on('clientConnected', client => { 37 | debug(`Client Connected: ${client.id}`) 38 | clients.set(client.id, null) 39 | }) 40 | 41 | server.on('clientDisconnected', async (client) => { 42 | debug(`Client Disconnected: ${client.id}`) 43 | const agent = clients.get(client.id) 44 | 45 | if (agent) { 46 | // Mark Agent as Disconnected 47 | agent.connected = false 48 | 49 | try { 50 | await Agent.createOrUpdate(agent) 51 | } catch (e) { 52 | return handleError(e) 53 | } 54 | 55 | // Delete Agent from Clients List 56 | clients.delete(client.id) 57 | 58 | server.publish({ 59 | topic: 'agent/disconnected', 60 | payload: JSON.stringify({ 61 | agent: { 62 | uuid: agent.uuid 63 | } 64 | }) 65 | }) 66 | debug(`Client (${client.id}) associated to Agent (${agent.uuid}) marked as disconnected`) 67 | } 68 | }) 69 | 70 | server.on('published', async (packet, client) => { 71 | debug(`Received: ${packet.topic}`) 72 | 73 | switch (packet.topic) { 74 | case 'agent/connected': 75 | case 'agent/disconnected': 76 | debug(`Payload: ${packet.payload}`) 77 | break 78 | case 'agent/message': 79 | debug(`Payload: ${packet.payload}`) 80 | 81 | const payload = parsePayload(packet.payload) 82 | 83 | if (payload) { 84 | payload.agent.connected = true 85 | 86 | let agent 87 | try { 88 | agent = await Agent.createOrUpdate(payload.agent) 89 | } catch (e) { 90 | return handleError(e) 91 | } 92 | 93 | debug(`Agent ${agent.uuid} saved`) 94 | 95 | // Notify Agent is Connected 96 | if (!clients.get(client.id)) { 97 | clients.set(client.id, agent) 98 | server.publish({ 99 | topic: 'agent/connected', 100 | payload: JSON.stringify({ 101 | agent: { 102 | uuid: agent.uuid, 103 | name: agent.name, 104 | hostname: agent.hostname, 105 | pid: agent.pid, 106 | connected: agent.connected 107 | } 108 | }) 109 | }) 110 | } 111 | 112 | // Store Metrics 113 | for (let metric of payload.metrics) { 114 | let m 115 | 116 | try { 117 | m = await Metric.create(agent.uuid, metric) 118 | } catch (e) { 119 | return handleError(e) 120 | } 121 | 122 | debug(`Metric ${m.id} saved on agent ${agent.uuid}`) 123 | } 124 | } 125 | break 126 | } 127 | }) 128 | 129 | server.on('ready', async () => { 130 | const services = await db(config).catch(handleFatalError) 131 | 132 | Agent = services.Agent 133 | Metric = services.Metric 134 | 135 | console.log(`${chalk.green('[platziverse-mqtt]')} server is running`) 136 | }) 137 | 138 | server.on('error', handleFatalError) 139 | 140 | function handleFatalError (err) { 141 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 142 | console.error(err.stack) 143 | process.exit(1) 144 | } 145 | 146 | function handleError (err) { 147 | console.error(`${chalk.red('[error]')} ${err.message}`) 148 | console.error(err.stack) 149 | } 150 | 151 | process.on('uncaughtException', handleFatalError) 152 | process.on('unhandledRejection', handleFatalError) 153 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-mqtt/files/platziverse-mqtt/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const debug = require('debug')('platziverse:mqtt') 4 | const mosca = require('mosca') 5 | const redis = require('redis') 6 | const chalk = require('chalk') 7 | const db = require('platziverse-db') 8 | 9 | const { parsePayload } = require('./utils') 10 | 11 | const backend = { 12 | type: 'redis', 13 | redis, 14 | return_buffers: true 15 | } 16 | 17 | const settings = { 18 | port: 1883, 19 | backend 20 | } 21 | 22 | const config = { 23 | database: process.env.DB_NAME || 'platziverse', 24 | username: process.env.DB_USER || 'platzi', 25 | password: process.env.DB_PASS || 'platzi', 26 | host: process.env.DB_HOST || 'localhost', 27 | dialect: 'postgres', 28 | logging: s => debug(s) 29 | } 30 | 31 | const server = new mosca.Server(settings) 32 | const clients = new Map() 33 | 34 | let Agent, Metric 35 | 36 | server.on('clientConnected', client => { 37 | debug(`Client Connected: ${client.id}`) 38 | clients.set(client.id, null) 39 | }) 40 | 41 | server.on('clientDisconnected', async (client) => { 42 | debug(`Client Disconnected: ${client.id}`) 43 | const agent = clients.get(client.id) 44 | 45 | if (agent) { 46 | // Mark Agent as Disconnected 47 | agent.connected = false 48 | 49 | try { 50 | await Agent.createOrUpdate(agent) 51 | } catch (e) { 52 | return handleError(e) 53 | } 54 | 55 | // Delete Agent from Clients List 56 | clients.delete(client.id) 57 | 58 | server.publish({ 59 | topic: 'agent/disconnected', 60 | payload: JSON.stringify({ 61 | agent: { 62 | uuid: agent.uuid 63 | } 64 | }) 65 | }) 66 | debug(`Client (${client.id}) associated to Agent (${agent.uuid}) marked as disconnected`) 67 | } 68 | }) 69 | 70 | server.on('published', async (packet, client) => { 71 | debug(`Received: ${packet.topic}`) 72 | 73 | switch (packet.topic) { 74 | case 'agent/connected': 75 | case 'agent/disconnected': 76 | debug(`Payload: ${packet.payload}`) 77 | break 78 | case 'agent/message': 79 | debug(`Payload: ${packet.payload}`) 80 | 81 | const payload = parsePayload(packet.payload) 82 | 83 | if (payload) { 84 | payload.agent.connected = true 85 | 86 | let agent 87 | try { 88 | agent = await Agent.createOrUpdate(payload.agent) 89 | } catch (e) { 90 | return handleError(e) 91 | } 92 | 93 | debug(`Agent ${agent.uuid} saved`) 94 | 95 | // Notify Agent is Connected 96 | if (!clients.get(client.id)) { 97 | clients.set(client.id, agent) 98 | server.publish({ 99 | topic: 'agent/connected', 100 | payload: JSON.stringify({ 101 | agent: { 102 | uuid: agent.uuid, 103 | name: agent.name, 104 | hostname: agent.hostname, 105 | pid: agent.pid, 106 | connected: agent.connected 107 | } 108 | }) 109 | }) 110 | } 111 | 112 | // Store Metrics 113 | for (let metric of payload.metrics) { 114 | let m 115 | 116 | try { 117 | m = await Metric.create(agent.uuid, metric) 118 | } catch (e) { 119 | return handleError(e) 120 | } 121 | 122 | debug(`Metric ${m.id} saved on agent ${agent.uuid}`) 123 | } 124 | } 125 | break 126 | } 127 | }) 128 | 129 | server.on('ready', async () => { 130 | const services = await db(config).catch(handleFatalError) 131 | 132 | Agent = services.Agent 133 | Metric = services.Metric 134 | 135 | console.log(`${chalk.green('[platziverse-mqtt]')} server is running`) 136 | }) 137 | 138 | server.on('error', handleFatalError) 139 | 140 | function handleFatalError (err) { 141 | console.error(`${chalk.red('[fatal error]')} ${err.message}`) 142 | console.error(err.stack) 143 | process.exit(1) 144 | } 145 | 146 | function handleError (err) { 147 | console.error(`${chalk.red('[error]')} ${err.message}`) 148 | console.error(err.stack) 149 | } 150 | 151 | process.on('uncaughtException', handleFatalError) 152 | process.on('unhandledRejection', handleFatalError) 153 | -------------------------------------------------------------------------------- /platziverse-db/tests/agent-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('ava') 4 | const sinon = require('sinon') 5 | const proxyquire = require('proxyquire') 6 | 7 | const agentFixtures = require('./fixtures/agent') 8 | 9 | let config = { 10 | logging () {} 11 | } 12 | 13 | let MetricStub = { 14 | belongsTo: sinon.spy() 15 | } 16 | 17 | let id = 1 18 | let uuid = 'yyy-yyy-yyy' 19 | let AgentStub = null 20 | let db = null 21 | let sandbox = null 22 | 23 | let single = Object.assign({}, agentFixtures.single) 24 | 25 | let connectedArgs = { 26 | where: { connected: true } 27 | } 28 | 29 | let usernameArgs = { 30 | where: { username: 'platzi', connected: true } 31 | } 32 | 33 | let uuidArgs = { 34 | where: { uuid } 35 | } 36 | 37 | let newAgent = { 38 | uuid: '123-123-123', 39 | name: 'test', 40 | username: 'test', 41 | hostname: 'test', 42 | pid: 0, 43 | connected: false 44 | } 45 | 46 | test.beforeEach(async () => { 47 | sandbox = sinon.sandbox.create() 48 | 49 | AgentStub = { 50 | hasMany: sandbox.spy() 51 | } 52 | 53 | // Model create Stub 54 | AgentStub.create = sandbox.stub() 55 | AgentStub.create.withArgs(newAgent).returns(Promise.resolve({ 56 | toJSON () { return newAgent } 57 | })) 58 | 59 | // Model update Stub 60 | AgentStub.update = sandbox.stub() 61 | AgentStub.update.withArgs(single, uuidArgs).returns(Promise.resolve(single)) 62 | 63 | // Model findById Stub 64 | AgentStub.findById = sandbox.stub() 65 | AgentStub.findById.withArgs(id).returns(Promise.resolve(agentFixtures.byId(id))) 66 | 67 | // Model findOne Stub 68 | AgentStub.findOne = sandbox.stub() 69 | AgentStub.findOne.withArgs(uuidArgs).returns(Promise.resolve(agentFixtures.byUuid(uuid))) 70 | 71 | // Model findAll Stub 72 | AgentStub.findAll = sandbox.stub() 73 | AgentStub.findAll.withArgs().returns(Promise.resolve(agentFixtures.all)) 74 | AgentStub.findAll.withArgs(connectedArgs).returns(Promise.resolve(agentFixtures.connected)) 75 | AgentStub.findAll.withArgs(usernameArgs).returns(Promise.resolve(agentFixtures.platzi)) 76 | 77 | const setupDatabase = proxyquire('../', { 78 | './models/agent': () => AgentStub, 79 | './models/metric': () => MetricStub 80 | }) 81 | 82 | db = await setupDatabase(config) 83 | }) 84 | 85 | test.afterEach(() => { 86 | sandbox && sinon.sandbox.restore() 87 | }) 88 | 89 | test('Agent', t => { 90 | t.truthy(db.Agent, 'Agent service should exist') 91 | }) 92 | 93 | test.serial('Setup', t => { 94 | t.true(AgentStub.hasMany.called, 'AgentModel.hasMany was executed') 95 | t.true(AgentStub.hasMany.calledWith(MetricStub), 'Argument should be the MetricModel') 96 | t.true(MetricStub.belongsTo.called, 'MetricModel.belongsTo was executed') 97 | t.true(MetricStub.belongsTo.calledWith(AgentStub), 'Argument should be the AgentModel') 98 | }) 99 | 100 | test.serial('Agent#findById', async t => { 101 | let agent = await db.Agent.findById(id) 102 | 103 | t.true(AgentStub.findById.called, 'findById should be called on model') 104 | t.true(AgentStub.findById.calledOnce, 'findById should be called once') 105 | t.true(AgentStub.findById.calledWith(id), 'findById should be called with specified id') 106 | 107 | t.deepEqual(agent, agentFixtures.byId(id), 'should be the same') 108 | }) 109 | 110 | test.serial('Agent#findByUuid', async t => { 111 | let agent = await db.Agent.findByUuid(uuid) 112 | 113 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 114 | t.true(AgentStub.findOne.calledOnce, 'findOne should be called once') 115 | t.true(AgentStub.findOne.calledWith(uuidArgs), 'findOne should be called with uuid args') 116 | 117 | t.deepEqual(agent, agentFixtures.byUuid(uuid), 'agent should be the same') 118 | }) 119 | 120 | test.serial('Agent#findAll', async t => { 121 | let agents = await db.Agent.findAll() 122 | 123 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 124 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 125 | t.true(AgentStub.findAll.calledWith(), 'findAll should be called without args') 126 | 127 | t.is(agents.length, agentFixtures.all.length, 'agents should be the same amount') 128 | t.deepEqual(agents, agentFixtures.all, 'agents should be the same') 129 | }) 130 | 131 | test.serial('Agent#findConnected', async t => { 132 | let agents = await db.Agent.findConnected() 133 | 134 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 135 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 136 | t.true(AgentStub.findAll.calledWith(connectedArgs), 'findAll should be called with connected args') 137 | 138 | t.is(agents.length, agentFixtures.connected.length, 'agents should be the same amount') 139 | t.deepEqual(agents, agentFixtures.connected, 'agents should be the same') 140 | }) 141 | 142 | test.serial('Agent#findByUsername', async t => { 143 | let agents = await db.Agent.findByUsername('platzi') 144 | 145 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 146 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 147 | t.true(AgentStub.findAll.calledWith(usernameArgs), 'findAll should be called with username args') 148 | 149 | t.is(agents.length, agentFixtures.platzi.length, 'agents should be the same amount') 150 | t.deepEqual(agents, agentFixtures.platzi, 'agents should be the same') 151 | }) 152 | 153 | test.serial('Agent#createOrUpdate - exists', async t => { 154 | let agent = await db.Agent.createOrUpdate(single) 155 | 156 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 157 | t.true(AgentStub.findOne.calledTwice, 'findOne should be called twice') 158 | t.true(AgentStub.findOne.calledWith(uuidArgs), 'findOne should be called with uuid args') 159 | t.true(AgentStub.update.called, 'agent.update called on model') 160 | t.true(AgentStub.update.calledOnce, 'agent.update should be called once') 161 | t.true(AgentStub.update.calledWith(single), 'agent.update should be called with specified args') 162 | 163 | t.deepEqual(agent, single, 'agent should be the same') 164 | }) 165 | 166 | test.serial('Agent#createOrUpdate - new', async t => { 167 | let agent = await db.Agent.createOrUpdate(newAgent) 168 | 169 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 170 | t.true(AgentStub.findOne.calledOnce, 'findOne should be called once') 171 | t.true(AgentStub.findOne.calledWith({ 172 | where: { uuid: newAgent.uuid } 173 | }), 'findOne should be called with uuid args') 174 | t.true(AgentStub.create.called, 'create should be called on model') 175 | t.true(AgentStub.create.calledOnce, 'create should be called once') 176 | t.true(AgentStub.create.calledWith(newAgent), 'create should be called with specified args') 177 | 178 | t.deepEqual(agent, newAgent, 'agent should be the same') 179 | }) 180 | -------------------------------------------------------------------------------- /platziverse-deploy/roles/platziverse-db/files/platziverse-db/tests/agent-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('ava') 4 | const sinon = require('sinon') 5 | const proxyquire = require('proxyquire') 6 | 7 | const agentFixtures = require('./fixtures/agent') 8 | 9 | let config = { 10 | logging () {} 11 | } 12 | 13 | let MetricStub = { 14 | belongsTo: sinon.spy() 15 | } 16 | 17 | let id = 1 18 | let uuid = 'yyy-yyy-yyy' 19 | let AgentStub = null 20 | let db = null 21 | let sandbox = null 22 | 23 | let single = Object.assign({}, agentFixtures.single) 24 | 25 | let connectedArgs = { 26 | where: { connected: true } 27 | } 28 | 29 | let usernameArgs = { 30 | where: { username: 'platzi', connected: true } 31 | } 32 | 33 | let uuidArgs = { 34 | where: { uuid } 35 | } 36 | 37 | let newAgent = { 38 | uuid: '123-123-123', 39 | name: 'test', 40 | username: 'test', 41 | hostname: 'test', 42 | pid: 0, 43 | connected: false 44 | } 45 | 46 | test.beforeEach(async () => { 47 | sandbox = sinon.sandbox.create() 48 | 49 | AgentStub = { 50 | hasMany: sandbox.spy() 51 | } 52 | 53 | // Model create Stub 54 | AgentStub.create = sandbox.stub() 55 | AgentStub.create.withArgs(newAgent).returns(Promise.resolve({ 56 | toJSON () { return newAgent } 57 | })) 58 | 59 | // Model update Stub 60 | AgentStub.update = sandbox.stub() 61 | AgentStub.update.withArgs(single, uuidArgs).returns(Promise.resolve(single)) 62 | 63 | // Model findById Stub 64 | AgentStub.findById = sandbox.stub() 65 | AgentStub.findById.withArgs(id).returns(Promise.resolve(agentFixtures.byId(id))) 66 | 67 | // Model findOne Stub 68 | AgentStub.findOne = sandbox.stub() 69 | AgentStub.findOne.withArgs(uuidArgs).returns(Promise.resolve(agentFixtures.byUuid(uuid))) 70 | 71 | // Model findAll Stub 72 | AgentStub.findAll = sandbox.stub() 73 | AgentStub.findAll.withArgs().returns(Promise.resolve(agentFixtures.all)) 74 | AgentStub.findAll.withArgs(connectedArgs).returns(Promise.resolve(agentFixtures.connected)) 75 | AgentStub.findAll.withArgs(usernameArgs).returns(Promise.resolve(agentFixtures.platzi)) 76 | 77 | const setupDatabase = proxyquire('../', { 78 | './models/agent': () => AgentStub, 79 | './models/metric': () => MetricStub 80 | }) 81 | 82 | db = await setupDatabase(config) 83 | }) 84 | 85 | test.afterEach(() => { 86 | sandbox && sinon.sandbox.restore() 87 | }) 88 | 89 | test('Agent', t => { 90 | t.truthy(db.Agent, 'Agent service should exist') 91 | }) 92 | 93 | test.serial('Setup', t => { 94 | t.true(AgentStub.hasMany.called, 'AgentModel.hasMany was executed') 95 | t.true(AgentStub.hasMany.calledWith(MetricStub), 'Argument should be the MetricModel') 96 | t.true(MetricStub.belongsTo.called, 'MetricModel.belongsTo was executed') 97 | t.true(MetricStub.belongsTo.calledWith(AgentStub), 'Argument should be the AgentModel') 98 | }) 99 | 100 | test.serial('Agent#findById', async t => { 101 | let agent = await db.Agent.findById(id) 102 | 103 | t.true(AgentStub.findById.called, 'findById should be called on model') 104 | t.true(AgentStub.findById.calledOnce, 'findById should be called once') 105 | t.true(AgentStub.findById.calledWith(id), 'findById should be called with specified id') 106 | 107 | t.deepEqual(agent, agentFixtures.byId(id), 'should be the same') 108 | }) 109 | 110 | test.serial('Agent#findByUuid', async t => { 111 | let agent = await db.Agent.findByUuid(uuid) 112 | 113 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 114 | t.true(AgentStub.findOne.calledOnce, 'findOne should be called once') 115 | t.true(AgentStub.findOne.calledWith(uuidArgs), 'findOne should be called with uuid args') 116 | 117 | t.deepEqual(agent, agentFixtures.byUuid(uuid), 'agent should be the same') 118 | }) 119 | 120 | test.serial('Agent#findAll', async t => { 121 | let agents = await db.Agent.findAll() 122 | 123 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 124 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 125 | t.true(AgentStub.findAll.calledWith(), 'findAll should be called without args') 126 | 127 | t.is(agents.length, agentFixtures.all.length, 'agents should be the same amount') 128 | t.deepEqual(agents, agentFixtures.all, 'agents should be the same') 129 | }) 130 | 131 | test.serial('Agent#findConnected', async t => { 132 | let agents = await db.Agent.findConnected() 133 | 134 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 135 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 136 | t.true(AgentStub.findAll.calledWith(connectedArgs), 'findAll should be called with connected args') 137 | 138 | t.is(agents.length, agentFixtures.connected.length, 'agents should be the same amount') 139 | t.deepEqual(agents, agentFixtures.connected, 'agents should be the same') 140 | }) 141 | 142 | test.serial('Agent#findByUsername', async t => { 143 | let agents = await db.Agent.findByUsername('platzi') 144 | 145 | t.true(AgentStub.findAll.called, 'findAll should be called on model') 146 | t.true(AgentStub.findAll.calledOnce, 'findAll should be called once') 147 | t.true(AgentStub.findAll.calledWith(usernameArgs), 'findAll should be called with username args') 148 | 149 | t.is(agents.length, agentFixtures.platzi.length, 'agents should be the same amount') 150 | t.deepEqual(agents, agentFixtures.platzi, 'agents should be the same') 151 | }) 152 | 153 | test.serial('Agent#createOrUpdate - exists', async t => { 154 | let agent = await db.Agent.createOrUpdate(single) 155 | 156 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 157 | t.true(AgentStub.findOne.calledTwice, 'findOne should be called twice') 158 | t.true(AgentStub.findOne.calledWith(uuidArgs), 'findOne should be called with uuid args') 159 | t.true(AgentStub.update.called, 'agent.update called on model') 160 | t.true(AgentStub.update.calledOnce, 'agent.update should be called once') 161 | t.true(AgentStub.update.calledWith(single), 'agent.update should be called with specified args') 162 | 163 | t.deepEqual(agent, single, 'agent should be the same') 164 | }) 165 | 166 | test.serial('Agent#createOrUpdate - new', async t => { 167 | let agent = await db.Agent.createOrUpdate(newAgent) 168 | 169 | t.true(AgentStub.findOne.called, 'findOne should be called on model') 170 | t.true(AgentStub.findOne.calledOnce, 'findOne should be called once') 171 | t.true(AgentStub.findOne.calledWith({ 172 | where: { uuid: newAgent.uuid } 173 | }), 'findOne should be called with uuid args') 174 | t.true(AgentStub.create.called, 'create should be called on model') 175 | t.true(AgentStub.create.calledOnce, 'create should be called once') 176 | t.true(AgentStub.create.calledWith(newAgent), 'create should be called with specified args') 177 | 178 | t.deepEqual(agent, newAgent, 'agent should be the same') 179 | }) 180 | --------------------------------------------------------------------------------