mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-28 09:36:16 +01:00
feat: #7743 , meta/cachebuster, meta/configs
This commit is contained in:
@@ -1,48 +1,45 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
const fs = require('fs');
|
||||||
var fs = require('fs');
|
const path = require('path');
|
||||||
var path = require('path');
|
const mkdirp = require('mkdirp');
|
||||||
var mkdirp = require('mkdirp');
|
const winston = require('winston');
|
||||||
var winston = require('winston');
|
const util = require('util');
|
||||||
|
const mkdirpAsync = util.promisify(mkdirp);
|
||||||
|
const writeFileAsync = util.promisify(fs.writeFile);
|
||||||
|
const readFileAsync = util.promisify(fs.readFile);
|
||||||
|
|
||||||
var filePath = path.join(__dirname, '../../build/cache-buster');
|
const filePath = path.join(__dirname, '../../build/cache-buster');
|
||||||
|
|
||||||
var cached;
|
let cached;
|
||||||
|
|
||||||
// cache buster is an 11-character, lowercase, alphanumeric string
|
// cache buster is an 11-character, lowercase, alphanumeric string
|
||||||
function generate() {
|
function generate() {
|
||||||
return (Math.random() * 1e18).toString(32).slice(0, 11);
|
return (Math.random() * 1e18).toString(32).slice(0, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.write = function write(callback) {
|
exports.write = async function write() {
|
||||||
async.waterfall([
|
await mkdirpAsync(path.dirname(filePath));
|
||||||
function (next) {
|
await writeFileAsync(filePath, generate());
|
||||||
mkdirp(path.dirname(filePath), next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
fs.writeFile(filePath, generate(), next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.read = function read(callback) {
|
exports.read = async function read() {
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return callback(null, cached);
|
return cached;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
fs.readFile(filePath, 'utf8', function (err, buster) {
|
const buster = await readFileAsync(filePath, 'utf8');
|
||||||
if (err) {
|
|
||||||
winston.warn('[cache-buster] could not read cache buster', err);
|
|
||||||
return callback(null, generate());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!buster || buster.length !== 11) {
|
if (!buster || buster.length !== 11) {
|
||||||
winston.warn('[cache-buster] cache buster string invalid: expected /[a-z0-9]{11}/, got `' + buster + '`');
|
winston.warn('[cache-buster] cache buster string invalid: expected /[a-z0-9]{11}/, got `' + buster + '`');
|
||||||
return callback(null, generate());
|
return generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
cached = buster;
|
cached = buster;
|
||||||
callback(null, cached);
|
return cached;
|
||||||
});
|
} catch (err) {
|
||||||
|
winston.warn('[cache-buster] could not read cache buster', err);
|
||||||
|
return generate();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
require('../promisify')(exports);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
const nconf = require('nconf');
|
||||||
var nconf = require('nconf');
|
const path = require('path');
|
||||||
var path = require('path');
|
const winston = require('winston');
|
||||||
var winston = require('winston');
|
const util = require('util');
|
||||||
|
|
||||||
var db = require('../database');
|
const db = require('../database');
|
||||||
var pubsub = require('../pubsub');
|
const pubsub = require('../pubsub');
|
||||||
var Meta = require('../meta');
|
const Meta = require('../meta');
|
||||||
var cacheBuster = require('./cacheBuster');
|
const cacheBuster = require('./cacheBuster');
|
||||||
const defaults = require('../../install/data/defaults');
|
const defaults = require('../../install/data/defaults');
|
||||||
|
|
||||||
var Configs = module.exports;
|
var Configs = module.exports;
|
||||||
@@ -48,152 +48,114 @@ function deserialize(config) {
|
|||||||
|
|
||||||
Configs.deserialize = deserialize;
|
Configs.deserialize = deserialize;
|
||||||
|
|
||||||
Configs.init = function (callback) {
|
Configs.init = async function () {
|
||||||
var config;
|
const config = await Configs.list();
|
||||||
async.waterfall([
|
const buster = await cacheBuster.read();
|
||||||
function (next) {
|
config['cache-buster'] = 'v=' + (buster || Date.now());
|
||||||
Configs.list(next);
|
Meta.config = config;
|
||||||
},
|
|
||||||
function (_config, next) {
|
|
||||||
config = _config;
|
|
||||||
cacheBuster.read(next);
|
|
||||||
},
|
|
||||||
function (buster, next) {
|
|
||||||
config['cache-buster'] = 'v=' + (buster || Date.now());
|
|
||||||
Meta.config = config;
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Configs.list = function (callback) {
|
Configs.list = async function () {
|
||||||
Configs.getFields([], callback);
|
return await Configs.getFields([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Configs.get = function (field, callback) {
|
Configs.get = async function (field) {
|
||||||
Configs.getFields([field], function (err, values) {
|
const values = await Configs.getFields([field]);
|
||||||
callback(err, values ? values[field] : null);
|
return (values.hasOwnProperty(field) && values[field] !== undefined) ? values[field] : null;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Configs.getFields = function (fields, callback) {
|
Configs.getFields = async function (fields) {
|
||||||
async.waterfall([
|
let values;
|
||||||
function (next) {
|
if (fields.length) {
|
||||||
if (fields.length) {
|
values = await db.getObjectFields('config', fields);
|
||||||
db.getObjectFields('config', fields, next);
|
} else {
|
||||||
} else {
|
values = await db.getObject('config');
|
||||||
db.getObject('config', next);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (values, next) {
|
|
||||||
try {
|
|
||||||
values = Object.assign({}, defaults, values ? deserialize(values) : {});
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
if (!fields.length) {
|
|
||||||
values.version = nconf.get('version');
|
|
||||||
values.registry = nconf.get('registry');
|
|
||||||
}
|
|
||||||
next(null, values);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Configs.set = function (field, value, callback) {
|
|
||||||
callback = callback || function () {};
|
|
||||||
if (!field) {
|
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Configs.setMultiple({
|
values = Object.assign({}, defaults, values ? deserialize(values) : {});
|
||||||
|
|
||||||
|
if (!fields.length) {
|
||||||
|
values.version = nconf.get('version');
|
||||||
|
values.registry = nconf.get('registry');
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
Configs.set = async function (field, value) {
|
||||||
|
if (!field) {
|
||||||
|
throw new Error('[[error:invalid-data]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
await Configs.setMultiple({
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
// data comes from client-side
|
|
||||||
Configs.setMultiple = function (data, callback) {
|
|
||||||
data = deserialize(data);
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
processConfig(data, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.setObject('config', data, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
updateConfig(data);
|
|
||||||
setImmediate(next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Configs.setOnEmpty = function (values, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
db.getObject('config', next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
var config = Object.assign({}, values, data ? deserialize(data) : {});
|
|
||||||
db.setObject('config', config, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Configs.remove = function (field, callback) {
|
|
||||||
db.deleteObjectField('config', field, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function processConfig(data, callback) {
|
|
||||||
async.parallel([
|
|
||||||
async.apply(saveRenderedCss, data),
|
|
||||||
function (next) {
|
|
||||||
var image = require('../image');
|
|
||||||
if (data['brand:logo']) {
|
|
||||||
image.size(path.join(nconf.get('upload_path'), 'system', 'site-logo-x50.png'), function (err, size) {
|
|
||||||
if (err && err.code === 'ENOENT') {
|
|
||||||
// For whatever reason the x50 logo wasn't generated, gracefully error out
|
|
||||||
winston.warn('[logo] The email-safe logo doesn\'t seem to have been created, please re-upload your site logo.');
|
|
||||||
size = {
|
|
||||||
height: 0,
|
|
||||||
width: 0,
|
|
||||||
};
|
|
||||||
} else if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
data['brand:emailLogo'] = nconf.get('url') + path.join(nconf.get('upload_url'), 'system', 'site-logo-x50.png');
|
|
||||||
data['brand:emailLogo:height'] = size.height;
|
|
||||||
data['brand:emailLogo:width'] = size.width;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setImmediate(next);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
callback(err);
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Configs.setMultiple = async function (data) {
|
||||||
|
data = deserialize(data);
|
||||||
|
await processConfig(data);
|
||||||
|
await db.setObject('config', data);
|
||||||
|
updateConfig(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
Configs.setOnEmpty = async function (values) {
|
||||||
|
const data = await db.getObject('config');
|
||||||
|
const config = Object.assign({}, values, data ? deserialize(data) : {});
|
||||||
|
await db.setObject('config', config);
|
||||||
|
};
|
||||||
|
|
||||||
|
Configs.remove = async function (field) {
|
||||||
|
await db.deleteObjectField('config', field);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function processConfig(data) {
|
||||||
|
await Promise.all([
|
||||||
|
saveRenderedCss(data),
|
||||||
|
getLogoSize(data),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveRenderedCss(data, callback) {
|
function lessRender(string, callback) {
|
||||||
|
var less = require('less');
|
||||||
|
less.render(string, {
|
||||||
|
compress: true,
|
||||||
|
}, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lessRenderAsync = util.promisify(lessRender);
|
||||||
|
|
||||||
|
async function saveRenderedCss(data) {
|
||||||
if (!data.customCSS) {
|
if (!data.customCSS) {
|
||||||
return setImmediate(callback);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var less = require('less');
|
const lessObject = await lessRenderAsync(data.customCSS);
|
||||||
async.waterfall([
|
data.renderedCustomCSS = lessObject.css;
|
||||||
function (next) {
|
}
|
||||||
less.render(data.customCSS, {
|
|
||||||
compress: true,
|
async function getLogoSize(data) {
|
||||||
}, next);
|
var image = require('../image');
|
||||||
},
|
if (!data['brand:logo']) {
|
||||||
function (lessObject, next) {
|
return;
|
||||||
data.renderedCustomCSS = lessObject.css;
|
}
|
||||||
setImmediate(next);
|
let size;
|
||||||
},
|
try {
|
||||||
], callback);
|
size = await image.size(path.join(nconf.get('upload_path'), 'system', 'site-logo-x50.png'));
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
// For whatever reason the x50 logo wasn't generated, gracefully error out
|
||||||
|
winston.warn('[logo] The email-safe logo doesn\'t seem to have been created, please re-upload your site logo.');
|
||||||
|
size = {
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data['brand:emailLogo'] = nconf.get('url') + path.join(nconf.get('upload_url'), 'system', 'site-logo-x50.png');
|
||||||
|
data['brand:emailLogo:height'] = size.height;
|
||||||
|
data['brand:emailLogo:width'] = size.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateConfig(config) {
|
function updateConfig(config) {
|
||||||
|
|||||||
Reference in New Issue
Block a user