automatic actor creation on finger, user lookup. followers collection endpoint populated from inbox activity side effects
This commit is contained in:
parent
0a3c758ad7
commit
e4c150b840
5 changed files with 71 additions and 49 deletions
|
@ -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'},
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue