From 737620e2ca7920621275c4cf980e115297d7b2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Syn=C3=A1=C4=8Dek?= Date: Sat, 12 Dec 2020 13:19:34 +0100 Subject: [PATCH 1/3] feature: add endpoint for logging instrumentation --- functions/logger.js | 4 ++-- lib/app/index.js | 2 ++ lib/routes/index.js | 2 ++ lib/routes/track.js | 24 ++++++++++++++++++++++++ lib/static/app/logger.js | 17 +++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 lib/routes/track.js diff --git a/functions/logger.js b/functions/logger.js index db59880..b3b189e 100644 --- a/functions/logger.js +++ b/functions/logger.js @@ -8,9 +8,9 @@ class FirebaseTransport extends Transport { this._db = options.db } - log(info, callback) { + async log(info, callback) { try { - this._db.ref(`log-${new Date().getTime()}`).set(info) + await this._db.ref(`log-${new Date().getTime()}`).set(info) callback(null, info) this.emit('logged', info) } catch (err) { diff --git a/lib/app/index.js b/lib/app/index.js index 4430848..a6413a1 100644 --- a/lib/app/index.js +++ b/lib/app/index.js @@ -10,6 +10,7 @@ const { notFound, download, downloadHTML, + track, } = require('../routes') const { @@ -92,6 +93,7 @@ const configureApplication = ({ app.post('/download/html', downloadHTML(appLogger || null)) app.post('/download', download(appLogger || null)) + app.post('/track', track(appLogger || null)) app.use(genericErrorHandler) diff --git a/lib/routes/index.js b/lib/routes/index.js index 30f31ac..3f9b9dd 100644 --- a/lib/routes/index.js +++ b/lib/routes/index.js @@ -1,9 +1,11 @@ const { error, notFound } = require('./error') const { download, downloadHTML } = require('./download') +const { track } = require('./track') module.exports = { error, notFound, download, downloadHTML, + track, } diff --git a/lib/routes/track.js b/lib/routes/track.js new file mode 100644 index 0000000..0399e2a --- /dev/null +++ b/lib/routes/track.js @@ -0,0 +1,24 @@ +const track = (logger) => async (req, res) => { + try { + if (!logger) { + return res.status(501) + } + + const { message, service, level, env } = req.body + + await logger.log({ + message, + level, + service, + env: process.env.NODE_APP || env || 'N/A', + }) + + res.status(200).send({ status: 'ok' }) + } catch (err) { + next(err) + } +} + +module.exports = { + track, +} diff --git a/lib/static/app/logger.js b/lib/static/app/logger.js index 95b8775..4ba5ad5 100644 --- a/lib/static/app/logger.js +++ b/lib/static/app/logger.js @@ -6,6 +6,23 @@ class Logger { 'color: grey', 'color: black' ) + + this._log({ message, level, service }) + } + + _log({ message, level, service }) { + return new Promise((resolve, reject) => { + fetch('/track', { + method: 'POST', + headers: { + 'Accept': 'text/html, application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ message, level, service, env: 'browser' }) + }) + .then(resolve) + .catch(reject) + }) } } From a249a23ddd4dc1c964259af2a00823f2f2150f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Syn=C3=A1=C4=8Dek?= Date: Sat, 12 Dec 2020 13:54:59 +0100 Subject: [PATCH 2/3] feature: add UI for user approval of tracking --- lib/static/app/logger.js | 12 +++++++ lib/static/app/storage.js | 26 +++++++++++++++ lib/static/index.js | 66 +++++++++++++++++++++++++++++++++++++++ lib/static/style.css | 20 ++++++++++++ 4 files changed, 124 insertions(+) diff --git a/lib/static/app/logger.js b/lib/static/app/logger.js index 4ba5ad5..76eb53d 100644 --- a/lib/static/app/logger.js +++ b/lib/static/app/logger.js @@ -1,4 +1,8 @@ class Logger { + constructor() { + this._remote = false + } + log({ message, level, service }) { console.info( `%c${level}\n%c${service}\n%c${message}`, @@ -7,9 +11,17 @@ class Logger { 'color: black' ) + if (!this._remote) { + return + } + this._log({ message, level, service }) } + setRemoteLogging(value) { + this._remote = value + } + _log({ message, level, service }) { return new Promise((resolve, reject) => { fetch('/track', { diff --git a/lib/static/app/storage.js b/lib/static/app/storage.js index 3988a18..bb60519 100644 --- a/lib/static/app/storage.js +++ b/lib/static/app/storage.js @@ -27,6 +27,21 @@ const getStorage = () => { return storage } +const getConfigStorage = () => { + if (!useStorage()) { + return null + } + + const storage = localStorage.getItem('fb-to-ical-config') + + if (!storage) { + localStorage.setItem('fb-to-ical-config', JSON.stringify({})) + return "{}" + } + + return storage +} + const getStorageContents = (storage) => { return JSON.parse(storage) } @@ -37,6 +52,15 @@ const updateStorage = (storageContents) => { localStorage.setItem('fb-to-ical-events', encodedStorage) } +const updateConfigStorage = (storageContents, key, value) => { + const encodedStorage = JSON.stringify({ + ...storageContents, + [key]: value, + }) + + localStorage.setItem('fb-to-ical-config', encodedStorage) +} + const saveRecord = ({ id, link, createdAt, startTime, title }) => { if (!useStorage()) { return @@ -80,8 +104,10 @@ const deleteRecord = (id) => { export { migrateRecord, getStorage, + getConfigStorage, getStorageContents, updateStorage, + updateConfigStorage, saveRecord, deleteRecord, } diff --git a/lib/static/index.js b/lib/static/index.js index bd0a4f5..8ee6608 100644 --- a/lib/static/index.js +++ b/lib/static/index.js @@ -2,8 +2,10 @@ import { uuidv4, parseStartTimeFromiCalString, useStorage } from './app/utils' import { migrateRecord, getStorage, + getConfigStorage, getStorageContents, updateStorage, + updateConfigStorage, saveRecord, deleteRecord, } from './app/storage' @@ -31,6 +33,40 @@ import generateICS from '../../lib/services/ics-generator' row.remove() } + const renderTrackingPanel = (storageContents) => { + const trackingPanel = document.createElement('div') + trackingPanel.id = 'tracking-panel' + + const text = document.createElement('p') + text.innerText = `Can we store anonymous logs? This data is only saved to \ +our internal database and is used to debug the parsing of the web pages.\n\ +We'll ask you only this time and won't bother you again.` + + const yesButton = document.createElement('button') + yesButton.id = 'tracking-panel__yes-button' + yesButton.innerText = 'Ok' + yesButton.addEventListener('click', () => { + updateConfigStorage(storageContents, 'track', true) + logger.setRemoteLogging(true) + trackingPanel.remove() + }) + + const noButton = document.createElement('button') + noButton.id = 'tracking-panel__no-button' + noButton.innerText = 'Nope' + noButton.addEventListener('click', () => { + updateConfigStorage(storageContents, 'track', false) + logger.setRemoteLogging(false) + trackingPanel.remove() + }) + + trackingPanel.appendChild(text) + trackingPanel.appendChild(yesButton) + trackingPanel.appendChild(noButton) + + document.body.appendChild(trackingPanel) + } + const insertTableRow = ({ id, link, createdAt, startTime, title }) => { showTable() @@ -122,6 +158,34 @@ import generateICS from '../../lib/services/ics-generator' }) } + const hydrateConfig = () => { + if (!useStorage()) { + return + } + + const prevStorage = getConfigStorage() + const storageContents = getStorageContents(prevStorage) + + const useTrackingSet = storageContents.track !== undefined + + if (!useTrackingSet) { + renderTrackingPanel(storageContents) + } + } + + const configureLogger = (logger) => { + if (!logger || !useStorage()) { + return + } + + const prevStorage = getConfigStorage() + const storageContents = getStorageContents(prevStorage) + + const shouldTrack = Boolean(storageContents.track) + + logger.setRemoteLogging(shouldTrack) + } + const clearStatuses = () => { document.querySelectorAll('.status-item').forEach((item) => { item.classList.remove('show') @@ -235,6 +299,8 @@ import generateICS from '../../lib/services/ics-generator' } hydrateList() + hydrateConfig() + configureLogger(logger) const handleHTMLResponse = (html, url) => { try { diff --git a/lib/static/style.css b/lib/static/style.css index 0aee628..7d51b37 100644 --- a/lib/static/style.css +++ b/lib/static/style.css @@ -166,3 +166,23 @@ a.delete-record { #nojs { margin: 5px 0; } + +#tracking-panel { + position: fixed; + bottom: 0; + border: 3px solid navy; + background-color: lightyellow; + max-width: 600px; + margin: 5px; + padding: 5px; +} + +#tracking-panel__yes-button, +#tracking-panel__no-button { + font-size: 1.2rem; +} + +#tracking-panel__yes-button { + font-weight: 600; + margin-right: 5px; +} From ebf75ef281e336a5ed1e6f7da735960e8a81b0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Syn=C3=A1=C4=8Dek?= Date: Sat, 12 Dec 2020 13:55:28 +0100 Subject: [PATCH 3/3] 1.3.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d87dbd..1cf4841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "facebook-events-ical-converter", - "version": "1.3.0", + "version": "1.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a523298..744ce0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "facebook-events-ical-converter", - "version": "1.3.0", + "version": "1.3.1", "description": "App that converts events on Facebook event page to iCal file.", "main": "lib/index.js", "engines": {