diff --git a/.env-base b/.env-base index c51cbfcd..1f6fb46a 100644 --- a/.env-base +++ b/.env-base @@ -135,3 +135,11 @@ REDIRECT_URLS="http://localhost:5050,https://localhost:8080,https://app.speckle. # "localhost" redirect urls will always work with or without TLS. ALLOW_INSECURE_REDIRECTS=false +# +# Telemetry +# see: https://discourse.speckle.works/t/community-consultation-time-telemetry/410 +# This helps us keep track anonymously of the number of speckle servers deployed. +# Disabling this will make Speckle sad :( +# +TELEMETRY=true + diff --git a/app/api/index.js b/app/api/index.js index 5ba4341a..adb23729 100644 --- a/app/api/index.js +++ b/app/api/index.js @@ -2,6 +2,7 @@ const passport = require( 'passport' ) const adminCheck = require( './middleware/AdminCheck' ) module.exports = function ( app, express, urlRoot, plugins ) { + var r = new express.Router( ) // strict auth will return a 401 if no authorization header is present. pass means req.user exists diff --git a/app/telemetry/appTelemetry.js b/app/telemetry/appTelemetry.js new file mode 100644 index 00000000..5d4044b7 --- /dev/null +++ b/app/telemetry/appTelemetry.js @@ -0,0 +1,42 @@ +const countly = require( 'countly-sdk-nodejs' ) +const myMachineId = require( 'node-machine-id' ).machineIdSync( ) + +const logger = require( '../../config/logger' ) + +// This module lets us know which routes are hit on a server, thus aggregating +// the "hot" endpoints. + +module.exports = ( app ) => { + + if ( process.env.TELEMETRY === 'false' ) + return + + try { + countly.init( { + // eslint-disable-next-line camelcase + app_key: '6b79ee267ff23c4b99108591c5b33f0ba8ed5e4b', + url: 'https://telemetry.speckle.works', + // eslint-disable-next-line camelcase + device_id: myMachineId, + debug: false + } ) + + countly.user_details( { + 'username': myMachineId + } ) + + app.use( ( req, res, next ) => { + + next( ) // let's not block things, in case telemetry server is down. + + try { + countly.track_view( `${req.method} "${req.route.path}"` ) + } catch ( err ) { + logger.info( 'Failed to initialise route based telemetry.' ) + } + + } ) + } catch ( err ) { + logger.info( 'Failed to initialise route based telemetry.' ) + } +} diff --git a/app/telemetry/initTelemetry.js b/app/telemetry/initTelemetry.js new file mode 100644 index 00000000..43a4ea5a --- /dev/null +++ b/app/telemetry/initTelemetry.js @@ -0,0 +1,55 @@ +const countly = require( 'countly-sdk-nodejs' ) +const machineIdSync = require( 'node-machine-id' ).machineIdSync +const { exec } = require( 'child_process' ) + +const logger = require( '../../config/logger' ) + +// This module lets us know the version of the running server, and how many times +// it's initialised. +module.exports = ( ) => { + + if ( process.env.TELEMETRY === 'false' ) + return + + let myMachineId = machineIdSync( ) + let tagVersion = 'unknown' + + try { + exec( 'git describe --tags', ( err, stdout ) => { + tagVersion = stdout.split( '-' )[ 0 ] + logger.info( `Version: ${tagVersion}` ) + + countly.init( { + // eslint-disable-next-line camelcase + app_key: '6b79ee267ff23c4b99108591c5b33f0ba8ed5e4b', + url: 'https://telemetry.speckle.works', + // eslint-disable-next-line camelcase + device_id: myMachineId, + // eslint-disable-next-line camelcase + app_version: tagVersion, + debug: false + } ) + + countly.user_details( { + 'username': myMachineId + } ) + + countly.add_event( { + "key": "server-deployment", + "segmentation": { + "machineId": myMachineId, + "version": tagVersion + } + } ) + + // one view to track version + countly.track_view( `SERVER DEPLOYMENT versioned at ${tagVersion}` ) + + // one view to track generic usage, regardless of version + countly.track_view( `SERVER DEPLOYMENT generic` ) + + } ) + } catch ( err ) { + logger.error( err ) + } +} diff --git a/package-lock.json b/package-lock.json index ff9cdf7f..7895ffa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1133,7 +1133,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -1717,6 +1717,11 @@ } } }, + "countly-sdk-nodejs": { + "version": "19.8.0", + "resolved": "https://registry.npmjs.org/countly-sdk-nodejs/-/countly-sdk-nodejs-19.8.0.tgz", + "integrity": "sha512-BPhQp3LL9zfS4lodAyFm/ziPYsdybth0IDuJdXBtXt8YiO2UKionRGE5JXQNfq5S4N2FrOfndQcKF7B9fMnZUA==" + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2356,7 +2361,7 @@ }, "doctrine": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -4088,7 +4093,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -4319,7 +4324,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memory-pager": { @@ -4504,7 +4509,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { @@ -4769,6 +4774,11 @@ "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", "dev": true }, + "node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==" + }, "nodemailer": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.3.0.tgz", @@ -8781,7 +8791,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -9984,7 +9994,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10605,7 +10615,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", diff --git a/package.json b/package.json index bcc9bece..fe4a271b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "chalk": "^1.1.3", "connect-redis": "^3.4.2", "cors": "^2.8.1", + "countly-sdk-nodejs": "^19.8.0", "crypto-random-string": "^3.0.1", "dotenv": "^6.2.0", "express": "^4.17.1", @@ -30,6 +31,7 @@ "jsonwebtoken": "^8.5.1", "lodash": "^4.17.15", "mongoose": "^5.6.11", + "node-machine-id": "^1.1.12", "nodemailer": "^6.3.0", "passport": "^0.4.0", "passport-anonymous": "^1.0.1", diff --git a/server.js b/server.js index 0359164c..0d92d5f1 100644 --- a/server.js +++ b/server.js @@ -37,10 +37,10 @@ if ( cluster.isMaster ) { █ The Open Source Data Platform for AEC. █ ` + -chalk.red( ` + chalk.red( ` █ Server running at: ${process.env.CANONICAL_URL} ` ) - ) + ) logger.level = 'debug' @@ -68,6 +68,8 @@ chalk.red( ` redisClient.flushdb( ) } ) + require( './app/telemetry/initTelemetry' )( ) + ///////////////////////////////////////////////////////////////////////// /// CHILD processes /////. ///////////////////////////////////////////////////////////////////////// @@ -111,6 +113,9 @@ chalk.red( ` app.use( passport.initialize( ) ) + // Telemetry + require( './app/telemetry/appTelemetry' )( app ) + if ( process.env.INDENT_RESPONSES === 'true' ) { app.set( 'json spaces', 2 ) } if ( process.env.EXPOSE_EMAILS === 'true' ) { app.enable( 'expose emails' ) } @@ -141,10 +146,6 @@ chalk.red( ` require( './app/api/index' )( app, express, '/api', plugins ) require( './app/api/index' )( app, express, '/api/v1', plugins ) - - // init email transport - // require( './app/email/index' ) // soon - // init default register/login routes require( './app/auth/index' )( app )