Additionl of WIP NodeInfo2 support, fix content-type for Actor images

This commit is contained in:
Bryan Ashby 2023-01-30 12:30:36 -07:00
parent 3bdce81bdb
commit 35b7c00d11
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
5 changed files with 143 additions and 3 deletions

View File

@ -26,6 +26,7 @@ const mimeTypes = require('mime-types');
const { getJson } = require('../http_util.js');
const { getISOTimestampString } = require('../database.js');
const moment = require('moment');
const paths = require('path');
const ActorCacheTTL = moment.duration(1, 'day');
@ -82,7 +83,8 @@ module.exports = class Actor extends ActivityPubObject {
const addImage = (o, t) => {
const url = userSettings[t];
if (url) {
const mt = mimeTypes.contentType(url);
const fn = paths.basename(url);
const mt = mimeTypes.contentType(fn);
if (mt) {
o[t] = {
mediaType: mt,

View File

@ -137,6 +137,9 @@ function getUserProfileTemplatedBody(
if (isString(v)) {
return v ? encode(v) : '';
} else {
if (isNaN(v)) {
return '';
}
return v ? v : 0;
}
};
@ -171,7 +174,7 @@ function getUserProfileTemplatedBody(
ACHIEVEMENT_COUNT: user.getPropertyAsNumber(
UserProps.AchievementTotalCount
),
ACHIEVEMENT_POINTS: user.getProperty(
ACHIEVEMENT_POINTS: user.getPropertyAsNumber(
UserProps.AchievementTotalPoints
),
BOARDNAME: Config().general.boardName,

View File

@ -306,6 +306,9 @@ module.exports = () => {
systemGeneral: {
enabled: true,
},
nodeInfo2: {
enabled: true,
},
},
resetPassword: {

View File

@ -0,0 +1,123 @@
const WebHandlerModule = require('../../../web_handler_module');
const { Errors } = require('../../../enig_error');
const EngiAssert = require('../../../enigma_assert');
const Config = require('../../../config').get;
const packageJson = require('../../../../package.json');
const StatLog = require('../../../stat_log');
const SysProps = require('../../../system_property');
const SysLogKeys = require('../../../system_log');
// deps
const moment = require('moment');
const async = require('async');
exports.moduleInfo = {
name: 'NodeInfo2',
desc: 'A NodeInfo2 Handler implementing https://github.com/jaywink/nodeinfo2',
author: 'NuSkooler',
packageName: 'codes.l33t.enigma.web.handler.nodeinfo2',
};
exports.getModule = class NodeInfo2WebHadnler extends WebHandlerModule {
constructor() {
super();
}
init(webServer, cb) {
// we rely on the web server
this.webServer = webServer;
EngiAssert(webServer, 'NodeInfo2 Web Handler init without webServer');
this.log = webServer.logger().child({ webHandler: 'NodeInfo2' });
const domain = this.webServer.getDomain();
if (!domain) {
return cb(Errors.UnexpectedState('Web server does not have "domain" set'));
}
this.webServer.addRoute({
method: 'GET',
path: /^\/\.well-known\/x-nodeinfo2$/,
handler: this._nodeInfo2Handler.bind(this),
});
return cb(null);
}
_nodeInfo2Handler(req, resp) {
this.log.info({ url: req.url }, 'Serving NodeInfo2 request');
this._getNodeInfo(nodeInfo => {
const body = JSON.stringify(nodeInfo);
const headers = {
'Content-Type': 'application/json',
'Content-Length': body.length,
};
resp.writeHead(200, headers);
return resp.end(body);
});
}
_getNodeInfo(cb) {
// https://github.com/jaywink/nodeinfo2/tree/master/schemas/1.0
const config = Config();
const nodeInfo = {
version: '1.0',
server: {
baseUrl: this.webServer.baseUrl(),
name: config.general.boardName,
software: 'ENiGMA½ Bulletin Board Software',
version: packageJson.version,
},
// :TODO: Only list what's enabled
protocols: ['telnet', 'ssh', 'gopher', 'nntp', 'ws', 'activitypub'],
services: {
inbound: [],
outbound: [''],
},
openRegistrations: !config.general.closedSystem,
usage: {
users: {
total: StatLog.getSystemStatNum(SysProps.TotalUserCount) || 1,
// others fetched dynamically below
},
// :TODO: pop with local message
// select count() from message_meta where meta_name='local_from_user_id';
localPosts: 0,
},
};
const setActive = (since, name, next) => {
const filter = {
logName: SysLogKeys.UserLoginHistory,
resultType: 'count',
dateNewer: moment().subtract(moment.duration(since, 'days')),
};
StatLog.findSystemLogEntries(filter, (err, count) => {
if (!err) {
nodeInfo.usage[name] = count;
}
return next(null);
});
};
async.series(
[
callback => {
return setActive(180, 'activeHalfyear', callback);
},
callback => {
return setActive(30, 'activeMonth', callback);
},
callback => {
return setActive(7, 'activeWeek', callback);
},
],
() => {
return cb(nodeInfo);
}
);
}
};

View File

@ -8,6 +8,7 @@ const SysProps = require('./system_property.js');
const UserProps = require('./user_property');
const Message = require('./message');
const { getActiveConnections, AllConnections } = require('./client_connections');
const Log = require('./logger').log;
// deps
const _ = require('lodash');
@ -349,6 +350,7 @@ class StatLog {
// - resultType: 'obj' | 'count' (default='obj')
// - limit: Limit returned results
// - date: exact date to filter against
// - dateNewer: Entries newer than this value
// - order: 'timestamp' | 'timestamp_asc' | 'timestamp_desc' | 'random'
// (default='timestamp')
//
@ -402,7 +404,9 @@ class StatLog {
this.setNonPersistentSystemStat(SysProps.SystemLoadStats, loadStats);
})
.catch(err => {
// :TODO: log me
if (err) {
Log.err({ error: err.message }, 'Error refreshing system stats');
}
});
}
@ -509,6 +513,11 @@ class StatLog {
sql += ` AND DATE(timestamp, "localtime") = DATE("${filter.date.format(
'YYYY-MM-DD'
)}")`;
} else if (filter.dateNewer) {
filter.dateNewer = moment(filter.dateNewer);
sql += ` AND DATE(timestamp, "localtime") > DATE("${filter.dateNewer.format(
'YYYY-MM-DD'
)}")`;
}
if ('count' !== filter.resultType) {