Plugin metrics (#7626)

* feat: add enable/disable checkbox for plugin usage

* feat: submit plugin data to packages.nodebb.org

only submit in production mode
submit once every 24 hours
dont submit for plugins that have "private": true in plugin.json
enabled on new installs
disabled on existing installs

* fix: hash not working after first send

fix statusCode

* fix: remove url

* feat: show compatibilty

* feat: add install question for submit plugin usage
v1.18.x
Barış Soner Uşaklı 6 years ago committed by GitHub
parent 3f4f8aface
commit 5fa5e999f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -122,5 +122,6 @@
"eventLoopInterval": 500,
"onlineCutoff": 30,
"timeagoCutoff": 30,
"categoryWatchState": "watching"
"categoryWatchState": "watching",
"submitPluginUsage": 1
}

@ -9,6 +9,7 @@
"plugin-search": "Plugin Search",
"plugin-search-placeholder": "Search for plugin...",
"submit-anonymous-usage": "Submit anonymous plugin usage data.",
"reorder-plugins": "Re-order Plugins",
"order-active": "Order Active Plugins",
"dev-interested": "Interested in writing plugins for NodeBB?",
@ -29,6 +30,8 @@
"plugin-item.more-info": "For more information:",
"plugin-item.unknown": "Unknown",
"plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.",
"plugin-item.compatible": "This plugin works on NodeBB %1",
"plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.",
"alert.enabled": "Plugin Enabled",
"alert.disabled": "Plugin Disabled",

@ -150,6 +150,16 @@ define('admin/extend/plugins', ['jqueryui', 'translator', 'benchpress'], functio
});
});
$('#plugin-submit-usage').on('click', function () {
socket.emit('admin.config.setMultiple', {
submitPluginUsage: $(this).prop('checked') ? '1' : '0',
}, function (err) {
if (err) {
return app.alertError(err.message);
}
});
});
$('#plugin-order').on('click', function () {
$('#order-active-plugins-modal').modal('show');
socket.emit('admin.plugins.getActive', function (err, activePlugins) {

@ -1,7 +1,9 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
var plugins = require('../../plugins');
var meta = require('../../meta');
var pluginsController = module.exports;
@ -57,6 +59,8 @@ pluginsController.get = function (req, res, next) {
incompatible: payload.all.filter(function (plugin) {
return !compatiblePkgNames.includes(plugin.name);
}),
submitPluginUsage: meta.config.submitPluginUsage,
version: nconf.get('version'),
});
},
], next);

@ -28,6 +28,11 @@ questions.main = [
description: 'Please enter a NodeBB secret',
default: nconf.get('secret') || utils.generateUUID(),
},
{
name: 'submitPluginUsage',
description: 'Would you like to submit anonymous plugin usage to nbbpm?',
default: 'yes',
},
{
name: 'database',
description: 'Which database to use',
@ -198,6 +203,14 @@ function completeConfigSetup(config, next) {
// ref: https://github.com/indexzero/nconf/issues/300
delete config.type;
var meta = require('./meta');
meta.configs.set('submitPluginUsage', config.submitPluginUsage === 'yes' ? 1 : 0, function (err) {
next(err, config);
});
},
function (config, next) {
delete config.submitPluginUsage;
install.save(config, next);
},
], next);

@ -15,6 +15,7 @@ var Plugins = module.exports;
require('./install')(Plugins);
require('./load')(Plugins);
require('./hooks')(Plugins);
require('./usage')(Plugins);
Plugins.data = require('./data');
Plugins.getPluginPaths = Plugins.data.getPluginPaths;
@ -33,6 +34,7 @@ Plugins.libraryPaths = [];
Plugins.versionWarning = [];
Plugins.soundpacks = [];
Plugins.languageData = {};
Plugins.loadedPlugins = [];
Plugins.initialized = false;
@ -105,6 +107,7 @@ Plugins.reload = function (callback) {
Plugins.clientScripts.length = 0;
Plugins.acpScripts.length = 0;
Plugins.libraryPaths.length = 0;
Plugins.loadedPlugins.length = 0;
async.waterfall([
Plugins.getPluginPaths,

@ -150,8 +150,16 @@ module.exports = function (Plugins) {
},
], function (err) {
if (err) {
winston.error(err.stack);
winston.verbose('[plugins] Could not load plugin : ' + pluginData.id);
return callback(err);
return callback();
}
if (!pluginData.private) {
Plugins.loadedPlugins.push({
id: pluginData.id,
version: pluginData.version,
});
}
winston.verbose('[plugins] Loaded plugin: ' + pluginData.id);
@ -196,9 +204,8 @@ module.exports = function (Plugins) {
callback();
}
} catch (err) {
winston.error(err.stack);
winston.warn('[plugins] Unable to parse library for: ' + pluginData.id);
callback();
callback(err);
}
}
};

@ -0,0 +1,43 @@
'use strict';
const nconf = require('nconf');
const request = require('request');
const winston = require('winston');
const crypto = require('crypto');
const cronJob = require('cron').CronJob;
const pkg = require('../../package.json');
const meta = require('../meta');
module.exports = function (Plugins) {
Plugins.startJobs = function () {
new cronJob('0 0 0 * * *', function () {
Plugins.submitUsageData();
}, null, true);
};
Plugins.submitUsageData = function () {
if (!meta.config.submitPluginUsage || !Plugins.loadedPlugins.length || global.env !== 'production') {
return;
}
const hash = crypto.createHash('sha256');
hash.update(nconf.get('url'));
request.post((nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugin/usage', {
form: {
id: hash.digest('hex'),
version: pkg.version,
plugins: Plugins.loadedPlugins,
},
timeout: 5000,
}, function (err, res, body) {
if (err) {
return winston.error(err);
}
if (res.statusCode !== 200) {
winston.error('[plugins.submitUsageData] received ' + res.statusCode + ' ' + body);
}
});
};
};

@ -50,6 +50,7 @@ start.start = function () {
if (nconf.get('runJobs')) {
require('./notifications').startJobs();
require('./user').startJobs();
require('./plugins').startJobs();
}
webserver.listen(next);

@ -0,0 +1,11 @@
'use strict';
const db = require('../../database');
module.exports = {
name: 'Disable plugin metrics for existing installs',
timestamp: Date.UTC(2019, 4, 21),
method: async function (callback) {
db.setObjectField('config', 'submitPluginUsage', 0, callback);
},
};

@ -28,6 +28,17 @@
</div>
</div>
<div class="panel panel-default">
<div class="panel-body">
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input id="plugin-submit-usage" class="mdl-switch__input" type="checkbox" data-field="submitPluginUsage" <!-- IF submitPluginUsage -->checked<!-- ENDIF submitPluginUsage -->/>
<span class="mdl-switch__label">[[admin/extend/plugins:submit-anonymous-usage]]</span>
</label>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">[[admin/extend/plugins:reorder-plugins]]</div>
<div class="panel-body">

@ -11,6 +11,13 @@
<!-- ENDIF download.description -->
<small>[[admin/extend/plugins:plugin-item.latest]] <strong class="latestVersion">{download.latest}</strong></small>
<p>
<!-- IF installed.isCompatible -->
<i class="fa fa-check text-success"></i> [[admin/extend/plugins:plugin-item.compatible, {version}]]
<!-- ELSE -->
<i class="fa fa-question text-warning"></i> [[admin/extend/plugins:plugin-item.not-compatible]]
<!-- ENDIF -->
</p>
<!-- IF download.url -->
<p>[[admin/extend/plugins:plugin-item.more-info]] <a target="_blank" href="{download.url}">{download.url}</a></p>

@ -24,9 +24,18 @@
<!-- ENDIF installed.description -->
<!-- IF installed.outdated --><i class="fa fa-exclamation-triangle text-danger"></i> <!-- ENDIF installed.outdated -->
<small>[[admin/extend/plugins:plugin-item.installed]] <strong class="currentVersion">{installed.version}</strong> | [[admin/extend/plugins:plugin-item.latest]] <strong class="latestVersion">{installed.latest}</strong></small>
<!-- IF installed.outdated -->
<button data-action="upgrade" class="btn btn-success btn-xs"><i class="fa fa-download"></i> [[admin/extend/plugins:plugin-item.upgrade]]</button>
<button data-action="upgrade" class="btn btn-success btn-xs"><i class="fa fa-download"></i> [[admin/extend/plugins:plugin-item.upgrade]]</button>
<p>
<!-- IF installed.isCompatible -->
<i class="fa fa-check text-success"></i> [[admin/extend/plugins:plugin-item.compatible, {version}]]
<!-- ELSE -->
<i class="fa fa-question text-warning"></i> [[admin/extend/plugins:plugin-item.not-compatible]]
<!-- ENDIF -->
</p>
<!-- ENDIF installed.outdated -->
<!-- IF installed.url -->
<p>[[admin/extend/plugins:plugin-item.more-info]] <a target="_blank" href="{installed.url}">{installed.url}</a></p>
<!-- ENDIF installed.url -->

Loading…
Cancel
Save