Implement remote follow
This commit is contained in:
		
							parent
							
								
									cba73d6bc1
								
							
						
					
					
						commit
						dc529711ce
					
				
					 11 changed files with 114 additions and 32 deletions
				
			
		| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
import * as mongo from 'mongodb';
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
import Notification from '../../../models/notification';
 | 
					import Notification from '../models/notification';
 | 
				
			||||||
import Mute from '../../../models/mute';
 | 
					import Mute from '../models/mute';
 | 
				
			||||||
import event from '../../../common/event';
 | 
					import event from './event';
 | 
				
			||||||
import { pack } from '../../../models/notification';
 | 
					import { pack } from '../models/notification';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (
 | 
					export default (
 | 
				
			||||||
	notifiee: mongo.ObjectID,
 | 
						notifiee: mongo.ObjectID,
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/common/remote/activitypub/renderer/follow.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/common/remote/activitypub/renderer/follow.ts
									
										
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,7 @@ export default async (value, usernameLower, hostLower, acctLower) => {
 | 
				
			||||||
				id: object.publicKey.id,
 | 
									id: object.publicKey.id,
 | 
				
			||||||
				publicKeyPem: object.publicKey.publicKeyPem
 | 
									publicKeyPem: object.publicKey.publicKeyPem
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								inbox: object.inbox,
 | 
				
			||||||
			uri: object.id,
 | 
								uri: object.id,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
const WebFinger = require('webfinger.js');
 | 
					const WebFinger = require('webfinger.js');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const webFinger = new WebFinger({});
 | 
					const webFinger = new WebFinger({ tls_only: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ILink = {
 | 
					type ILink = {
 | 
				
			||||||
  href: string;
 | 
					  href: string;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@ export type ILocalAccount = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type IRemoteAccount = {
 | 
					export type IRemoteAccount = {
 | 
				
			||||||
 | 
						inbox: string;
 | 
				
			||||||
	uri: string;
 | 
						uri: string;
 | 
				
			||||||
	publicKey: {
 | 
						publicKey: {
 | 
				
			||||||
		id: string;
 | 
							id: string;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										89
									
								
								src/processor/http/follow.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/processor/http/follow.ts
									
										
									
									
									
										Normal 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);
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
 | 
					import follow from './follow';
 | 
				
			||||||
import performActivityPub from './perform-activitypub';
 | 
					import performActivityPub from './perform-activitypub';
 | 
				
			||||||
import reportGitHubFailure from './report-github-failure';
 | 
					import reportGitHubFailure from './report-github-failure';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handlers = {
 | 
					const handlers = {
 | 
				
			||||||
 | 
					  follow,
 | 
				
			||||||
  performActivityPub,
 | 
					  performActivityPub,
 | 
				
			||||||
  reportGitHubFailure,
 | 
					  reportGitHubFailure,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,10 +2,9 @@
 | 
				
			||||||
 * Module dependencies
 | 
					 * Module dependencies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import User, { pack as packUser } from '../../../../models/user';
 | 
					import User from '../../../../models/user';
 | 
				
			||||||
import Following from '../../../../models/following';
 | 
					import Following from '../../../../models/following';
 | 
				
			||||||
import notify from '../../common/notify';
 | 
					import queue from '../../../../queue';
 | 
				
			||||||
import event from '../../../../common/event';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Follow a user
 | 
					 * Follow a user
 | 
				
			||||||
| 
						 | 
					@ -52,33 +51,15 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create following
 | 
						// Create following
 | 
				
			||||||
	await Following.insert({
 | 
						const { _id } = await Following.insert({
 | 
				
			||||||
		createdAt: new Date(),
 | 
							createdAt: new Date(),
 | 
				
			||||||
		followerId: follower._id,
 | 
							followerId: follower._id,
 | 
				
			||||||
		followeeId: followee._id
 | 
							followeeId: followee._id
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						queue.create('http', { type: 'follow', following: _id }).save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Send response
 | 
						// Send response
 | 
				
			||||||
	res();
 | 
						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');
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,9 +14,9 @@ import DriveFile from '../../../../models/drive-file';
 | 
				
			||||||
import Watching from '../../../../models/post-watching';
 | 
					import Watching from '../../../../models/post-watching';
 | 
				
			||||||
import ChannelWatching from '../../../../models/channel-watching';
 | 
					import ChannelWatching from '../../../../models/channel-watching';
 | 
				
			||||||
import { pack } from '../../../../models/post';
 | 
					import { pack } from '../../../../models/post';
 | 
				
			||||||
import notify from '../../common/notify';
 | 
					 | 
				
			||||||
import watch from '../../common/watch-post';
 | 
					import watch from '../../common/watch-post';
 | 
				
			||||||
import event, { pushSw, publishChannelStream } from '../../../../common/event';
 | 
					import event, { pushSw, publishChannelStream } from '../../../../common/event';
 | 
				
			||||||
 | 
					import notify from '../../../../common/notify';
 | 
				
			||||||
import getAcct from '../../../../common/user/get-acct';
 | 
					import getAcct from '../../../../common/user/get-acct';
 | 
				
			||||||
import parseAcct from '../../../../common/user/parse-acct';
 | 
					import parseAcct from '../../../../common/user/parse-acct';
 | 
				
			||||||
import config from '../../../../conf';
 | 
					import config from '../../../../conf';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,9 @@ import $ from 'cafy';
 | 
				
			||||||
import Vote from '../../../../../models/poll-vote';
 | 
					import Vote from '../../../../../models/poll-vote';
 | 
				
			||||||
import Post from '../../../../../models/post';
 | 
					import Post from '../../../../../models/post';
 | 
				
			||||||
import Watching from '../../../../../models/post-watching';
 | 
					import Watching from '../../../../../models/post-watching';
 | 
				
			||||||
import notify from '../../../common/notify';
 | 
					 | 
				
			||||||
import watch from '../../../common/watch-post';
 | 
					import watch from '../../../common/watch-post';
 | 
				
			||||||
import { publishPostStream } from '../../../../../common/event';
 | 
					import { publishPostStream } from '../../../../../common/event';
 | 
				
			||||||
 | 
					import notify from '../../../../../common/notify';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Vote poll of a post
 | 
					 * Vote poll of a post
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,9 @@ import Reaction from '../../../../../models/post-reaction';
 | 
				
			||||||
import Post, { pack as packPost } from '../../../../../models/post';
 | 
					import Post, { pack as packPost } from '../../../../../models/post';
 | 
				
			||||||
import { pack as packUser } from '../../../../../models/user';
 | 
					import { pack as packUser } from '../../../../../models/user';
 | 
				
			||||||
import Watching from '../../../../../models/post-watching';
 | 
					import Watching from '../../../../../models/post-watching';
 | 
				
			||||||
import notify from '../../../common/notify';
 | 
					 | 
				
			||||||
import watch from '../../../common/watch-post';
 | 
					import watch from '../../../common/watch-post';
 | 
				
			||||||
import { publishPostStream, pushSw } from '../../../../../common/event';
 | 
					import { publishPostStream, pushSw } from '../../../../../common/event';
 | 
				
			||||||
 | 
					import notify from '../../../../../common/notify';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * React to a post
 | 
					 * React to a post
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue