Merge pull request #46 from lbryio/analytics-v2

Analytics v2
This commit is contained in:
Bill Bittner 2017-06-29 22:33:59 -07:00 committed by GitHub
commit 6685511aa5
12 changed files with 133 additions and 84 deletions

View file

@ -5,7 +5,7 @@
"LbryChangeAddress": "none" "LbryChangeAddress": "none"
}, },
"AnalyticsConfig":{ "AnalyticsConfig":{
"GoogleId": "UA-60403362-2" "GoogleId": "UA-60403362-3"
}, },
"Database": { "Database": {
"MySqlConnectionUri": "none", "MySqlConnectionUri": "none",

View file

@ -1,7 +1,60 @@
const logger = require('winston'); const logger = require('winston');
const ua = require('universal-analytics');
const config = require('config');
const db = require('../models'); const db = require('../models');
const googleApiKey = config.get('AnalyticsConfig.GoogleId');
module.exports = { module.exports = {
postToStats: (action, url, ipAddress, result) => {
logger.silly('creating record for statistics db');
// make sure the result is a string
if (result && (typeof result !== 'string')) {
result = result.toString();
}
// // make sure the ip address(es) are a string
if (ipAddress && (typeof ipAddress !== 'string')) {
ipAddress = ipAddress.toString();
}
// create record in the db
db.Stats.create({
action,
url,
ipAddress,
result,
})
.then()
.catch(error => {
logger.error('sequelize error', error);
});
},
sendGoogleAnalytics: (action, ip, originalUrl) => {
const visitorId = ip.replace(/\./g, '-');
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
switch (action) {
case 'serve':
visitor.event('serve', originalUrl, (err) => {
if (err) {
logger.error('Google Analytics Event Error >>', err);
}
});
break;
case 'show':
visitor.pageview(originalUrl, 'https://spee.ch', 'show route', (err) => {
if (err) {
logger.error('Google Analytics Pageview Error >>', err);
}
});
break;
case 'publish':
visitor.event('publish', originalUrl, (err) => {
if (err) {
logger.error('Google Analytics Event Error >>', err);
}
});
break;
default: break;
}
},
getStatsSummary: () => { getStatsSummary: () => {
logger.debug('retrieving site statistics'); logger.debug('retrieving site statistics');
const deferred = new Promise((resolve, reject) => { const deferred = new Promise((resolve, reject) => {

View file

@ -1,5 +1,5 @@
const logger = require('winston'); const logger = require('winston');
const { postToStats } = require('./statsHelpers.js'); const { postToStats } = require('../../controllers/statsController.js');
module.exports = { module.exports = {
handleRequestError (action, originalUrl, ip, error, res) { handleRequestError (action, originalUrl, ip, error, res) {

View file

@ -1,27 +0,0 @@
const db = require('../../models');
const logger = require('winston');
module.exports = {
postToStats: (action, url, ipAddress, result) => {
logger.silly('creating record for statistics db');
// make sure the result is a string
if (result && (typeof result !== 'string')) {
result = result.toString();
}
// // make sure the ip address(es) are a string
if (ipAddress && (typeof ipAddress !== 'string')) {
ipAddress = ipAddress.toString();
}
// create record in the db
db.Stats.create({
action,
url,
ipAddress,
result,
})
.then()
.catch(error => {
logger.error('sequelize error', error);
});
},
};

View file

@ -128,6 +128,12 @@ uploader.addEventListener('start', function(event){
document.getElementById('publish-active-area').innerHTML = '<div id="publish-status"></div><div id="progress-bar"></div>'; document.getElementById('publish-active-area').innerHTML = '<div id="publish-status"></div><div id="progress-bar"></div>';
// start a progress animation // start a progress animation
createProgressBar(document.getElementById('progress-bar'), 12); createProgressBar(document.getElementById('progress-bar'), 12);
// google analytics
ga('send', {
hitType: 'event',
eventCategory: 'publish',
eventAction: name
});
}); });
uploader.addEventListener('progress', function(event){ uploader.addEventListener('progress', function(event){
var percent = event.bytesLoaded / event.file.size * 100; var percent = event.bytesLoaded / event.file.size * 100;

View file

@ -108,6 +108,12 @@ uploader.addEventListener('start', function(event){
document.getElementById('publish-active-area').innerHTML = '<div id="publish-status"></div><div id="progress-bar"></div>'; document.getElementById('publish-active-area').innerHTML = '<div id="publish-status"></div><div id="progress-bar"></div>';
// start a progress animation // start a progress animation
createProgressBar(document.getElementById('progress-bar'), 12); createProgressBar(document.getElementById('progress-bar'), 12);
// google analytics
ga('send', {
hitType: 'event',
eventCategory: 'publish',
eventAction: nameInput.value
});
}); });
uploader.addEventListener('progress', function(event){ uploader.addEventListener('progress', function(event){
var percent = event.bytesLoaded / event.file.size * 100; var percent = event.bytesLoaded / event.file.size * 100;

View file

@ -5,12 +5,16 @@ const publishController = require('../controllers/publishController.js');
const lbryApi = require('../helpers/libraries/lbryApi.js'); const lbryApi = require('../helpers/libraries/lbryApi.js');
const publishHelpers = require('../helpers/libraries/publishHelpers.js'); const publishHelpers = require('../helpers/libraries/publishHelpers.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { postToStats } = require('../helpers/libraries/statsHelpers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
module.exports = app => { module.exports = app => {
// route to run a claim_list request on the daemon // route to run a claim_list request on the daemon
app.get('/api/claim_list/:claim', ({ originalUrl, params, ip }, res) => { app.get('/api/claim_list/:claim', ({ ip, originalUrl, params }, res) => {
// google analytics
sendGoogleAnalytics('serve', ip, originalUrl);
// log
logger.verbose(`GET request on ${originalUrl} from ${ip}`); logger.verbose(`GET request on ${originalUrl} from ${ip}`);
// serve the content
lbryApi lbryApi
.getClaimsList(params.claim) .getClaimsList(params.claim)
.then(claimsList => { .then(claimsList => {
@ -22,8 +26,12 @@ module.exports = app => {
}); });
}); });
// route to run a resolve request on the daemon // route to run a resolve request on the daemon
app.get('/api/resolve/:uri', ({ originalUrl, params, ip }, res) => { app.get('/api/resolve/:uri', ({ ip, originalUrl, params }, res) => {
// google analytics
sendGoogleAnalytics('serve', ip, originalUrl);
// log
logger.verbose(`GET request on ${originalUrl} from ${ip}`); logger.verbose(`GET request on ${originalUrl} from ${ip}`);
// serve content
lbryApi lbryApi
.resolveUri(params.uri) .resolveUri(params.uri)
.then(resolvedUri => { .then(resolvedUri => {
@ -35,7 +43,10 @@ module.exports = app => {
}); });
}); });
// route to run a publish request on the daemon // route to run a publish request on the daemon
app.post('/api/publish', multipartMiddleware, ({ originalUrl, body, files, ip }, res) => { app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl }, res) => {
// google analytics
sendGoogleAnalytics('publish', ip, originalUrl);
// log
logger.verbose(`POST request on ${originalUrl} from ${ip}`); logger.verbose(`POST request on ${originalUrl} from ${ip}`);
// validate that a file was provided // validate that a file was provided
const file = files.speech || files.null; const file = files.speech || files.null;

View file

@ -1,16 +1,25 @@
const logger = require('winston'); const logger = require('winston');
const { postToStats } = require('../helpers/libraries/statsHelpers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
module.exports = app => { module.exports = app => {
// route for the home page // route for the home page
app.get('/', ({ originalUrl, ip, headers }, res) => { app.get('/', ({ headers, ip, originalUrl }, res) => {
// google analytics
sendGoogleAnalytics('serve', ip, originalUrl);
// logging
logger.verbose(`GET request on ${originalUrl} from ${ip}`); logger.verbose(`GET request on ${originalUrl} from ${ip}`);
// send response
res.status(200).render('index'); res.status(200).render('index');
}); });
// a catch-all route if someone visits a page that does not exist // a catch-all route if someone visits a page that does not exist
app.use('*', ({ originalUrl, ip }, res) => { app.use('*', ({ originalUrl, ip }, res) => {
// google analytics
sendGoogleAnalytics('serve', ip, originalUrl);
// logging
logger.error(`Get request on ${originalUrl} from ${ip} which was a 404`); logger.error(`Get request on ${originalUrl} from ${ip} which was a 404`);
postToStats('post', originalUrl, ip, 'Error: 404'); // post to stats
postToStats('show', originalUrl, ip, 'Error: 404');
// send response
res.status(404).render('fourOhFour'); res.status(404).render('fourOhFour');
}); });
}; };

View file

@ -1,21 +1,7 @@
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const serveController = require('../controllers/serveController.js'); const serveController = require('../controllers/serveController.js');
const logger = require('winston'); const logger = require('winston');
const { postToStats } = require('../helpers/libraries/statsHelpers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
function sendGoogleAnalytics (ua, googleApiKey, ip, originalUrl) {
const visitorId = ip.replace(/\./g, '-');
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
visitor.pageview(originalUrl, 'https://spee.ch', 'Serve Route', (err) => {
if (err) {
logger.error('Google Analytics Pageview Error >>', err);
}
}).event('Serve', originalUrl, (err) => {
if (err) {
logger.error('Google Analytics Event Error >>', err);
}
});
}
function serveFile ({ fileName, fileType, filePath }, res) { function serveFile ({ fileName, fileType, filePath }, res) {
logger.info(`serving file ${fileName}`); logger.info(`serving file ${fileName}`);
@ -46,13 +32,17 @@ function serveFile ({ fileName, fileType, filePath }, res) {
res.status(200).sendFile(filePath, options); res.status(200).sendFile(filePath, options);
} }
module.exports = (app, ua, googleApiKey) => { function sendAnalyticsAndLog (ip, originalUrl) {
// route to fetch one free public claim
app.get('/:name/:claim_id', ({ originalUrl, params, ip }, res) => {
// google analytics // google analytics
sendGoogleAnalytics(ua, googleApiKey, ip, originalUrl); sendGoogleAnalytics('serve', ip, originalUrl);
// logging // logging
logger.verbose(`GET request on ${originalUrl} from ${ip}`); logger.verbose(`GET request on ${originalUrl} from ${ip}`);
}
module.exports = (app) => {
// route to fetch one free public claim
app.get('/:name/:claim_id', ({ ip, originalUrl, params }, res) => {
sendAnalyticsAndLog(ip, originalUrl);
// begin image-serve processes // begin image-serve processes
serveController serveController
.getClaimByClaimId(params.name, params.claim_id) .getClaimByClaimId(params.name, params.claim_id)
@ -65,11 +55,8 @@ module.exports = (app, ua, googleApiKey) => {
}); });
}); });
// route to fetch one free public claim // route to fetch one free public claim
app.get('/:name', ({ originalUrl, params, ip }, res) => { app.get('/:name', ({ ip, originalUrl, params }, res) => {
// google analytics sendAnalyticsAndLog(ip, originalUrl);
sendGoogleAnalytics(ua, googleApiKey, ip, originalUrl);
// logging
logger.verbose(`GET request on ${originalUrl} from ${ip}`);
// begin image-serve processes // begin image-serve processes
serveController serveController
.getClaimByName(params.name) .getClaimByName(params.name)

View file

@ -1,16 +1,22 @@
const logger = require('winston'); const logger = require('winston');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const showController = require('../controllers/showController.js'); const { getAllClaims } = require('../controllers/showController.js');
const { postToStats } = require('../helpers/libraries/statsHelpers.js'); const { getStatsSummary, postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const statsController = require('../controllers/statsController.js');
function sendAnalyticsAndLog (ip, originalUrl) {
// google analytics
sendGoogleAnalytics('show', ip, originalUrl);
// logging
logger.verbose(`POST request on ${originalUrl} from ${ip}`);
// get and serve the content
}
module.exports = (app) => { module.exports = (app) => {
// route to show the meme-fodder meme maker // route to show the meme-fodder meme maker
app.get('/meme-fodder/play', ({ originalUrl, ip }, res) => { app.get('/meme-fodder/play', ({ ip, originalUrl }, res) => {
logger.verbose(`POST request on ${originalUrl} from ${ip}`); sendAnalyticsAndLog(ip, originalUrl);
// get and serve the content // get and render the content
showController getAllClaims('meme-fodder')
.getAllClaims('meme-fodder')
.then(orderedFreePublicClaims => { .then(orderedFreePublicClaims => {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, 'success');
res.status(200).render('memeFodder', { claims: orderedFreePublicClaims }); res.status(200).render('memeFodder', { claims: orderedFreePublicClaims });
@ -20,11 +26,10 @@ module.exports = (app) => {
}); });
}); });
// route to show statistics for spee.ch // route to show statistics for spee.ch
app.get('/stats', ({ originalUrl, ip }, res) => { app.get('/stats', ({ ip, originalUrl }, res) => {
logger.verbose(`POST request on ${originalUrl} from ${ip}`); sendAnalyticsAndLog(ip, originalUrl);
// get and serve the content // get and render the content
statsController getStatsSummary()
.getStatsSummary()
.then(result => { .then(result => {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, 'success');
res.status(200).render('statistics', result); res.status(200).render('statistics', result);
@ -34,11 +39,10 @@ module.exports = (app) => {
}); });
}); });
// route to display all free public claims at a given name // route to display all free public claims at a given name
app.get('/:name/all', ({ originalUrl, params, ip }, res) => { app.get('/:name/all', ({ ip, originalUrl, params }, res) => {
logger.verbose(`POST request on ${originalUrl} from ${ip}`); sendAnalyticsAndLog(ip, originalUrl);
// get and serve the content // get and render the content
showController getAllClaims(params.name)
.getAllClaims(params.name)
.then(orderedFreePublicClaims => { .then(orderedFreePublicClaims => {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, 'success');
res.status(200).render('allClaims', { claims: orderedFreePublicClaims }); res.status(200).render('allClaims', { claims: orderedFreePublicClaims });

View file

@ -2,7 +2,7 @@ const logger = require('winston');
const publishController = require('../controllers/publishController.js'); const publishController = require('../controllers/publishController.js');
const publishHelpers = require('../helpers/libraries/publishHelpers.js'); const publishHelpers = require('../helpers/libraries/publishHelpers.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { postToStats } = require('../helpers/libraries/statsHelpers.js'); const { postToStats } = require('../controllers/statsController.js');
module.exports = (app, siofu, hostedContentPath) => { module.exports = (app, siofu, hostedContentPath) => {
const http = require('http').Server(app); const http = require('http').Server(app);
@ -16,6 +16,7 @@ module.exports = (app, siofu, hostedContentPath) => {
uploader.listen(socket); uploader.listen(socket);
// listener for when file upload starts // listener for when file upload starts
uploader.on('start', ({ file }) => { uploader.on('start', ({ file }) => {
// log
logger.info('client started an upload:', file.name); logger.info('client started an upload:', file.name);
// server side test to make sure file is not a bad file type // server side test to make sure file is not a bad file type
if (/\.exe$/.test(file.name)) { if (/\.exe$/.test(file.name)) {

View file

@ -6,8 +6,7 @@ const expressHandlebars = require('express-handlebars');
const Handlebars = require('handlebars'); const Handlebars = require('handlebars');
const config = require('config'); const config = require('config');
const winston = require('winston'); const winston = require('winston');
const ua = require('universal-analytics');
const googleApiKey = config.get('AnalyticsConfig.GoogleId');
const hostedContentPath = config.get('Database.PublishUploadPath'); const hostedContentPath = config.get('Database.PublishUploadPath');
// configure logging // configure logging
@ -58,7 +57,7 @@ app.set('view engine', 'handlebars');
// require express routes // require express routes
require('./routes/api-routes.js')(app); require('./routes/api-routes.js')(app);
require('./routes/show-routes.js')(app); require('./routes/show-routes.js')(app);
require('./routes/serve-routes.js')(app, ua, googleApiKey); require('./routes/serve-routes.js')(app);
require('./routes/home-routes.js')(app); require('./routes/home-routes.js')(app);
// require socket.io routes // require socket.io routes