automatic actor creation on finger, user lookup. followers collection endpoint populated from inbox activity side effects

This commit is contained in:
Will Murphy 2019-09-16 20:58:32 -05:00
parent 0a3c758ad7
commit e4c150b840
5 changed files with 71 additions and 49 deletions

View file

@ -2,14 +2,20 @@ const utils = require('../utils')
const crypto = require('crypto') const crypto = require('crypto')
module.exports = async function dbSetup (db, domain) { module.exports = async function dbSetup (db, domain) {
// inbox
await db.collection('streams').createIndex({ await db.collection('streams').createIndex({
_target: 1, _target: 1,
_id: -1, _id: -1,
}) })
// outbox
await db.collection('streams').createIndex({ await db.collection('streams').createIndex({
actor: 1, actor: 1,
_id: -1, _id: -1,
}) })
// object lookup
await db.collection('objects').createIndex({
id: 1
})
const dummyUser = await utils.createLocalActor('dummy', 'Person') const dummyUser = await utils.createLocalActor('dummy', 'Person')
await db.collection('objects').findOneAndReplace( await db.collection('objects').findOneAndReplace(
{preferredUsername: 'dummy'}, {preferredUsername: 'dummy'},

View file

@ -10,15 +10,8 @@ router.get('/:name', async function (req, res) {
return res.status(400).send('Bad request.'); return res.status(400).send('Bad request.');
} }
else { else {
let objs = req.app.get('objs');
let db = req.app.get('db') let db = req.app.get('db')
const id = utils.userNameToIRI(name) const user = await utils.getOrCreateActor(name, db)
console.log(`looking up '${id}'`)
const user = await db.collection('objects')
.find({type: 'Person', id: id})
.limit(1)
.project({_id: 0, _meta: 0})
.next()
if (user) { if (user) {
return res.json(toJSONLD(user)) return res.json(toJSONLD(user))
} }
@ -26,33 +19,28 @@ router.get('/:name', async function (req, res) {
} }
}); });
// router.get('/:name/followers', function (req, res) { router.get('/:name/followers', function (req, res) {
// let name = req.params.name; let name = req.params.name;
// if (!name) { if (!name) {
// return res.status(400).send('Bad request.'); return res.status(400).send('Bad request.');
// } }
// else { const db = req.app.get('db')
// let db = req.app.get('db'); db.collection('streams')
// let domain = req.app.get('domain'); .find({
// let result = db.prepare('select followers from accounts where name = ?').get(`${name}@${domain}`); type: 'Follow',
// console.log(result); _target: name,
// result.followers = result.followers || '[]'; 'object.id': utils.usernameToIRI(name)
// let followers = JSON.parse(result.followers); })
// let followersCollection = { .project({_id: 0, actor: 1})
// "type":"OrderedCollection", .toArray()
// "totalItems":followers.length, .then(follows => {
// "id":`https://${domain}/u/${name}/followers`, const followers = follows.map(utils.actorFromActivity)
// "first": { return res.json(utils.arrayToCollection(followers))
// "type":"OrderedCollectionPage", })
// "totalItems":followers.length, .catch(err => {
// "partOf":`https://${domain}/u/${name}/followers`, console.log(err)
// "orderedItems": followers, return res.status(500).send()
// "id":`https://${domain}/u/${name}/followers?page=1` })
// }, });
// "@context":["https://www.w3.org/ns/activitystreams"]
// };
// res.json(toJSONLD(followersCollection));
// }
// });
module.exports = router; module.exports = router;

View file

@ -13,12 +13,7 @@ router.get('/', function (req, res) {
return res.status(400).send('Requested user is not from this domain') return res.status(400).send('Requested user is not from this domain')
} }
let db = req.app.get('db'); let db = req.app.get('db');
const userId = utils.userNameToIRI(acct[1]); utils.getOrCreateActor(acct[1], db)
db.collection('objects')
.find({id: userId})
.limit(1)
.project({_id: 0})
.next()
.then(result => { .then(result => {
if (!result) { if (!result) {
return res.status(404).send(`${acct[1]}@${acct[2]} not found`) return res.status(404).send(`${acct[1]}@${acct[2]} not found`)
@ -29,7 +24,7 @@ router.get('/', function (req, res) {
{ {
'rel': 'self', 'rel': 'self',
'type': 'application/activity+json', 'type': 'application/activity+json',
'href': userId 'href': result.id
} }
] ]
} }

View file

@ -1,9 +1,10 @@
const crypto = require('crypto') const crypto = require('crypto')
const {promisify} = require('util') const {promisify} = require('util')
const {ASContext} = require('./consts') const {ASContext} = require('./consts')
module.exports.validators = require('./validators');
const config = require('../config.json') const config = require('../config.json')
module.exports.validators = require('./validators');
function isObject(value) { function isObject(value) {
return value && typeof value === 'object' && value.constructor === Object return value && typeof value === 'object' && value.constructor === Object
} }
@ -34,13 +35,13 @@ module.exports.arrayToCollection = function (arr, ordered) {
} }
} }
function userNameToIRI (user) { function usernameToIRI (user) {
return `https://${config.DOMAIN}/u/${user}` return `https://${config.DOMAIN}/u/${user}`
} }
module.exports.userNameToIRI = userNameToIRI module.exports.usernameToIRI = usernameToIRI
const generateKeyPairPromise = promisify(crypto.generateKeyPair) const generateKeyPairPromise = promisify(crypto.generateKeyPair)
module.exports.createLocalActor = function (name, type) { function createLocalActor (name, type) {
return generateKeyPairPromise('rsa', { return generateKeyPairPromise('rsa', {
modulusLength: 4096, modulusLength: 4096,
publicKeyEncoding: { publicKeyEncoding: {
@ -54,7 +55,7 @@ module.exports.createLocalActor = function (name, type) {
passphrase: config.KEYPASS passphrase: config.KEYPASS
} }
}).then(pair => { }).then(pair => {
const actorBase = userNameToIRI(name); const actorBase = usernameToIRI(name);
return { return {
_meta: { _meta: {
privateKey: pair.privateKey, privateKey: pair.privateKey,
@ -81,3 +82,35 @@ module.exports.createLocalActor = function (name, type) {
} }
}) })
} }
module.exports.createLocalActor = createLocalActor
async function getOrCreateActor(preferredUsername, db) {
const id = usernameToIRI(preferredUsername)
let user = await db.collection('objects')
.find({id: id})
.limit(1)
.project({_id: 0, _meta: 0})
.next()
if (user) {
return user
}
// auto create groups whenever an unknown actor is referenced
user = await createLocalActor(preferredUsername, 'Group')
await db.collection('objects').insertOne(user)
// only executed on success
delete user._meta
delete user._id
return user
}
module.exports.getOrCreateActor = getOrCreateActor
function actorFromActivity (activity) {
if (Object.prototype.toString.call(activity.actor) === '[object String]') {
return activity.actor
}
if (activity.actor.type === 'Link') {
return activity.actor.href
}
return activity.actor.id
}
module.exports.actorFromActivity = actorFromActivity

View file

@ -28,7 +28,7 @@ module.exports.outboxActivity = function outboxActivity (req, res, next) {
if (!validateObject(req.body)) { if (!validateObject(req.body)) {
return res.status(400).send('Invalid activity') return res.status(400).send('Invalid activity')
} }
const newID = ObjectId() const newID = new ObjectId()
req.body = { req.body = {
_id: newID, _id: newID,
'@context': ASContext, '@context': ASContext,