From ec97b3e8d405ea62b1f862be9b6b9c88da015ebc Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sun, 25 Nov 2018 19:05:16 -0700 Subject: [PATCH 01/12] More user/system stat constants & usage --- core/bbs.js | 7 ++++--- core/config.js | 2 +- core/database.js | 8 -------- core/file_area_web.js | 11 +++++++---- core/file_base_area.js | 3 ++- core/file_base_area_select.js | 3 ++- core/file_transfer.js | 20 ++++++++++++-------- core/last_callers.js | 3 ++- core/predefined_mci.js | 29 +++++++++++++++-------------- core/rumorz.js | 21 +++++++++++++-------- core/stat_log.js | 3 +-- core/system_log.js | 11 +++++++++++ core/system_property.js | 25 +++++++++++++++++++++++++ core/user_login.js | 6 ++++-- 14 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 core/system_log.js create mode 100644 core/system_property.js diff --git a/core/bbs.js b/core/bbs.js index ede2b82e..000b19a8 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -11,6 +11,7 @@ const logger = require('./logger.js'); const database = require('./database.js'); const resolvePath = require('./misc_util.js').resolvePath; const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); // deps const async = require('async'); @@ -246,13 +247,13 @@ function initialize(cb) { if(err) { propLoadOpts.names.concat('username').forEach(v => { - StatLog.setNonPeristentSystemStat(`sysop_${v}`, 'N/A'); + StatLog.setNonPersistentSystemStat(`sysop_${v}`, 'N/A'); }); } else { opProps.username = opUserName; _.each(opProps, (v, k) => { - StatLog.setNonPeristentSystemStat(`sysop_${k}`, v); + StatLog.setNonPersistentSystemStat(`sysop_${k}`, v); }); } @@ -265,7 +266,7 @@ function initialize(cb) { getAreaStats( (err, stats) => { if(!err) { const StatLog = require('./stat_log.js'); - StatLog.setNonPeristentSystemStat('file_base_area_stats', stats); + StatLog.setNonPersistentSystemStat(SysProps.FileBaseAreaStats, stats); } return callback(null); diff --git a/core/config.js b/core/config.js index 701c8494..a90ff50d 100644 --- a/core/config.js +++ b/core/config.js @@ -969,7 +969,7 @@ function getDefaultConfig() { statLog : { systemEvents : { - loginHistoryMax: -1 // forever + loginHistoryMax: 5000, // set to -1 for forever } } }; diff --git a/core/database.js b/core/database.js index 017aa949..90f15692 100644 --- a/core/database.js +++ b/core/database.js @@ -189,14 +189,6 @@ const DB_INIT_TABLE = { );` ); - dbs.user.run( - `CREATE TABLE IF NOT EXISTS user_login_history ( - user_id INTEGER NOT NULL, - user_name VARCHAR NOT NULL, - timestamp DATETIME NOT NULL - );` - ); - return cb(null); }, diff --git a/core/file_area_web.js b/core/file_area_web.js index a1d12989..b36c8b72 100644 --- a/core/file_area_web.js +++ b/core/file_area_web.js @@ -15,6 +15,8 @@ const Log = require('./logger.js').log; const getConnectionByUserId = require('./client_connections.js').getConnectionByUserId; const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName; const Events = require('./events.js'); +const UserProps = require('./user_property.js'); +const SysProps = require('./system_menu_method.js'); // deps const hashids = require('hashids'); @@ -470,10 +472,11 @@ class FileAreaWebAccess { }); }, function updateStats(user, callback) { - StatLog.incrementUserStat(user, 'dl_total_count', 1); - StatLog.incrementUserStat(user, 'dl_total_bytes', dlBytes); - StatLog.incrementSystemStat('dl_total_count', 1); - StatLog.incrementSystemStat('dl_total_bytes', dlBytes); + StatLog.incrementUserStat(user, UserProps.FileDlTotalCount, 1); + StatLog.incrementUserStat(user, UserProps.FileDlTotalBytes, dlBytes); + + StatLog.incrementSystemStat(SysProps.FileDlTotalCount, 1); + StatLog.incrementSystemStat(SysProps.FileDlTotalBytes, dlBytes); return callback(null, user); }, diff --git a/core/file_base_area.js b/core/file_base_area.js index 760eb87c..f699a543 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -15,6 +15,7 @@ const stringFormat = require('./string_format.js'); const wordWrapText = require('./word_wrap.js').wordWrapText; const StatLog = require('./stat_log.js'); const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); // deps const _ = require('lodash'); @@ -1012,7 +1013,7 @@ function getAreaStats(cb) { function updateAreaStatsScheduledEvent(args, cb) { getAreaStats( (err, stats) => { if(!err) { - StatLog.setNonPeristentSystemStat('file_base_area_stats', stats); + StatLog.setNonPersistentSystemStat(SysProps.FileBaseAreaStats, stats); } return cb(err); diff --git a/core/file_base_area_select.js b/core/file_base_area_select.js index 1b77eb21..a8f74322 100644 --- a/core/file_base_area_select.js +++ b/core/file_base_area_select.js @@ -5,6 +5,7 @@ const MenuModule = require('./menu_module.js').MenuModule; const { getSortedAvailableFileAreas } = require('./file_base_area.js'); const StatLog = require('./stat_log.js'); +const SysProps = require('./system_property.js'); // deps const async = require('async'); @@ -52,7 +53,7 @@ exports.getModule = class FileAreaSelectModule extends MenuModule { async.waterfall( [ function mergeAreaStats(callback) { - const areaStats = StatLog.getSystemStat('file_base_area_stats') || { areas : {} }; + const areaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats) || { areas : {} }; // we could use 'sort' alone, but area/conf sorting has some special properties; user can still override const availAreas = getSortedAvailableFileAreas(self.client); diff --git a/core/file_transfer.js b/core/file_transfer.js index 340ca6b9..83dedc13 100644 --- a/core/file_transfer.js +++ b/core/file_transfer.js @@ -11,6 +11,8 @@ const StatLog = require('./stat_log.js'); const FileEntry = require('./file_entry.js'); const Log = require('./logger.js').log; const Events = require('./events.js'); +const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); // deps const async = require('async'); @@ -479,10 +481,11 @@ exports.getModule = class TransferFileModule extends MenuModule { }); }, () => { // All stats/meta currently updated via fire & forget - if this is ever a issue, we can wait for callbacks - StatLog.incrementUserStat(this.client.user, 'dl_total_count', downloadCount); - StatLog.incrementUserStat(this.client.user, 'dl_total_bytes', downloadBytes); - StatLog.incrementSystemStat('dl_total_count', downloadCount); - StatLog.incrementSystemStat('dl_total_bytes', downloadBytes); + StatLog.incrementUserStat(this.client.user, UserProps.FileDlTotalCount, downloadCount); + StatLog.incrementUserStat(this.client.user, UserProps.FileDlTotalBytes, downloadBytes); + + StatLog.incrementSystemStat(SysProps.FileDlTotalCount, downloadCount); + StatLog.incrementSystemStat(SysProps.FileDlTotalBytes, downloadBytes); fileIds.forEach(fileId => { FileEntry.incrementAndPersistMetaValue(fileId, 'dl_count', 1); @@ -509,10 +512,11 @@ exports.getModule = class TransferFileModule extends MenuModule { return next(null); }); }, () => { - StatLog.incrementUserStat(this.client.user, 'ul_total_count', uploadCount); - StatLog.incrementUserStat(this.client.user, 'ul_total_bytes', uploadBytes); - StatLog.incrementSystemStat('ul_total_count', uploadCount); - StatLog.incrementSystemStat('ul_total_bytes', uploadBytes); + StatLog.incrementUserStat(this.client.user, UserProps.FileUlTotalCount, uploadCount); + StatLog.incrementUserStat(this.client.user, UserProps.FileUlTotalBytes, uploadBytes); + + StatLog.incrementSystemStat(SysProps.FileUlTotalCount, uploadCount); + StatLog.incrementSystemStat(SysProps.FileUlTotalBytes, uploadBytes); return cb(null); }); diff --git a/core/last_callers.js b/core/last_callers.js index 6b25caf6..c80ce336 100644 --- a/core/last_callers.js +++ b/core/last_callers.js @@ -8,6 +8,7 @@ const User = require('./user.js'); const sysDb = require('./database.js').dbs.system; const { Errors } = require('./enig_error.js'); const UserProps = require('./user_property.js'); +const SysLogKeys = require('./system_log.js'); // deps const moment = require('moment'); @@ -91,7 +92,7 @@ exports.getModule = class LastCallersModule extends MenuModule { } StatLog.getSystemLogEntries( - 'user_login_history', + SysLogKeys.UserLoginHistory, StatLog.Order.TimestampDesc, 200, // max items to fetch - we need more than max displayed for filtering/etc. (err, loginHistory) => { diff --git a/core/predefined_mci.js b/core/predefined_mci.js index cda9f5b4..5b92e0e3 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -14,6 +14,7 @@ const FileBaseFilters = require('./file_base_filter.js'); const { formatByteSize } = require('./string_util.js'); const ANSI = require('./ansi_term.js'); const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); // deps const packageJson = require('../package.json'); @@ -34,7 +35,7 @@ function setNextRandomRumor(cb) { entry = entry[0]; } const randRumor = entry && entry.log_value ? entry.log_value : ''; - StatLog.setNonPeristentSystemStat('random_rumor', randRumor); + StatLog.setNonPersistentSystemStat(SysProps.NextRandomRumor, randRumor); if(cb) { return cb(null); } @@ -67,12 +68,12 @@ const PREDEFINED_MCI_GENERATORS = { VN : function version() { return packageJson.version; }, // +op info - SN : function opUserName() { return StatLog.getSystemStat('sysop_username'); }, - SR : function opRealName() { return StatLog.getSystemStat('sysop_real_name'); }, - SL : function opLocation() { return StatLog.getSystemStat('sysop_location'); }, - SA : function opAffils() { return StatLog.getSystemStat('sysop_affiliation'); }, - SS : function opSex() { return StatLog.getSystemStat('sysop_sex'); }, - SE : function opEmail() { return StatLog.getSystemStat('sysop_email_address'); }, + SN : function opUserName() { return StatLog.getSystemStat(SysProps.SysOpUsername); }, + SR : function opRealName() { return StatLog.getSystemStat(SysProps.SysOpRealName); }, + SL : function opLocation() { return StatLog.getSystemStat(SysProps.SysOpLocation); }, + SA : function opAffils() { return StatLog.getSystemStat(SysProps.SysOpAffiliations); }, + SS : function opSex() { return StatLog.getSystemStat(SysProps.SysOpSex); }, + SE : function opEmail() { return StatLog.getSystemStat(SysProps.SysOpEmailAddress); }, // :TODO: op age, web, ????? // @@ -189,7 +190,7 @@ const PREDEFINED_MCI_GENERATORS = { AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); }, - TC : function totalCalls() { return StatLog.getSystemStat('login_count').toLocaleString(); }, + TC : function totalCalls() { return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString(); }, RR : function randomRumor() { // start the process of picking another random one @@ -203,22 +204,22 @@ const PREDEFINED_MCI_GENERATORS = { // // :TODO: DD - Today's # of downloads (iNiQUiTY) // - SD : function systemNumDownloads() { return sysStatAsString('dl_total_count', 0); }, + SD : function systemNumDownloads() { return sysStatAsString(SysProps.FileDlTotalCount, 0); }, SO : function systemByteDownload() { - const byteSize = StatLog.getSystemStatNum('dl_total_bytes'); + const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes); return formatByteSize(byteSize, true); // true=withAbbr }, - SU : function systemNumUploads() { return sysStatAsString('ul_total_count', 0); }, + SU : function systemNumUploads() { return sysStatAsString(SysProps.FileUlTotalCount, 0); }, SP : function systemByteUpload() { - const byteSize = StatLog.getSystemStatNum('ul_total_bytes'); + const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTotalBytes); return formatByteSize(byteSize, true); // true=withAbbr }, TF : function totalFilesOnSystem() { - const areaStats = StatLog.getSystemStat('file_base_area_stats'); + const areaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats); return _.get(areaStats, 'totalFiles', 0).toLocaleString(); }, TB : function totalBytesOnSystem() { - const areaStats = StatLog.getSystemStat('file_base_area_stats'); + const areaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats); const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); return formatByteSize(totalBytes, true); // true=withAbbr }, diff --git a/core/rumorz.js b/core/rumorz.js index 31aea104..51e58d53 100644 --- a/core/rumorz.js +++ b/core/rumorz.js @@ -8,6 +8,7 @@ const theme = require('./theme.js'); const resetScreen = require('./ansi_term.js').resetScreen; const StatLog = require('./stat_log.js'); const renderStringLength = require('./string_util.js').renderStringLength; +const SystemLogKeys = require('./system_log.js'); // deps const async = require('async'); @@ -20,8 +21,6 @@ exports.moduleInfo = { packageName : 'codes.l33t.enigma.rumorz', }; -const STATLOG_KEY_RUMORZ = 'system_rumorz'; - const FormIds = { View : 0, Add : 1, @@ -52,10 +51,16 @@ exports.getModule = class RumorzModule extends MenuModule { if(_.isString(formData.value.rumor) && renderStringLength(formData.value.rumor) > 0) { const rumor = formData.value.rumor.trim(); // remove any trailing ws - StatLog.appendSystemLogEntry(STATLOG_KEY_RUMORZ, rumor, StatLog.KeepDays.Forever, StatLog.KeepType.Forever, () => { - this.clearAddForm(); - return this.displayViewScreen(true, cb); // true=cls - }); + StatLog.appendSystemLogEntry( + SystemLogKeys.UserAddedRumorz, + rumor, + StatLog.KeepDays.Forever, + StatLog.KeepType.Forever, + () => { + this.clearAddForm(); + return this.displayViewScreen(true, cb); // true=cls + } + ); } else { // empty message - treat as if cancel was hit return this.displayViewScreen(true, cb); // true=cls @@ -149,7 +154,7 @@ exports.getModule = class RumorzModule extends MenuModule { function fetchEntries(callback) { const entriesView = self.viewControllers.view.getView(MciCodeIds.ViewForm.Entries); - StatLog.getSystemLogEntries(STATLOG_KEY_RUMORZ, StatLog.Order.Timestamp, (err, entries) => { + StatLog.getSystemLogEntries(SystemLogKeys.UserAddedRumorz, StatLog.Order.Timestamp, (err, entries) => { return callback(err, entriesView, entries); }); }, @@ -158,7 +163,7 @@ exports.getModule = class RumorzModule extends MenuModule { return { text : e.log_value, // standard rumor : e.log_value, - } + }; })); entriesView.redraw(); diff --git a/core/stat_log.js b/core/stat_log.js index 173222b9..80840ddb 100644 --- a/core/stat_log.js +++ b/core/stat_log.js @@ -70,8 +70,7 @@ class StatLog { }; } - // :TODO: fix spelling :) - setNonPeristentSystemStat(statName, statValue) { + setNonPersistentSystemStat(statName, statValue) { this.systemStats[statName] = statValue; } diff --git a/core/system_log.js b/core/system_log.js new file mode 100644 index 00000000..e753c68b --- /dev/null +++ b/core/system_log.js @@ -0,0 +1,11 @@ +/* jslint node: true */ +'use strict'; + +// +// Common SYSTEM/global log keys +// +module.exports = { + UserAddedRumorz : 'system_rumorz', + UserLoginHistory : 'user_login_history', +}; + diff --git a/core/system_property.js b/core/system_property.js new file mode 100644 index 00000000..f2743088 --- /dev/null +++ b/core/system_property.js @@ -0,0 +1,25 @@ +/* jslint node: true */ +'use strict'; + +// +// Common SYSTEM/global properties/stats used throughout the system. +// +// This IS NOT a full list. Custom modules & the like can create +// their own! +// +module.exports = { + LoginCount : 'login_count', + + FileBaseAreaStats : 'file_base_area_stats', // object - see file_base_area.js::getAreaStats + FileUlTotalCount : 'ul_total_count', + FileUlTotalBytes : 'ul_total_bytes', + + SysOpUsername : 'sysop_username', + SysOpRealName : 'sysop_real_name', + SysOpLocation : 'sysop_location', + SysOpAffiliations : 'sysop_affiliation', + SysOpSex : 'sysop_sex', + SysOpEmailAddress : 'sysop_email_address', + + NextRandomRumor : 'random_rumor', +}; diff --git a/core/user_login.js b/core/user_login.js index a46be5b1..b02066a1 100644 --- a/core/user_login.js +++ b/core/user_login.js @@ -13,6 +13,8 @@ const { ErrorReasons } = require('./enig_error.js'); const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); +const SystemLogKeys = require('./system_log.js'); // deps const async = require('async'); @@ -86,7 +88,7 @@ function userLogin(client, username, password, cb) { return callback(null); }, function updateSystemLoginCount(callback) { - return StatLog.incrementSystemStat('login_count', 1, callback); // :TODO: create system_property.js + return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback); }, function recordLastLogin(callback) { return StatLog.setUserStat(user, UserProps.LastLoginTs, StatLog.now, callback); @@ -102,7 +104,7 @@ function userLogin(client, username, password, cb) { }); return StatLog.appendSystemLogEntry( - 'user_login_history', + SystemLogKeys.UserLoginHistory, historyItem, loginHistoryMax, StatLog.KeepType.Max, From f471fd0ebecbd38b7c0c523c8dc37517ffd8d2e0 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sun, 25 Nov 2018 20:13:48 -0700 Subject: [PATCH 02/12] Finally implement "Total Calls Today" MCI: TT + Add findSystemLogEntries() to StatLog amongst others --- art/themes/luciano_blocktronics/SYSSTAT.ANS | Bin 2860 -> 2877 bytes art/themes/luciano_blocktronics/theme.hjson | 1 + core/bbs.js | 17 +++ core/database.js | 2 +- core/predefined_mci.js | 7 +- core/stat_log.js | 130 ++++++++++++++------ core/system_property.js | 3 + core/user_login.js | 1 + docs/art/mci.md | 3 +- 9 files changed, 121 insertions(+), 43 deletions(-) diff --git a/art/themes/luciano_blocktronics/SYSSTAT.ANS b/art/themes/luciano_blocktronics/SYSSTAT.ANS index 97beb53d8a39949e2982fb876b065d599cc6d492..19a3d39e0b11353a7c8b7415742ea3870a3c6fcb 100644 GIT binary patch delta 42 xcmZ1@wpVOJJ*SX#v_Y { + if(!err) { + StatLog.setNonPersistentSystemStat(SysProps.LoginsToday, callsToday); + } + return callback(null); + }); + }, function initMCI(callback) { return require('./predefined_mci.js').init(callback); }, diff --git a/core/database.js b/core/database.js index 90f15692..55fdb1c7 100644 --- a/core/database.js +++ b/core/database.js @@ -73,7 +73,7 @@ function getISOTimestampString(ts) { } ts = moment(ts); } - return ts.utc().format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'); + return ts.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'); } function sanatizeString(s) { diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 5b92e0e3..39310559 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -15,6 +15,7 @@ const { formatByteSize } = require('./string_util.js'); const ANSI = require('./ansi_term.js'); const UserProps = require('./user_property.js'); const SysProps = require('./system_property.js'); +const SysLogKeys = require('./system_log.js'); // deps const packageJson = require('../package.json'); @@ -30,7 +31,7 @@ function init(cb) { } function setNextRandomRumor(cb) { - StatLog.getSystemLogEntries('system_rumorz', StatLog.Order.Random, 1, (err, entry) => { + StatLog.getSystemLogEntries(SysLogKeys.UserAddedRumorz, StatLog.Order.Random, 1, (err, entry) => { if(entry) { entry = entry[0]; } @@ -191,6 +192,9 @@ const PREDEFINED_MCI_GENERATORS = { AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); }, TC : function totalCalls() { return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString(); }, + TT : function totalCallsToday() { + return StatLog.getSystemStat(SysProps.LoginsToday).toLocaleString(); + }, RR : function randomRumor() { // start the process of picking another random one @@ -227,7 +231,6 @@ const PREDEFINED_MCI_GENERATORS = { // :TODO: PT - Messages posted *today* (Obv/2) // -> Include FTN/etc. // :TODO: NT - New users today (Obv/2) - // :TODO: CT - Calls *today* (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/stat_log.js b/core/stat_log.js index 80840ddb..805198bd 100644 --- a/core/stat_log.js +++ b/core/stat_log.js @@ -5,9 +5,11 @@ const sysDb = require('./database.js').dbs.system; const { getISOTimestampString } = require('./database.js'); +const Errors = require('./enig_error.js'); // deps const _ = require('lodash'); +const moment = require('moment'); /* System Event Log & Stats @@ -74,6 +76,19 @@ class StatLog { this.systemStats[statName] = statValue; } + incrementNonPersistentSystemStat(statName, incrementBy) { + incrementBy = incrementBy || 1; + + let newValue = parseInt(this.systemStats[statName]); + if(!isNaN(newValue)) { + newValue += incrementBy; + } else { + newValue = incrementBy; + } + this.setNonPersistentSystemStat(statName, newValue); + return newValue; + } + setSystemStat(statName, statValue, cb) { // live stats this.systemStats[statName] = statValue; @@ -99,19 +114,7 @@ class StatLog { } incrementSystemStat(statName, incrementBy, cb) { - incrementBy = incrementBy || 1; - - let newValue = parseInt(this.systemStats[statName]); - if(newValue) { - if(!_.isNumber(newValue)) { - return cb(new Error(`Value for ${statName} is not a number!`)); - } - - newValue += incrementBy; - } else { - newValue = incrementBy; - } - + const newValue = this.incrementNonPersistentSystemStat(statName, incrementBy); return this.setSystemStat(statName, newValue, cb); } @@ -216,26 +219,78 @@ class StatLog { ); } - getSystemLogEntries(logName, order, limit, cb) { - let sql = - `SELECT timestamp, log_value - FROM system_event_log - WHERE log_name = ?`; + /* + Find System Log entries by |filter|: - switch(order) { - case 'timestamp' : - case 'timestamp_asc' : - sql += ' ORDER BY timestamp ASC'; - break; - - case 'timestamp_desc' : - sql += ' ORDER BY timestamp DESC'; - break; - - case 'random' : - sql += ' ORDER BY RANDOM()'; + filter.logName (required) + filter.resultType = (obj) | count + where obj contains timestamp and log_value + filter.limit + filter.date - exact date to filter against + filter.order = (timestamp) | timestamp_asc | timestamp_desc | random + */ + findSystemLogEntries(filter, cb) { + filter = filter || {}; + if(!_.isString(filter.logName)) { + return cb(Errors.MissingParam('filter.logName is required')); } + filter.resultType = filter.resultType || 'obj'; + filter.order = filter.order || 'timestamp'; + + let sql; + if('count' === filter.resultType) { + sql = + `SELECT COUNT() AS count + FROM system_event_log`; + } else { + sql = + `SELECT timestamp, log_value + FROM system_event_log`; + } + + sql += ' WHERE log_name = ?'; + + if(filter.date) { + filter.date = moment(filter.date); + sql += ` AND DATE(timestamp) = DATE("${filter.date.format('YYYY-MM-DD')}")`; + } + + if('count' !== filter.resultType) { + switch(filter.order) { + case 'timestamp' : + case 'timestamp_asc' : + sql += ' ORDER BY timestamp ASC'; + break; + + case 'timestamp_desc' : + sql += ' ORDER BY timestamp DESC'; + break; + + case 'random' : + sql += ' ORDER BY RANDOM()'; + break; + } + } + + if(_.isNumber(filter.limit) && 0 !== filter.limit) { + sql += ` LIMIT ${filter.limit}`; + } + + sql += ';'; + + if('count' === filter.resultType) { + sysDb.get(sql, [ filter.logName ], (err, row) => { + return cb(err, row ? row.count : 0); + }); + } else { + sysDb.all(sql, [ filter.logName ], (err, rows) => { + return cb(err, rows); + }); + } + } + + getSystemLogEntries(logName, order, limit, cb) { if(!cb && _.isFunction(limit)) { cb = limit; limit = 0; @@ -243,15 +298,12 @@ class StatLog { limit = limit || 0; } - if(0 !== limit) { - sql += ` LIMIT ${limit}`; - } - - sql += ';'; - - sysDb.all(sql, [ logName ], (err, rows) => { - return cb(err, rows); - }); + const filter = { + logName, + order, + limit, + }; + return this.findSystemLogEntries(filter, cb); } appendUserLogEntry(user, logName, logValue, keepDays, cb) { diff --git a/core/system_property.js b/core/system_property.js index f2743088..52830a06 100644 --- a/core/system_property.js +++ b/core/system_property.js @@ -9,17 +9,20 @@ // module.exports = { LoginCount : 'login_count', + LoginsToday : 'logins_today', // non-persistent FileBaseAreaStats : 'file_base_area_stats', // object - see file_base_area.js::getAreaStats FileUlTotalCount : 'ul_total_count', FileUlTotalBytes : 'ul_total_bytes', + // begin +op non-persistent... SysOpUsername : 'sysop_username', SysOpRealName : 'sysop_real_name', SysOpLocation : 'sysop_location', SysOpAffiliations : 'sysop_affiliation', SysOpSex : 'sysop_sex', SysOpEmailAddress : 'sysop_email_address', + // end +op non-persistent NextRandomRumor : 'random_rumor', }; diff --git a/core/user_login.js b/core/user_login.js index b02066a1..2959e3a7 100644 --- a/core/user_login.js +++ b/core/user_login.js @@ -88,6 +88,7 @@ function userLogin(client, username, password, cb) { return callback(null); }, function updateSystemLoginCount(callback) { + StatLog.incrementNonPersistentSystemStat(SysProps.LoginsToday, 1); return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback); }, function recordLastLogin(callback) { diff --git a/docs/art/mci.md b/docs/art/mci.md index 04e177c2..4f351dad 100644 --- a/docs/art/mci.md +++ b/docs/art/mci.md @@ -65,7 +65,8 @@ for a full listing. Many codes attempt to pay homage to Oblivion/2, iNiQUiTY, et | `SC` | System CPU model | | `NV` | System underlying Node.js version | | `AN` | Current active node count | -| `TC` | Total login/calls to system | +| `TC` | Total login/calls to the system *ever* | +| `TT` | Total login/calls to the system *today* | | `RR` | Displays a random rumor | | `SD` | Total downloads, system wide | | `SO` | Total downloaded amount, system wide (formatted to appropriate bytes/megs/etc.) | From e606ec6f63f42f409bc8229f9c33c0e3414d2467 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sun, 25 Nov 2018 20:29:36 -0700 Subject: [PATCH 03/12] Fix ISO timestamps hopefully --- core/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/database.js b/core/database.js index 55fdb1c7..7193adee 100644 --- a/core/database.js +++ b/core/database.js @@ -73,7 +73,7 @@ function getISOTimestampString(ts) { } ts = moment(ts); } - return ts.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'); + return ts.format('YYYY-MM-DDTHH:mm:ss.SSS'); } function sanatizeString(s) { From 4b5f26b31bdcf7d84aa463613b526e411e15ae44 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sun, 25 Nov 2018 20:31:25 -0700 Subject: [PATCH 04/12] Record offeset again... dur --- core/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/database.js b/core/database.js index 7193adee..e4a812d4 100644 --- a/core/database.js +++ b/core/database.js @@ -73,7 +73,7 @@ function getISOTimestampString(ts) { } ts = moment(ts); } - return ts.format('YYYY-MM-DDTHH:mm:ss.SSS'); + return ts.format('YYYY-MM-DDTHH:mm:ss.SSSZ'); } function sanatizeString(s) { From fb13381bb5daeb3c55d24addfd221a37ffae9762 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 19:45:36 -0700 Subject: [PATCH 05/12] Default back to 'forever' for login history. It's small data... --- core/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config.js b/core/config.js index a90ff50d..3f4aeb61 100644 --- a/core/config.js +++ b/core/config.js @@ -969,7 +969,7 @@ function getDefaultConfig() { statLog : { systemEvents : { - loginHistoryMax: 5000, // set to -1 for forever + loginHistoryMax: -1, // set to -1 for forever } } }; From 6cce013187df1fcaff85ce862a21275e377f52cc Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 21:21:00 -0700 Subject: [PATCH 06/12] + MCI: PT - total messages posted today (non-private) + MCI: TP - total messages/posts on system (non-private, includes imports, only counts *current*, not all of time) * Move some stats to startup() calls * Fix some DATE() comparisons in SQL to use 'localtime' as our timestamps include TZ * Update luciano_blocktronics SYSSTAT to show more info --- art/themes/luciano_blocktronics/SYSSTAT.ANS | Bin 2877 -> 3066 bytes art/themes/luciano_blocktronics/theme.hjson | 7 ++-- core/bbs.js | 14 ++------ core/file_base_area.js | 20 ++++++++++- core/fse.js | 6 +++- core/message.js | 8 ++++- core/message_area.js | 36 ++++++++++++++++++++ core/msg_area_post_fse.js | 2 +- core/predefined_mci.js | 16 ++++++--- core/scanner_tossers/ftn_bso.js | 10 ++++-- core/stat_log.js | 2 +- core/system_property.js | 3 ++ 12 files changed, 98 insertions(+), 26 deletions(-) diff --git a/art/themes/luciano_blocktronics/SYSSTAT.ANS b/art/themes/luciano_blocktronics/SYSSTAT.ANS index 19a3d39e0b11353a7c8b7415742ea3870a3c6fcb..a76e3bd654c45d46566905990ca88b74c0b5e868 100644 GIT binary patch delta 493 zcmdlh_Dg(1qn~uNv3ag^w3#!IVU(-j3KTHR&6JKdum*D7eH}sE)ljZNS!z*nW_})* zf&y3@h%(Gob@DR=DgYW{XgRUhLLVXpF%HaD4f8YN0`q}N41g9H<|+UIM33dfi{X+G zz+stxHGILX*G745;XQ?`em;ps5>u|VofRuuq zJDHDDO~@D&PGB?c-?^`FN8vu#lo7{?#YteDgfF0f*t?> delta 261 zcmew*zE^BQBM+B?f^@X8d9HM{k?F+q7RY?V$!3hUjoHuJR6|=Gf zb*Vb}8BSi#K84@dELSzm$4ENb0BDk-<>V-icu}xYAkW&^Aa^6s9FUapWC2cBRxoFB zBd5sZX`F_W_jAZie#U7vIf+wI2dr2D2q4N$oq<}+a+3>6q2?$kSb?QfgPlz#@8vX| zY{H?!WNbKDic5t>I@%QIMlLn6lFZyxn0lyk=Mb~Wd0cuxH5Sg3`M8fUUFDj5javl( DX{|*^ diff --git a/art/themes/luciano_blocktronics/theme.hjson b/art/themes/luciano_blocktronics/theme.hjson index a5100e6b..330075ff 100644 --- a/art/themes/luciano_blocktronics/theme.hjson +++ b/art/themes/luciano_blocktronics/theme.hjson @@ -127,15 +127,16 @@ mainMenuSystemStats: { mci: { BN1: { width: 17 } - VL2: { width: 17 } + VN2: { width: 17 } OS3: { width: 33 } SC4: { width: 33 } - DT5: { width: 33 } - CT6: { width: 33 } AN7: { width: 6 } ND8: { width: 6 } TC9: { width: 6 } TT11: { width: 6 } + PT12: { width: 6 } + TP13: { width: 6 } + NV14: { width: 17 } } } diff --git a/core/bbs.js b/core/bbs.js index c154f285..4d371fb3 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -263,17 +263,6 @@ 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.setNonPersistentSystemStat(SysProps.FileBaseAreaStats, stats); - } - - return callback(null); - }); - }, function initCallsToday(callback) { const StatLog = require('./stat_log.js'); const filter = { @@ -289,6 +278,9 @@ function initialize(cb) { return callback(null); }); }, + function initMessageStats(callback) { + return require('./message_area.js').startup(callback); + }, function initMCI(callback) { return require('./predefined_mci.js').init(callback); }, diff --git a/core/file_base_area.js b/core/file_base_area.js index f699a543..293ab265 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -57,7 +57,25 @@ const WellKnownAreaTags = exports.WellKnownAreaTags = { }; function startup(cb) { - return cleanUpTempSessionItems(cb); + async.series( + [ + (callback) => { + return cleanUpTempSessionItems(callback); + }, + (callback) => { + getAreaStats( (err, stats) => { + if(!err) { + StatLog.setNonPersistentSystemStat(SysProps.FileBaseAreaStats, stats); + } + + return callback(null); + }); + } + ], + err => { + return cb(err); + } + ); } function isInternalArea(areaTag) { diff --git a/core/fse.js b/core/fse.js index 9ecf3bdd..d294f10b 100644 --- a/core/fse.js +++ b/core/fse.js @@ -25,6 +25,7 @@ const Config = require('./config.js').get; const { getAddressedToInfo } = require('./mail_util.js'); const Events = require('./events.js'); const UserProps = require('./user_property.js'); +const SysProps = require('./system_property.js'); // deps const async = require('async'); @@ -470,7 +471,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul ); } - updateUserStats(cb) { + updateUserAndSystemStats(cb) { if(Message.isPrivateAreaTag(this.message.areaTag)) { Events.emit(Events.getSystemEvents().UserSendMail, { user : this.client.user }); if(cb) { @@ -480,6 +481,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } Events.emit(Events.getSystemEvents().UserPostMessage, { user : this.client.user, areaTag : this.message.areaTag }); + + StatLog.incrementNonPersistentSystemStat(SysProps.MessageTotalCount, 1); + StatLog.incrementNonPersistentSystemStat(SysProps.MessagesToday, 1); return StatLog.incrementUserStat(this.client.user, UserProps.MessagePostCount, 1, cb); } diff --git a/core/message.js b/core/message.js index 768cd116..df0804d5 100644 --- a/core/message.js +++ b/core/message.js @@ -239,7 +239,10 @@ module.exports = class Message { filter.toUserName filter.fromUserName filter.replyToMesageId - filter.newerThanTimestamp + + filter.newerThanTimestamp - may not be used with |date| + filter.date - moment object - may not be used with |newerThanTimestamp| + filter.newerThanMessageId filter.areaTag - note if you want by conf, send in all areas for a conf *filter.metaTuples - {category, name, value} @@ -356,7 +359,10 @@ module.exports = class Message { }); if(_.isString(filter.newerThanTimestamp) && filter.newerThanTimestamp.length > 0) { + // :TODO: should be using "localtime" here? appendWhereClause(`DATETIME(m.modified_timestamp) > DATETIME("${filter.newerThanTimestamp}", "+1 seconds")`); + } else if(moment.isMoment(filter.date)) { + appendWhereClause(`DATE(m.modified_timestamp, "localtime") = DATE("${filter.date.format('YYYY-MM-DD')}")`); } if(_.isNumber(filter.newerThanMessageId)) { diff --git a/core/message_area.js b/core/message_area.js index a465b235..7f4993ec 100644 --- a/core/message_area.js +++ b/core/message_area.js @@ -9,12 +9,17 @@ const Log = require('./logger.js').log; const msgNetRecord = require('./msg_network.js').recordMessage; const sortAreasOrConfs = require('./conf_area_util.js').sortAreasOrConfs; const UserProps = require('./user_property.js'); +const StatLog = require('./stat_log.js'); +const SysProps = require('./system_property.js'); // deps const async = require('async'); const _ = require('lodash'); const assert = require('assert'); +const moment = require('moment'); +exports.startup = startup; +exports.shutdown = shutdown; exports.getAvailableMessageConferences = getAvailableMessageConferences; exports.getSortedAvailMessageConferences = getSortedAvailMessageConferences; exports.getAvailableMessageAreasByConfTag = getAvailableMessageAreasByConfTag; @@ -35,6 +40,37 @@ exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId; exports.persistMessage = persistMessage; exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent; +function startup(cb) { + // by default, private messages are NOT included + async.series( + [ + (callback) => { + Message.findMessages( { resultType : 'count' }, (err, count) => { + if(count) { + StatLog.setNonPersistentSystemStat(SysProps.MessageTotalCount, count); + } + return callback(err); + }); + }, + (callback) => { + Message.findMessages( { resultType : 'count', date : moment() }, (err, count) => { + if(count) { + StatLog.setNonPersistentSystemStat(SysProps.MessagesToday, count); + } + return callback(err); + }); + } + ], + err => { + return cb(err); + } + ); +} + +function shutdown(cb) { + return cb(null); +} + function getAvailableMessageConferences(client, options) { options = options || { includeSystemInternal : false }; diff --git a/core/msg_area_post_fse.js b/core/msg_area_post_fse.js index 123ce13c..613cee04 100644 --- a/core/msg_area_post_fse.js +++ b/core/msg_area_post_fse.js @@ -38,7 +38,7 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule { return persistMessage(msg, callback); }, function updateStats(callback) { - self.updateUserStats(callback); + self.updateUserAndSystemStats(callback); } ], function complete(err) { diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 39310559..76b34fd5 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -155,13 +155,14 @@ const PREDEFINED_MCI_GENERATORS = { // // Date/Time // - // :TODO: change to CD for 'Current Date' DT : function date(client) { return moment().format(client.currentTheme.helpers.getDateFormat()); }, CT : function time(client) { return moment().format(client.currentTheme.helpers.getTimeFormat()) ;}, // // OS/System Info // + // https://github.com/nodejs/node-v0.x-archive/issues/25769 + // OS : function operatingSystem() { return { linux : 'Linux', @@ -169,6 +170,9 @@ const PREDEFINED_MCI_GENERATORS = { win32 : 'Windows', sunos : 'SunOS', freebsd : 'FreeBSD', + android : 'Android', + openbsd : 'OpenBSD', + aix : 'IBM AIX', }[os.platform()] || os.type(); }, @@ -227,14 +231,16 @@ const PREDEFINED_MCI_GENERATORS = { const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); return formatByteSize(totalBytes, true); // true=withAbbr }, + PT : function messagesPostedToday() { // Obv/2 + return sysStatAsString(SysProps.MessagesToday, 0); + }, + TP : function totalMessagesOnSystem() { // Obv/2 + return sysStatAsString(SysProps.MessageTotalCount, 0); + }, - // :TODO: PT - Messages posted *today* (Obv/2) - // -> Include FTN/etc. // :TODO: NT - New users today (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) - // -> Include FTN/etc. // :TODO: LC - name of last caller to system (Obv/2) // :TODO: TZ - Average *system* post/call ratio (iNiQUiTY) diff --git a/core/scanner_tossers/ftn_bso.js b/core/scanner_tossers/ftn_bso.js index 3292588d..36f0d769 100644 --- a/core/scanner_tossers/ftn_bso.js +++ b/core/scanner_tossers/ftn_bso.js @@ -21,6 +21,8 @@ const copyFileWithCollisionHandling = require('../file_util.js').copyFileWit const getAreaStorageDirectoryByTag = require('../file_base_area.js').getAreaStorageDirectoryByTag; const isValidStorageTag = require('../file_base_area.js').isValidStorageTag; const User = require('../user.js'); +const StatLog = require('../stat_log.js'); +const SysProps = require('../system_property.js'); // deps const moment = require('moment'); @@ -1261,12 +1263,12 @@ function FTNMessageScanTossModule() { } } - // we do this after such that error cases can be preseved above + // we do this after such that error cases can be preserved above if(lookupName !== message.toUserName) { message.toUserName = localUserName; } - // set the meta information - used elsehwere for retrieval + // set the meta information - used elsewhere for retrieval message.meta.System[Message.SystemMetaNames.LocalToUserID] = localToUserId; return callback(null); }); @@ -1277,6 +1279,10 @@ function FTNMessageScanTossModule() { // save to disc message.persist(err => { + if(!message.isPrivate()) { + StatLog.incrementNonPersistentSystemStat(SysProps.MessageTotalCount, 1); + StatLog.incrementNonPersistentSystemStat(SysProps.MessagesToday, 1); + } return callback(err); }); } diff --git a/core/stat_log.js b/core/stat_log.js index 805198bd..b97ea417 100644 --- a/core/stat_log.js +++ b/core/stat_log.js @@ -253,7 +253,7 @@ class StatLog { if(filter.date) { filter.date = moment(filter.date); - sql += ` AND DATE(timestamp) = DATE("${filter.date.format('YYYY-MM-DD')}")`; + sql += ` AND DATE(timestamp, "localtime") = DATE("${filter.date.format('YYYY-MM-DD')}")`; } if('count' !== filter.resultType) { diff --git a/core/system_property.js b/core/system_property.js index 52830a06..28250f20 100644 --- a/core/system_property.js +++ b/core/system_property.js @@ -15,6 +15,9 @@ module.exports = { FileUlTotalCount : 'ul_total_count', FileUlTotalBytes : 'ul_total_bytes', + MessageTotalCount : 'message_post_total_count', // total non-private messages on the system; non-persistent + MessagesToday : 'message_post_today', // non-private messages posted/imported today; non-persistent + // begin +op non-persistent... SysOpUsername : 'sysop_username', SysOpRealName : 'sysop_real_name', From 098c24e48a22ce11e62e0c23d533a5cf123e239b Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 21:29:35 -0700 Subject: [PATCH 07/12] Fix D/L stats --- core/system_property.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/system_property.js b/core/system_property.js index 28250f20..ca3cf7cd 100644 --- a/core/system_property.js +++ b/core/system_property.js @@ -14,6 +14,8 @@ module.exports = { FileBaseAreaStats : 'file_base_area_stats', // object - see file_base_area.js::getAreaStats FileUlTotalCount : 'ul_total_count', FileUlTotalBytes : 'ul_total_bytes', + FileDlTotalCount : 'dl_total_count', + FileDlTotalBytes : 'dl_total_bytes', MessageTotalCount : 'message_post_total_count', // total non-private messages on the system; non-persistent MessagesToday : 'message_post_today', // non-private messages posted/imported today; non-persistent From 6d2c0959766ef5e26a3c30c83a908e4402b9b43c Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 22:01:14 -0700 Subject: [PATCH 08/12] Fix cb --- core/user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/user.js b/core/user.js index 307dbacc..0121133a 100644 --- a/core/user.js +++ b/core/user.js @@ -473,7 +473,9 @@ module.exports = class User { return this.removeProperty(name, next); }, err => { - return cb(err); + if(cb) { + return cb(err); + } }); } From 965c7b9ed1fc3ef8578ddf42333251141319c003 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 22:59:26 -0700 Subject: [PATCH 09/12] Update MCI docs --- docs/art/mci.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/art/mci.md b/docs/art/mci.md index 4f351dad..e365f393 100644 --- a/docs/art/mci.md +++ b/docs/art/mci.md @@ -72,6 +72,8 @@ for a full listing. Many codes attempt to pay homage to Oblivion/2, iNiQUiTY, et | `SO` | Total downloaded amount, system wide (formatted to appropriate bytes/megs/etc.) | | `SU` | Total uploads, system wide | | `SP` | Total uploaded amount, system wide (formatted to appropriate bytes/megs/etc.) | +| `TP` | Total messages posted/imported to the system *currently* | +| `PT` | Total messages posted/imported to the system *today* | Some additional special case codes also exist: From e1862e6916af70a478c734549ebc4504af9a045a Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 23:22:09 -0700 Subject: [PATCH 10/12] Improve ACS & uploads docs around FB --- docs/filebase/acs.md | 16 +++++++++++----- docs/filebase/uploads.md | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/docs/filebase/acs.md b/docs/filebase/acs.md index 63884c71..618c1843 100644 --- a/docs/filebase/acs.md +++ b/docs/filebase/acs.md @@ -3,9 +3,10 @@ layout: page title: ACS --- ## File Base ACS -[ACS Codes](/docs/configuration/acs.md) may be used to control acess to File Base areas by specifying an `acs` string in a file area's definition. If no `acs` is supplied in a file area definition, the following defaults apply to an area: -* `read` (list, download, etc.): `GM[users]` -* `write` (upload): `GM[sysops]` +[ACS Codes](/docs/configuration/acs.md) may be used to control access to File Base areas by specifying an `acs` string in a file area's definition. If no `acs` is supplied in a file area definition, the following defaults apply to an area: +* `read` : `GM[users]`: List/view the area and it's contents. +* `write` : `GM[sysops]`: Upload. +* `download` : `GM[users]`: Download. To override read and/or write ACS, supply a valid `acs` member. @@ -18,8 +19,13 @@ areas: { desc: Oldschool PC/DOS storageTags: [ "retro_pc", "retro_pc_bbs" ] acs: { - write: GM[users] + // only users of the "l33t" group or those who have + // uploaded 10+ files can download from here... + download: GM[l33t]|UP10 } } } -``` \ No newline at end of file +``` + +## See Also +[Access Condition System (ACS)](/docs/configuration/acs.md) diff --git a/docs/filebase/uploads.md b/docs/filebase/uploads.md index 4b1e9c38..8e1e2530 100644 --- a/docs/filebase/uploads.md +++ b/docs/filebase/uploads.md @@ -2,7 +2,24 @@ layout: page title: Uploads --- +## Uploads +The default ACS for file areas areas in ENiGMA½ is to allow read (viewing of the area), and downloads for users while only permitting SysOps to write (upload). See [File Base ACS](acs.md) for more information. -Note that `storageTags` may contain *1:n* storage tag references. +To allow uploads to a particular area, change the ACS level for `write`. For example: +```hjson +uploads: { + name: Uploads + desc: User Uploads + storageTags: [ + "uploads" + ] + acs: { + write: GM[users] + } +} +```` + +:information_source: Remember that uploads in a particular area are stored **using the first storage tag defined in that area.** + +:information_source: Any ACS checks are allowed. See [ACS](/docs/acs.md) -**Uploads in a particular area are stored in the first storage tag defined in an area.** From 555326771a32305926d1a7222b152f9feed43a1f Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 27 Nov 2018 23:36:45 -0700 Subject: [PATCH 11/12] About File Areas updates --- docs/filebase/index.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/filebase/index.md b/docs/filebase/index.md index c225338e..6557d7cc 100644 --- a/docs/filebase/index.md +++ b/docs/filebase/index.md @@ -2,18 +2,21 @@ layout: page title: About File Areas --- -## A Different Approach -ENiGMA½ has strayed away from the old familure setup here and instead takes a more modern approach: -* [Gazelle](https://whatcd.github.io/Gazelle/) inspired system for searching & browsing files -* No File Conferences (just areas!) -* File Areas are still around but should generally be used less. Instead, files can have one or more tags. Think things like `dos.retro`, `pc.warez`, `games`, etc. -* Temporary web (http:// or https://) download links in additional to standard X/Y/Z protocol support -* Users can star rate files & search/filter by ratings -* Concept of user defined filters +## About File Areas -## Other bells and whistles -* A given area can span one to many physical storage locations -* Upload processor can extract and use `FILE_ID.DIZ`/`DESC.SDI`, for standard descriptions as well as `README.TXT`, `*.NFO`, and so on for longer descriptions -* Upload processor also attempts release year estimation by scanning prementioned description file(s) -* Fast indexed Full Text Search (FTS) -* Duplicates validated by SHA-256 \ No newline at end of file +### A Different Approach +ENiGMA½ has strayed away from the old familiar setup here and instead takes a more modern approach: +* [Gazelle](https://whatcd.github.io/Gazelle/) inspired system for searching & browsing files. +* No conferences (just areas!) +* File areas are still around but should *generally* be used less. Instead, files can have one or more tags. Think things like `dos.retro`, `pc.warez`, `games`, etc. + +### Other bells and whistles +* Temporary web (http:// or https://) download links in additional to standard X/Y/Z protocol support. Batch downloads of many files can be downloaded as a single ZIP archive. +* Users can star rate files & search/filter by ratings +* Concept of user defined filters/searches including the ability to save them. For example, "Latest Artscene Releases". +* A given area can span one to many physical storage locations. +* Upload processor can extract and use `FILE_ID.DIZ`/`DESC.SDI`, for standard descriptions as well as `README.TXT`, `*.NFO`, and so on for longer descriptions. The processor also attempts release year estimation by scanning aforementioned description file(s). +* Fast indexed Full Text Search (FTS) across descriptions and filenames. +* Duplicates are checked for by SHA-256. +* Support for many archive and file formats. External utilities can easily be added to the configuration to extend for additional formats. +* Much, much more! From 6335483a6739834b8518c6691aff10f23279c259 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Wed, 28 Nov 2018 18:55:48 -0700 Subject: [PATCH 12/12] About File Areas updates...again --- docs/filebase/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/filebase/index.md b/docs/filebase/index.md index 6557d7cc..d5dac134 100644 --- a/docs/filebase/index.md +++ b/docs/filebase/index.md @@ -12,11 +12,11 @@ ENiGMA½ has strayed away from the old familiar setup here and instead takes a m ### Other bells and whistles * Temporary web (http:// or https://) download links in additional to standard X/Y/Z protocol support. Batch downloads of many files can be downloaded as a single ZIP archive. -* Users can star rate files & search/filter by ratings -* Concept of user defined filters/searches including the ability to save them. For example, "Latest Artscene Releases". +* Users can rate files & search/filter by ratings. +* Users can also create and save their own filters for later use such as "Latest Artscene Releases" or "C64 SIDs". * A given area can span one to many physical storage locations. * Upload processor can extract and use `FILE_ID.DIZ`/`DESC.SDI`, for standard descriptions as well as `README.TXT`, `*.NFO`, and so on for longer descriptions. The processor also attempts release year estimation by scanning aforementioned description file(s). -* Fast indexed Full Text Search (FTS) across descriptions and filenames. -* Duplicates are checked for by SHA-256. +* Fast indexed [Full Text Search (FTS)](https://sqlite.org/fts3.html) across descriptions and filenames. +* Duplicates are checked for by cryptographically secure [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hashes. * Support for many archive and file formats. External utilities can easily be added to the configuration to extend for additional formats. * Much, much more!