diff --git a/core/bbs.js b/core/bbs.js index c43d63a3..02058bce 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -234,6 +234,17 @@ function initialize(cb) { } ); }, + function initFileAreaStats(callback) { + const getAreaStats = require('./file_base_area.js').getAreaStats; + getAreaStats( (err, stats) => { + if(!err) { + const StatLog = require('./stat_log.js'); + StatLog.setNonPeristentSystemStat('file_base_area_stats', stats); + } + + return callback(null); + }); + }, function initMCI(callback) { return require('./predefined_mci.js').init(callback); }, diff --git a/core/config.js b/core/config.js index 449a5c98..85632000 100644 --- a/core/config.js +++ b/core/config.js @@ -713,6 +713,11 @@ function getDefaultConfig() { action : '@method:core/message_area.js:trimMessageAreasScheduledEvent', }, + updateFileAreaStats : { + schedule : 'every 1 hours', + action : '@method:core/file_base_area.js:updateAreaStatsScheduledEvent', + }, + forgotPasswordMaintenance : { schedule : 'every 24 hours', action : '@method:core/web_password_reset.js:performMaintenanceTask', diff --git a/core/file_base_area.js b/core/file_base_area.js index 79b91ce8..93587aa7 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -13,6 +13,7 @@ const Log = require('./logger.js').log; const resolveMimeType = require('./mime_util.js').resolveMimeType; const stringFormat = require('./string_format.js'); const wordWrapText = require('./word_wrap.js').wordWrapText; +const StatLog = require('./stat_log.js'); // deps const _ = require('lodash'); @@ -39,6 +40,10 @@ exports.changeFileAreaWithOptions = changeFileAreaWithOptions; exports.scanFile = scanFile; exports.scanFileAreaForChanges = scanFileAreaForChanges; exports.getDescFromFileName = getDescFromFileName; +exports.getAreaStats = getAreaStats; + +// for scheduler: +exports.updateAreaStatsScheduledEvent = updateAreaStatsScheduledEvent; const WellKnownAreaTags = exports.WellKnownAreaTags = { Invalid : '', @@ -856,4 +861,64 @@ function getDescFromFileName(fileName) { const name = paths.basename(fileName, ext); return _.upperFirst(name.replace(/[\-_.+]/g, ' ').replace(/\s+/g, ' ')); +} + +// +// Return an object of stats about an area(s) +// +// { +// +// totalFiles : , +// totalBytes : , +// areas : { +// : { +// files : , +// bytes : +// } +// } +// } +// +function getAreaStats(cb) { + FileDb.all( + `SELECT DISTINCT f.area_tag, COUNT(f.file_id) AS total_files, SUM(m.meta_value) AS total_byte_size + FROM file f, file_meta m + WHERE f.file_id = m.file_id AND m.meta_name='byte_size' + GROUP BY f.area_tag;`, + (err, statRows) => { + if(err) { + return cb(err); + } + + if(!statRows || 0 === statRows.length) { + return cb(Errors.DoesNotExist('No file areas to acquire stats from')); + } + + return cb( + null, + statRows.reduce( (stats, v) => { + stats.totalFiles = (stats.totalFiles || 0) + v.total_files; + stats.totalBytes = (stats.totalBytes || 0) + v.total_byte_size; + + stats.areas = stats.areas || {}; + + stats.areas[v.area_tag] = { + files : v.total_files, + bytes : v.total_byte_size, + }; + return stats; + }, {}) + ); + } + ); +} + +// method exposed for event scheduler +function updateAreaStatsScheduledEvent(args, cb) { + getAreaStats( (err, stats) => { + if(!err) { + StatLog.setNonPeristentSystemStat('file_base_area_stats', stats); + } + + return cb(err); + }); } \ No newline at end of file diff --git a/core/predefined_mci.js b/core/predefined_mci.js index afebd24c..ac8cffe7 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -201,12 +201,20 @@ const PREDEFINED_MCI_GENERATORS = { const byteSize = StatLog.getSystemStatNum('ul_total_bytes'); return formatByteSize(byteSize, true); // true=withAbbr }, + TF : function totalFilesOnSystem() { + const areaStats = StatLog.getSystemStat('file_base_area_stats'); + return _.get(areaStats, 'totalFiles', 0).toString(); + }, + TB : function totalBytesOnSystem() { + const areaStats = StatLog.getSystemStat('file_base_area_stats'); + const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); + return formatByteSize(totalBytes, true); // true=withAbbr + }, // :TODO: PT - Messages posted *today* (Obv/2) // -> Include FTN/etc. // :TODO: NT - New users today (Obv/2) // :TODO: CT - Calls *today* (Obv/2) - // :TODO: TF - Total files on the system (Obv/2) // :TODO: FT - Files uploaded/added *today* (Obv/2) // :TODO: DD - Files downloaded *today* (iNiQUiTY) // :TODO: TP - total message/posts on the system (Obv/2) diff --git a/core/string_util.js b/core/string_util.js index ed6df231..d6f5e08a 100644 --- a/core/string_util.js +++ b/core/string_util.js @@ -326,9 +326,7 @@ function formatByteSizeAbbr(byteSize) { return SIZE_ABBRS[Math.floor(Math.log(byteSize) / Math.log(1024))]; } -function formatByteSize(byteSize, withAbbr, decimals) { - withAbbr = withAbbr || false; - decimals = decimals || 3; +function formatByteSize(byteSize, withAbbr = false, decimals = 2) { const i = 0 === byteSize ? byteSize : Math.floor(Math.log(byteSize) / Math.log(1024)); let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)); if(withAbbr) {