diff --git a/app.js b/app.js index 1b5577b..6ea88ae 100644 --- a/app.js +++ b/app.js @@ -38,6 +38,8 @@ commander global.commander = commander; +var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); + app.set('views', path.join(__dirname, 'core/views')); app.set('user', path.join(__dirname, global.opts.core.common.pathToUser)); @@ -52,12 +54,17 @@ var log = logger.log; global.log = log; if (commander.html) { + trackStats.staticEvent('features', 'enabled html parser'); + global.opts.plugins.htmlParser.enabled = true; global.opts.plugins.htmlParser.onStart = true; } if (commander.port) global.opts.core.server.port = parseInt(commander.port); if (commander.hostname) global.opts.core.server.hostname = commander.hostname; -if (!commander.watch) global.opts.core.watch.enabled = false; +if (!commander.watch) { + trackStats.staticEvent('features', 'disabled watch'); + global.opts.core.watch.enabled = false; +} /* /Globals */ @@ -241,6 +248,12 @@ if (!module.parent) { process.exit(1); } }); + } else { + if (global.opts.core.common.trackAnonymusStatistics) { + trackStats.staticEvent('start', 'default'); + } else { + trackStats.staticEvent('start', 'no stats', true); + } } } /* Server start */ diff --git a/core/middlewares/clarify.js b/core/middlewares/clarify.js index 3046f8f..56fb43a 100644 --- a/core/middlewares/clarify.js +++ b/core/middlewares/clarify.js @@ -8,6 +8,7 @@ var _ = require('lodash'); var jsdom = require('jsdom'); var ejs = require('ejs'); +var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); var pathToApp = path.dirname(require.main.filename); var specUtils = require(path.join(pathToApp, 'core/lib/specUtils')); var parseData = require(path.join(pathToApp, 'core/lib/parseData')); @@ -313,6 +314,8 @@ module.exports.process = function(req, res, next) { html = msg; } + trackStats.page('clarify', req.sessionID); + res.send(html); }).fail(function(err) { var msg = 'ERROR: Could not find requested or default template for Clarify'; diff --git a/core/middlewares/send.js b/core/middlewares/send.js index 4da3510..8b34155 100644 --- a/core/middlewares/send.js +++ b/core/middlewares/send.js @@ -1,6 +1,8 @@ 'use strict'; +var path = require('path'); var prettyHrtime = require('pretty-hrtime'); +var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); /** * In case if request contains rendered html, then send it as response and stop spec content post-processing. @@ -11,6 +13,8 @@ var prettyHrtime = require('pretty-hrtime'); * */ exports.process = function (req, res, next) { if (req.specData && req.specData.renderedHtml) { + trackStats.specs(req); + global.log.trace('Spec loading time: ', prettyHrtime(process.hrtime(global.specLoadTime))); res.send(req.specData.renderedHtml); diff --git a/core/trackStats.js b/core/trackStats.js new file mode 100644 index 0000000..1da416c --- /dev/null +++ b/core/trackStats.js @@ -0,0 +1,76 @@ +var url = require('url'); +var path = require('path'); +var macaddress = require('macaddress'); +var ua = require('universal-analytics'); +var log = require(path.join(global.pathToApp, 'core/logger')).log; +var crypto = require('crypto'); + +var generateMachineID = function(){ + var macNums = macaddress.networkInterfaces(); + var unique = ''; + var macItem; + + for (macItem in macNums) { + var val = macNums[macItem]; + + if (val.mac) { + unique += val.mac; + } else if (val.ipv4) { + unique += val.ipv4; + } + } + + return 'host_' + crypto.createHash('md5').update(unique).digest('hex').slice(0, 5); +}; + +var staticVisitor = ua('UA-66924051-1', generateMachineID(), {strictCidFormat: false}); + +// Track page visits by unique session ID +var trackPage = function(opts){ + if (!global.opts.core.common.trackAnonymusStatistics) return; + + var visitor = ua('UA-66924051-1', opts.sessionID, {strictCidFormat: false}); + + log.trace('track page', opts.pageName); + + visitor.pageview(opts.pageName).send(); +}; + +// Track host-initiated events (by unique machine id) +var staticEvent = function(group, event, force){ + if (!force && !global.opts.core.common.trackAnonymusStatistics) return; + + log.trace('track event', group, event); + + staticVisitor.event(group, event).send(); +}; + +module.exports.specs = function(req) { + var pageName = 'spec'; + var sessionID = req.sessionID; + + var parsedUrl = url.parse(req.url, true); + var q = parsedUrl.query || {}; + + if (q.internal) return; + + if (req.originalPath === '/') { + pageName = '/'; + } else if (req.specData && req.specData.info && req.specData.info.role === 'navigation') { + pageName = 'navigation'; + } + + trackPage({ + sessionID: sessionID, + pageName: pageName + }); +}; + +module.exports.page = function(pageName, sessionID) { + trackPage({ + sessionID: sessionID, + pageName: pageName + }); +}; + +module.exports.staticEvent = staticEvent; \ No newline at end of file diff --git a/core/warn.js b/core/warn.js new file mode 100644 index 0000000..dd27c2c --- /dev/null +++ b/core/warn.js @@ -0,0 +1,21 @@ +'use strict'; + +var path = require('path'); + +var rootPath = __dirname.replace(/^\w:\\/, function (match) { + return match.toLowerCase(); +}); +var enginePath = global.pathToApp = path.join(rootPath, '../'); + +var loadOptions = require('./loadOptions'); +var options = global.opts = loadOptions(enginePath); + +var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); + +if (options && options.core.common && options.core.common.trackAnonymusStatistics) { + trackStats.staticEvent('install', 'default'); + + console.log('\n[SOURCEJS] Note: engine tracks anonymous usage statistics. To disable it, edit `core.common.trackAnonymusStatistics` configuration.\n'); +} else { + trackStats.staticEvent('install', 'no stats', true); +} \ No newline at end of file diff --git a/options.js b/options.js index 57fafb4..566da49 100644 --- a/options.js +++ b/options.js @@ -24,7 +24,9 @@ module.exports = { infoFile: 'info.json', // Name of options field in info.json, used to override configuration per spec - infoFileOptions: 'sourcejs' + infoFileOptions: 'sourcejs', + + trackAnonymusStatistics: true }, // Server options are passed to app.listen (https://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback) diff --git a/package.json b/package.json index a454dad..1b17972 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "async": "~0.9.0", "body-parser": "~1.6.4", "cheerio": "^0.19.0", + "chokidar": "git+https://github.com/operatino/chokidar.git", "colors": "0.6.x", "commander": "^2.8.1", "compression": "~1.0.11", @@ -25,6 +26,7 @@ "flat": "^1.2.1", "fs-extra": "~0.11.1", "fs-finder": "operatino/node-fs-finder#fix-windows-find-up", + "fsevents": "^0.3.8", "grunt": "~0.4.5", "grunt-autoprefixer": "~1.0.0", "grunt-cli": "^0.1.13", @@ -43,6 +45,7 @@ "load-grunt-tasks": "~0.3.0", "lodash": "^3.10.1", "log4js": "~0.6.20", + "macaddress": "^0.2.8", "marked": "^0.3.2", "path": "^0.4.9", "phantomjs": "1.9.7-15", @@ -50,10 +53,11 @@ "q": "^1.1.1", "serve-favicon": "^2.2.0", "time-grunt": "~0.2.10", - "tinyforever": "0.0.3" + "tinyforever": "0.0.3", + "universal-analytics": "^0.3.9" }, "scripts": { - "postinstall": "node ./core/postInstall && grunt update", + "postinstall": "node ./core/postInstall && grunt update && node ./core/warn", "build": "npm i", "start": "node app", "test": "grunt ci-pre-run && node app.js --test",