Implement remote follow

This commit is contained in:
Akihiko Odaki 2018-04-01 19:43:26 +09:00
parent 92b3501c6b
commit dedd5bbdba
11 changed files with 114 additions and 32 deletions

View File

@ -1,8 +1,8 @@
import * as mongo from 'mongodb';
import Notification from '../../../models/notification';
import Mute from '../../../models/mute';
import event from '../../../common/event';
import { pack } from '../../../models/notification';
import Notification from '../models/notification';
import Mute from '../models/mute';
import event from './event';
import { pack } from '../models/notification';
export default (
notifiee: mongo.ObjectID,

View File

@ -0,0 +1,8 @@
import config from '../../../../conf';
import { IRemoteAccount } from '../../../../models/user';
export default ({ username }, { account }) => ({
type: 'Follow',
actor: `${config.url}/@${username}`,
object: (account as IRemoteAccount).uri
});

View File

@ -66,6 +66,7 @@ export default async (value, usernameLower, hostLower, acctLower) => {
id: object.publicKey.id,
publicKeyPem: object.publicKey.publicKeyPem
},
inbox: object.inbox,
uri: object.id,
},
});

View File

@ -1,6 +1,6 @@
const WebFinger = require('webfinger.js');
const webFinger = new WebFinger({});
const webFinger = new WebFinger({ tls_only: false });
type ILink = {
href: string;

View File

@ -70,6 +70,7 @@ export type ILocalAccount = {
};
export type IRemoteAccount = {
inbox: string;
uri: string;
publicKey: {
id: string;

View File

@ -0,0 +1,89 @@
import { request } from 'https';
import { sign } from 'http-signature';
import { URL } from 'url';
import User, { ILocalAccount, IRemoteAccount, pack as packUser } from '../../models/user';
import Following from '../../models/following';
import event from '../../common/event';
import notify from '../../common/notify';
import context from '../../common/remote/activitypub/renderer/context';
import render from '../../common/remote/activitypub/renderer/follow';
import config from '../../conf';
export default ({ data }, done) => Following.findOne({ _id: data.following }).then(({ followerId, followeeId }) => {
const promisedFollower = User.findOne({ _id: followerId });
const promisedFollowee = User.findOne({ _id: followeeId });
return Promise.all([
// Increment following count
User.update(followerId, {
$inc: {
followingCount: 1
}
}),
// Increment followers count
User.update({ _id: followeeId }, {
$inc: {
followersCount: 1
}
}),
// Notify
promisedFollowee.then(followee => followee.host === null ?
notify(followeeId, followerId, 'follow') : null),
// Publish follow event
Promise.all([promisedFollower, promisedFollowee]).then(([follower, followee]) => {
const followerEvent = packUser(followee, follower)
.then(packed => event(follower._id, 'follow', packed));
let followeeEvent;
if (followee.host === null) {
followeeEvent = packUser(follower, followee)
.then(packed => event(followee._id, 'followed', packed));
} else {
followeeEvent = new Promise((resolve, reject) => {
const {
protocol,
hostname,
port,
pathname,
search
} = new URL(followee.account as IRemoteAccount).inbox);
const req = request({
protocol,
hostname,
port,
method: 'POST',
path: pathname + search,
}, res => {
res.on('close', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(res);
}
});
res.on('data', () => {});
res.on('error', reject);
});
sign(req, {
authorizationHeaderName: 'Signature',
key: (follower.account as ILocalAccount).keypair,
keyId: `acct:${follower.username}@${config.host}`
});
const rendered = render(follower, followee);
rendered['@context'] = context;
req.end(JSON.stringify(rendered));
});
}
return Promise.all([followerEvent, followeeEvent]);
})
]);
}).then(done, done);

View File

@ -1,7 +1,9 @@
import follow from './follow';
import performActivityPub from './perform-activitypub';
import reportGitHubFailure from './report-github-failure';
const handlers = {
follow,
performActivityPub,
reportGitHubFailure,
};

View File

@ -2,10 +2,9 @@
* Module dependencies
*/
import $ from 'cafy';
import User, { pack as packUser } from '../../../../models/user';
import User from '../../../../models/user';
import Following from '../../../../models/following';
import notify from '../../common/notify';
import event from '../../../../common/event';
import queue from '../../../../queue';
/**
* Follow a user
@ -52,33 +51,15 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}
// Create following
await Following.insert({
const { _id } = await Following.insert({
createdAt: new Date(),
followerId: follower._id,
followeeId: followee._id
});
queue.create('http', { type: 'follow', following: _id }).save();
// Send response
res();
// Increment following count
User.update(follower._id, {
$inc: {
followingCount: 1
}
});
// Increment followers count
User.update({ _id: followee._id }, {
$inc: {
followersCount: 1
}
});
// Publish follow event
event(follower._id, 'follow', await packUser(followee, follower));
event(followee._id, 'followed', await packUser(follower, followee));
// Notify
notify(followee._id, follower._id, 'follow');
});

View File

@ -14,9 +14,9 @@ import DriveFile from '../../../../models/drive-file';
import Watching from '../../../../models/post-watching';
import ChannelWatching from '../../../../models/channel-watching';
import { pack } from '../../../../models/post';
import notify from '../../common/notify';
import watch from '../../common/watch-post';
import event, { pushSw, publishChannelStream } from '../../../../common/event';
import notify from '../../../../common/notify';
import getAcct from '../../../../common/user/get-acct';
import parseAcct from '../../../../common/user/parse-acct';
import config from '../../../../conf';

View File

@ -5,9 +5,9 @@ import $ from 'cafy';
import Vote from '../../../../../models/poll-vote';
import Post from '../../../../../models/post';
import Watching from '../../../../../models/post-watching';
import notify from '../../../common/notify';
import watch from '../../../common/watch-post';
import { publishPostStream } from '../../../../../common/event';
import notify from '../../../../../common/notify';
/**
* Vote poll of a post

View File

@ -6,9 +6,9 @@ import Reaction from '../../../../../models/post-reaction';
import Post, { pack as packPost } from '../../../../../models/post';
import { pack as packUser } from '../../../../../models/user';
import Watching from '../../../../../models/post-watching';
import notify from '../../../common/notify';
import watch from '../../../common/watch-post';
import { publishPostStream, pushSw } from '../../../../../common/event';
import notify from '../../../../../common/notify';
/**
* React to a post