Hand back a profile for self
This commit is contained in:
parent
d4f74447ec
commit
5055337eff
|
@ -3,10 +3,32 @@ const User = require('./user');
|
|||
const { Errors } = require('./enig_error');
|
||||
const UserProps = require('./user_property');
|
||||
|
||||
// deps
|
||||
const _ = require('lodash');
|
||||
const mimeTypes = require('mime-types');
|
||||
const waterfall = require('async/waterfall');
|
||||
const fs = require('graceful-fs');
|
||||
const paths = require('path');
|
||||
const moment = require('moment');
|
||||
|
||||
exports.makeUserUrl = makeUserUrl;
|
||||
exports.webFingerProfileUrl = webFingerProfileUrl;
|
||||
exports.selfUrl = selfUrl;
|
||||
exports.userFromAccount = userFromAccount;
|
||||
exports.getUserProfileTemplatedBody = getUserProfileTemplatedBody;
|
||||
|
||||
// :TODO: more info in default
|
||||
// this profile template is the *default* for both WebFinger
|
||||
// profiles and 'self' requests without the
|
||||
// Accept: application/activity+json headers present
|
||||
exports.DefaultProfileTemplate = `
|
||||
User information for: %USERNAME%
|
||||
|
||||
Real Name: %REAL_NAME%
|
||||
Login Count: %LOGIN_COUNT%
|
||||
Affiliations: %AFFILIATIONS%
|
||||
Achievement Points: %ACHIEVEMENT_POINTS%
|
||||
`;
|
||||
|
||||
function makeUserUrl(webServer, user, relPrefix) {
|
||||
return webServer.buildUrl(
|
||||
|
@ -45,3 +67,73 @@ function userFromAccount(accountName, cb) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getUserProfileTemplatedBody(
|
||||
templateFile,
|
||||
user,
|
||||
defaultTemplate,
|
||||
defaultContentType,
|
||||
cb
|
||||
) {
|
||||
const Log = require('./logger').log;
|
||||
const Config = require('./config').get;
|
||||
|
||||
waterfall(
|
||||
[
|
||||
callback => {
|
||||
return fs.readFile(templateFile || '', 'utf8', (err, template) => {
|
||||
return callback(null, template);
|
||||
});
|
||||
},
|
||||
(template, callback) => {
|
||||
if (!template) {
|
||||
if (templateFile) {
|
||||
Log.warn(`Failed to load profile template "${templateFile}"`);
|
||||
}
|
||||
return callback(null, defaultTemplate, defaultContentType);
|
||||
}
|
||||
|
||||
const contentType = mimeTypes.contentType(paths.basename(templateFile));
|
||||
return callback(null, template, contentType);
|
||||
},
|
||||
(template, contentType, callback) => {
|
||||
const up = (p, na = 'N/A') => {
|
||||
return user.getProperty(p) || na;
|
||||
};
|
||||
|
||||
let birthDate = up(UserProps.Birthdate);
|
||||
if (moment.isDate(birthDate)) {
|
||||
birthDate = moment(birthDate);
|
||||
}
|
||||
|
||||
const varMap = {
|
||||
USERNAME: user.username,
|
||||
REAL_NAME: user.getSanitizedName('real'),
|
||||
SEX: up(UserProps.Sex),
|
||||
BIRTHDATE: birthDate,
|
||||
AGE: user.getAge(),
|
||||
LOCATION: up(UserProps.Location),
|
||||
AFFILIATIONS: up(UserProps.Affiliations),
|
||||
EMAIL: up(UserProps.EmailAddress),
|
||||
WEB_ADDRESS: up(UserProps.WebAddress),
|
||||
ACCOUNT_CREATED: moment(user.getProperty(UserProps.AccountCreated)),
|
||||
LAST_LOGIN: moment(user.getProperty(UserProps.LastLoginTs)),
|
||||
LOGIN_COUNT: up(UserProps.LoginCount),
|
||||
ACHIEVEMENT_COUNT: up(UserProps.AchievementTotalCount, '0'),
|
||||
ACHIEVEMENT_POINTS: up(UserProps.AchievementTotalPoints, '0'),
|
||||
BOARDNAME: Config().general.boardName,
|
||||
};
|
||||
|
||||
let body = template;
|
||||
_.each(varMap, (val, varName) => {
|
||||
body = body.replace(new RegExp(`%${varName}%`, 'g'), val);
|
||||
});
|
||||
|
||||
return callback(null, body, contentType);
|
||||
},
|
||||
],
|
||||
(err, data, contentType) => {
|
||||
return cb(err, data, contentType);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -317,8 +317,8 @@ exports.getModule = class WebServerModule extends ServerModule {
|
|||
req.url.substr(req.url.lastIndexOf('/', 1)),
|
||||
tryFile
|
||||
);
|
||||
const filePath = this.resolveStaticPath(fileName);
|
||||
|
||||
const filePath = this.resolveStaticPath(fileName);
|
||||
fs.stat(filePath, (err, stats) => {
|
||||
if (err || !stats.isFile()) {
|
||||
return nextTryFile(null, false);
|
||||
|
|
|
@ -4,9 +4,15 @@ const {
|
|||
webFingerProfileUrl,
|
||||
selfUrl,
|
||||
userFromAccount,
|
||||
getUserProfileTemplatedBody,
|
||||
DefaultProfileTemplate,
|
||||
} = require('../../../activitypub_util');
|
||||
const UserProps = require('../../../user_property');
|
||||
const { Errors } = require('../../../enig_error');
|
||||
const Config = require('../../../config').get;
|
||||
|
||||
// deps
|
||||
const _ = require('lodash');
|
||||
|
||||
exports.moduleInfo = {
|
||||
name: 'ActivityPub',
|
||||
|
@ -36,15 +42,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
}
|
||||
|
||||
_selfUrlRequestHandler(req, resp) {
|
||||
const accept = req.headers['accept'] || '*/*';
|
||||
if (accept === 'application/activity+json') {
|
||||
return this._selfAsActorHandler(req, resp);
|
||||
}
|
||||
|
||||
return this._standardSelfHandler(req, resp);
|
||||
}
|
||||
|
||||
_selfAsActorHandler(req, resp) {
|
||||
const url = new URL(req.url, `https://${req.headers.host}`);
|
||||
const accountName = url.pathname.substring(url.pathname.lastIndexOf('/') + 1);
|
||||
|
||||
|
@ -53,6 +50,16 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
return this._notFound(resp);
|
||||
}
|
||||
|
||||
const accept = req.headers['accept'] || '*/*';
|
||||
if (accept === 'application/activity+json') {
|
||||
return this._selfAsActorHandler(user, req, resp);
|
||||
}
|
||||
|
||||
return this._standardSelfHandler(user, req, resp);
|
||||
});
|
||||
}
|
||||
|
||||
_selfAsActorHandler(user, req, resp) {
|
||||
const body = JSON.stringify({
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
|
@ -83,14 +90,39 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
|
||||
resp.writeHead(200, headers);
|
||||
return resp.end(body);
|
||||
});
|
||||
}
|
||||
|
||||
_standardSelfHandler(req, resp) {
|
||||
// :TODO: this should also be their profile page?! Perhaps that should also be shared...
|
||||
_standardSelfHandler(user, req, resp) {
|
||||
let templateFile = _.get(
|
||||
Config(),
|
||||
'contentServers.web.handlers.activityPub.selfTemplate'
|
||||
);
|
||||
if (templateFile) {
|
||||
templateFile = this.webServer.resolveTemplatePath(templateFile);
|
||||
}
|
||||
|
||||
// we'll fall back to the same default profile info as the WebFinger profile
|
||||
getUserProfileTemplatedBody(
|
||||
templateFile,
|
||||
user,
|
||||
DefaultProfileTemplate,
|
||||
'text/plain',
|
||||
(err, body, contentType) => {
|
||||
if (err) {
|
||||
return this._notFound(resp);
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': body.length,
|
||||
};
|
||||
|
||||
resp.writeHead(200, headers);
|
||||
return resp.end(body);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_notFound(resp) {
|
||||
this.webServer.respondWithError(
|
||||
resp,
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
const WebHandlerModule = require('../../../web_handler_module');
|
||||
const Config = require('../../../config').get;
|
||||
const { Errors, ErrorReasons } = require('../../../enig_error');
|
||||
const { Errors } = require('../../../enig_error');
|
||||
const { WellKnownLocations } = require('../web');
|
||||
const {
|
||||
selfUrl,
|
||||
webFingerProfileUrl,
|
||||
userFromAccount,
|
||||
getUserProfileTemplatedBody,
|
||||
DefaultProfileTemplate,
|
||||
} = require('../../../activitypub_util');
|
||||
|
||||
const _ = require('lodash');
|
||||
const User = require('../../../user');
|
||||
const UserProps = require('../../../user_property');
|
||||
const Log = require('../../../logger').log;
|
||||
const mimeTypes = require('mime-types');
|
||||
|
||||
const fs = require('graceful-fs');
|
||||
const paths = require('path');
|
||||
const moment = require('moment');
|
||||
|
||||
exports.moduleInfo = {
|
||||
name: 'WebFinger',
|
||||
|
@ -25,16 +20,6 @@ exports.moduleInfo = {
|
|||
packageName: 'codes.l33t.enigma.web.handler.webfinger',
|
||||
};
|
||||
|
||||
// :TODO: more info in default
|
||||
const DefaultProfileTemplate = `
|
||||
User information for: %USERNAME%
|
||||
|
||||
Real Name: %REAL_NAME%
|
||||
Login Count: %LOGIN_COUNT%
|
||||
Affiliations: %AFFILIATIONS%
|
||||
Achievement Points: %ACHIEVEMENT_POINTS%
|
||||
`;
|
||||
|
||||
//
|
||||
// WebFinger: https://www.rfc-editor.org/rfc/rfc7033
|
||||
//
|
||||
|
@ -117,51 +102,6 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
|
|||
return this._notFound(resp);
|
||||
}
|
||||
|
||||
this._getProfileTemplate((template, mimeType) => {
|
||||
const up = (p, na = 'N/A') => {
|
||||
return user.getProperty(p) || na;
|
||||
};
|
||||
|
||||
let birthDate = up(UserProps.Birthdate);
|
||||
if (moment.isDate(birthDate)) {
|
||||
birthDate = moment(birthDate);
|
||||
}
|
||||
|
||||
const varMap = {
|
||||
USERNAME: user.username,
|
||||
REAL_NAME: user.getSanitizedName('real'),
|
||||
SEX: up(UserProps.Sex),
|
||||
BIRTHDATE: birthDate,
|
||||
AGE: user.getAge(),
|
||||
LOCATION: up(UserProps.Location),
|
||||
AFFILIATIONS: up(UserProps.Affiliations),
|
||||
EMAIL: up(UserProps.EmailAddress),
|
||||
WEB_ADDRESS: up(UserProps.WebAddress),
|
||||
ACCOUNT_CREATED: moment(user.getProperty(UserProps.AccountCreated)),
|
||||
LAST_LOGIN: moment(user.getProperty(UserProps.LastLoginTs)),
|
||||
LOGIN_COUNT: up(UserProps.LoginCount),
|
||||
ACHIEVEMENT_COUNT: up(UserProps.AchievementTotalCount, '0'),
|
||||
ACHIEVEMENT_POINTS: up(UserProps.AchievementTotalPoints, '0'),
|
||||
BOARDNAME: Config().general.boardName,
|
||||
};
|
||||
|
||||
let body = template;
|
||||
_.each(varMap, (val, varName) => {
|
||||
body = body.replace(new RegExp(`%${varName}%`, 'g'), val);
|
||||
});
|
||||
|
||||
const headers = {
|
||||
'Content-Type': mimeType,
|
||||
'Content-Length': body.length,
|
||||
};
|
||||
|
||||
resp.writeHead(200, headers);
|
||||
return resp.end(body);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_getProfileTemplate(cb) {
|
||||
let templateFile = _.get(
|
||||
Config(),
|
||||
'contentServers.web.handlers.webFinger.profileTemplate'
|
||||
|
@ -169,18 +109,26 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
|
|||
if (templateFile) {
|
||||
templateFile = this.webServer.resolveTemplatePath(templateFile);
|
||||
}
|
||||
fs.readFile(templateFile || '', 'utf8', (err, data) => {
|
||||
|
||||
getUserProfileTemplatedBody(
|
||||
templateFile,
|
||||
user,
|
||||
DefaultProfileTemplate,
|
||||
'text/plain',
|
||||
(err, body, contentType) => {
|
||||
if (err) {
|
||||
if (templateFile) {
|
||||
Log.warn(
|
||||
{ error: err.message },
|
||||
`Failed to load profile template "${templateFile}"`
|
||||
);
|
||||
return this._notFound(resp);
|
||||
}
|
||||
|
||||
return cb(DefaultProfileTemplate, 'text/plain');
|
||||
const headers = {
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': body.length,
|
||||
};
|
||||
|
||||
resp.writeHead(200, headers);
|
||||
return resp.end(body);
|
||||
}
|
||||
return cb(data, mimeTypes.contentType(paths.basename(templateFile)));
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue