diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js index 74ce768a745c..959c8a541948 100644 --- a/src/activitypub/helpers.js +++ b/src/activitypub/helpers.js @@ -3,9 +3,11 @@ const request = require('request-promise-native'); const { generateKeyPairSync } = require('crypto'); const winston = require('winston'); +const nconf = require('nconf'); const db = require('../database'); const ttl = require('../cache/ttl'); +const user = require('../user'); const webfingerCache = ttl({ ttl: 1000 * 60 * 60 * 24 }); // 24 hours @@ -65,3 +67,13 @@ Helpers.generateKeys = async (uid) => { await db.setObject(`uid:${uid}:keys`, { publicKey, privateKey }); return { publicKey, privateKey }; }; + +Helpers.resolveLocalUid = async (id) => { + const [slug, host] = id.split('@'); + + if (id.indexOf('@') === -1 || host !== nconf.get('url_parsed').host) { + throw new Error('[[activitypub:invalid-id]]'); + } + + return await user.getUidByUserslug(slug); +}; diff --git a/src/activitypub/index.js b/src/activitypub/index.js index 3bb2177a3cd1..a5b8d8c11b07 100644 --- a/src/activitypub/index.js +++ b/src/activitypub/index.js @@ -12,6 +12,7 @@ const actorCache = ttl({ ttl: 1000 * 60 * 60 * 24 }); // 24 hours const ActivityPub = module.exports; ActivityPub.helpers = require('./helpers'); +ActivityPub.local = require('./local'); ActivityPub.getActor = async (id) => { if (actorCache.has(id)) { diff --git a/src/activitypub/local.js b/src/activitypub/local.js new file mode 100644 index 000000000000..f91ca4f7a838 --- /dev/null +++ b/src/activitypub/local.js @@ -0,0 +1,45 @@ +'use strict'; + +const db = require('../database'); +const user = require('../user'); + +const helpers = require('./helpers'); + +const local = module.exports; + +local.follow = async (actorId, objectId) => { + // Sanity checks + const actorExists = await helpers.query(actorId); + if (!actorId || !actorExists) { + throw new Error('[[error:invalid-uid]]'); // should probably be AP specific + } + + if (!objectId) { + throw new Error('[[error:invalid-uid]]'); // should probably be AP specific + } + const localUid = await helpers.resolveLocalUid(objectId); + + console.log(actorId, localUid); + + // The below logic matches toggleFollow() in src/user/follow.js + // const isFollowing = await user.isFollowing(actorId, localUid); + // if (isFollowing) { + // throw new Error('[[error:already-following]]'); + // } + // const now = Date.now(); + // await Promise.all([ + // db.sortedSetAddBulk([ + // [`following:${actorId}`, now, localUid], + // [`followers:${localUid}`, now, actorId], + // ]), + // ]); + + // const [followingCount, followerCount] = await Promise.all([ + // db.sortedSetCard(`following:${actorId}`), + // db.sortedSetCard(`followers:${localUid}`), + // ]); + // await Promise.all([ + // user.setUserField(actorId, 'followingCount', followingCount), + // user.setUserField(localUid, 'followerCount', followerCount), + // ]); +}; diff --git a/src/controllers/activitypub/index.js b/src/controllers/activitypub/index.js index f7e7dc460a79..0a13c21ee13a 100644 --- a/src/controllers/activitypub/index.js +++ b/src/controllers/activitypub/index.js @@ -101,6 +101,12 @@ Controller.getInbox = async (req, res) => { Controller.postInbox = async (req, res) => { console.log('received', req.body); + switch (req.body.type) { + case 'Follow': { + await activitypub.local.follow(req.body.actor.name, req.body.object.name); + } + } + res.sendStatus(201); };