mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-06 23:52:58 +01:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a836017de | ||
|
|
a6ab71f373 | ||
|
|
6f504c4142 | ||
|
|
01bff2ae05 | ||
|
|
827d42a931 | ||
|
|
ada45a34d5 | ||
|
|
e5228179c1 | ||
|
|
a26011e756 | ||
|
|
d484731d8c | ||
|
|
6108064ea8 | ||
|
|
7fee153438 | ||
|
|
9005f67852 | ||
|
|
e327d1247e | ||
|
|
ed6bfd05df | ||
|
|
8c373eb028 | ||
|
|
f2907908c9 | ||
|
|
feb748a44d | ||
|
|
a665881b35 | ||
|
|
cb662e15ce |
356
Gruntfile.js
356
Gruntfile.js
@@ -1,95 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
var async = require('async');
|
||||
var fork = require('child_process').fork;
|
||||
var env = process.env;
|
||||
const path = require('path');
|
||||
const nconf = require('nconf');
|
||||
nconf.argv().env({
|
||||
separator: '__',
|
||||
});
|
||||
const winston = require('winston');
|
||||
const fork = require('child_process').fork;
|
||||
const env = process.env;
|
||||
var worker;
|
||||
var updateWorker;
|
||||
var initWorker;
|
||||
var incomplete = [];
|
||||
var running = 0;
|
||||
|
||||
env.NODE_ENV = env.NODE_ENV || 'development';
|
||||
|
||||
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
|
||||
const prestart = require('./src/prestart');
|
||||
prestart.loadConfig(configFile);
|
||||
|
||||
var nconf = require('nconf');
|
||||
nconf.file({
|
||||
file: 'config.json',
|
||||
});
|
||||
|
||||
nconf.defaults({
|
||||
base_dir: __dirname,
|
||||
views_dir: './build/public/templates',
|
||||
});
|
||||
var winston = require('winston');
|
||||
winston.configure({
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
handleExceptions: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
var db = require('./src/database');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
var args = [];
|
||||
var initArgs = ['--build'];
|
||||
|
||||
if (!grunt.option('verbose')) {
|
||||
args.push('--log-level=info');
|
||||
initArgs.push('--log-level=info');
|
||||
}
|
||||
|
||||
function update(action, filepath, target) {
|
||||
var updateArgs = args.slice();
|
||||
var compiling;
|
||||
var time = Date.now();
|
||||
|
||||
if (target === 'lessUpdated_Client') {
|
||||
compiling = 'clientCSS';
|
||||
} else if (target === 'lessUpdated_Admin') {
|
||||
compiling = 'acpCSS';
|
||||
} else if (target === 'clientUpdated') {
|
||||
compiling = 'js';
|
||||
} else if (target === 'templatesUpdated') {
|
||||
compiling = 'tpl';
|
||||
} else if (target === 'langUpdated') {
|
||||
compiling = 'lang';
|
||||
} else if (target === 'serverUpdated') {
|
||||
// Do nothing, just restart
|
||||
}
|
||||
|
||||
if (compiling && !incomplete.includes(compiling)) {
|
||||
incomplete.push(compiling);
|
||||
}
|
||||
|
||||
updateArgs.push('--build');
|
||||
updateArgs.push(incomplete.join(','));
|
||||
|
||||
worker.kill();
|
||||
if (updateWorker) {
|
||||
updateWorker.kill('SIGKILL');
|
||||
}
|
||||
updateWorker = fork('app.js', updateArgs, { env: env });
|
||||
running += 1;
|
||||
updateWorker.on('exit', function () {
|
||||
running -= 1;
|
||||
if (running === 0) {
|
||||
worker = fork('app.js', args, {
|
||||
env: env,
|
||||
});
|
||||
worker.on('message', function () {
|
||||
if (incomplete.length) {
|
||||
incomplete = [];
|
||||
|
||||
if (grunt.option('verbose')) {
|
||||
grunt.log.writeln('NodeBB restarted in ' + (Date.now() - time) + ' ms');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
nconf.set('log-level', 'info');
|
||||
}
|
||||
prestart.setupWinston();
|
||||
|
||||
grunt.initConfig({
|
||||
watch: {},
|
||||
@@ -99,148 +35,166 @@ module.exports = function (grunt) {
|
||||
|
||||
grunt.registerTask('default', ['watch']);
|
||||
|
||||
grunt.registerTask('init', function () {
|
||||
grunt.registerTask('init', async function () {
|
||||
var done = this.async();
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.init(next);
|
||||
},
|
||||
function (next) {
|
||||
db.getSortedSetRange('plugins:active', 0, -1, next);
|
||||
},
|
||||
function (plugins, next) {
|
||||
addBaseThemes(plugins, next);
|
||||
},
|
||||
function (plugins, next) {
|
||||
if (!plugins.includes('nodebb-plugin-composer-default')) {
|
||||
plugins.push('nodebb-plugin-composer-default');
|
||||
}
|
||||
let plugins = [];
|
||||
if (!process.argv.includes('--core')) {
|
||||
await db.init();
|
||||
plugins = await db.getSortedSetRange('plugins:active', 0, -1);
|
||||
addBaseThemes(plugins);
|
||||
if (!plugins.includes('nodebb-plugin-composer-default')) {
|
||||
plugins.push('nodebb-plugin-composer-default');
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv.includes('--core')) {
|
||||
plugins = [];
|
||||
}
|
||||
const styleUpdated_Client = plugins.map(p => 'node_modules/' + p + '/*.less')
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/*.css'))
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/+(public|static|less)/**/*.less'))
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/+(public|static)/**/*.css'));
|
||||
|
||||
const lessUpdated_Client = plugins.map(p => 'node_modules/' + p + '/**/*.less');
|
||||
const lessUpdated_Admin = plugins.map(p => 'node_modules/' + p + '/**/*.less');
|
||||
const clientUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.js');
|
||||
const templatesUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.tpl');
|
||||
const langUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.json');
|
||||
const styleUpdated_Admin = plugins.map(p => 'node_modules/' + p + '/*.less')
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/*.css'))
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/+(public|static|less)/**/*.less'))
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/+(public|static)/**/*.css'));
|
||||
|
||||
grunt.config(['watch'], {
|
||||
lessUpdated_Client: {
|
||||
files: [
|
||||
'public/less/*.less',
|
||||
'!public/less/admin/**/*.less',
|
||||
...lessUpdated_Client,
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
lessUpdated_Admin: {
|
||||
files: [
|
||||
'public/less/admin/**/*.less',
|
||||
...lessUpdated_Admin,
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
clientUpdated: {
|
||||
files: [
|
||||
'public/src/**/*.js',
|
||||
...clientUpdated,
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'node_modules/benchpressjs/build/benchpress.js',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
serverUpdated: {
|
||||
files: ['*.js', 'install/*.js', 'src/**/*.js', '!src/upgrades/**'],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
templatesUpdated: {
|
||||
files: [
|
||||
'src/views/**/*.tpl',
|
||||
...templatesUpdated,
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
langUpdated: {
|
||||
files: [
|
||||
'public/language/en-GB/*.json',
|
||||
'public/language/en-GB/**/*.json',
|
||||
...langUpdated,
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
'!node_modules/nodebb-*/plugin.json',
|
||||
'!node_modules/nodebb-*/package.json',
|
||||
'!node_modules/nodebb-*/theme.json',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
next();
|
||||
const clientUpdated = plugins.map(p => 'node_modules/' + p + '/+(public|static)/**/*.js');
|
||||
const serverUpdated = plugins.map(p => 'node_modules/' + p + '/*.js')
|
||||
.concat(plugins.map(p => 'node_modules/' + p + '/+(lib|src)/**/*.js'));
|
||||
|
||||
const templatesUpdated = plugins.map(p => 'node_modules/' + p + '/+(public|static|templates)/**/*.tpl');
|
||||
const langUpdated = plugins.map(p => 'node_modules/' + p + '/+(public|static|languages)/**/*.json');
|
||||
|
||||
grunt.config(['watch'], {
|
||||
styleUpdated_Client: {
|
||||
files: [
|
||||
'public/less/**/*.less',
|
||||
...styleUpdated_Client,
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
], done);
|
||||
styleUpdated_Admin: {
|
||||
files: [
|
||||
'public/less/**/*.less',
|
||||
...styleUpdated_Admin,
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
clientUpdated: {
|
||||
files: [
|
||||
'public/src/**/*.js',
|
||||
...clientUpdated,
|
||||
'node_modules/benchpressjs/build/benchpress.js',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
serverUpdated: {
|
||||
files: [
|
||||
'app.js',
|
||||
'install/*.js',
|
||||
'src/**/*.js',
|
||||
'public/src/modules/translator.js',
|
||||
'public/src/modules/helpers.js',
|
||||
'public/src/utils.js',
|
||||
serverUpdated,
|
||||
'!src/upgrades/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
templatesUpdated: {
|
||||
files: [
|
||||
'src/views/**/*.tpl',
|
||||
...templatesUpdated,
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
langUpdated: {
|
||||
files: [
|
||||
'public/language/en-GB/*.json',
|
||||
'public/language/en-GB/**/*.json',
|
||||
...langUpdated,
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
const build = require('./src/meta/build');
|
||||
if (!grunt.option('skip')) {
|
||||
await build.build(true);
|
||||
}
|
||||
run();
|
||||
done();
|
||||
});
|
||||
|
||||
grunt.task.run('init');
|
||||
|
||||
env.NODE_ENV = 'development';
|
||||
|
||||
if (grunt.option('skip')) {
|
||||
function run() {
|
||||
if (worker) {
|
||||
worker.kill();
|
||||
}
|
||||
worker = fork('app.js', args, {
|
||||
env: env,
|
||||
});
|
||||
} else {
|
||||
initWorker = fork('app.js', initArgs, {
|
||||
env: env,
|
||||
});
|
||||
|
||||
initWorker.on('exit', function () {
|
||||
worker = fork('app.js', args, {
|
||||
env: env,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
grunt.event.on('watch', update);
|
||||
grunt.task.run('init');
|
||||
|
||||
grunt.event.removeAllListeners('watch');
|
||||
grunt.event.on('watch', function update(action, filepath, target) {
|
||||
var compiling;
|
||||
if (target === 'styleUpdated_Client') {
|
||||
compiling = 'clientCSS';
|
||||
} else if (target === 'styleUpdated_Admin') {
|
||||
compiling = 'acpCSS';
|
||||
} else if (target === 'clientUpdated') {
|
||||
compiling = 'js';
|
||||
} else if (target === 'templatesUpdated') {
|
||||
compiling = 'tpl';
|
||||
} else if (target === 'langUpdated') {
|
||||
compiling = 'lang';
|
||||
} else if (target === 'serverUpdated') {
|
||||
// empty require cache
|
||||
const paths = ['./src/meta/build.js', './src/meta/index.js'];
|
||||
paths.forEach(p => delete require.cache[require.resolve(p)]);
|
||||
return run();
|
||||
}
|
||||
|
||||
require('./src/meta/build').build([compiling], function (err) {
|
||||
if (err) {
|
||||
winston.error(err.stack);
|
||||
}
|
||||
if (worker) {
|
||||
worker.send({ compiling: compiling });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function addBaseThemes(plugins, callback) {
|
||||
const themeId = plugins.find(p => p.startsWith('nodebb-theme-'));
|
||||
function addBaseThemes(plugins) {
|
||||
let themeId = plugins.find(p => p.includes('nodebb-theme-'));
|
||||
if (!themeId) {
|
||||
return setImmediate(callback, null, plugins);
|
||||
return plugins;
|
||||
}
|
||||
function getBaseRecursive(themeId) {
|
||||
let baseTheme;
|
||||
do {
|
||||
try {
|
||||
const baseTheme = require(themeId + '/theme').baseTheme;
|
||||
|
||||
if (baseTheme) {
|
||||
plugins.push(baseTheme);
|
||||
getBaseRecursive(baseTheme);
|
||||
}
|
||||
baseTheme = require(themeId + '/theme').baseTheme;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
getBaseRecursive(themeId);
|
||||
callback(null, plugins);
|
||||
if (baseTheme) {
|
||||
plugins.push(baseTheme);
|
||||
themeId = baseTheme;
|
||||
}
|
||||
} while (baseTheme);
|
||||
return plugins;
|
||||
}
|
||||
|
||||
1
app.js
1
app.js
@@ -31,6 +31,7 @@ const path = require('path');
|
||||
|
||||
const file = require('./src/file');
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
// Alternate configuration file support
|
||||
|
||||
@@ -32,9 +32,8 @@
|
||||
"registrationType": "normal",
|
||||
"registrationApprovalType": "normal",
|
||||
"allowAccountDelete": 1,
|
||||
"allowFileUploads": 0,
|
||||
"privateUploads": 0,
|
||||
"allowedFileExtensions": "png,jpg,bmp",
|
||||
"allowedFileExtensions": "png,jpg,bmp,txt",
|
||||
"allowUserHomePage": 1,
|
||||
"allowMultipleBadges": 0,
|
||||
"maximumFileSize": 2048,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "1.13.4-1",
|
||||
"version": "1.13.4-4",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -79,7 +79,7 @@
|
||||
"mousetrap": "^1.6.5",
|
||||
"@nodebb/mubsub": "^1.6.0",
|
||||
"nconf": "^0.10.0",
|
||||
"nodebb-plugin-composer-default": "6.3.29",
|
||||
"nodebb-plugin-composer-default": "6.3.31",
|
||||
"nodebb-plugin-dbsearch": "4.0.7",
|
||||
"nodebb-plugin-emoji": "^3.3.0",
|
||||
"nodebb-plugin-emoji-android": "2.0.0",
|
||||
@@ -143,7 +143,7 @@
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
"husky": "4.2.5",
|
||||
"jsdom": "16.2.2",
|
||||
"lint-staged": "10.2.0",
|
||||
"lint-staged": "10.2.4",
|
||||
"mocha": "7.1.2",
|
||||
"mocha-lcov-reporter": "1.3.0",
|
||||
"nyc": "15.0.1",
|
||||
|
||||
@@ -2795,8 +2795,6 @@ paths:
|
||||
type: boolean
|
||||
allowGuestHandles:
|
||||
type: boolean
|
||||
allowFileUploads:
|
||||
type: boolean
|
||||
allowTopicsThumbnail:
|
||||
type: boolean
|
||||
usePagination:
|
||||
|
||||
@@ -596,7 +596,7 @@ app.cacheBuster = null;
|
||||
$(window).trigger('action:search.quick', { data: data });
|
||||
search.api(data, function (data) {
|
||||
data.posts.forEach(function (p) {
|
||||
p.snippet = utils.escapeHTML($(p.content).text().slice(0, 80) + '...');
|
||||
p.snippet = utils.escapeHTML($('<div>' + p.content + '</div>').text().slice(0, 80) + '...');
|
||||
});
|
||||
app.parseAndTranslate(template, data, function (html) {
|
||||
if (html.length) {
|
||||
|
||||
@@ -527,6 +527,12 @@
|
||||
unescape: Translator.unescape,
|
||||
getLanguage: Translator.getLanguage,
|
||||
|
||||
flush: function () {
|
||||
Object.keys(Translator.cache).forEach(function (code) {
|
||||
Translator.cache[code].translations = {};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Legacy translator function for backwards compatibility
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,6 @@ apiController.loadConfig = async function (req) {
|
||||
useOutgoingLinksPage: meta.config.useOutgoingLinksPage === 1,
|
||||
outgoingLinksWhitelist: meta.config.useOutgoingLinksPage === 1 ? meta.config['outgoingLinks:whitelist'] : undefined,
|
||||
allowGuestHandles: meta.config.allowGuestHandles === 1,
|
||||
allowFileUploads: meta.config.allowFileUploads === 1,
|
||||
allowTopicsThumbnail: meta.config.allowTopicsThumbnail === 1,
|
||||
usePagination: meta.config.usePagination === 1,
|
||||
disableChat: meta.config.disableChat === 1,
|
||||
|
||||
@@ -84,10 +84,6 @@ async function uploadAsFile(req, uploadedFile) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
if (!meta.config.allowFileUploads) {
|
||||
throw new Error('[[error:uploads-are-disabled]]');
|
||||
}
|
||||
|
||||
const fileObj = await uploadsController.uploadFile(req.uid, uploadedFile);
|
||||
return {
|
||||
url: fileObj.url,
|
||||
|
||||
@@ -5,18 +5,54 @@ module.exports = function (module) {
|
||||
if (!Array.isArray(keys) || !keys.length) {
|
||||
return 0;
|
||||
}
|
||||
const objects = module.client.collection('objects');
|
||||
const counts = await countSets(keys, 50000);
|
||||
if (counts.minCount === 0) {
|
||||
return 0;
|
||||
}
|
||||
let items = await objects.find({ _key: counts.smallestSet }, {
|
||||
projection: { _id: 0, value: 1 },
|
||||
}).toArray();
|
||||
|
||||
var pipeline = [
|
||||
{ $match: { _key: { $in: keys } } },
|
||||
{ $group: { _id: { value: '$value' }, count: { $sum: 1 } } },
|
||||
{ $match: { count: keys.length } },
|
||||
{ $group: { _id: null, count: { $sum: 1 } } },
|
||||
];
|
||||
|
||||
const data = await module.client.collection('objects').aggregate(pipeline).toArray();
|
||||
return Array.isArray(data) && data.length ? data[0].count : 0;
|
||||
items = items.map(i => i.value);
|
||||
const otherSets = keys.filter(s => s !== counts.smallestSet);
|
||||
if (otherSets.length === 1) {
|
||||
return await objects.countDocuments({
|
||||
_key: otherSets[0], value: { $in: items },
|
||||
});
|
||||
}
|
||||
items = await intersectValuesWithSets(items, otherSets);
|
||||
return items.length;
|
||||
};
|
||||
|
||||
async function intersectValuesWithSets(items, sets) {
|
||||
for (let i = 0; i < sets.length; i++) {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
items = await module.client.collection('objects').find({
|
||||
_key: sets[i], value: { $in: items },
|
||||
}, {
|
||||
projection: { _id: 0, value: 1 },
|
||||
}).toArray();
|
||||
items = items.map(i => i.value);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
async function countSets(sets, limit) {
|
||||
const objects = module.client.collection('objects');
|
||||
const counts = await Promise.all(
|
||||
sets.map(s => objects.countDocuments({ _key: s }, {
|
||||
limit: limit || 25000,
|
||||
}))
|
||||
);
|
||||
const minCount = Math.min(...counts);
|
||||
const index = counts.indexOf(minCount);
|
||||
const smallestSet = sets[index];
|
||||
return {
|
||||
minCount: minCount,
|
||||
smallestSet: smallestSet,
|
||||
};
|
||||
}
|
||||
|
||||
module.getSortedSetIntersect = async function (params) {
|
||||
params.sort = 1;
|
||||
@@ -29,10 +65,100 @@ module.exports = function (module) {
|
||||
};
|
||||
|
||||
async function getSortedSetRevIntersect(params) {
|
||||
var sets = params.sets;
|
||||
var start = params.hasOwnProperty('start') ? params.start : 0;
|
||||
var stop = params.hasOwnProperty('stop') ? params.stop : -1;
|
||||
var weights = params.weights || [];
|
||||
params.start = params.hasOwnProperty('start') ? params.start : 0;
|
||||
params.stop = params.hasOwnProperty('stop') ? params.stop : -1;
|
||||
params.weights = params.weights || [];
|
||||
|
||||
params.limit = params.stop - params.start + 1;
|
||||
if (params.limit <= 0) {
|
||||
params.limit = 0;
|
||||
}
|
||||
params.counts = await countSets(params.sets);
|
||||
if (params.counts.minCount === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const simple = params.weights.filter(w => w === 1).length === 1 && params.limit !== 0;
|
||||
if (params.counts.minCount < 25000 && simple) {
|
||||
return await intersectSingle(params);
|
||||
} else if (simple) {
|
||||
return await intersectBatch(params);
|
||||
}
|
||||
return await intersectAggregate(params);
|
||||
}
|
||||
|
||||
async function intersectSingle(params) {
|
||||
const objects = module.client.collection('objects');
|
||||
let items = await objects.find({ _key: params.counts.smallestSet }, {
|
||||
projection: { _id: 0, value: 1 },
|
||||
}).toArray();
|
||||
if (!items.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const otherSets = params.sets.filter(s => s !== params.counts.smallestSet);
|
||||
items = await intersectValuesWithSets(items.map(i => i.value), otherSets);
|
||||
if (!items.length) {
|
||||
return [];
|
||||
}
|
||||
const project = { _id: 0, value: 1 };
|
||||
if (params.withScores) {
|
||||
project.score = 1;
|
||||
}
|
||||
const sortSet = params.sets[params.weights.indexOf(1)];
|
||||
let res = await objects
|
||||
.find({ _key: sortSet, value: { $in: items } }, { projection: project })
|
||||
.sort({ score: params.sort })
|
||||
.skip(params.start)
|
||||
.limit(params.limit)
|
||||
.toArray();
|
||||
if (!params.withScores) {
|
||||
res = res.map(i => i.value);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async function intersectBatch(params) {
|
||||
const project = { _id: 0, value: 1 };
|
||||
if (params.withScores) {
|
||||
project.score = 1;
|
||||
}
|
||||
const sortSet = params.sets[params.weights.indexOf(1)];
|
||||
const batchSize = 10000;
|
||||
const cursor = await module.client.collection('objects')
|
||||
.find({ _key: sortSet }, { projection: project })
|
||||
.sort({ score: params.sort })
|
||||
.batchSize(batchSize);
|
||||
|
||||
const otherSets = params.sets.filter(s => s !== sortSet);
|
||||
let inters = [];
|
||||
let done = false;
|
||||
while (!done) {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
const items = [];
|
||||
while (items.length < batchSize) {
|
||||
const nextItem = await cursor.next();
|
||||
if (!nextItem) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
items.push(nextItem);
|
||||
}
|
||||
|
||||
const members = await Promise.all(otherSets.map(s => module.isSortedSetMembers(s, items.map(i => i.value))));
|
||||
inters = inters.concat(items.filter((item, idx) => members.every(arr => arr[idx])));
|
||||
if (inters.length >= params.stop) {
|
||||
done = true;
|
||||
inters = inters.slice(params.start, params.stop + 1);
|
||||
}
|
||||
}
|
||||
if (!params.withScores) {
|
||||
inters = inters.map(item => item.value);
|
||||
}
|
||||
return inters;
|
||||
}
|
||||
|
||||
async function intersectAggregate(params) {
|
||||
var aggregate = {};
|
||||
|
||||
if (params.aggregate) {
|
||||
@@ -40,15 +166,9 @@ module.exports = function (module) {
|
||||
} else {
|
||||
aggregate.$sum = '$score';
|
||||
}
|
||||
const pipeline = [{ $match: { _key: { $in: params.sets } } }];
|
||||
|
||||
var limit = stop - start + 1;
|
||||
if (limit <= 0) {
|
||||
limit = 0;
|
||||
}
|
||||
|
||||
var pipeline = [{ $match: { _key: { $in: sets } } }];
|
||||
|
||||
weights.forEach(function (weight, index) {
|
||||
params.weights.forEach(function (weight, index) {
|
||||
if (weight !== 1) {
|
||||
pipeline.push({
|
||||
$project: {
|
||||
@@ -56,7 +176,7 @@ module.exports = function (module) {
|
||||
score: {
|
||||
$cond: {
|
||||
if: {
|
||||
$eq: ['$_key', sets[index]],
|
||||
$eq: ['$_key', params.sets[index]],
|
||||
},
|
||||
then: {
|
||||
$multiply: ['$score', weight],
|
||||
@@ -70,25 +190,24 @@ module.exports = function (module) {
|
||||
});
|
||||
|
||||
pipeline.push({ $group: { _id: { value: '$value' }, totalScore: aggregate, count: { $sum: 1 } } });
|
||||
pipeline.push({ $match: { count: sets.length } });
|
||||
pipeline.push({ $match: { count: params.sets.length } });
|
||||
pipeline.push({ $sort: { totalScore: params.sort } });
|
||||
|
||||
if (start) {
|
||||
pipeline.push({ $skip: start });
|
||||
if (params.start) {
|
||||
pipeline.push({ $skip: params.start });
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
pipeline.push({ $limit: limit });
|
||||
if (params.limit > 0) {
|
||||
pipeline.push({ $limit: params.limit });
|
||||
}
|
||||
|
||||
var project = { _id: 0, value: '$_id.value' };
|
||||
const project = { _id: 0, value: '$_id.value' };
|
||||
if (params.withScores) {
|
||||
project.score = '$totalScore';
|
||||
}
|
||||
pipeline.push({ $project: project });
|
||||
|
||||
let data = await module.client.collection('objects').aggregate(pipeline).toArray();
|
||||
|
||||
if (!params.withScores) {
|
||||
data = data.map(item => item.value);
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ CSS.buildBundle = function (target, fork, callback) {
|
||||
getBundleMetadata(target, next);
|
||||
},
|
||||
function (data, next) {
|
||||
var minify = global.env !== 'development';
|
||||
var minify = process.env.NODE_ENV !== 'development';
|
||||
minifier.css.bundle(data.imports, data.paths, minify, fork, next);
|
||||
},
|
||||
function (bundle, next) {
|
||||
|
||||
@@ -260,7 +260,7 @@ JS.buildModules = function (fork, callback) {
|
||||
async.waterfall([
|
||||
clearModules,
|
||||
function (next) {
|
||||
if (global.env === 'development') {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return linkModules(callback);
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ function getBundleScriptList(target, callback) {
|
||||
|
||||
var scripts = JS.scripts.base;
|
||||
|
||||
if (target === 'client' && global.env !== 'development') {
|
||||
if (target === 'client' && process.env.NODE_ENV !== 'development') {
|
||||
scripts = scripts.concat(JS.scripts.rjs);
|
||||
} else if (target === 'acp') {
|
||||
scripts = scripts.concat(JS.scripts.admin);
|
||||
@@ -357,7 +357,7 @@ JS.buildBundle = function (target, fork, callback) {
|
||||
});
|
||||
},
|
||||
function (files, next) {
|
||||
var minify = global.env !== 'development';
|
||||
var minify = process.env.NODE_ENV !== 'development';
|
||||
var filePath = path.join(__dirname, '../../build/public', fileNames[target]);
|
||||
|
||||
minifier.js.bundle({
|
||||
|
||||
@@ -56,7 +56,7 @@ async function getTranslationMetadata() {
|
||||
}
|
||||
|
||||
async function writeLanguageFile(language, namespace, translations) {
|
||||
const dev = global.env === 'development';
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
const filePath = path.join(buildLanguagesPath, language, namespace + '.json');
|
||||
|
||||
await mkdirp(path.dirname(filePath));
|
||||
|
||||
@@ -115,7 +115,7 @@ async function compileTemplate(filename, source) {
|
||||
|
||||
source = await processImports(paths, filename, source);
|
||||
const compiled = await Benchpress.precompile(source, {
|
||||
minify: global.env !== 'development',
|
||||
minify: process.env.NODE_ENV !== 'development',
|
||||
});
|
||||
return await fsWriteFile(path.join(viewsPath, filename.replace(/\.tpl$/, '.js')), compiled);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ async function compile() {
|
||||
await mkdirp(path.join(viewsPath, path.dirname(name)));
|
||||
|
||||
await fsWriteFile(path.join(viewsPath, name), imported);
|
||||
const compiled = await Benchpress.precompile(imported, { minify: global.env !== 'development' });
|
||||
const compiled = await Benchpress.precompile(imported, { minify: process.env.NODE_ENV !== 'development' });
|
||||
await fsWriteFile(path.join(viewsPath, name.replace(/\.tpl$/, '.js')), compiled);
|
||||
}));
|
||||
|
||||
|
||||
@@ -56,9 +56,8 @@ module.exports = function (middleware) {
|
||||
}
|
||||
|
||||
// Ensure that the session is valid. This block guards against edge-cases where the server-side session has
|
||||
// been deleted (but client-side cookie still exists).
|
||||
// req.session.flash is present if you visit register/login, so all logged-in users have it, but it is missing if your server-side session got destroyed.
|
||||
if (!req.session.flash && !req.session.meta && !res.get('Set-Cookie')) {
|
||||
// been deleted (but client-side cookie still exists)
|
||||
if (req.uid > 0 && !req.session.meta && !res.get('Set-Cookie')) {
|
||||
res.clearCookie(nconf.get('sessionKey'), meta.configs.cookie.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,15 @@ function addProcessHandlers() {
|
||||
require('./meta').js.killMinifier();
|
||||
shutdown(1);
|
||||
});
|
||||
process.on('message', function (msg) {
|
||||
if (msg && msg.compiling === 'tpl') {
|
||||
const benchpressjs = require('benchpressjs');
|
||||
benchpressjs.flush();
|
||||
} else if (msg && msg.compiling === 'lang') {
|
||||
const translator = require('./translator');
|
||||
translator.flush();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function restart() {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var semver = require('semver');
|
||||
var readline = require('readline');
|
||||
var winston = require('winston');
|
||||
@@ -140,7 +141,7 @@ Upgrade.process = function (files, skipCount, callback) {
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
async.eachSeries(files, function (file, next) {
|
||||
async.eachSeries(files, async (file) => {
|
||||
var scriptExport = require(file);
|
||||
var date = new Date(scriptExport.timestamp);
|
||||
var version = path.dirname(file).split('/').pop();
|
||||
@@ -152,35 +153,34 @@ Upgrade.process = function (files, skipCount, callback) {
|
||||
date: date,
|
||||
};
|
||||
|
||||
console.log(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '...');
|
||||
process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '...');
|
||||
|
||||
// For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it
|
||||
if ((!results.schemaDate && !results.schemaLogCount) || (scriptExport.timestamp <= results.schemaDate && semver.lt(version, '1.5.0'))) {
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
readline.moveCursor(process.stdout, 0, -1);
|
||||
console.log(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... ' + 'skipped'.grey);
|
||||
db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next);
|
||||
process.stdout.write(' skipped\n'.grey);
|
||||
await db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Promisify method if necessary
|
||||
if (scriptExport.method.constructor && scriptExport.method.constructor.name !== 'AsyncFunction') {
|
||||
scriptExport.method = util.promisify(scriptExport.method);
|
||||
}
|
||||
|
||||
// Do the upgrade...
|
||||
scriptExport.method.bind({
|
||||
progress: progress,
|
||||
})(function (err) {
|
||||
if (err) {
|
||||
console.error('Error occurred');
|
||||
return next(err);
|
||||
}
|
||||
try {
|
||||
await scriptExport.method.bind({
|
||||
progress: progress,
|
||||
})();
|
||||
} catch (err) {
|
||||
console.error('Error occurred');
|
||||
throw err;
|
||||
}
|
||||
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
readline.moveCursor(process.stdout, 0, -1);
|
||||
console.log(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... ' + 'OK'.green);
|
||||
process.stdout.write(' OK\n'.green);
|
||||
|
||||
// Record success in schemaLog
|
||||
db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next);
|
||||
});
|
||||
// Record success in schemaLog
|
||||
await db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'));
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
|
||||
@@ -6,7 +6,7 @@ const batch = require('../../batch');
|
||||
module.exports = {
|
||||
name: 'Clean flag byCid zsets',
|
||||
timestamp: Date.UTC(2019, 8, 24),
|
||||
method: async function (callback) {
|
||||
method: async function () {
|
||||
const progress = this.progress;
|
||||
|
||||
await batch.processSortedSet('flags:datetime', async function (flagIds) {
|
||||
@@ -23,6 +23,5 @@ module.exports = {
|
||||
}, {
|
||||
progress: progress,
|
||||
});
|
||||
callback();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,11 +6,10 @@ const batch = require('../../batch');
|
||||
module.exports = {
|
||||
name: 'Clean up post hash data',
|
||||
timestamp: Date.UTC(2019, 9, 7),
|
||||
method: async function (callback) {
|
||||
method: async function () {
|
||||
const progress = this.progress;
|
||||
await cleanPost(progress);
|
||||
await cleanTopic(progress);
|
||||
callback();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const user = require('../../user');
|
||||
module.exports = {
|
||||
name: 'Clean up old notifications and hash data',
|
||||
timestamp: Date.UTC(2019, 9, 7),
|
||||
method: async function (callback) {
|
||||
method: async function () {
|
||||
const progress = this.progress;
|
||||
const week = 604800000;
|
||||
const cutoffTime = Date.now() - week;
|
||||
@@ -47,6 +47,5 @@ module.exports = {
|
||||
batch: 500,
|
||||
progress: progress,
|
||||
});
|
||||
callback();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ const batch = require('../../batch');
|
||||
module.exports = {
|
||||
name: 'Fix user sorted sets',
|
||||
timestamp: Date.UTC(2020, 4, 2),
|
||||
method: async function (callback) {
|
||||
method: async function () {
|
||||
const progress = this.progress;
|
||||
const nextUid = await db.getObjectField('global', 'nextUid');
|
||||
const allUids = [];
|
||||
@@ -58,6 +58,5 @@ module.exports = {
|
||||
});
|
||||
|
||||
await db.setObjectField('global', 'userCount', totalUserCount);
|
||||
callback();
|
||||
},
|
||||
};
|
||||
|
||||
22
src/upgrades/1.13.4/remove_allowFileUploads_priv.js
Normal file
22
src/upgrades/1.13.4/remove_allowFileUploads_priv.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('../../database');
|
||||
const privileges = require('../../privileges');
|
||||
|
||||
module.exports = {
|
||||
name: 'Removing file upload privilege if file uploads were disabled (`allowFileUploads`)',
|
||||
timestamp: Date.UTC(2020, 4, 21),
|
||||
method: async () => {
|
||||
const allowFileUploads = parseInt(await db.getObjectField('config', 'allowFileUploads'), 10);
|
||||
if (allowFileUploads === 1) {
|
||||
await db.deleteObjectField('config', 'allowFileUploads');
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove `upload:post:file` privilege for all groups
|
||||
await privileges.categories.rescind(['upload:post:file'], 0, ['guests', 'registered-users', 'Global Moderators']);
|
||||
|
||||
// Clean up the old option from the config hash
|
||||
await db.deleteObjectField('config', 'allowFileUploads');
|
||||
},
|
||||
};
|
||||
@@ -1,17 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
const db = require('../../database');
|
||||
const winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
// you should use spaces
|
||||
// the underscores are there so you can double click to select the whole thing
|
||||
name: 'User_friendly_upgrade_script_name',
|
||||
// remember, month is zero-indexed (so January is 0, December is 11)
|
||||
timestamp: Date.UTC(2019, 0, 1),
|
||||
method: function (callback) {
|
||||
timestamp: Date.UTC(2020, 0, 1),
|
||||
method: async () => {
|
||||
// Do stuff here...
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
</div>
|
||||
<div class="col-sm-10 col-xs-12">
|
||||
<form>
|
||||
<div class="checkbox">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" data-field="allowFileUploads">
|
||||
<span class="mdl-switch__label"><strong>[[admin/settings/uploads:allow-files]]</strong></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" data-field="privateUploads">
|
||||
|
||||
@@ -12,7 +12,9 @@ const nconf = require('nconf');
|
||||
const url = require('url');
|
||||
const util = require('util');
|
||||
|
||||
global.env = process.env.TEST_ENV || 'production';
|
||||
process.env.NODE_ENV = process.env.TEST_ENV || 'production';
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
|
||||
const winston = require('winston');
|
||||
const packageInfo = require('../../package');
|
||||
@@ -133,7 +135,7 @@ before(async function () {
|
||||
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
||||
nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json'));
|
||||
nconf.set('bcrypt_rounds', 1);
|
||||
|
||||
nconf.set('socket.io:origins', '*:*');
|
||||
nconf.set('version', packageInfo.version);
|
||||
|
||||
await meta.dependencies.check();
|
||||
|
||||
@@ -131,7 +131,6 @@ describe('Upload Controllers', function () {
|
||||
|
||||
|
||||
it('should upload a file to a post', function (done) {
|
||||
meta.config.allowFileUploads = 1;
|
||||
var oldValue = meta.config.allowedFileExtensions;
|
||||
meta.config.allowedFileExtensions = 'png,jpg,bmp,html';
|
||||
helpers.uploadFile(nconf.get('url') + '/api/post/upload', path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token, function (err, res, body) {
|
||||
|
||||
Reference in New Issue
Block a user