Merge branch 'add-track-endpoint-for-instrumentation' into 'master'

Add track endpoint for instrumentation

See merge request ondrejsynacek/facebook-events-ical-converter!2
This commit is contained in:
Ondřej Synáček 2020-12-12 12:58:29 +00:00
commit c4cbc3ca4c
10 changed files with 173 additions and 4 deletions

View File

@ -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) {

View File

@ -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)

View File

@ -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,
}

24
lib/routes/track.js Normal file
View File

@ -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,
}

View File

@ -1,4 +1,8 @@
class Logger {
constructor() {
this._remote = false
}
log({ message, level, service }) {
console.info(
`%c${level}\n%c${service}\n%c${message}`,
@ -6,6 +10,31 @@ class Logger {
'color: grey',
'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', {
method: 'POST',
headers: {
'Accept': 'text/html, application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ message, level, service, env: 'browser' })
})
.then(resolve)
.catch(reject)
})
}
}

View File

@ -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,
}

View File

@ -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 {

View File

@ -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;
}

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "facebook-events-ical-converter",
"version": "1.3.0",
"version": "1.3.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -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": {