From bb2892de88a10a50b04b5fa7afdee39a12acb509 Mon Sep 17 00:00:00 2001 From: zurfyx Date: Mon, 3 Jul 2017 21:32:27 +0200 Subject: [PATCH 01/11] new db rules --- firebase/database.rules.json | 60 +++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/firebase/database.rules.json b/firebase/database.rules.json index 94786a3..0c5d971 100644 --- a/firebase/database.rules.json +++ b/firebase/database.rules.json @@ -1,27 +1,45 @@ { "rules": { - "up": { - "$publicIp": { + "servers": { + "$uid": { + ".write": "$uid === auth.token.email.beginsWith($uid + '@')", ".read": true, - ".validate": "$publicIp.matches(/^[0-9]{1,3}%[0-9]{1,3}%[0-9]{1,3}%[0-9]{1,3}$/)", - "$reportUid": { - ".write": "!data.exists() && newData.exists()", - ".validate": "newData.hasChildren(['localIp', 'port', 'timestamp'])", - "localIp": { - ".validate": "newData.val().matches(/^(10|172|192)\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$/)" - }, - "port": { - ".validate": "newData.val() > 0 && newData.val() < 65535" - }, - "timestamp": { - ".validate": "newData.val() === now" - }, - "optional": { - ".validate": "newData.val().length < 200" - }, - "$other": { - ".validate": false - } + "displayName": { + ".validate": "newData.val().length < 200" + }, + "lastOnline": { + ".validate": "newData.val() === now" + }, + "isOnline": { + ".validate": "newData.isBoolean()" + }, + "ip": { + ".validate": "newData.val().matches(/^[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}$/)", + }, + "hasPassword": { + ".validate": "newData.isBoolean()" + } + } + }, + "passwords": { + "$serverUid": { + ".write": "$serverUid === auth.token.email.beginsWith($serverUid + '@') || newData.val() === data.val()", + ".read": false, + ".validate": "newData.isString()" + } + }, + "queue": { + "$serverUidWithKey": { + ".write": true, + ".read": true, + "type": { + ".validate": "newData.isString()" + }, + "value": { + ".validate": "newData.isString()" + }, + "timestamp": { + ".validate": "newData.val() === now" } } } From 89474e5859aead5866f062b2d1eb18d80d1535fb Mon Sep 17 00:00:00 2001 From: zurfyx Date: Mon, 3 Jul 2017 23:14:19 +0200 Subject: [PATCH 02/11] firebase rules $others validate --- firebase/database.rules.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firebase/database.rules.json b/firebase/database.rules.json index 0c5d971..f208783 100644 --- a/firebase/database.rules.json +++ b/firebase/database.rules.json @@ -42,6 +42,9 @@ ".validate": "newData.val() === now" } } + }, + "$other": { + ".validate": false } } } \ No newline at end of file From f25764f11c25a8306f9adf0b72a5fd7f7c22d3fd Mon Sep 17 00:00:00 2001 From: zurfyx Date: Mon, 3 Jul 2017 23:23:27 +0200 Subject: [PATCH 03/11] refactor cron --- src/cron/CronTask.js | 16 ++++++++++++++++ src/cron/index.js | 15 +-------------- 2 files changed, 17 insertions(+), 14 deletions(-) create mode 100644 src/cron/CronTask.js diff --git a/src/cron/CronTask.js b/src/cron/CronTask.js new file mode 100644 index 0000000..777590e --- /dev/null +++ b/src/cron/CronTask.js @@ -0,0 +1,16 @@ +const { CronJob } = require('cron'); + +const log = require('../helpers/log'); + +class CronTask { + constructor(name, cronTime, action) { + const newAction = async () => { + log.dev(`[CRON] Started "${name}"`); + await action(); + log.dev(`[CRON] Finished "${name}"`); + }; + this.cronJob = new CronJob(cronTime, newAction); + } +} + +module.exports = CronTask; diff --git a/src/cron/index.js b/src/cron/index.js index 8729a4a..68cca8b 100644 --- a/src/cron/index.js +++ b/src/cron/index.js @@ -1,19 +1,6 @@ -const { CronJob } = require('cron'); - -const log = require('../helpers/log'); +const CronTask = require('./CronTask'); const { up } = require('../services'); -class CronTask { - constructor(name, cronTime, action) { - const newAction = async () => { - log.dev(`[CRON] Started "${name}"`); - await action(); - log.dev(`[CRON] Finished "${name}"`); - }; - this.cronJob = new CronJob(cronTime, newAction); - } -} - const cronTasks = [ new CronTask('Report Alive', '0,30 * * * * *', () => up.reportAlive()), ]; From d6ea9d1eef85e425854930e30bb3c35b9dbadb3b Mon Sep 17 00:00:00 2001 From: zurfyx Date: Mon, 3 Jul 2017 23:35:27 +0200 Subject: [PATCH 04/11] new firebase file structure --- config/default.js | 3 --- src/cron/index.js | 14 +------------- src/firebase/queue.js | 0 src/firebase/servers.js | 0 src/server.js | 7 +------ 5 files changed, 2 insertions(+), 22 deletions(-) create mode 100644 src/firebase/queue.js create mode 100644 src/firebase/servers.js diff --git a/config/default.js b/config/default.js index 32e0de2..357e440 100644 --- a/config/default.js +++ b/config/default.js @@ -23,9 +23,6 @@ module.exports = { // File that handles Liquid Galaxy commands (such as flyto). queriesPath: '/tmp/query.txt', - // Run Cron jobs (such as alive reports). - cronJobsEnabled: true, - // Firebase repository where all alive reports will be sent. firebase: { apiKey: 'AIzaSyAddzwazFGRJiC-GW35Zgr7XdhUk8x0890', diff --git a/src/cron/index.js b/src/cron/index.js index 68cca8b..c01a5ee 100644 --- a/src/cron/index.js +++ b/src/cron/index.js @@ -1,15 +1,3 @@ const CronTask = require('./CronTask'); -const { up } = require('../services'); -const cronTasks = [ - new CronTask('Report Alive', '0,30 * * * * *', () => up.reportAlive()), -]; - -function startAll() { - this.cronTasks.map(cronTask => cronTask.cronJob.start()); -} - -module.exports = { - cronTasks, - startAll, -}; +module.exports = { CronTask }; diff --git a/src/firebase/queue.js b/src/firebase/queue.js new file mode 100644 index 0000000..e69de29 diff --git a/src/firebase/servers.js b/src/firebase/servers.js new file mode 100644 index 0000000..e69de29 diff --git a/src/server.js b/src/server.js index ff60410..853ea8b 100644 --- a/src/server.js +++ b/src/server.js @@ -20,14 +20,9 @@ const server = http.createServer(app); // Hey you! care about my order http://stackoverflow.com/a/16781554/2034015 -// Databases. +// Firebase stuff. firebase.initialize(); -// Cron jobs. -if (config.get('cronJobsEnabled')) { - cron.startAll(); -} - // Cookies. app.use(cookieParser()); From 724e34ec453b8d5085a28564b14e74e2b2c62f6b Mon Sep 17 00:00:00 2001 From: zurfyx Date: Tue, 4 Jul 2017 17:00:56 +0200 Subject: [PATCH 05/11] firebase auth --- package-lock.json | 16 +++++++--- package.json | 3 ++ src/firebase/auth.js | 72 +++++++++++++++++++++++++++++++++++++++++++ src/firebase/index.js | 5 ++- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 src/firebase/auth.js diff --git a/package-lock.json b/package-lock.json index 422cf8c..f04435d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1904,6 +1904,11 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true }, + "generate-password": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/generate-password/-/generate-password-1.3.0.tgz", + "integrity": "sha1-TaTBVFMNIcGZWneqxaPqBIgvyK0=" + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -2663,14 +2668,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" }, "mocha": { "version": "3.4.2", @@ -3431,6 +3434,11 @@ "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true }, + "shortid": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.8.tgz", + "integrity": "sha1-AzsRfWoul1gE9vCWnb59PQs1UTE=" + }, "sinon": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.4.tgz", diff --git a/package.json b/package.json index 09ca3fb..82bca36 100644 --- a/package.json +++ b/package.json @@ -81,11 +81,14 @@ "debug": "^2.6.1", "express": "^4.14.1", "firebase": "^4.1.2", + "generate-password": "^1.3.0", "http": "0.0.0", "ip": "^1.1.5", + "mkdirp": "^0.5.1", "mongoose": "^4.8.4", "morgan": "^1.8.1", "public-ip": "^2.3.5", + "shortid": "^2.2.8", "socket.io": "^2.0.3", "uuid": "^3.1.0", "validator": "^7.0.0" diff --git a/src/firebase/auth.js b/src/firebase/auth.js new file mode 100644 index 0000000..2927313 --- /dev/null +++ b/src/firebase/auth.js @@ -0,0 +1,72 @@ +/** + * Authenticate server. + * Signs up new server with random credentials (stored in data/credentials.txt) + * Sign in with the stored credentials. + * (format deviceUid:editKey:devicePassword) + */ + +const Promise = require('bluebird'); +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); +const shortid = require('shortid'); +const generatePassword = require('generate-password'); +const firebase = require('firebase'); + +const readFile = Promise.promisify(fs.readFile); +const writeFile = Promise.promisify(fs.writeFile); +const createDir = Promise.promisify(mkdirp); + +const CREDENTIALS_PATH = path.join('data/credentials.txt'); + +async function readCredentials() { + try { + const contents = await readFile(CREDENTIALS_PATH, { encoding: 'utf-8' }); + const [uid, editKey, password] = contents.split(':'); + return [uid, editKey, password]; + } catch (error) { + return null; + } +} + +async function generateCredentials() { + const uid = shortid.generate(); + const editKey = generatePassword.generate({ + length: 20, + numbers: true, + }); + const password = ''; + return [uid, editKey, password]; +} + +async function saveCredentials(values) { + const contents = values.join(':'); + await createDir(path.join(CREDENTIALS_PATH, '..')); + return writeFile(CREDENTIALS_PATH, contents); +} + +function firebaseEmail(uid) { + return `${uid}@firebase.com`; +} + +async function signup([uid, editKey]) { + const emailVal = firebaseEmail(uid); + const passwordVal = editKey; + return firebase.auth().createUserWithEmailAndPassword(emailVal, passwordVal); +} + +async function signin([uid, editKey]) { + const emailVal = firebaseEmail(uid); + const passwordVal = editKey; + return firebase.auth().signInWithEmailAndPassword(emailVal, passwordVal); +} + +module.exports = async () => { + let credentials = await readCredentials(); + if (!credentials) { + credentials = await generateCredentials(); + await saveCredentials(credentials); + await signup(credentials); + } + return signin(credentials); +}; diff --git a/src/firebase/index.js b/src/firebase/index.js index 0c5b324..a6635dc 100644 --- a/src/firebase/index.js +++ b/src/firebase/index.js @@ -1,10 +1,13 @@ const firebase = require('firebase'); const config = require('config'); +const auth = require('./auth'); + const firebaseConfig = config.get('firebase'); -function initialize() { +async function initialize() { firebase.initializeApp(firebaseConfig); + await auth(); } module.exports = { From d3ed992938a1a08c71c77874e60d80ad6c43ca5c Mon Sep 17 00:00:00 2001 From: zurfyx Date: Tue, 4 Jul 2017 18:55:44 +0200 Subject: [PATCH 06/11] firebase auth --- firebase/database.rules.json | 10 +++++++-- src/firebase/__tests__/.eslintrc | 5 +++++ src/firebase/__tests__/auth.test.js | 10 +++++++++ src/firebase/auth.js | 33 ++++++++++++++++++----------- src/firebase/index.js | 24 +++++++++++++++++---- src/firebase/server.js | 14 ++++++++++++ src/firebase/servers.js | 0 src/server.js | 3 +-- 8 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 src/firebase/__tests__/.eslintrc create mode 100644 src/firebase/__tests__/auth.test.js create mode 100644 src/firebase/server.js delete mode 100644 src/firebase/servers.js diff --git a/firebase/database.rules.json b/firebase/database.rules.json index f208783..7154bcd 100644 --- a/firebase/database.rules.json +++ b/firebase/database.rules.json @@ -2,7 +2,7 @@ "rules": { "servers": { "$uid": { - ".write": "$uid === auth.token.email.beginsWith($uid + '@')", + ".write": "auth.token.email.beginsWith($uid + '@')", ".read": true, "displayName": { ".validate": "newData.val().length < 200" @@ -18,12 +18,15 @@ }, "hasPassword": { ".validate": "newData.isBoolean()" + }, + "$other": { + ".validate": false } } }, "passwords": { "$serverUid": { - ".write": "$serverUid === auth.token.email.beginsWith($serverUid + '@') || newData.val() === data.val()", + ".write": "auth.token.email.beginsWith($serverUid + '@') || newData.val() === data.val()", ".read": false, ".validate": "newData.isString()" } @@ -40,6 +43,9 @@ }, "timestamp": { ".validate": "newData.val() === now" + }, + "$other": { + ".validate": false } } }, diff --git a/src/firebase/__tests__/.eslintrc b/src/firebase/__tests__/.eslintrc new file mode 100644 index 0000000..7eeefc3 --- /dev/null +++ b/src/firebase/__tests__/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "mocha": true + } +} diff --git a/src/firebase/__tests__/auth.test.js b/src/firebase/__tests__/auth.test.js new file mode 100644 index 0000000..d60ebeb --- /dev/null +++ b/src/firebase/__tests__/auth.test.js @@ -0,0 +1,10 @@ +const { expect } = require('chai'); +const auth = require('../auth'); + +describe('Firebase Auth', () => { + it('encoded email address should decode alright', () => { + const UID = 'loLlipOp'; + const expected = '1234@firebase.com'; + expect(auth._encodeFirebaseEmail(UID)).to.be(expected); // eslint-disable-line + }); +}); diff --git a/src/firebase/auth.js b/src/firebase/auth.js index 2927313..7eaad38 100644 --- a/src/firebase/auth.js +++ b/src/firebase/auth.js @@ -30,6 +30,7 @@ async function readCredentials() { } async function generateCredentials() { + // Uid is part of the firebase email name. Use email acceptable characters only. const uid = shortid.generate(); const editKey = generatePassword.generate({ length: 20, @@ -45,28 +46,36 @@ async function saveCredentials(values) { return writeFile(CREDENTIALS_PATH, contents); } -function firebaseEmail(uid) { - return `${uid}@firebase.com`; +function encodeFirebaseEmail(uid) { + const encodedUid = uid.split('').reduce((prev, curr) => { + const encodedCurr = curr.charCodeAt(0); + return `${prev}${encodedCurr}`; + }); + return `${encodedUid}@firebase.com`; } async function signup([uid, editKey]) { - const emailVal = firebaseEmail(uid); + const emailVal = encodeFirebaseEmail(uid); const passwordVal = editKey; return firebase.auth().createUserWithEmailAndPassword(emailVal, passwordVal); } async function signin([uid, editKey]) { - const emailVal = firebaseEmail(uid); + const emailVal = encodeFirebaseEmail(uid); const passwordVal = editKey; return firebase.auth().signInWithEmailAndPassword(emailVal, passwordVal); } -module.exports = async () => { - let credentials = await readCredentials(); - if (!credentials) { - credentials = await generateCredentials(); - await saveCredentials(credentials); - await signup(credentials); - } - return signin(credentials); +module.exports = { + _encodeFirebaseEmail: encodeFirebaseEmail, + init: async () => { + let credentials = await readCredentials(); + if (!credentials) { + credentials = await generateCredentials(); + await saveCredentials(credentials); + await signup(credentials); + } + await signin(credentials); + return credentials; + }, }; diff --git a/src/firebase/index.js b/src/firebase/index.js index a6635dc..1b21bf4 100644 --- a/src/firebase/index.js +++ b/src/firebase/index.js @@ -1,15 +1,31 @@ const firebase = require('firebase'); const config = require('config'); +const log = require('../helpers/log'); const auth = require('./auth'); +const { reportAlive } = require('./server'); const firebaseConfig = config.get('firebase'); async function initialize() { firebase.initializeApp(firebaseConfig); - await auth(); + return auth.init(); } -module.exports = { - initialize, -}; +function bgReportAlive(serverUid) { + reportAlive(serverUid); +} + +function bgProcessQueue() { + +} + +async function start() { + const [uid, , password] = await initialize(); + log.info(`[Firebase] Signed in as ${uid} (${password ? `password ${password}` : 'no password'})`); + + bgReportAlive(uid); + bgProcessQueue(); +} + +module.exports = { start }; diff --git a/src/firebase/server.js b/src/firebase/server.js new file mode 100644 index 0000000..3b03cbf --- /dev/null +++ b/src/firebase/server.js @@ -0,0 +1,14 @@ +const firebase = require('firebase'); +const CronTask = require('../cron/CronTask'); + +const SERVER_TIME = firebase.database.ServerValue.TIMESTAMP; + +function reportAlive(uid) { + console.info(firebase.auth().currentUser); + console.info(`/servers/${uid}/lastOnline`); + firebase.database().ref(`/servers/${uid}/lastOnline`).set(SERVER_TIME); +} + +module.exports = { + reportAlive, +}; diff --git a/src/firebase/servers.js b/src/firebase/servers.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/server.js b/src/server.js index 853ea8b..10a1b11 100644 --- a/src/server.js +++ b/src/server.js @@ -10,7 +10,6 @@ const cors = require('cors'); const log = require('./helpers/log'); const routes = require('./routes'); const firebase = require('./firebase'); -const cron = require('./cron'); const socketConnectionHandler = require('./sockets'); const PORT = config.get('port'); @@ -21,7 +20,7 @@ const server = http.createServer(app); // Hey you! care about my order http://stackoverflow.com/a/16781554/2034015 // Firebase stuff. -firebase.initialize(); +firebase.start(); // Cookies. app.use(cookieParser()); From d1c90b56c51a684d9763c59b47c6bd064759ba19 Mon Sep 17 00:00:00 2001 From: zurfyx Date: Tue, 4 Jul 2017 21:27:45 +0200 Subject: [PATCH 07/11] cron report alive --- src/cron/CronTask.js | 9 +++++ .../__tests__/{auth.test.js => utils.test.js} | 6 +-- src/firebase/auth.js | 39 ++++++++----------- src/firebase/index.js | 11 ++++-- src/firebase/server.js | 12 +++--- src/firebase/utils.js | 15 +++++++ 6 files changed, 58 insertions(+), 34 deletions(-) rename src/firebase/__tests__/{auth.test.js => utils.test.js} (51%) create mode 100644 src/firebase/utils.js diff --git a/src/cron/CronTask.js b/src/cron/CronTask.js index 777590e..fadc3be 100644 --- a/src/cron/CronTask.js +++ b/src/cron/CronTask.js @@ -9,8 +9,17 @@ class CronTask { await action(); log.dev(`[CRON] Finished "${name}"`); }; + this.action = newAction; this.cronJob = new CronJob(cronTime, newAction); } + + executeOnce() { + return this.action(); + } + + start() { + this.cronJob.start(); + } } module.exports = CronTask; diff --git a/src/firebase/__tests__/auth.test.js b/src/firebase/__tests__/utils.test.js similarity index 51% rename from src/firebase/__tests__/auth.test.js rename to src/firebase/__tests__/utils.test.js index d60ebeb..046a9a1 100644 --- a/src/firebase/__tests__/auth.test.js +++ b/src/firebase/__tests__/utils.test.js @@ -1,10 +1,10 @@ const { expect } = require('chai'); -const auth = require('../auth'); +const { encodeUid } = require('../utils'); describe('Firebase Auth', () => { it('encoded email address should decode alright', () => { const UID = 'loLlipOp'; - const expected = '1234@firebase.com'; - expect(auth._encodeFirebaseEmail(UID)).to.be(expected); // eslint-disable-line + const expected = [108, 111, 76, 108, 105, 112, 79, 112].join(''); + expect(encodeUid(UID)).to.equal(expected); }); }); diff --git a/src/firebase/auth.js b/src/firebase/auth.js index 7eaad38..b4325fe 100644 --- a/src/firebase/auth.js +++ b/src/firebase/auth.js @@ -13,6 +13,8 @@ const shortid = require('shortid'); const generatePassword = require('generate-password'); const firebase = require('firebase'); +const { encodeUid } = require('./utils'); + const readFile = Promise.promisify(fs.readFile); const writeFile = Promise.promisify(fs.writeFile); const createDir = Promise.promisify(mkdirp); @@ -30,7 +32,6 @@ async function readCredentials() { } async function generateCredentials() { - // Uid is part of the firebase email name. Use email acceptable characters only. const uid = shortid.generate(); const editKey = generatePassword.generate({ length: 20, @@ -46,36 +47,30 @@ async function saveCredentials(values) { return writeFile(CREDENTIALS_PATH, contents); } -function encodeFirebaseEmail(uid) { - const encodedUid = uid.split('').reduce((prev, curr) => { - const encodedCurr = curr.charCodeAt(0); - return `${prev}${encodedCurr}`; - }); +function encodeEmail(uid) { + const encodedUid = encodeUid(uid); return `${encodedUid}@firebase.com`; } -async function signup([uid, editKey]) { - const emailVal = encodeFirebaseEmail(uid); +function signup([uid, editKey]) { + const emailVal = encodeEmail(uid); const passwordVal = editKey; return firebase.auth().createUserWithEmailAndPassword(emailVal, passwordVal); } -async function signin([uid, editKey]) { - const emailVal = encodeFirebaseEmail(uid); +function signin([uid, editKey]) { + const emailVal = encodeEmail(uid); const passwordVal = editKey; return firebase.auth().signInWithEmailAndPassword(emailVal, passwordVal); } -module.exports = { - _encodeFirebaseEmail: encodeFirebaseEmail, - init: async () => { - let credentials = await readCredentials(); - if (!credentials) { - credentials = await generateCredentials(); - await saveCredentials(credentials); - await signup(credentials); - } - await signin(credentials); - return credentials; - }, +module.exports = async () => { + let credentials = await readCredentials(); + if (!credentials) { + credentials = await generateCredentials(); + await saveCredentials(credentials); + await signup(credentials); + } + await signin(credentials); + return credentials; }; diff --git a/src/firebase/index.js b/src/firebase/index.js index 1b21bf4..dc3838f 100644 --- a/src/firebase/index.js +++ b/src/firebase/index.js @@ -2,18 +2,21 @@ const firebase = require('firebase'); const config = require('config'); const log = require('../helpers/log'); +const CronTask = require('../cron/CronTask'); const auth = require('./auth'); -const { reportAlive } = require('./server'); +const server = require('./server'); const firebaseConfig = config.get('firebase'); -async function initialize() { +function initialize() { firebase.initializeApp(firebaseConfig); - return auth.init(); + return auth(); } function bgReportAlive(serverUid) { - reportAlive(serverUid); + const cron = new CronTask('Report Alive', '0,30 * * * * *', () => server.reportAlive(serverUid)); + cron.executeOnce(); + cron.start(); } function bgProcessQueue() { diff --git a/src/firebase/server.js b/src/firebase/server.js index 3b03cbf..95d9204 100644 --- a/src/firebase/server.js +++ b/src/firebase/server.js @@ -1,12 +1,14 @@ const firebase = require('firebase'); -const CronTask = require('../cron/CronTask'); + +const { encodeUid } = require('./utils'); const SERVER_TIME = firebase.database.ServerValue.TIMESTAMP; -function reportAlive(uid) { - console.info(firebase.auth().currentUser); - console.info(`/servers/${uid}/lastOnline`); - firebase.database().ref(`/servers/${uid}/lastOnline`).set(SERVER_TIME); +async function reportAlive(uid) { + const encodedUid = encodeUid(uid); + await firebase.database().ref(`/servers/${encodedUid}/lastOnline`).set(SERVER_TIME); + await firebase.database().ref(`/servers/${encodedUid}/isOnline`).set(true); + firebase.database().ref(`/servers/${encodedUid}/isOnline`).onDisconnect().set(false); } module.exports = { diff --git a/src/firebase/utils.js b/src/firebase/utils.js new file mode 100644 index 0000000..17b738d --- /dev/null +++ b/src/firebase/utils.js @@ -0,0 +1,15 @@ +/** + * Uid is part of the firebase email name. This encoding makes sure to use only email valid + * characters. + * @param uid + */ +function encodeUid(uid) { + return uid.split('').reduce((prev, curr) => { + const encodedCurr = curr.charCodeAt(0); + return `${prev}${encodedCurr}`; + }, ''); +} + +module.exports = { + encodeUid, +}; From 6d5d33b79f15dc00298a05fb37638a648cd160cb Mon Sep 17 00:00:00 2001 From: zurfyx Date: Tue, 4 Jul 2017 23:36:49 +0200 Subject: [PATCH 08/11] firebase queue structure --- src/firebase/index.js | 4 ++-- src/firebase/queue.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/firebase/index.js b/src/firebase/index.js index dc3838f..399adb9 100644 --- a/src/firebase/index.js +++ b/src/firebase/index.js @@ -19,7 +19,7 @@ function bgReportAlive(serverUid) { cron.start(); } -function bgProcessQueue() { +function bgListenQueue() { } @@ -28,7 +28,7 @@ async function start() { log.info(`[Firebase] Signed in as ${uid} (${password ? `password ${password}` : 'no password'})`); bgReportAlive(uid); - bgProcessQueue(); + bgListenQueue(); } module.exports = { start }; diff --git a/src/firebase/queue.js b/src/firebase/queue.js index e69de29..62a38dc 100644 --- a/src/firebase/queue.js +++ b/src/firebase/queue.js @@ -0,0 +1,13 @@ +const firebase = require('firebase'); + +const { encodeUid } = require('./utils'); + +const router = { + 'kml:value': () => {}, +}; + +function listenQueue() { + // firebase stuff +} + +module.exports = listenQueue; From 5098ee5d88e3f411649a61eb282272492a97c37e Mon Sep 17 00:00:00 2001 From: zurfyx Date: Wed, 5 Jul 2017 18:07:46 +0200 Subject: [PATCH 09/11] firebase process queue --- firebase/database.rules.json | 24 ++++++++-------- src/firebase/index.js | 7 +++-- src/firebase/queue.js | 53 ++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/firebase/database.rules.json b/firebase/database.rules.json index 7154bcd..07b0d2c 100644 --- a/firebase/database.rules.json +++ b/firebase/database.rules.json @@ -35,17 +35,19 @@ "$serverUidWithKey": { ".write": true, ".read": true, - "type": { - ".validate": "newData.isString()" - }, - "value": { - ".validate": "newData.isString()" - }, - "timestamp": { - ".validate": "newData.val() === now" - }, - "$other": { - ".validate": false + "$queueUid": { + "type": { + ".validate": "newData.isString()" + }, + "value": { + ".validate": "newData.isString()" + }, + "timestamp": { + ".validate": "newData.val() === now" + }, + "$other": { + ".validate": false + } } } }, diff --git a/src/firebase/index.js b/src/firebase/index.js index 399adb9..72aba8f 100644 --- a/src/firebase/index.js +++ b/src/firebase/index.js @@ -5,6 +5,7 @@ const log = require('../helpers/log'); const CronTask = require('../cron/CronTask'); const auth = require('./auth'); const server = require('./server'); +const queue = require('./queue'); const firebaseConfig = config.get('firebase'); @@ -19,8 +20,8 @@ function bgReportAlive(serverUid) { cron.start(); } -function bgListenQueue() { - +function bgListenQueue(serverUid) { + queue.listenQueue(serverUid); } async function start() { @@ -28,7 +29,7 @@ async function start() { log.info(`[Firebase] Signed in as ${uid} (${password ? `password ${password}` : 'no password'})`); bgReportAlive(uid); - bgListenQueue(); + bgListenQueue(uid); } module.exports = { start }; diff --git a/src/firebase/queue.js b/src/firebase/queue.js index 62a38dc..431a0e1 100644 --- a/src/firebase/queue.js +++ b/src/firebase/queue.js @@ -1,13 +1,54 @@ const firebase = require('firebase'); +const log = require('../helpers/log'); const { encodeUid } = require('./utils'); +const controllers = require('../controllers'); -const router = { - 'kml:value': () => {}, -}; +const { kml } = controllers; + +const SERVER_TIME = firebase.database.ServerValue.TIMESTAMP; + +const KML_VALUE = 'kml:value'; +const KML_HREF = 'kml:href'; +const QUERIES = 'queries'; -function listenQueue() { - // firebase stuff +const routes = value => ({ + [KML_VALUE]: () => kml.createKml({ contents: value }), + [KML_HREF]: () => kml.createKml({ uri: value }), + [QUERIES]: () => kml.createQuery({ contents: value }), +}); + +function controllerHandler(route, value) { + const routeAction = routes(value)[route]; + if (!routeAction) { + log.dev(`[Firebase] Unrecognised queue type: ${route}`); + return; + } + log.dev(`[Firebase] Executing queue type: ${route}`); + routeAction(); } -module.exports = listenQueue; +function listenQueue(uid) { + const encodedUid = encodeUid(uid); + const dbRef = firebase.database().ref(`queue/${encodedUid}`); + dbRef.orderByChild('timestamp').on('child_added', (snapshot) => { + const snapshotVal = snapshot.val(); + controllerHandler(snapshotVal.type, snapshotVal.value); + snapshot.ref.remove(); + }); +} + +function demoKml(uid) { + const encodedUid = encodeUid(uid); + const dbRef = firebase.database().ref(`queue/${encodedUid}`); + dbRef.push({ + type: KML_VALUE, + value: '123', + timestamp: SERVER_TIME, + }); +} + +module.exports = { + listenQueue, + demoKml, +}; From cb3ca4fe25c78d17b040ec151be233efe025cdb4 Mon Sep 17 00:00:00 2001 From: zurfyx Date: Wed, 5 Jul 2017 18:56:17 +0200 Subject: [PATCH 10/11] firebase report ip --- firebase/database.rules.json | 24 +++++++++++++++++++++--- src/firebase/server.js | 15 ++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/firebase/database.rules.json b/firebase/database.rules.json index 07b0d2c..02a2e9e 100644 --- a/firebase/database.rules.json +++ b/firebase/database.rules.json @@ -13,9 +13,6 @@ "isOnline": { ".validate": "newData.isBoolean()" }, - "ip": { - ".validate": "newData.val().matches(/^[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}$/)", - }, "hasPassword": { ".validate": "newData.isBoolean()" }, @@ -51,6 +48,27 @@ } } }, + "ips": { + "$ip": { + ".read": true, + ".validate": "$ip.matches(/^[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}$/)", + "$serverUid": { + ".write": "auth.token.email.beginsWith($serverUid + '@') || newData.val() === data.val()", + "displayName": { + ".validate": "newData.val().length < 200" + }, + "lastOnline": { + ".validate": "newData.val() === now" + }, + "isOnline": { + ".validate": "newData.isBoolean()" + }, + "$other":{ + ".validate": false + } + } + } + }, "$other": { ".validate": false } diff --git a/src/firebase/server.js b/src/firebase/server.js index 95d9204..9b29b07 100644 --- a/src/firebase/server.js +++ b/src/firebase/server.js @@ -1,4 +1,5 @@ const firebase = require('firebase'); +const publicIp = require('public-ip'); const { encodeUid } = require('./utils'); @@ -6,9 +7,17 @@ const SERVER_TIME = firebase.database.ServerValue.TIMESTAMP; async function reportAlive(uid) { const encodedUid = encodeUid(uid); - await firebase.database().ref(`/servers/${encodedUid}/lastOnline`).set(SERVER_TIME); - await firebase.database().ref(`/servers/${encodedUid}/isOnline`).set(true); - firebase.database().ref(`/servers/${encodedUid}/isOnline`).onDisconnect().set(false); + const encodedPublicIp = (await publicIp.v4()).replace(/\./g, ':'); + + const serverRef = firebase.database().ref(`/servers/${encodedUid}`); + await serverRef.child('lastOnline').set(SERVER_TIME); + await serverRef.child('isOnline').set(true); + serverRef.child('isOnline').onDisconnect().set(false); + + const ipRef = firebase.database().ref(`/ips/${encodedPublicIp}/${encodedUid}`); + await ipRef.child('lastOnline').set(SERVER_TIME); + await ipRef.child('isOnline').set(true); + ipRef.child('isOnline').onDisconnect().set(false); } module.exports = { From e6e4a8833309d1f688ffe1b5fc332e661c81fb24 Mon Sep 17 00:00:00 2001 From: zurfyx Date: Thu, 6 Jul 2017 17:48:00 +0200 Subject: [PATCH 11/11] removed cors from server --- package-lock.json | 5 ----- package.json | 1 - src/server.js | 3 --- 3 files changed, 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index f04435d..157ccf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -536,11 +536,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cors": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.3.tgz", - "integrity": "sha1-TPeOHSMymnSWsvwiJbd8pbteuAI=" - }, "create-error-class": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", diff --git a/package.json b/package.json index 82bca36..d1a5f50 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,6 @@ "config": "^1.25.1", "connect-redis": "^3.2.0", "cookie-parser": "^1.4.3", - "cors": "^2.8.3", "cron": "^1.2.1", "debug": "^2.6.1", "express": "^4.14.1", diff --git a/src/server.js b/src/server.js index 10a1b11..7142557 100644 --- a/src/server.js +++ b/src/server.js @@ -5,7 +5,6 @@ const cookieParser = require('cookie-parser'); const morgan = require('morgan'); const Socketio = require('socket.io'); const config = require('config'); -const cors = require('cors'); const log = require('./helpers/log'); const routes = require('./routes'); @@ -33,12 +32,10 @@ app.use(bodyParser.json()); app.use(morgan('combined', { stream: { write: msg => log.info(msg) } })); // URLs. -app.use(cors()); app.use('/', routes); // Socket.io const io = Socketio(server); -io.set('origins', '*:*'); io.on('connection', socketConnectionHandler); server.listen(PORT);