feat: ability to view federated profiles via url manipulation

This commit is contained in:
Julian Lam
2023-05-29 17:42:44 -04:00
parent 7e1dac39ea
commit a05b674e27
4 changed files with 95 additions and 2 deletions

View File

@@ -0,0 +1,32 @@
'use strict';
const request = require('request-promise-native');
const Helpers = module.exports;
Helpers.query = async (id) => {
const [username, hostname] = id.split('@');
if (!username || !hostname) {
return false;
}
// Make a webfinger query to retrieve routing information
const response = await request(`https://${hostname}/.well-known/webfinger?resource=acct:${id}`, {
simple: false,
resolveWithFullResponse: true,
json: true,
});
if (response.statusCode !== 200 || !response.body.hasOwnProperty('links')) {
return false;
}
// Parse links to find actor endpoint
let actorUri = response.body.links.filter(link => link.type === 'application/activity+json' && link.rel === 'self');
if (actorUri.length) {
actorUri = actorUri.pop();
({ href: actorUri } = actorUri);
}
return { username, hostname, actorUri };
};

View File

@@ -3,11 +3,32 @@
const { generateKeyPairSync } = require('crypto'); const { generateKeyPairSync } = require('crypto');
const winston = require('winston'); const winston = require('winston');
const request = require('request-promise-native');
const db = require('./database'); const db = require('../database');
const helpers = require('./helpers');
const ActivityPub = module.exports; const ActivityPub = module.exports;
ActivityPub.getActor = async (id) => {
const { hostname, actorUri: uri } = await helpers.query(id);
if (!uri) {
return false;
}
const actor = await request({
uri,
headers: {
Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
},
json: true,
});
actor.hostname = hostname;
return actor;
};
ActivityPub.getPublicKey = async (uid) => { ActivityPub.getPublicKey = async (uid) => {
let publicKey; let publicKey;

View File

@@ -10,12 +10,18 @@ const categories = require('../../categories');
const plugins = require('../../plugins'); const plugins = require('../../plugins');
const privileges = require('../../privileges'); const privileges = require('../../privileges');
const accountHelpers = require('./helpers'); const accountHelpers = require('./helpers');
const { getActor } = require('../../activitypub');
const helpers = require('../helpers'); const helpers = require('../helpers');
const slugify = require('../../slugify');
const utils = require('../../utils'); const utils = require('../../utils');
const profileController = module.exports; const profileController = module.exports;
profileController.get = async function (req, res, next) { profileController.get = async function (req, res, next) {
if (res.locals.uid === -2) {
return profileController.getFederated(req, res, next);
}
const lowercaseSlug = req.params.userslug.toLowerCase(); const lowercaseSlug = req.params.userslug.toLowerCase();
if (req.params.userslug !== lowercaseSlug) { if (req.params.userslug !== lowercaseSlug) {
@@ -58,6 +64,32 @@ profileController.get = async function (req, res, next) {
res.render('account/profile', userData); res.render('account/profile', userData);
}; };
profileController.getFederated = async function (req, res, next) {
const { userslug: uid } = req.params;
const actor = await getActor(uid);
if (!actor) {
return next();
}
// console.log(actor);
const { preferredUsername, published, icon, image, name, summary, hostname } = actor;
const payload = {
uid,
username: `${preferredUsername}@${hostname}`,
userslug: slugify(`${preferredUsername}@${hostname}`),
fullname: name,
joindate: new Date(published).getTime(),
picture: typeof icon === 'string' ? icon : icon.url,
uploadedpicture: typeof icon === 'string' ? icon : icon.url,
'cover:url': typeof image === 'string' ? image : image.url,
'cover:position': '50% 50%',
aboutme: summary,
aboutmeParsed: summary,
};
res.render('account/profile', payload);
};
async function incrementProfileViews(req, userData) { async function incrementProfileViews(req, userData) {
if (req.uid >= 1) { if (req.uid >= 1) {
req.session.uids_viewed = req.session.uids_viewed || {}; req.session.uids_viewed = req.session.uids_viewed || {};

View File

@@ -172,7 +172,15 @@ async function expose(exposedField, method, field, req, res, next) {
if (!req.params.hasOwnProperty(field)) { if (!req.params.hasOwnProperty(field)) {
return next(); return next();
} }
const value = await method(String(req.params[field]).toLowerCase()); const param = String(req.params[field]).toLowerCase();
// potential hostname — ActivityPub
if (param.indexOf('@') !== -1) {
res.locals[exposedField] = -2;
return next();
}
const value = await method(param);
if (!value) { if (!value) {
next('route'); next('route');
return; return;