diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f695ee2 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +COBOT_CLIENT_ID='...' +COBOT_CLIENT_SECRET='...' +COBOT_REDIRECT_URL='http://10.0.1.48:8080/success' +COBOT_AUTHORIZE_URL='https://www.cobot.me/oauth/authorize' +COBOT_TOKEN_URL='https://www.cobot.me/oauth/access_token' +COBOT_SCOPE='checkin_tokens' +COBOT_USER_EMAIL='...' +COBOT_USER_PASSWORD='...' +COBOT_CARDS_API='https://chimera.cobot.me/api/check_in_tokens' +DOOR_OPEN_DELAY=6000 +PORT=8080 diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..d09d73f --- /dev/null +++ b/.env.test @@ -0,0 +1,11 @@ +COBOT_CLIENT_ID='fake-id' +COBOT_CLIENT_SECRET='fake-secret' +COBOT_REDIRECT_URL='http://localhost:8080/success' +COBOT_AUTHORIZE_URL='https://www.cobot.me/oauth/authorize' +COBOT_TOKEN_URL='https://www.cobot.me/oauth/access_token' +COBOT_SCOPE='checkin_tokens' +COBOT_USER_EMAIL='info@chimeraarts.org' +COBOT_USER_PASSWORD='somepassword' +COBOT_CARDS_API='https://chimera.cobot.me/api/check_in_tokens' +DOOR_OPEN_DELAY=1 +PORT=8080 diff --git a/.gitignore b/.gitignore index ade7c86..cabb3c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env node_modules .DS_Store diff --git a/.tesselinclude b/.tesselinclude index 8992346..004d8e2 100644 --- a/.tesselinclude +++ b/.tesselinclude @@ -4,4 +4,4 @@ # .tesselinclude works the same as .npminclude # You DO NOT need to list node_modules or package.json # For more information, visit: https://tessel.io/docs/cli#usage -index.html +.env diff --git a/config/custom-environment-variables.js b/config/custom-environment-variables.js deleted file mode 100644 index 433c6fb..0000000 --- a/config/custom-environment-variables.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - env: 'ENV', -} diff --git a/config/default.js b/config/default.js deleted file mode 100644 index 95e441a..0000000 --- a/config/default.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - cardApi: 'https://chimera.cobot.me/api/check_in_tokens', - env: 'development', - openDelay: 6000, -} diff --git a/config/test.js b/config/test.js deleted file mode 100644 index 11c35d9..0000000 --- a/config/test.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - env: 'test', - openDelay: 1, -} diff --git a/package-lock.json b/package-lock.json index 21218d5..90280e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -355,6 +355,11 @@ } } }, + "dotenv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" + }, "eslint": { "version": "4.17.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz", diff --git a/package.json b/package.json index ad622f0..7a8b44a 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,11 @@ "watch-test": "npm test -- --watch" }, "dependencies": { - "axios": "0.17.1", + "axios": "0.18.0", "chalk": "2.3.1", - "config": "1.30.0", - "consolidate": "0.15.0", + "dotenv": "5.0.1", "eslint": "4.17.0", - "express": "4.16.2", "node-hid": "0.7.2", - "pug": "2.0.0-rc.4", - "serialport": "6.0.5", "tessel": "0.3.25" }, "devDependencies": { diff --git a/readme.md b/readme.md index 95c47a2..6392fbc 100644 --- a/readme.md +++ b/readme.md @@ -66,6 +66,10 @@ t2 erase t2 ap -n doorlock ``` +### Networking + +To find the IP address of your Tessel, download the iOS app Fing and look for a device on your network called `doorlock`. + ### USB Storage * Make sure to format USB to be FAT32! diff --git a/src/boot.js b/src/boot.js index d6eec59..58c6ef9 100644 --- a/src/boot.js +++ b/src/boot.js @@ -1,6 +1,19 @@ -const Door = require('./models/door') +const Cobot = require('./models/cobot') -Door.open() +Cobot.authorize().then(cobot => { + cobot.cards().then(resp => console.log('RESP:', resp)) +}) + +// const server = require('./server') + +// server() + +// const Cards = require('./models/cards') +// const Door = require('./models/door') + +// Door.open() + +// Cards.update() // const server = require('./server') diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..5de026c --- /dev/null +++ b/src/constants.js @@ -0,0 +1,14 @@ +require('dotenv').config() + +const ENV = process.env.NODE_ENV || 'development' + +module.exports = { + COBOT_CARDS_API: process.env.COBOT_CARDS_API, + COBOT_CLIENT_ID: process.env.COBOT_CLIENT_ID, + COBOT_CLIENT_SECRET: process.env.COBOT_CLIENT_SECRET, + COBOT_SCOPE: process.env.COBOT_SCOPE, + COBOT_USER_EMAIL: process.env.COBOT_USER_EMAIL, + COBOT_USER_PASSWORD: process.env.COBOT_USER_PASSWORD, + DOOR_OPEN_DELAY: process.env.DOOR_OPEN_DELAY, + ENV, +} diff --git a/src/models/cards.js b/src/models/cards.js index dfaec83..0e5ed59 100644 --- a/src/models/cards.js +++ b/src/models/cards.js @@ -1,9 +1,9 @@ const axios = require('axios') -const config = require('config') +const { COBOT_CARDS_API } = require('../constants') class Cards { static update() { - return axios.get(config.get('cardApi')) + return axios.get(COBOT_CARDS_API) } } diff --git a/src/models/cobot.js b/src/models/cobot.js new file mode 100644 index 0000000..b19f372 --- /dev/null +++ b/src/models/cobot.js @@ -0,0 +1,64 @@ +const axios = require('axios') +const { + COBOT_CARDS_API, + COBOT_CLIENT_ID, + COBOT_CLIENT_SECRET, + COBOT_SCOPE, + COBOT_USER_EMAIL, + COBOT_USER_PASSWORD, +} = require('../constants') + +class Cobot { + constructor(token) { + this.token = token + } + + cards() { + if (!COBOT_CARDS_API) + throw new Error('missing "COBOT_CARDS_API" env variable!') + return axios + .get(COBOT_CARDS_API, { + headers: { + Authorization: `Bearer ${this.token}`, + }, + }) + .then(resp => + resp.data.map(card => ({ + name: card.membership.name, + number: card.token, + })) + ) + } + + static authorize() { + if (!COBOT_SCOPE) throw new Error('missing "COBOT_SCOPE" env variable!') + if (!COBOT_USER_EMAIL) + throw new Error('missing "COBOT_USER_EMAIL" env variable!') + if (!COBOT_USER_PASSWORD) + throw new Error('missing "COBOT_USER_PASSWORD" env variable!') + if (!COBOT_CLIENT_ID) + throw new Error('missing "COBOT_CLIENT_ID" env variable!') + if (!COBOT_CLIENT_SECRET) + throw new Error('missing "COBOT_CLIENT_SECRET" env variable!') + console.log( + COBOT_SCOPE, + COBOT_USER_EMAIL, + COBOT_USER_PASSWORD, + COBOT_CLIENT_ID, + COBOT_CLIENT_SECRET + ) + const qs = [ + `scope=${COBOT_SCOPE}`, + `grant_type=password`, + `username=${COBOT_USER_EMAIL}`, + `password=${COBOT_USER_PASSWORD}`, + `client_id=${COBOT_CLIENT_ID}`, + `client_secret=${COBOT_CLIENT_SECRET}`, + ].join('&') + return axios + .post(`https://www.cobot.me/oauth/access_token?${qs}`) + .then(resp => new Cobot(resp.data.access_token)) + } +} + +module.exports = Cobot diff --git a/src/models/cobot.test.js b/src/models/cobot.test.js new file mode 100644 index 0000000..f7bdf1f --- /dev/null +++ b/src/models/cobot.test.js @@ -0,0 +1,41 @@ +const axios = require('axios') +const Cobot = require('./cobot') + +jest.mock('axios') + +describe('models/cobot', () => { + describe('.authorize', () => { + test('should fetch a new token', () => { + const resp = { data: { access_token: 'meow' } } + jest.spyOn(axios, 'post').mockResolvedValue(resp) + return Cobot.authorize().then(cobot => { + expect(axios.post).toBeCalled() + expect(cobot).toBeInstanceOf(Cobot) + expect(cobot.token).toBe('meow') + }) + }) + }) + + describe('.cards', () => { + test('returns a structured list of cards', () => { + const resp = { + data: [ + { membership: { id: 'foo', name: 'John' }, token: '001' }, + { membership: { id: 'bar', name: 'Jane' }, token: '002' }, + ], + } + const expected = [ + { name: 'John', number: '001' }, + { name: 'Jane', number: '002' }, + ] + jest.spyOn(axios, 'get').mockResolvedValue(resp) + + // TODO: why doesnt this work? It should... + // axios.get.mockResolvedValue(resp) + + return new Cobot('fake-value') + .cards() + .then(actual => expect(actual).toEqual(expected)) + }) + }) +}) diff --git a/src/models/door.js b/src/models/door.js index 969ab6f..536a03d 100644 --- a/src/models/door.js +++ b/src/models/door.js @@ -1,9 +1,7 @@ -const config = require('config') +const { DOOR_OPEN_DELAY } = require('../constants') const logger = require('../utils/logger') const tessel = require('tessel') -const DOOR_OPEN_DELAY = config.get('openDelay') - // const fs = require('fs') // const path = require('path') diff --git a/src/utils/logger.js b/src/utils/logger.js index 9f8fdb1..ecea74c 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -1,5 +1,4 @@ -const config = require('config') -const ENV = config.get('env') +const { ENV } = require('../constants') class Logger { log() {