From a7d8889a6ae2373dfaf43baa5f3d494e69b849a1 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 3 Jul 2020 17:59:06 +0200 Subject: [PATCH] Add telemtry Signed-off-by: Alexandre Bodin --- packages/strapi/lib/Strapi.js | 5 ++- packages/strapi/lib/services/metrics/index.js | 21 ++++++++++ .../lib/services/metrics/rate-limiter.js | 6 +-- .../strapi/lib/services/metrics/sender.js | 41 +++++++++++-------- packages/strapi/lib/utils/ee.js | 38 ++++++++++++++++- 5 files changed, 88 insertions(+), 23 deletions(-) diff --git a/packages/strapi/lib/Strapi.js b/packages/strapi/lib/Strapi.js index 5f2d32a402..1d35bab64a 100644 --- a/packages/strapi/lib/Strapi.js +++ b/packages/strapi/lib/Strapi.js @@ -57,7 +57,6 @@ class Strapi { this.config = loadConfiguration(this.dir, opts); this.isLoaded = false; - this.EE = ee({ dir: this.dir, logger }); // internal services. this.fs = createStrapiFs(this); this.eventHub = createEventHub(); @@ -65,6 +64,10 @@ class Strapi { this.requireProjectBootstrap(); } + get EE() { + return ee({ dir: this.dir, logger }); + } + requireProjectBootstrap() { const bootstrapPath = path.resolve(this.dir, 'config/functions/bootstrap.js'); diff --git a/packages/strapi/lib/services/metrics/index.js b/packages/strapi/lib/services/metrics/index.js index bf1c736baa..7ea7214fe5 100644 --- a/packages/strapi/lib/services/metrics/index.js +++ b/packages/strapi/lib/services/metrics/index.js @@ -10,6 +10,7 @@ const wrapWithRateLimit = require('./rate-limiter'); const createSender = require('./sender'); const createMiddleware = require('./middleware'); const isTruthy = require('./is-truthy'); +const ee = require('../../utils/ee'); const LIMITED_EVENTS = [ 'didSaveMediaWithAlternativeText', @@ -30,6 +31,26 @@ const createTelemetryInstance = strapi => { strapi.app.use(createMiddleware({ sendEvent })); } + if (strapi.EE === true && ee.isEE === true) { + const pingDisabled = + isTruthy(process.env.STRAPI_LICENSE_PING_DISABLED) && ee.licenseInfo.type === 'enterprise'; + + const sendLicenseCheck = () => { + return sendEvent( + 'didCheckLicense', + { licenseInfo: ee.licenseInfo }, + { + headers: { 'x-strapi-project': 'enterprise' }, + } + ); + }; + + if (!pingDisabled) { + scheduleJob('0 0 0 * * 7', () => sendLicenseCheck()); + sendLicenseCheck(); + } + } + return { async send(event, payload) { if (isDisabled) return true; diff --git a/packages/strapi/lib/services/metrics/rate-limiter.js b/packages/strapi/lib/services/metrics/rate-limiter.js index 0b338b4ef8..9f2aec74fc 100644 --- a/packages/strapi/lib/services/metrics/rate-limiter.js +++ b/packages/strapi/lib/services/metrics/rate-limiter.js @@ -7,9 +7,9 @@ module.exports = (sender, { limitedEvents = [] } = {}) => { let currentDay = new Date().getDate(); const eventCache = new Map(); - return async (event, payload) => { + return async (event, ...args) => { if (!limitedEvents.includes(event)) { - return sender(event, payload); + return sender(event, ...args); } if (new Date().getDate() !== currentDay) { @@ -22,6 +22,6 @@ module.exports = (sender, { limitedEvents = [] } = {}) => { } eventCache.set(event, true); - return sender(event, payload); + return sender(event, ...args); }; }; diff --git a/packages/strapi/lib/services/metrics/sender.js b/packages/strapi/lib/services/metrics/sender.js index bdd8b8f931..339445b77e 100644 --- a/packages/strapi/lib/services/metrics/sender.js +++ b/packages/strapi/lib/services/metrics/sender.js @@ -1,12 +1,19 @@ 'use strict'; const os = require('os'); - +const _ = require('lodash'); const isDocker = require('is-docker'); const { machineIdSync } = require('node-machine-id'); const fetch = require('node-fetch'); const ciEnv = require('ci-info'); +const defaultQueryOpts = { + timeout: 1000, + headers: { 'Content-Type': 'application/json' }, +}; + +const ANALYTICS_URI = 'http://localhost:1338'; // https://analytics.strapi.io + /** * Create a send function for event with all the necessary metadatas * @param {Object} strapi strapi app @@ -28,23 +35,23 @@ module.exports = strapi => { strapiVersion: strapi.config.info.strapi, }; - return async (event, payload = {}) => { - try { - const res = await fetch('https://analytics.strapi.io/track', { - method: 'POST', - body: JSON.stringify({ - event, - uuid, - deviceId, - properties: { - ...payload, - ...anonymous_metadata, - }, - }), - timeout: 1000, - headers: { 'Content-Type': 'application/json' }, - }); + return async (event, payload = {}, opts = {}) => { + const reqParams = { + method: 'POST', + body: JSON.stringify({ + event, + uuid, + deviceId, + properties: { + ...payload, + ...anonymous_metadata, + }, + }), + ..._.merge({}, defaultQueryOpts, opts), + }; + try { + const res = await fetch(`${ANALYTICS_URI}/track`, reqParams); return res.ok; } catch (err) { return false; diff --git a/packages/strapi/lib/utils/ee.js b/packages/strapi/lib/utils/ee.js index c48a91c62d..9891c9d120 100644 --- a/packages/strapi/lib/utils/ee.js +++ b/packages/strapi/lib/utils/ee.js @@ -14,13 +14,19 @@ const noLog = { info: noop, }; +const internals = {}; + module.exports = ({ dir, logger = noLog }) => { + if (_.has(internals, 'isEE')) return internals.isEE; + const warnAndReturn = (msg = 'Invalid license. Starting in CE.') => { logger.warn(msg); + internals.isEE = false; return false; }; if (process.env.STRAPI_DISABLE_EE === 'true') { + internals.isEE = false; return false; } @@ -34,6 +40,7 @@ module.exports = ({ dir, logger = noLog }) => { } if (_.isNil(license)) { + internals.isEE = false; return false; } @@ -51,14 +58,41 @@ module.exports = ({ dir, logger = noLog }) => { const isValid = verifier.verify(publicKey, signature); if (!isValid) return warnAndReturn(); - const licenseInfo = JSON.parse(content); + internals.licenseInfo = JSON.parse(content); - if (licenseInfo.expireAt < new Date().getTime()) { + if (internals.licenseInfo.expireAt < new Date().getTime()) { return warnAndReturn('License expired'); } } catch (err) { return warnAndReturn(); } + internals.isEE = true; return true; }; + +Object.defineProperty(module.exports, 'licenseInfo', { + get: () => { + mustHaveKey('licenseInfo'); + return internals.licenseInfo; + }, + configurable: false, + enumerable: false, +}); + +Object.defineProperty(module.exports, 'isEE', { + get: () => { + mustHaveKey('isEE'); + return internals.isEE; + }, + configurable: false, + enumerable: false, +}); + +const mustHaveKey = key => { + if (!_.has(internals, key)) { + const err = new Error('Tampering with license'); + err.stack = null; + throw err; + } +};